From 49fa0d82a40345342966e810b44429aec0480ef3 Mon Sep 17 00:00:00 2001 From: web <candymxq888@outlook.com> Date: 星期二, 01 七月 2025 17:00:18 +0800 Subject: [PATCH] fix:修改.gitgnore文件,移除打包文件 --- uni_modules/lime-shared/getCurrentPage/index.ts | 9 uni_modules/uni-section/components/uni-section/uni-section.vue | 167 uni_modules/uni-transition/readme.md | 11 uni_modules/lime-shared/cloneDeep/uvue.ts | 17 uni_modules/uni-fab/changelog.md | 25 uni_modules/uni-load-more/components/uni-load-more/i18n/index.js | 8 uni_modules/uni-ui/components/uni-ui/uni-ui.vue | 7 uni_modules/uni-popup/changelog.md | 92 uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js | 287 uni_modules/uni-segmented-control/components/uni-segmented-control/uni-segmented-control.vue | 146 uni_modules/lime-shared/animation/uvue.uts | 117 uni_modules/uni-table/components/uni-tr/table-checkbox.vue | 179 uni_modules/uni-search-bar/components/uni-search-bar/i18n/en.json | 4 uni_modules/lime-shared/isBrowser/index.ts | 8 uni_modules/uni-combox/changelog.md | 17 uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue | 431 uni_modules/uni-collapse/changelog.md | 38 uni_modules/lime-shared/components/lime-shared/lime-shared.vue | 156 uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue | 323 uni_modules/uni-list/components/uni-list/uni-refresh.wxs | 87 uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hant.json | 12 uni_modules/uni-tag/package.json | 84 uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hant.json | 6 uni_modules/uni-easyinput/readme.md | 11 uni_modules/uni-scss/styles/setting/_text.scss | 24 uni_modules/lime-shared/animation/vue.ts | 123 uni_modules/lime-shared/toBoolean/index.ts | 40 uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json | 22 uni_modules/uni-countdown/readme.md | 10 uni_modules/uni-icons/readme.md | 8 uni_modules/uni-drawer/changelog.md | 13 uni_modules/uni-swipe-action/changelog.md | 47 uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue | 177 uni_modules/uni-notice-bar/changelog.md | 20 uni_modules/uni-fav/package.json | 89 uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hans.json | 4 uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue | 143 uni_modules/uni-swipe-action/components/uni-swipe-action-item/render.js | 270 uni_modules/uni-list/readme.md | 346 uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hans.json | 6 uni_modules/lime-shared/getStyleStr/index.ts | 54 uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue | 325 uni_modules/uni-table/readme.md | 13 uni_modules/lime-shared/exif/index.ts | 9 uni_modules/lime-shared/animation/index.ts | 10 uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js | 8 uni_modules/lime-shared/getCurrentPage/uvue.uts | 5 uni_modules/uni-pagination/package.json | 83 uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list.vue | 367 uni_modules/uni-swipe-action/package.json | 84 uni_modules/lime-shared/exif/uvue.ts | 7 uni_modules/uni-scss/styles/setting/_variables.scss | 146 uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.uvue | 140 uni_modules/uni-pagination/components/uni-pagination/i18n/index.js | 12 uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue | 849 + uni_modules/lime-shared/selectElement/index.uts | 275 uni_modules/uni-countdown/package.json | 84 uni_modules/lime-shared/getRect/index.ts | 9 uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue | 562 uni_modules/lime-shared/addUnit/index.ts | 42 uni_modules/uni-load-more/readme.md | 14 uni_modules/uni-scss/readme.md | 4 uni_modules/lime-shared/floatMul/index.ts | 44 uni_modules/lime-qrcode/components/l-qrcode/qrcode.js | 6 uni_modules/uni-dateformat/changelog.md | 10 uni_modules/lime-shared/exif/vue.ts | 1057 + uni_modules/uni-file-picker/package.json | 84 uni_modules/uni-popup/package.json | 88 uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue | 309 uni_modules/uni-row/readme.md | 10 uni_modules/lime-shared/getLocalFilePath/index.ts | 62 uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hant.json | 4 uni_modules/uni-list/components/uni-list-chat/uni-list-chat.scss | 58 uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json | 4 uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue | 567 uni_modules/uni-transition/components/uni-transition/uni-transition.vue | 286 uni_modules/uni-list/components/uni-list/uni-refresh.vue | 65 uni_modules/uni-easyinput/changelog.md | 115 uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json | 7 uni_modules/uni-scss/styles/setting/_border.scss | 3 uni_modules/lime-shared/camelCase/index.ts | 21 uni_modules/uni-dateformat/components/uni-dateformat/uni-dateformat.vue | 88 uni_modules/lime-shared/getRect/vue.ts | 117 uni_modules/lime-shared/platform/index.ts | 34 uni_modules/lime-shared/range/index.ts | 36 uni_modules/uni-pagination/changelog.md | 27 uni_modules/lime-shared/animation/bezier.ts | 82 uni_modules/lime-shared/unitConvert/index.ts | 79 uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue | 232 uni_modules/uni-link/readme.md | 11 uni_modules/uni-fav/changelog.md | 19 uni_modules/uni-badge/changelog.md | 33 uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpalipay.js | 195 uni_modules/uni-goods-nav/components/uni-goods-nav/uni-goods-nav.vue | 229 uni_modules/uni-link/package.json | 87 uni_modules/uni-ui/readme.md | 247 uni_modules/uni-fav/readme.md | 10 uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hant.json | 6 uni_modules/uni-data-picker/components/uni-data-picker/keypress.js | 45 uni_modules/lime-shared/vue/index.ts | 16 uni_modules/uni-easyinput/package.json | 88 uni_modules/uni-ui/package.json | 130 uni_modules/uni-card/readme.md | 12 uni_modules/uni-calendar/components/uni-calendar/i18n/index.js | 8 uni_modules/uni-icons/components/uni-icons/uniicons.ttf | 0 uni_modules/uni-forms/components/uni-forms/utils.js | 293 uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue | 676 uni_modules/uni-scss/styles/setting/_color.scss | 66 uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json | 5 uni_modules/uni-icons/changelog.md | 42 uni_modules/uni-popup/components/uni-popup/keypress.js | 45 uni_modules/lime-shared/debounce/uvue.ts | 36 uni_modules/uni-table/package.json | 84 uni_modules/lime-shared/getStyleStr/index_.uts | 39 uni_modules/uni-section/package.json | 87 uni_modules/uni-grid/package.json | 86 uni_modules/uni-popup/components/uni-popup/i18n/en.json | 7 uni_modules/uni-drawer/package.json | 87 uni_modules/uni-calendar/components/uni-calendar/calendar.js | 544 uni_modules/lime-shared/isObject/index.ts | 19 uni_modules/uni-countdown/changelog.md | 28 uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hans.json | 5 uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue | 1064 + uni_modules/uni-popup/components/uni-popup-dialog/keypress.js | 45 uni_modules/lime-shared/getClassStr/index.ts | 53 uni_modules/uni-data-checkbox/readme.md | 18 uni_modules/uni-swiper-dot/readme.md | 11 uni_modules/uni-list/components/uni-list/uni-list.vue | 123 uni_modules/uni-scss/styles/tools/functions.scss | 19 uni_modules/uni-data-picker/package.json | 91 uni_modules/uni-pagination/components/uni-pagination/i18n/fr.json | 5 uni_modules/lime-shared/fillZero/index.ts | 11 uni_modules/lime-shared/raf/vue.ts | 32 uni_modules/lime-shared/clamp/index.ts | 16 uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue | 316 uni_modules/lime-shared/cloneDeep/vue.ts | 103 uni_modules/lime-shared/cloneDeep/index.ts | 10 uni_modules/lime-shared/isPromise/index.ts | 22 uni_modules/uni-collapse/components/uni-collapse-item/uni-collapse-item.vue | 402 uni_modules/uni-list/components/uni-list-chat/uni-list-chat.vue | 593 uni_modules/uni-drawer/components/uni-drawer/keypress.js | 45 uni_modules/uni-goods-nav/readme.md | 10 uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json | 22 uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue | 357 uni_modules/uni-tag/changelog.md | 23 uni_modules/uni-icons/components/uni-icons/uniicons.css | 664 uni_modules/uni-list/package.json | 88 uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.uvue | 381 uni_modules/uni-scss/theme.scss | 31 uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/index.js | 8 uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list-item.vue | 144 uni_modules/lime-shared/animation/useTransition.ts | 98 uni_modules/uni-popup/components/uni-popup/uni-popup.uvue | 90 uni_modules/uni-number-box/package.json | 83 uni_modules/uni-row/components/uni-row/uni-row.vue | 190 uni_modules/lime-shared/index.ts | 43 uni_modules/uni-icons/components/uni-icons/uni-icons.vue | 110 uni_modules/uni-section/readme.md | 8 uni_modules/uni-data-checkbox/package.json | 84 uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.vue | 223 uni_modules/uni-countdown/components/uni-countdown/i18n/index.js | 8 uni_modules/uni-table/components/uni-th/uni-th.vue | 295 uni_modules/uni-calendar/readme.md | 103 uni_modules/uni-nav-bar/readme.md | 15 uni_modules/uni-countdown/components/uni-countdown/uni-countdown.vue | 276 uni_modules/uni-goods-nav/package.json | 88 uni_modules/uni-swiper-dot/components/uni-swiper-dot/uni-swiper-dot.vue | 218 uni_modules/uni-transition/changelog.md | 24 uni_modules/uni-icons/components/uni-icons/uni-icons.uvue | 91 uni_modules/uni-rate/package.json | 88 uni_modules/lime-qrcode/components/l-qrcode/useCanvas.ts | 78 uni_modules/lime-shared/hasOwn/index.ts | 9 uni_modules/uni-table/components/uni-thead/uni-thead.vue | 137 uni_modules/uni-countdown/components/uni-countdown/i18n/en.json | 6 uni_modules/uni-swiper-dot/package.json | 87 uni_modules/lime-shared/arrayBufferToFile/vue.ts | 63 uni_modules/uni-title/package.json | 88 uni_modules/uni-fab/package.json | 85 uni_modules/uni-collapse/package.json | 86 uni_modules/lime-shared/kebabCase/index.ts | 24 uni_modules/uni-row/package.json | 87 uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue | 292 uni_modules/uni-datetime-picker/readme.md | 21 uni_modules/uni-popup/components/uni-popup/popup.js | 26 uni_modules/uni-popup/readme.md | 17 uni_modules/uni-calendar/components/uni-calendar/i18n/en.json | 12 uni_modules/uni-group/components/uni-group/uni-group.vue | 134 uni_modules/uni-calendar/changelog.md | 30 uni_modules/uni-scss/changelog.md | 8 uni_modules/uni-table/i18n/zh-Hans.json | 9 uni_modules/lime-shared/capitalizedAmount/index.ts | 111 uni_modules/uni-data-select/changelog.md | 39 uni_modules/uni-calendar/components/uni-calendar/util.js | 360 uni_modules/lime-shared/floatDiv/index.ts | 45 uni_modules/uni-card/package.json | 90 uni_modules/uni-tag/readme.md | 13 uni_modules/lime-shared/createAnimation/type.ts | 25 uni_modules/uni-steps/readme.md | 13 uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue | 404 uni_modules/uni-scss/styles/index.scss | 7 uni_modules/lime-shared/debounce/vue.ts | 40 uni_modules/uni-steps/components/uni-steps/uni-steps.vue | 280 uni_modules/uni-list/components/uni-list-ad/uni-list-ad.vue | 107 uni_modules/uni-indexed-list/package.json | 89 uni_modules/uni-table/i18n/fr.json | 9 uni_modules/uni-table/i18n/index.js | 12 uni_modules/uni-swipe-action/readme.md | 11 uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue | 940 + uni_modules/uni-list/changelog.md | 46 uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json | 5 uni_modules/uni-icons/package.json | 89 uni_modules/uni-grid/changelog.md | 13 uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/en.json | 6 uni_modules/lime-shared/createAnimation/uvue.ts | 5 uni_modules/lime-shared/selectAllComponent/index.ts | 8 uni_modules/uni-scss/variables.scss | 62 uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue | 147 uni_modules/uni-datetime-picker/package.json | 88 uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue | 668 uni_modules/uni-indexed-list/readme.md | 11 uni_modules/uni-forms/package.json | 89 uni_modules/uni-nav-bar/changelog.md | 57 uni_modules/lime-qrcode/changelog.md | 37 uni_modules/lime-shared/hasOwn/uvue.ts | 39 uni_modules/uni-scss/styles/setting/_space.scss | 56 uni_modules/uni-scss/styles/setting/_styles.scss | 167 uni_modules/uni-load-more/changelog.md | 25 uni_modules/uni-steps/package.json | 87 uni_modules/uni-segmented-control/package.json | 85 uni_modules/uni-group/readme.md | 9 uni_modules/lime-shared/pathToBase64/index.ts | 9 uni_modules/lime-shared/package.json | 86 uni_modules/uni-combox/readme.md | 11 uni_modules/uni-link/changelog.md | 17 uni_modules/uni-table/components/uni-th/filter-dropdown.vue | 511 uni_modules/uni-tooltip/changelog.md | 16 uni_modules/uni-badge/package.json | 85 uni_modules/lime-qrcode/hybrid/html/index.html | 77 uni_modules/uni-table/i18n/zh-Hant.json | 9 uni_modules/uni-swipe-action/components/uni-swipe-action-item/uni-swipe-action-item.vue | 348 uni_modules/lime-shared/pathToBase64/uvue.uts | 17 uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpother.js | 260 uni_modules/uni-link/components/uni-link/uni-link.vue | 128 uni_modules/uni-data-picker/changelog.md | 79 uni_modules/lime-shared/canIUseCanvas2d/index.ts | 67 uni_modules/uni-table/i18n/en.json | 9 uni_modules/uni-scss/styles/setting/_radius.scss | 55 uni_modules/lime-shared/random/index.ts | 24 uni_modules/lime-shared/floatSub/index.ts | 32 uni_modules/uni-fab/readme.md | 9 uni_modules/uni-group/package.json | 87 uni_modules/uni-search-bar/changelog.md | 47 uni_modules/uni-file-picker/readme.md | 11 uni_modules/lime-shared/selectComponent/vue.ts | 149 uni_modules/uni-easyinput/components/uni-easyinput/common.js | 54 uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue | 30 uni_modules/lime-shared/selectAllComponent/uvue.uts | 39 uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json | 4 uni_modules/uni-search-bar/components/uni-search-bar/i18n/index.js | 8 uni_modules/lime-shared/throttle/index.ts | 77 uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue | 465 uni_modules/uni-forms/readme.md | 23 uni_modules/uni-list/components/uni-list-item/uni-list-item.vue | 534 uni_modules/lime-shared/isDef/index.ts | 23 uni_modules/uni-tag/components/uni-tag/uni-tag.vue | 252 uni_modules/uni-pagination/components/uni-pagination/i18n/es.json | 5 uni_modules/uni-search-bar/readme.md | 14 uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js | 421 uni_modules/lime-shared/base64ToPath/uvue.uts | 22 uni_modules/uni-section/changelog.md | 2 uni_modules/uni-drawer/readme.md | 10 uni_modules/uni-group/changelog.md | 16 uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json | 7 uni_modules/uni-pagination/readme.md | 11 uni_modules/uni-load-more/components/uni-load-more/i18n/en.json | 5 uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.uts | 692 .gitignore | 1 uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hant.json | 5 uni_modules/uni-table/changelog.md | 33 uni_modules/uni-swipe-action/components/uni-swipe-action-item/wx.wxs | 341 uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.uvue | 69 uni_modules/uni-pagination/components/uni-pagination/i18n/en.json | 5 uni_modules/uni-collapse/readme.md | 12 uni_modules/lime-shared/selectComponent/uvue.uts | 75 uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue | 108 uni_modules/lime-shared/floatAdd/index.ts | 36 uni_modules/lime-shared/toArray/index.ts | 21 uni_modules/uni-popup/components/uni-popup/i18n/index.js | 8 uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue | 187 uni_modules/uni-dateformat/components/uni-dateformat/date-format.js | 200 uni_modules/lime-shared/raf/index.ts | 10 uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpwxs.js | 84 uni_modules/uni-data-picker/readme.md | 22 uni_modules/uni-dateformat/package.json | 88 uni_modules/lime-shared/isNumeric/index.ts | 33 uni_modules/uni-tooltip/readme.md | 8 uni_modules/uni-file-picker/changelog.md | 81 uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue | 187 uni_modules/uni-ui/changelog.md | 578 uni_modules/lime-shared/createImage/index.ts | 71 uni_modules/uni-badge/components/uni-badge/uni-badge.vue | 268 uni_modules/uni-fab/components/uni-fab/uni-fab.vue | 491 uni_modules/lime-qrcode/hybrid/html/qrcode.min.js | 6 uni_modules/uni-calendar/package.json | 86 uni_modules/uni-title/components/uni-title/uni-title.vue | 171 uni_modules/uni-notice-bar/package.json | 87 uni_modules/uni-notice-bar/readme.md | 13 uni_modules/uni-card/changelog.md | 26 uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.vue | 79 uni_modules/uni-table/components/uni-td/uni-td.vue | 95 uni_modules/uni-swipe-action/components/uni-swipe-action-item/bindingx.js | 302 uni_modules/lime-qrcode/components/l-qrcode/props.ts | 36 uni_modules/lime-shared/isBase64/index.ts | 23 uni_modules/uni-data-select/readme.md | 8 uni_modules/lime-shared/isNumber/index.ts | 26 uni_modules/uni-fav/components/uni-fav/i18n/index.js | 8 uni_modules/uni-table/i18n/es.json | 9 uni_modules/uni-segmented-control/changelog.md | 15 uni_modules/lime-shared/debounce/index.ts | 10 uni_modules/lime-shared/createAnimation/index.ts | 9 uni_modules/lime-qrcode/components/l-qrcode/type.ts | 48 uni_modules/uni-load-more/package.json | 84 uni_modules/uni-rate/components/uni-rate/uni-rate.vue | 361 uni_modules/uni-grid/readme.md | 11 uni_modules/uni-fav/components/uni-fav/i18n/en.json | 4 uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hans.json | 12 uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue | 947 + uni_modules/uni-nav-bar/package.json | 87 uni_modules/lime-shared/animation/ease.ts | 3 uni_modules/uni-datetime-picker/changelog.md | 168 uni_modules/uni-table/components/uni-table/uni-table.vue | 460 uni_modules/uni-fav/components/uni-fav/uni-fav.vue | 161 uni_modules/uni-combox/package.json | 88 uni_modules/uni-row/components/uni-col/uni-col.vue | 317 uni_modules/lime-shared/arrayBufferToFile/index.ts | 8 uni_modules/uni-search-bar/package.json | 87 uni_modules/uni-dateformat/readme.md | 11 uni_modules/uni-popup/components/uni-popup/uni-popup.vue | 518 uni_modules/uni-steps/changelog.md | 18 uni_modules/uni-tooltip/package.json | 86 uni_modules/lime-shared/isEmpty/index.ts | 83 uni_modules/uni-forms/changelog.md | 100 uni_modules/lime-shared/getRect/uvue.uts | 16 uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue | 551 uni_modules/uni-swipe-action/components/uni-swipe-action-item/isPC.js | 12 uni_modules/uni-combox/components/uni-combox/uni-combox.vue | 284 uni_modules/uni-icons/components/uni-icons/uniicons_file_vue.js | 649 uni_modules/lime-shared/selectAllComponent/vue.ts | 151 uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js | 622 uni_modules/uni-grid/components/uni-grid/uni-grid.vue | 142 uni_modules/uni-rate/readme.md | 12 uni_modules/lime-shared/hasOwn/vue.ts | 30 uni_modules/lime-shared/readme.md | 445 uni_modules/uni-card/components/uni-card/uni-card.vue | 270 uni_modules/uni-transition/package.json | 85 uni_modules/lime-shared/pathToBase64/vue.ts | 121 uni_modules/lime-shared/isFunction/index.ts | 16 uni_modules/uni-swipe-action/components/uni-swipe-action/uni-swipe-action.vue | 60 uni_modules/uni-table/components/uni-tbody/uni-tbody.vue | 34 uni_modules/uni-icons/components/uni-icons/uniicons_file.ts | 664 uni_modules/lime-shared/getCurrentPage/vue.ts | 6 uni_modules/lime-qrcode/hybrid/html/uni.webview.1.5.3.js | 1 uni_modules/uni-grid/components/uni-grid-item/uni-grid-item.vue | 127 uni_modules/lime-shared/isString/index.ts | 19 uni_modules/lime-qrcode/readme.md | 152 uni_modules/lime-shared/changelog.md | 40 uni_modules/uni-transition/components/uni-transition/createAnimation.js | 131 uni_modules/uni-title/readme.md | 14 uni_modules/lime-shared/sleep/index.ts | 44 uni_modules/uni-forms/components/uni-forms/validate.js | 486 uni_modules/lime-shared/toNumber/index.ts | 28 uni_modules/lime-shared/closest/index.ts | 22 uni_modules/uni-file-picker/components/uni-file-picker/utils.js | 110 uni_modules/lime-shared/base64ToPath/vue.ts | 75 uni_modules/lime-shared/base64ToArrayBuffer/index.ts | 13 uni_modules/uni-goods-nav/changelog.md | 18 uni_modules/uni-segmented-control/readme.md | 13 uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.css | 76 uni_modules/uni-number-box/readme.md | 13 uni_modules/uni-badge/readme.md | 10 uni_modules/uni-data-select/package.json | 86 uni_modules/lime-qrcode/components/l-qrcode/utils.uts | 35 uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json | 22 uni_modules/lime-qrcode/package.json | 91 uni_modules/uni-forms/components/uni-forms/uni-forms.vue | 404 uni_modules/lime-shared/createAnimation/vue.ts | 148 uni_modules/lime-shared/raf/uvue.ts | 48 uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue | 183 uni_modules/uni-table/components/uni-tr/uni-tr.vue | 184 uni_modules/lime-shared/createCanvas/index.ts | 73 uni_modules/uni-scss/index.scss | 1 uni_modules/uni-rate/changelog.md | 25 uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.uvue | 235 uni_modules/lime-shared/arrayBufferToFile/uvue.uts | 10 uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hans.json | 6 uni_modules/uni-number-box/changelog.md | 39 uni_modules/uni-data-checkbox/changelog.md | 49 uni_modules/uni-scss/package.json | 82 uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue | 632 uni_modules/lime-shared/base64ToPath/index.ts | 9 uni_modules/lime-shared/selectComponent/index.ts | 7 uni_modules/uni-indexed-list/changelog.md | 17 uni_modules/uni-title/changelog.md | 10 uni_modules/uni-swiper-dot/changelog.md | 12 /dev/null | 28 uni_modules/uni-row/changelog.md | 10 406 files changed, 46,531 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index 3c3629e..8bdd202 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +unpackage/* \ No newline at end of file diff --git a/uni_modules/lime-qrcode/changelog.md b/uni_modules/lime-qrcode/changelog.md new file mode 100644 index 0000000..779ab02 --- /dev/null +++ b/uni_modules/lime-qrcode/changelog.md @@ -0,0 +1,37 @@ +## 0.1.8(2024-08-05) +- fix: 修复因shared升级导致微信小程序可能不生效的问题 +## 0.1.7(2024-05-15) +- fix: 修复uvue因缺少依赖无法使用 +## 0.1.6(2024-05-09) +- fix: 修复vue2因type问题导致无法使用 +## 0.1.5(2024-04-14) +- fix: 修复缺少依赖 +## 0.1.4(2024-04-10) +- chore: 更新文档 +## 0.1.3(2024-04-01) +- chore: 兼容uniapp x ios(app-js) +## 0.1.2(2023-12-14) +- fix: uvue 引入 API 自定义包出错 +## 0.1.1(2023-12-11) +- chore: uvue的二维码API独立,需要单独下载 +## 0.1.0(2023-12-07) +- fix: 修复因utssdk目录导致无法运行 +## 0.0.9(2023-12-06) +- feat: 支持uvue +## 0.0.8(2023-12-06) +- feat: 支持uvue +## 0.0.7(2023-12-06) +- feat: 支持uvue +## 0.0.6(2023-12-06) +- feat: 支持uvue +## 0.0.5(2023-07-30) +- fix: 修复再次生成前没有清空,导致图形叠加 +## 0.0.4(2023-07-27) +- fix: 修复相同尺寸无法再次生成 +## 0.0.3(2023-06-09) +- feat: 支持通过`@vue/composition-api`在`vue2`上使用 +- chore: 更新文档 +## 0.0.2(2023-06-08) +- chore: 更新文档 +## 0.0.1(2023-06-08) +- 首次 diff --git a/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.uvue b/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.uvue new file mode 100644 index 0000000..fcab10a --- /dev/null +++ b/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.uvue @@ -0,0 +1,235 @@ +<template> + <!-- #ifdef MP --> + <canvas :style="styles" type="2d" :canvas-id="canvasId" :id="canvasId"></canvas> + <!-- #endif --> + <!-- #ifdef APP --> + <view class="l-qrcode" ref="drawableRef" :style="[styles]"> + <image class="l-qrcode__icon" v-if="icon" :src="icon" :style="[iconStyle]"></image> + </view> + <!-- #endif --> + <!-- #ifdef WEB --> + <view class="l-qrcode" ref="drawableRef" :style="[styles]"> + + </view> + <!-- #endif --> +</template> +<script lang="uts" setup> + import { type PropType, nextTick } from 'vue' + // #ifndef APP + import { QRCodeCanvas } from './qrcode.js'; + import { QRCodePropsTypes , ImageSettings } from './type' + // #endif + // #ifdef APP + import { QRCodeCanvas, QRCodePropsTypes , ImageSettings } from '@/uni_modules/lime-qrcodegen' + // #endif + // import { addUnit } from '@/uni_modules/lime-shared/addUnit' + // import { unitConvert } from '@/uni_modules/lime-shared/unitConvert' + // import { toBoolean } from '@/uni_modules/lime-shared/toBoolean' + import { addUnit, unitConvert } from './utils' + import { LQrcodeFailCallback, LQrcodeCompleteCallback, LQrcodeSuccessCallback} from './type' + + const name = 'l-qrcode' + const props = defineProps({ + value: { + type: String + }, + icon: { + type: String + }, + // #ifdef APP-ANDROID + size: { + type: Object, + default: 160 + }, + iconSize: { + type: Object, + default: 40 + }, + // #endif + // #ifndef APP-ANDROID + size: { + type: [Number, String], + default: 160 + }, + iconSize: { + type: [Number, String], + default: 40 + }, + // #endif + marginSize: { + type: Number, + default: 0 + }, + color: { + type: String, + default: '#000' + }, + bgColor: { + type: String, + default: 'transparent' + }, + bordered: { + type: Boolean, + default: true + }, + errorLevel: { + type: String as PropType<'L' | 'M' | 'Q' | 'H'>, + default: 'M' // 'L' | 'M' | 'Q' | 'H' + }, + useCanvasToTempFilePath: { + type: Boolean, + default: false + } + // status: { + // type: String as PropType<'active'|'expired'|'loading'>, + // default: 'active' // active | expired | loading + // } + }) + const emits = defineEmits(['success']) + const context = getCurrentInstance(); + const canvasId = `l-qrcode${context!.uid}` + const styles = computed<Map<string, any>>(():Map<string, any>=>{ + const style = new Map<string, any>() + const size = addUnit(props.size); + if(size!=null){ + style.set('width', size) + style.set('height', size) + } + style.set('background', props.bgColor) + return style + }) + // #ifdef APP + const iconStyle = computed<Map<string, any>>(():Map<string, any>=>{ + const style = new Map<string, any>() + const size = addUnit(props.iconSize); + if(size!=null){ + style.set('width', size) + style.set('height', size) + } + return style + }) + // #endif + const drawableRef = ref<UniElement|null>(null); + // #ifdef WEB + let canvas:HTMLCanvasElement|null = null + // #endif + let qrcode:QRCodeCanvas|null = null + + const canvasToTempFilePath = (options: UTSJSONObject)=>{ + + const format = options.getString('format') ?? 'png'; + const fail = options.get('fail') as LQrcodeFailCallback | null; + const complete = options.get('complete') as LQrcodeCompleteCallback | null; + const success = options.get('success') as LQrcodeSuccessCallback | null; + // #ifdef APP + const newOptions = { + format, + fail, + complete, + success, + } as TakeSnapshotOptions + drawableRef.value!.takeSnapshot(newOptions) + // #endif + // #ifdef WEB + success?.({ + tempFilePath: canvas?.toDataURL('image/'+format) + }) + // #endif + + } + const render = ()=>{ + const param:QRCodePropsTypes = { + value: props.value, + size: unitConvert(props.size), + fgColor: props.color, + level: ['L', 'M', 'Q', 'H'].includes(props.errorLevel) ? props.errorLevel : 'M', + marginSize: props.marginSize, + includeMargin: props.bordered, + imageSettings: null, + } as QRCodePropsTypes + + if(props.icon != null){ + // if(toBoolean(props.iconSize) && toBoolean(props.icon)){ + const size = unitConvert(props.iconSize) + param.imageSettings = { + src: props.icon, + width: size, + height: size, + excavate: true + } as ImageSettings + } + qrcode?.render(param) + + if(props.useCanvasToTempFilePath){ + setTimeout(()=>{ + canvasToTempFilePath({ + success: (res: TakeSnapshotSuccess)=>{ + emits('success', res.tempFilePath) + } + }) + },100) + } + } + defineExpose({ + canvasToTempFilePath + }) + onMounted(()=>{ + nextTick(()=>{ + // #ifdef APP + const ctx = drawableRef.value!.getDrawableContext(); + qrcode = new QRCodeCanvas(ctx!) + // #endif + // #ifdef WEB + canvas = document.createElement('canvas') + canvas.style.width = '100%' + canvas.style.height = '100%' + drawableRef.value!.appendChild(canvas) + qrcode = new QRCodeCanvas(canvas, { + pixelRatio: uni.getSystemInfoSync().pixelRatio, + createImage: () => { + const image = new Image(); + image.crossOrigin = 'anonymous'; + return image; + } + }) + // #endif + + watchEffect(()=>{ + render() + }) + }) + + }) + onUnmounted(()=>{ + // #ifdef WEB + canvas?.remove(); + // #endif + qrcode = null; + }) +</script> +<style lang="scss"> + .l-qrcode { + position: relative; + background-color: aqua; + justify-content: center; + align-items: center; + + &-mask { + position: absolute; + // inset: 0; + // inset-block-start: 0; + // inset-inline-start: 0; + z-index: 10; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + // width: 100%; + // height: 100%; + color: rgba(0, 0, 0, 0.88); + line-height: 1.5714285714285714; + background: rgba(255, 255, 255, 0.96); + text-align: center; + } + } +</style> \ No newline at end of file diff --git a/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.vue b/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.vue new file mode 100644 index 0000000..f228583 --- /dev/null +++ b/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.vue @@ -0,0 +1,223 @@ +<template> + <view class="l-qrcode" :style="[styles]"> + <!-- #ifndef APP-NVUE --> + <canvas :style="styles" type="2d" :canvas-id="canvasId" :id="canvasId"></canvas> + <!-- #endif --> + <!-- #ifdef APP-NVUE --> + <web-view + ref="qrcodeRef" + @pagefinish="onFinished" + @error="onError" + @onPostMessage="onMessage" + :style="styles" src="/uni_modules/lime-qrcode/hybrid/html/index.html?v=1"></web-view> + <!-- #endif --> + <!-- <view class="l-qrcode-mask" v-if="['loading', 'expired'].includes(props.status)"> + <l-loading v-if="props.status == 'loading'"></l-loading> + <view class="l-qrcode-expired" v-if="props.status == 'expired'"> + <slot></slot> + </view> + </view> --> + </view> +</template> +<script lang="ts"> + // @ts-nocheck + import { computed, defineComponent, getCurrentInstance, watch, onUnmounted, onMounted } from '@/uni_modules/lime-shared/vue'; + import QRCodeProps from './props' + // #ifndef APP-NVUE + import { getCanvas, isCanvas2d } from './useCanvas' + import { QRCodeCanvas } from './qrcode.js'; + // #endif + import { addUnit } from '@/uni_modules/lime-shared/addUnit' + import { createImage } from '@/uni_modules/lime-shared/createImage' + import { unitConvert } from '@/uni_modules/lime-shared/unitConvert' + import { isBase64 } from '@/uni_modules/lime-shared/isBase64' + import { pathToBase64 } from '@/uni_modules/lime-shared/pathToBase64' + import { debounce } from '@/uni_modules/lime-shared/debounce' + const name = 'l-qrcode' + export default defineComponent({ + name, + props: QRCodeProps, + emits: ['success'], + setup(props, {emit}) { + const context = getCurrentInstance(); + const canvasId = `l-qrcode${context.uid}` + const styles = computed(() => `width: ${addUnit(props.size)}; height: ${addUnit(props.size)};`) + let qrcode = null + let canvas = null + const qrCodeProps = computed(() => { + const { value, icon, size, color, bgColor, bordered, iconSize, errorLevel, marginSize } = props + const imageSettings = { + src: icon, + x: undefined, + y: undefined, + height: unitConvert(iconSize), + width: unitConvert(iconSize), + excavate: true, + } + return { + value, + size: unitConvert(size), + level: errorLevel, + bgColor, + fgColor: color, + imageSettings: icon ? imageSettings : undefined, + includeMargin: bordered, + marginSize: marginSize ?? 0 + } + + }) + + // #ifdef APP-NVUE + const stacks = new Map() + // #endif + const canvasToTempFilePath = debounce((args: UniNamespace.CanvasToTempFilePathRes) => { + if(!canvas) return + // #ifndef APP-NVUE + const copyArgs = Object.assign({ + canvasId, + canvas: null + }, args) + + if (isCanvas2d) { + copyArgs.canvas = canvas + } + if ('toTempFilePath' in canvas) { + canvas.toTempFilePath(copyArgs) + } else { + uni.canvasToTempFilePath(copyArgs, context); + } + // #endif + // #ifdef APP-NVUE + if(!stacks.size) { + const flie = 'file-' + Math.random(); + const stack = {args, time: +new Date()} + stacks.set(`${flie}`, stack) + canvas.toDataURL(flie) + setTimeout(() => { + const stack = stacks.get(flie) + if(stack && 'fail' in stack.args) { + stack.args.fail({ + error: '超时' + }) + stacks.delete(flie) + } + },5000) + } + // #endif + }) + const useCanvasToTempFilePath = () => { + if(props.useCanvasToTempFilePath) { + canvasToTempFilePath({ + success(res: UniNamespace.CanvasToTempFilePathRes) { + emit('success', res.tempFilePath) + } + }) + } + } + // #ifdef APP-NVUE + const onFinished = () => { + const { pixelRatio } = uni.getSystemInfoSync() + canvas = { + toDataURL(flie: string) { + const ref: any = context.refs['qrcodeRef']; + if(ref) { + ref?.evalJS(`toDataURL('${flie}')`) + } + } + }; + qrcode = { + async render(props: any) { + const ref: any = context.refs['qrcodeRef']; + const { src } = props.imageSettings || { }; + if(!ref) return + if(src && !isBase64(src) && !/^http/.test(src) && /^\/static/.test(src)) { + props.imageSettings.src = await pathToBase64(src) + } + const _props = JSON.stringify(Object.assign({}, props, {pixelRatio})); + ref?.evalJS(`render(${_props})`); + } + } + qrcode.render(qrCodeProps.value) + useCanvasToTempFilePath() + } + const onError = () => { + console.warn('lime-qrcode 加载失败') + } + const onMessage = (e: any) => { + const {detail:{data: [res]}} = e + if(res.event == 'toDataURL') { + const {file, image, msg} = res.data; + const stack = stacks.get(file) + if(stack && image && 'success' in stack.args) { + stack.args.success({tempFilePath: image}) + stacks.delete(file) + } else if(stack && 'fails' in stack.args) { + stack.args.fail({error: msg}) + stacks.delete(file) + } + } + } + // #endif + const propsWatch = watch(props, () => { + if (qrcode) { + qrcode.render(qrCodeProps.value) + useCanvasToTempFilePath() + } + }) + onMounted(() => { + // #ifndef APP-NVUE + getCanvas(canvasId, { context }).then(res => { + canvas = res + qrcode = new QRCodeCanvas(res, { + // #ifdef H5 + path2D: false, + // #endif + pixelRatio: isCanvas2d ? uni.getSystemInfoSync().pixelRatio : 1, + createImage + }) + qrcode.render(qrCodeProps.value) + useCanvasToTempFilePath() + }) + // #endif + }) + onUnmounted(() => { + propsWatch && propsWatch() + }) + return { + canvasId, + styles, + props, + canvasToTempFilePath, + + // #ifdef APP-NVUE + onFinished, + onError, + onMessage + // #endif + } + } + }) +</script> +<style lang="scss"> + .l-qrcode { + position: relative; + + &-mask { + position: absolute; + inset: 0; + // inset-block-start: 0; + // inset-inline-start: 0; + z-index: 10; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + // width: 100%; + // height: 100%; + color: rgba(0, 0, 0, 0.88); + line-height: 1.5714285714285714; + background: rgba(255, 255, 255, 0.96); + text-align: center; + } + } +</style> \ No newline at end of file diff --git a/uni_modules/lime-qrcode/components/l-qrcode/props.ts b/uni_modules/lime-qrcode/components/l-qrcode/props.ts new file mode 100644 index 0000000..fb4d101 --- /dev/null +++ b/uni_modules/lime-qrcode/components/l-qrcode/props.ts @@ -0,0 +1,36 @@ +// @ts-nocheck +// import type { PropType } from './vue' +export default { + value: String, + icon: String, + size: { + type: [Number, String], + default: 160 + }, + iconSize: { + type: [Number, String], + default: 40 + }, + marginSize: Number, + color: { + type: String, + default: '#000' + }, + bgColor: { + type: String, + default: 'transparent' + }, + bordered: { + type: Boolean, + default: true + }, + errorLevel: { + type: String as PropType<'L'|'M'|'Q'|'H'>, + default: 'M' // 'L' | 'M' | 'Q' | 'H' + }, + useCanvasToTempFilePath: Boolean + // status: { + // type: String as PropType<'active'|'expired'|'loading'>, + // default: 'active' // active | expired | loading + // } +} \ No newline at end of file diff --git a/uni_modules/lime-qrcode/components/l-qrcode/qrcode.js b/uni_modules/lime-qrcode/components/l-qrcode/qrcode.js new file mode 100644 index 0000000..b04047b --- /dev/null +++ b/uni_modules/lime-qrcode/components/l-qrcode/qrcode.js @@ -0,0 +1,6 @@ +function e(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function t(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function n(e,n,r){return n&&t(e.prototype,n),r&&t(e,r),Object.defineProperty(e,"prototype",{writable:!1}),e}function r(e,t){if(null==e)return{};var n,r,i=function(e,t){if(null==e)return{};var n,r,i={},o=Object.keys(e);for(r=0;o.length>r;r++)0>t.indexOf(n=o[r])&&(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;o.length>r;r++)0>t.indexOf(n=o[r])&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);t>n;n++)r[n]=e[n];return r}function o(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return i(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?i(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return e.length>r?{done:!1,value:e[r++]}:{done:!0}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,s=!0,u=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){u=!0,a=e},f:function(){try{s||null==n.return||n.return()}finally{if(u)throw a}}}} +/** + * @license QR Code generator library (TypeScript) + * Copyright (c) Project Nayuki. + * SPDX-License-Identifier: MIT + */var a;!function(t){var r=function(){function r(t,n,i,o){if(e(this,r),this.version=void 0,this.errorCorrectionLevel=void 0,this.size=void 0,this.mask=void 0,this.modules=[],this.isFunction=[],this.version=t,this.errorCorrectionLevel=n,r.MIN_VERSION>t||t>r.MAX_VERSION)throw new RangeError("Version value out of range");if(-1>o||o>7)throw new RangeError("Mask value out of range");this.size=4*t+17;for(var a=[],u=0;this.size>u;u++)a.push(!1);for(var l=0;this.size>l;l++)this.modules.push(a.slice()),this.isFunction.push(a.slice());this.drawFunctionPatterns();var h=this.addEccAndInterleave(i);if(this.drawCodewords(h),-1==o)for(var c=1e9,f=0;8>f;f++){this.applyMask(f),this.drawFormatBits(f);var v=this.getPenaltyScore();c>v&&(o=f,c=v),this.applyMask(f)}s(o>=0&&7>=o),this.mask=o,this.applyMask(o),this.drawFormatBits(o),this.isFunction=[]}return n(r,[{key:"getModule",value:function(e,t){return e>=0&&this.size>e&&t>=0&&this.size>t&&this.modules[t][e]}},{key:"getModules",value:function(){return this.modules}},{key:"drawFunctionPatterns",value:function(){for(var e=0;this.size>e;e++)this.setFunctionModule(6,e,e%2==0),this.setFunctionModule(e,6,e%2==0);this.drawFinderPattern(3,3),this.drawFinderPattern(this.size-4,3),this.drawFinderPattern(3,this.size-4);for(var t=this.getAlignmentPatternPositions(),n=t.length,r=0;n>r;r++)for(var i=0;n>i;i++)0==r&&0==i||0==r&&i==n-1||r==n-1&&0==i||this.drawAlignmentPattern(t[r],t[i]);this.drawFormatBits(0),this.drawVersion()}},{key:"drawFormatBits",value:function(e){for(var t=this.errorCorrectionLevel.formatBits<<3|e,n=t,r=0;10>r;r++)n=n<<1^1335*(n>>>9);var i=21522^(t<<10|n);s(i>>>15==0);for(var o=0;5>=o;o++)this.setFunctionModule(8,o,a(i,o));this.setFunctionModule(8,7,a(i,6)),this.setFunctionModule(8,8,a(i,7)),this.setFunctionModule(7,8,a(i,8));for(var u=9;15>u;u++)this.setFunctionModule(14-u,8,a(i,u));for(var l=0;8>l;l++)this.setFunctionModule(this.size-1-l,8,a(i,l));for(var h=8;15>h;h++)this.setFunctionModule(8,this.size-15+h,a(i,h));this.setFunctionModule(8,this.size-8,!0)}},{key:"drawVersion",value:function(){if(this.version>=7){for(var e=this.version,t=0;12>t;t++)e=e<<1^7973*(e>>>11);var n=this.version<<12|e;s(n>>>18==0);for(var r=0;18>r;r++){var i=a(n,r),o=this.size-11+r%3,u=Math.floor(r/3);this.setFunctionModule(o,u,i),this.setFunctionModule(u,o,i)}}}},{key:"drawFinderPattern",value:function(e,t){for(var n=-4;4>=n;n++)for(var r=-4;4>=r;r++){var i=Math.max(Math.abs(r),Math.abs(n)),o=e+r,a=t+n;o>=0&&this.size>o&&a>=0&&this.size>a&&this.setFunctionModule(o,a,2!=i&&4!=i)}}},{key:"drawAlignmentPattern",value:function(e,t){for(var n=-2;2>=n;n++)for(var r=-2;2>=r;r++)this.setFunctionModule(e+r,t+n,1!=Math.max(Math.abs(r),Math.abs(n)))}},{key:"setFunctionModule",value:function(e,t,n){this.modules[t][e]=n,this.isFunction[t][e]=!0}},{key:"addEccAndInterleave",value:function(e){var t=this.version,n=this.errorCorrectionLevel;if(e.length!=r.getNumDataCodewords(t,n))throw new RangeError("Invalid argument");for(var i=r.NUM_ERROR_CORRECTION_BLOCKS[n.ordinal][t],o=r.ECC_CODEWORDS_PER_BLOCK[n.ordinal][t],a=Math.floor(r.getNumRawDataModules(t)/8),u=i-a%i,l=Math.floor(a/i),h=[],c=r.reedSolomonComputeDivisor(o),f=0,v=0;i>f;f++){var d=e.slice(v,v+l-o+(u>f?0:1));v+=d.length;var m=r.reedSolomonComputeRemainder(d,c);u>f&&d.push(0),h.push(d.concat(m))}for(var g=[],y=function(e){h.forEach((function(t,n){e==l-o&&u>n||g.push(t[e])}))},E=0;h[0].length>E;E++)y(E);return s(g.length==a),g}},{key:"drawCodewords",value:function(e){if(e.length!=Math.floor(r.getNumRawDataModules(this.version)/8))throw new RangeError("Invalid argument");for(var t=0,n=this.size-1;n>=1;n-=2){6==n&&(n=5);for(var i=0;this.size>i;i++)for(var o=0;2>o;o++){var u=n-o,l=0==(n+1&2)?this.size-1-i:i;!this.isFunction[l][u]&&8*e.length>t&&(this.modules[l][u]=a(e[t>>>3],7-(7&t)),t++)}}s(t==8*e.length)}},{key:"applyMask",value:function(e){if(0>e||e>7)throw new RangeError("Mask value out of range");for(var t=0;this.size>t;t++)for(var n=0;this.size>n;n++){var r=void 0;switch(e){case 0:r=(n+t)%2==0;break;case 1:r=t%2==0;break;case 2:r=n%3==0;break;case 3:r=(n+t)%3==0;break;case 4:r=(Math.floor(n/3)+Math.floor(t/2))%2==0;break;case 5:r=n*t%2+n*t%3==0;break;case 6:r=(n*t%2+n*t%3)%2==0;break;case 7:r=((n+t)%2+n*t%3)%2==0;break;default:throw Error("Unreachable")}!this.isFunction[t][n]&&r&&(this.modules[t][n]=!this.modules[t][n])}}},{key:"getPenaltyScore",value:function(){for(var e=0,t=0;this.size>t;t++){for(var n=!1,i=0,a=[0,0,0,0,0,0,0],u=0;this.size>u;u++)this.modules[t][u]==n?5==++i?e+=r.PENALTY_N1:i>5&&e++:(this.finderPenaltyAddHistory(i,a),n||(e+=this.finderPenaltyCountPatterns(a)*r.PENALTY_N3),n=this.modules[t][u],i=1);e+=this.finderPenaltyTerminateAndCount(n,i,a)*r.PENALTY_N3}for(var l=0;this.size>l;l++){for(var h=!1,c=0,f=[0,0,0,0,0,0,0],v=0;this.size>v;v++)this.modules[v][l]==h?5==++c?e+=r.PENALTY_N1:c>5&&e++:(this.finderPenaltyAddHistory(c,f),h||(e+=this.finderPenaltyCountPatterns(f)*r.PENALTY_N3),h=this.modules[v][l],c=1);e+=this.finderPenaltyTerminateAndCount(h,c,f)*r.PENALTY_N3}for(var d=0;this.size-1>d;d++)for(var m=0;this.size-1>m;m++){var g=this.modules[d][m];g==this.modules[d][m+1]&&g==this.modules[d+1][m]&&g==this.modules[d+1][m+1]&&(e+=r.PENALTY_N2)}var y,E=0,w=o(this.modules);try{for(w.s();!(y=w.n()).done;){E=y.value.reduce((function(e,t){return e+(t?1:0)}),E)}}catch(e){w.e(e)}finally{w.f()}var M=this.size*this.size,C=Math.ceil(Math.abs(20*E-10*M)/M)-1;return s(C>=0&&9>=C),s((e+=C*r.PENALTY_N4)>=0&&2568888>=e),e}},{key:"getAlignmentPatternPositions",value:function(){if(1==this.version)return[];for(var e=Math.floor(this.version/7)+2,t=32==this.version?26:2*Math.ceil((4*this.version+4)/(2*e-2)),n=[6],r=this.size-7;e>n.length;r-=t)n.splice(1,0,r);return n}},{key:"finderPenaltyCountPatterns",value:function(e){var t=e[1];s(3*this.size>=t);var n=t>0&&e[2]==t&&e[3]==3*t&&e[4]==t&&e[5]==t;return(!n||4*t>e[0]||t>e[6]?0:1)+(!n||4*t>e[6]||t>e[0]?0:1)}},{key:"finderPenaltyTerminateAndCount",value:function(e,t,n){return e&&(this.finderPenaltyAddHistory(t,n),t=0),this.finderPenaltyAddHistory(t+=this.size,n),this.finderPenaltyCountPatterns(n)}},{key:"finderPenaltyAddHistory",value:function(e,t){0==t[0]&&(e+=this.size),t.pop(),t.unshift(e)}}],[{key:"encodeText",value:function(e,n){var i=t.QrSegment.makeSegments(e);return r.encodeSegments(i,n)}},{key:"encodeBinary",value:function(e,n){var i=t.QrSegment.makeBytes(e);return r.encodeSegments([i],n)}},{key:"encodeSegments",value:function(e,t){var n,a,l=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1,h=arguments.length>3&&void 0!==arguments[3]?arguments[3]:40,c=arguments.length>4&&void 0!==arguments[4]?arguments[4]:-1,f=5>=arguments.length||void 0===arguments[5]||arguments[5];if(r.MIN_VERSION>l||l>h||h>r.MAX_VERSION||-1>c||c>7)throw new RangeError("Invalid value");for(n=l;;n++){var v=8*r.getNumDataCodewords(n,t),d=u.getTotalBits(e,n);if(v>=d){a=d;break}if(n>=h)throw new RangeError("Data too long")}for(var m=0,g=[r.Ecc.MEDIUM,r.Ecc.QUARTILE,r.Ecc.HIGH];g.length>m;m++){var y=g[m];f&&a<=8*r.getNumDataCodewords(n,y)&&(t=y)}var E,w=[],M=o(e);try{for(M.s();!(E=M.n()).done;){var C=E.value;i(C.mode.modeBits,4,w),i(C.numChars,C.mode.numCharCountBits(n),w);var R,A=o(C.getData());try{for(A.s();!(R=A.n()).done;){var p=R.value;w.push(p)}}catch(e){A.e(e)}finally{A.f()}}}catch(e){M.e(e)}finally{M.f()}s(w.length==a);var P=8*r.getNumDataCodewords(n,t);s(P>=w.length),i(0,Math.min(4,P-w.length),w),i(0,(8-w.length%8)%8,w),s(w.length%8==0);for(var N=236;P>w.length;N^=253)i(N,8,w);for(var k=[];w.length>8*k.length;)k.push(0);return w.forEach((function(e,t){return k[t>>>3]|=e<<7-(7&t)})),new r(n,t,k,c)}},{key:"getNumRawDataModules",value:function(e){if(r.MIN_VERSION>e||e>r.MAX_VERSION)throw new RangeError("Version number out of range");var t=(16*e+128)*e+64;if(e>=2){var n=Math.floor(e/7)+2;t-=(25*n-10)*n-55,7>e||(t-=36)}return s(t>=208&&29648>=t),t}},{key:"getNumDataCodewords",value:function(e,t){return Math.floor(r.getNumRawDataModules(e)/8)-r.ECC_CODEWORDS_PER_BLOCK[t.ordinal][e]*r.NUM_ERROR_CORRECTION_BLOCKS[t.ordinal][e]}},{key:"reedSolomonComputeDivisor",value:function(e){if(1>e||e>255)throw new RangeError("Degree out of range");for(var t=[],n=0;e-1>n;n++)t.push(0);t.push(1);for(var i=1,o=0;e>o;o++){for(var a=0;t.length>a;a++)t[a]=r.reedSolomonMultiply(t[a],i),t.length>a+1&&(t[a]^=t[a+1]);i=r.reedSolomonMultiply(i,2)}return t}},{key:"reedSolomonComputeRemainder",value:function(e,t){var n,i=t.map((function(e){return 0})),a=o(e);try{var s=function(){var e=n.value^i.shift();i.push(0),t.forEach((function(t,n){return i[n]^=r.reedSolomonMultiply(t,e)}))};for(a.s();!(n=a.n()).done;)s()}catch(e){a.e(e)}finally{a.f()}return i}},{key:"reedSolomonMultiply",value:function(e,t){if(e>>>8!=0||t>>>8!=0)throw new RangeError("Byte out of range");for(var n=0,r=7;r>=0;r--)n=n<<1^285*(n>>>7),n^=(t>>>r&1)*e;return s(n>>>8==0),n}}]),r}();function i(e,t,n){if(0>t||t>31||e>>>t!=0)throw new RangeError("Value out of range");for(var r=t-1;r>=0;r--)n.push(e>>>r&1)}function a(e,t){return 0!=(e>>>t&1)}function s(e){if(!e)throw Error("Assertion error")}r.MIN_VERSION=1,r.MAX_VERSION=40,r.PENALTY_N1=3,r.PENALTY_N2=3,r.PENALTY_N3=40,r.PENALTY_N4=10,r.ECC_CODEWORDS_PER_BLOCK=[[-1,7,10,15,20,26,18,20,24,30,18,20,24,26,30,22,24,28,30,28,28,28,28,30,30,26,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,10,16,26,18,24,16,18,22,22,26,30,22,22,24,24,28,28,26,26,26,26,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28],[-1,13,22,18,26,18,24,18,22,20,24,28,26,24,20,30,24,28,28,26,30,28,30,30,30,30,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,17,28,22,16,22,28,26,26,24,28,24,28,22,24,24,30,28,28,26,28,30,24,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30]],r.NUM_ERROR_CORRECTION_BLOCKS=[[-1,1,1,1,1,1,2,2,2,2,4,4,4,4,4,6,6,6,6,7,8,8,9,9,10,12,12,12,13,14,15,16,17,18,19,19,20,21,22,24,25],[-1,1,1,1,2,2,4,4,4,5,5,5,8,9,9,10,10,11,13,14,16,17,17,18,20,21,23,25,26,28,29,31,33,35,37,38,40,43,45,47,49],[-1,1,1,2,2,4,4,6,6,8,8,8,10,12,16,12,17,16,18,21,20,23,23,25,27,29,34,34,35,38,40,43,45,48,51,53,56,59,62,65,68],[-1,1,1,2,4,4,4,5,6,8,8,11,11,16,16,18,16,19,21,25,25,25,34,30,32,35,37,40,42,45,48,51,54,57,60,63,66,70,74,77,81]],t.QrCode=r;var u=function(){function t(n,r,i){if(e(this,t),this.mode=void 0,this.numChars=void 0,this.bitData=void 0,this.mode=n,this.numChars=r,this.bitData=i,0>r)throw new RangeError("Invalid argument");this.bitData=i.slice()}return n(t,[{key:"getData",value:function(){return this.bitData.slice()}}],[{key:"makeBytes",value:function(e){var n,r=[],a=o(e);try{for(a.s();!(n=a.n()).done;){i(n.value,8,r)}}catch(e){a.e(e)}finally{a.f()}return new t(t.Mode.BYTE,e.length,r)}},{key:"makeNumeric",value:function(e){if(!t.isNumeric(e))throw new RangeError("String contains non-numeric characters");for(var n=[],r=0;e.length>r;){var o=Math.min(e.length-r,3);i(parseInt(e.substring(r,r+o),10),3*o+1,n),r+=o}return new t(t.Mode.NUMERIC,e.length,n)}},{key:"makeAlphanumeric",value:function(e){if(!t.isAlphanumeric(e))throw new RangeError("String contains unencodable characters in alphanumeric mode");var n,r=[];for(n=0;e.length>=n+2;n+=2){var o=45*t.ALPHANUMERIC_CHARSET.indexOf(e.charAt(n));i(o+=t.ALPHANUMERIC_CHARSET.indexOf(e.charAt(n+1)),11,r)}return e.length>n&&i(t.ALPHANUMERIC_CHARSET.indexOf(e.charAt(n)),6,r),new t(t.Mode.ALPHANUMERIC,e.length,r)}},{key:"makeSegments",value:function(e){return""==e?[]:t.isNumeric(e)?[t.makeNumeric(e)]:t.isAlphanumeric(e)?[t.makeAlphanumeric(e)]:[t.makeBytes(t.toUtf8ByteArray(e))]}},{key:"makeEci",value:function(e){var n=[];if(0>e)throw new RangeError("ECI assignment value out of range");if(128>e)i(e,8,n);else if(16384>e)i(2,2,n),i(e,14,n);else{if(e>=1e6)throw new RangeError("ECI assignment value out of range");i(6,3,n),i(e,21,n)}return new t(t.Mode.ECI,0,n)}},{key:"isNumeric",value:function(e){return t.NUMERIC_REGEX.test(e)}},{key:"isAlphanumeric",value:function(e){return t.ALPHANUMERIC_REGEX.test(e)}},{key:"getTotalBits",value:function(e,t){var n,r=0,i=o(e);try{for(i.s();!(n=i.n()).done;){var a=n.value,s=a.mode.numCharCountBits(t);if(a.numChars>=1<<s)return 1/0;r+=4+s+a.bitData.length}}catch(e){i.e(e)}finally{i.f()}return r}},{key:"toUtf8ByteArray",value:function(e){e=encodeURI(e);for(var t=[],n=0;e.length>n;n++)"%"!=e.charAt(n)?t.push(e.charCodeAt(n)):(t.push(parseInt(e.substring(n+1,n+3),16)),n+=2);return t}}]),t}();u.NUMERIC_REGEX=/^[0-9]*$/,u.ALPHANUMERIC_REGEX=/^[A-Z0-9 $%*+.\/:-]*$/,u.ALPHANUMERIC_CHARSET="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:",t.QrSegment=u}(a||(a={})),function(t){!function(t){var r=n((function t(n,r){e(this,t),this.ordinal=void 0,this.formatBits=void 0,this.ordinal=n,this.formatBits=r}));r.LOW=new r(0,1),r.MEDIUM=new r(1,0),r.QUARTILE=new r(2,3),r.HIGH=new r(3,2),t.Ecc=r}(t.QrCode||(t.QrCode={}))}(a||(a={})),function(t){!function(t){var r=function(){function t(n,r){e(this,t),this.modeBits=void 0,this.numBitsCharCount=void 0,this.modeBits=n,this.numBitsCharCount=r}return n(t,[{key:"numCharCountBits",value:function(e){return this.numBitsCharCount[Math.floor((e+7)/17)]}}]),t}();r.NUMERIC=new r(1,[10,12,14]),r.ALPHANUMERIC=new r(2,[9,11,13]),r.BYTE=new r(4,[8,16,16]),r.KANJI=new r(8,[8,10,12]),r.ECI=new r(7,[0,0,0]),t.Mode=r}(t.QrSegment||(t.QrSegment={}))}(a||(a={}));var s=a,u=["value","size","level","bgColor","fgColor","includeMargin","marginSize","imageSettings"],l={L:s.QrCode.Ecc.LOW,M:s.QrCode.Ecc.MEDIUM,Q:s.QrCode.Ecc.QUARTILE,H:s.QrCode.Ecc.HIGH};function h(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=[];return e.forEach((function(e,r){var i=null;e.forEach((function(o,a){if(!o&&null!==i)return n.push("M".concat(i+t," ").concat(r+t,"h").concat(a-i,"v1H").concat(i+t,"z")),void(i=null);if(a!==e.length-1)o&&null===i&&(i=a);else{if(!o)return;n.push(null===i?"M".concat(a+t,",").concat(r+t," h1v1H").concat(a+t,"z"):"M".concat(i+t,",").concat(r+t," h").concat(a+1-i,"v1H").concat(i+t,"z"))}}))})),n.join("")}function c(e,t){return e.slice().map((function(e,n){return t.y>n||n>=t.y+t.h?e:e.map((function(e,n){return(t.x>n||n>=t.x+t.w)&&e}))}))}var f=function(){function t(n,r){var i=this;for(var o in e(this,t),this.canvas=void 0,this.pixelRatio="undefined"!=typeof window?window.devicePixelRatio:1,this.path2D=!0,this.SUPPORTS_PATH2D=void 0,this.createImage=function(){return new Image},this.createPath2D=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0,t="createPath2D";return i.canvas&&t in i.canvas?i.canvas[t](e):new Path2D(e)},this.canvas=n,r)o in this&&(this[o]=r[o]);this.SUPPORTS_PATH2D=function(){try{i.createPath2D()}catch(e){return!1}return!0}()}return n(t,[{key:"render",value:function(e,t){var n=this,i=e.value,o=e.size,a=void 0===o?128:o,f=e.level,v=void 0===f?"L":f,d=e.bgColor,m=void 0===d?"#FFFFFF":d,g=e.fgColor,y=void 0===g?"#000000":g,E=e.includeMargin,w=void 0!==E&&E,M=e.marginSize,C=e.imageSettings,R=(r(e,u),null==C?void 0:C.src),A=this.canvas,p=A.getContext("2d");if(p&&i){var P=s.QrCode.encodeText(i,l[v]).getModules(),N=function(e,t){return null!=t?Math.floor(t):e?4:0}(w,M),k=P.length+2*N,S=function(e,t,n,r){if(null==r)return null;var i=e.length+2*n,o=Math.floor(.1*t),a=i/t,s=(r.width||o)*a,u=(r.height||o)*a,l=null==r.x?e.length/2-s/2:r.x*a,h=null==r.y?e.length/2-u/2:r.y*a,c=null;if(r.excavate){var f=Math.floor(l),v=Math.floor(h);c={x:f,y:v,w:Math.ceil(s+l-f),h:Math.ceil(u+h-v)}}return{x:l,y:h,h:u,w:s,excavation:c}}(P,a,N,C),I=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,r=null!=S&&null!==e&&e.complete&&0!==e.naturalHeight&&0!==e.naturalWidth;r&&null!=S.excavation&&(P=c(P,S.excavation)),t&&t(P);var i=n.pixelRatio;p.clearRect(0,0,A.width,A.height),p.setTransform(1,0,0,1,0,0),A.height=A.width=a*i;var o=a/k*i;if(p.scale(o,o),p.fillStyle=m,p.fillRect(0,0,k,k),p.fillStyle=y,n.SUPPORTS_PATH2D&&n.path2D){var s=n.createPath2D(h(P,N));p.fill(s)}else P.forEach((function(e,t){e.forEach((function(e,n){e&&p.fillRect(n+N,t+N,1,1)}))}));var u=(null==e?void 0:e.path)||e;r&&p.drawImage(u,S.x+N,S.y+N,S.w,S.h),"draw"in p&&p.draw()};if(R){var b=this.createImage(A);b.onload=function(){I(b)},b.onerror=function(e){I(),console.warn(e)},b.src=R}else I()}}}]),t}();export{f as QRCodeCanvas}; diff --git a/uni_modules/lime-qrcode/components/l-qrcode/type.ts b/uni_modules/lime-qrcode/components/l-qrcode/type.ts new file mode 100644 index 0000000..8a1556b --- /dev/null +++ b/uni_modules/lime-qrcode/components/l-qrcode/type.ts @@ -0,0 +1,48 @@ +// @ts-nocheck +export type ImageSettings = { + width: number + height: number + x?: number + y?: number + excavate: boolean +} +export type QRCodePropsTypes = { + value?: string + size?: number + fgColor?: string + level?: string + marginSize: number + includeMargin: boolean + imageSettings?: ImageSettings +} + +export type QRCodeCallback = (cells : boolean[][]) => void + +export type Excavation = { + x: number + y: number + h: number + w: number +} + + + + + +/** + * 成功回调函数定义 + */ +export type TakeSnapshotSuccessCallback = (res: TakeSnapshotSuccess) => void +/** + * 失败回调函数定义 + */ +export type TakeSnapshotFailCallback = (res: TakeSnapshotFail) => void +/** + * 完成回调函数定义 + */ +export type TakeSnapshotCompleteCallback = (res: any) => void + + +export type LQrcodeFailCallback = TakeSnapshotFailCallback +export type LQrcodeCompleteCallback = TakeSnapshotCompleteCallback +export type LQrcodeSuccessCallback = TakeSnapshotSuccessCallback \ No newline at end of file diff --git a/uni_modules/lime-qrcode/components/l-qrcode/useCanvas.ts b/uni_modules/lime-qrcode/components/l-qrcode/useCanvas.ts new file mode 100644 index 0000000..78ce660 --- /dev/null +++ b/uni_modules/lime-qrcode/components/l-qrcode/useCanvas.ts @@ -0,0 +1,78 @@ + +// @ts-nocheck +import type { ComponentInternalInstance } from '@/uni_modules/lime-shared/vue' +import { getRect } from '@/uni_modules/lime-shared/getRect' +import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d' +export const isCanvas2d = canIUseCanvas2d() + +export async function getCanvas(canvasId: string, options: {context: ComponentInternalInstance}) { + let { context } = options + // #ifdef MP || VUE2 + if (context.proxy) context = context.proxy + // #endif + return getRect('#' + canvasId, context, isCanvas2d).then(res => { + if(res.node){ + return res.node + } else { + const ctx = uni.createCanvasContext(canvasId, context) + return { + getContext(type: string) { + if(type == '2d') { + return ctx + } + }, + width: res.width, + height: res.height, + } + // #ifdef H5 + // canvas.value = context.proxy.$el.querySelector('#'+ canvasId) + // #endif + } + }) +} + +// #ifndef H5 || APP-NVUE +class Image { + currentSrc: string | null = null + naturalHeight: number = 0 + naturalWidth: number = 0 + width: number = 0 + height: number = 0 + tagName: string = 'IMG' + path: any = '' + crossOrigin: any = '' + referrerPolicy: any = '' + onload: () => void + onerror: () => void + constructor() {} + set src(src) { + this.currentSrc = src + uni.getImageInfo({ + src, + success: (res) => { + this.path = res.path + this.naturalWidth = this.width = res.width + this.naturalHeight = this.height = res.height + this.onload() + }, + fail: () => { + this.onerror() + } + }) + } + get src() { + return this.currentSrc + } +} +// #endif + +export function createImage(canvas: WechatMiniprogram.Canvas) { + if(canvas && canvas.createImage) { + return canvas.createImage() + } else if(typeof window != 'undefined' && window.Image) { + return new window.Image() + } + // #ifndef H5 || APP-NVUE + return new Image() + // #endif +} \ No newline at end of file diff --git a/uni_modules/lime-qrcode/components/l-qrcode/utils.uts b/uni_modules/lime-qrcode/components/l-qrcode/utils.uts new file mode 100644 index 0000000..a88a930 --- /dev/null +++ b/uni_modules/lime-qrcode/components/l-qrcode/utils.uts @@ -0,0 +1,35 @@ +export function addUnit(value: any|null):string{ + if(value == null){ + return '' + } + value = `${value}` + return /^(-)?\d+(\\.\d+)?$/.test(value) ? `${value}px` : value +} + +export function unitConvert(value: any|null): number{ + if(typeof value == 'number'){ + return value as number + } + if(typeof value == 'string'){ + value = `${value}` + if(/^(-)?\d+(\\.\d+)?$/.test(value)){ + return parseFloat(value); + } + + const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g; + const results = reg.exec(value); + if (results == null) { + return 0; + } + const unit = results[3]; + const v = parseFloat(value); + if (unit == 'rpx') { + const { windowWidth } = uni.getWindowInfo() + return windowWidth / 750 * v; + } + if (unit == 'px') { + return v; + } + } + return 0; +} \ No newline at end of file diff --git a/uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.uvue b/uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.uvue new file mode 100644 index 0000000..93e10d2 --- /dev/null +++ b/uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.uvue @@ -0,0 +1,140 @@ +<template> + <view class="demo-block"> + <text class="demo-block__title-text ultra">QRCode</text> + <text class="demo-block__desc-text">能够将文本转换生成二维码的组件,支持自定义配色和 Logo 配置</text> + <view class="demo-block__body"> + <view class="demo-block card"> + <text class="demo-block__title-text large">基础</text> + <view class="demo-block__body"> + <l-qrcode value="https://limeui.qcoon.cn" size="300rpx"></l-qrcode> + </view> + </view> + + <view class="demo-block card"> + <text class="demo-block__title-text large">icon</text> + <view class="demo-block__body"> + <image v-if="image !=''" :src="image" style="width: 300rpx;" mode="widthFix"></image> + <view style="flex-direction: row; justify-content: space-between"> + <l-qrcode ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode> + <l-qrcode :useCanvasToTempFilePath="true" @success="success" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode> + </view> + <button type="primary" style="margin-top: 20rpx;" @click="onClick">生成图片</button> + </view> + </view> + + <view class="demo-block card"> + <text class="demo-block__title-text large">颜色</text> + <view class="demo-block__body"> + <view style="flex-direction: row; justify-content: space-between"> + <l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(82,196,26)"></l-qrcode> + <l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(22,119,255)" bgColor="rgb(245,245,245)"></l-qrcode> + </view> + </view> + </view> + + <view class="demo-block card"> + <text class="demo-block__title-text large">纠错比例</text> + <view class="demo-block__body"> + <l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" :errorLevel="levels[index]"></l-qrcode> + <button type="primary" style="margin-top: 20rpx;" @click="onToggle">切换纠错等级:{{levels[index]}}</button> + </view> + </view> + + <view class="demo-block card"> + <text class="demo-block__title-text large">动态</text> + <view class="demo-block__body"> + <l-qrcode :value="text" size="300rpx" :marginSize="1" bgColor="white"></l-qrcode> + <button type="primary" style="margin-top: 20rpx;" @click="update">更新</button> + </view> + </view> + + </view> + </view> + +</template> +<script> + // import {ComponentPublicInstance} from 'vue' + export default { + name: 'lime-qrcode', + data() { + return { + text: 'qcoon.com.cn', + image: '', + index: 0, + levels: ['L', 'M', 'Q', 'H'] + } + }, + methods:{ + success(src: string) { + console.log(`src`, src) + }, + update() { + this.text = `qcoon.cn?v=${Math.random()}` + }, + onToggle() { + this.index++ + this.index = this.index % this.levels.length + }, + onClick() { + const el:LQrcodeComponentPublicInstance = this.$refs['qrcodeRef'] as LQrcodeComponentPublicInstance + el.canvasToTempFilePath({ + success:(res: TakeSnapshotSuccess)=>{ + this.image = res.tempFilePath + } + }) + } + }, + mounted() { + + } + } +</script> +<style lang="scss"> + + .demo-block { + margin: 32px 10px 0; + overflow: visible; + &.card{ + background-color: white; + padding: 30rpx; + margin-bottom: 20rpx; + } + &__title { + margin: 0; + margin-top: 8px; + &-text { + color: rgba(0, 0, 0, 0.6); + font-weight: 400; + font-size: 14px; + line-height: 16px; + + &.large { + color: rgba(0, 0, 0, 0.9); + font-size: 18px; + font-weight: 700; + line-height: 26px; + } + &.ultra { + color: rgba(0, 0, 0, 0.9); + font-size: 24px; + font-weight: 700; + line-height: 32px; + } + } + } + &__desc-text { + color: rgba(0, 0, 0, 0.6); + margin: 8px 16px 0 0; + font-size: 14px; + line-height: 22px; + } + &__body { + margin: 16px 0; + overflow: visible; + .demo-block { + // margin-top: 0px; + margin: 0; + } + } + } +</style> \ No newline at end of file diff --git a/uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.vue b/uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.vue new file mode 100644 index 0000000..113ef87 --- /dev/null +++ b/uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.vue @@ -0,0 +1,79 @@ +<template> + <demo-block title="QRCode" type="ultra"> + <demo-block title="基础"> + <l-qrcode value="https://limeui.qcoon.cn" size="300rpx"></l-qrcode> + </demo-block> + <demo-block title="icon"> + <view style="display: flex; gap: 10px"> + <image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image> + <l-qrcode ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode> + <l-qrcode useCanvasToTempFilePath @success="success" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode> + </view> + <button @click="onClick">生成图片</button> + </demo-block> + <demo-block title="颜色"> + <view style="display: flex; gap: 10px"> + <l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(82,196,26)"></l-qrcode> + <l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(22,119,255)" bgColor="rgb(245,245,245)"></l-qrcode> + </view> + </demo-block> + <demo-block title="纠错比例"> + <l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" :errorLevel="levels[index]"></l-qrcode> + <button @click="onToggle">切换纠错等级:{{levels[index]}}</button> + </demo-block> + <demo-block title="动态"> + <l-qrcode :value="text" size="300rpx" :marginSize="1" bgColor="white"></l-qrcode> + <button @click="update">更新</button> + </demo-block> + </demo-block> +</template> +<script> + import {ref, defineComponent} from '@/uni_modules/lime-shared/vue' + export default defineComponent({ + setup() { + const qrcodeRef = ref(null) + const image = ref(null) + const text = ref('qcoon.com.cn') + const levels = ['L', 'M', 'Q', 'H'] + let index = ref(0) + const onToggle = () => { + index.value++ + index.value = index.value % levels.length + } + const onClick = () => { + if(qrcodeRef.value) { + qrcodeRef.value.canvasToTempFilePath({ + success(res) { + image.value = res.tempFilePath + console.log('success:::', res) + }, + fail(err) { + console.log('err:::', err) + } + }) + } + } + const success = (res) => { + console.log('res', res) + } + + const update = () =>{ + text.value = `qcoon.cn?v=${Math.random()}` + } + + return { + levels, + index, + image, + text, + qrcodeRef, + onClick, + update, + success, + onToggle, + } + } + }) +</script> +<style> +</style> diff --git a/uni_modules/lime-qrcode/hybrid/html/index.html b/uni_modules/lime-qrcode/hybrid/html/index.html new file mode 100644 index 0000000..bf4def9 --- /dev/null +++ b/uni_modules/lime-qrcode/hybrid/html/index.html @@ -0,0 +1,77 @@ +<!DOCTYPE html> +<html lang="zh"> +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>lime-qrcode</title> + <style> + html,body,canvas { + margin: 0; + padding: 0; + width: 100%; + height: 100%; + pointer-events: none; + /* background-color: rgba(255,0,0,0.1) */ + } + </style> +</head> +<body> + <canvas id="lime-qrcode"></canvas> + <script type="text/javascript" src="./uni.webview.1.5.3.js"></script> + <script type="text/javascript" src="./qrcode.min.js"></script> + <script> + var canvas = document.querySelector('#lime-qrcode') + var pixelRatio = window.devicePixelRatio || 1 + function appendWatermark(image) { + emit('append', mark.toDataURL()) + } + + var qrcode = new lime.QRCodeCanvas(canvas, { + pixelRatio, + }) + function render(props) { + if(props.pixelRatio) { + pixelRatio = props.pixelRatio + } + if(qrcode) { + qrcode.render(props) + } + } + function toDataURL(file) { + if(qrcode && canvas) { + try{ + const image = canvas.toDataURL() + emit('toDataURL', { + file, + image + }) + }catch(e){ + emit('toDataURL', { + file, + msg: e + }) + } + + } + } + function emit(event, data) { + postMessage({ + event, + data + }); + }; + function postMessage(data) { + uni.postMessage({ + data + }); + }; + // render({ + // content: ['Lime UI'], + // // rotate: -22, + // // baseSize: 2, + // // fontGap: 3 + // }) + </script> +</body> +</html> \ No newline at end of file diff --git a/uni_modules/lime-qrcode/hybrid/html/qrcode.min.js b/uni_modules/lime-qrcode/hybrid/html/qrcode.min.js new file mode 100644 index 0000000..a0c79ce --- /dev/null +++ b/uni_modules/lime-qrcode/hybrid/html/qrcode.min.js @@ -0,0 +1,6 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).lime={})}(this,(function(e){"use strict";function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function r(e,t,r){return t&&n(e.prototype,t),r&&n(e,r),Object.defineProperty(e,"prototype",{writable:!1}),e}function i(e,t){if(null==e)return{};var n,r,i=function(e,t){if(null==e)return{};var n,r,i={},o=Object.keys(e);for(r=0;o.length>r;r++)0>t.indexOf(n=o[r])&&(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;o.length>r;r++)0>t.indexOf(n=o[r])&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function o(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);t>n;n++)r[n]=e[n];return r}function a(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return o(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?o(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,i=function(){};return{s:i,n:function(){return e.length>r?{done:!1,value:e[r++]}:{done:!0}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,s=!0,u=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){u=!0,a=e},f:function(){try{s||null==n.return||n.return()}finally{if(u)throw a}}}} +/** + * @license QR Code generator library (TypeScript) + * Copyright (c) Project Nayuki. + * SPDX-License-Identifier: MIT + */var s;!function(e){var n=function(){function n(e,r,i,o){if(t(this,n),this.version=void 0,this.errorCorrectionLevel=void 0,this.size=void 0,this.mask=void 0,this.modules=[],this.isFunction=[],this.version=e,this.errorCorrectionLevel=r,n.MIN_VERSION>e||e>n.MAX_VERSION)throw new RangeError("Version value out of range");if(-1>o||o>7)throw new RangeError("Mask value out of range");this.size=4*e+17;for(var a=[],u=0;this.size>u;u++)a.push(!1);for(var l=0;this.size>l;l++)this.modules.push(a.slice()),this.isFunction.push(a.slice());this.drawFunctionPatterns();var h=this.addEccAndInterleave(i);if(this.drawCodewords(h),-1==o)for(var c=1e9,f=0;8>f;f++){this.applyMask(f),this.drawFormatBits(f);var d=this.getPenaltyScore();c>d&&(o=f,c=d),this.applyMask(f)}s(o>=0&&7>=o),this.mask=o,this.applyMask(o),this.drawFormatBits(o),this.isFunction=[]}return r(n,[{key:"getModule",value:function(e,t){return e>=0&&this.size>e&&t>=0&&this.size>t&&this.modules[t][e]}},{key:"getModules",value:function(){return this.modules}},{key:"drawFunctionPatterns",value:function(){for(var e=0;this.size>e;e++)this.setFunctionModule(6,e,e%2==0),this.setFunctionModule(e,6,e%2==0);this.drawFinderPattern(3,3),this.drawFinderPattern(this.size-4,3),this.drawFinderPattern(3,this.size-4);for(var t=this.getAlignmentPatternPositions(),n=t.length,r=0;n>r;r++)for(var i=0;n>i;i++)0==r&&0==i||0==r&&i==n-1||r==n-1&&0==i||this.drawAlignmentPattern(t[r],t[i]);this.drawFormatBits(0),this.drawVersion()}},{key:"drawFormatBits",value:function(e){for(var t=this.errorCorrectionLevel.formatBits<<3|e,n=t,r=0;10>r;r++)n=n<<1^1335*(n>>>9);var i=21522^(t<<10|n);s(i>>>15==0);for(var a=0;5>=a;a++)this.setFunctionModule(8,a,o(i,a));this.setFunctionModule(8,7,o(i,6)),this.setFunctionModule(8,8,o(i,7)),this.setFunctionModule(7,8,o(i,8));for(var u=9;15>u;u++)this.setFunctionModule(14-u,8,o(i,u));for(var l=0;8>l;l++)this.setFunctionModule(this.size-1-l,8,o(i,l));for(var h=8;15>h;h++)this.setFunctionModule(8,this.size-15+h,o(i,h));this.setFunctionModule(8,this.size-8,!0)}},{key:"drawVersion",value:function(){if(this.version>=7){for(var e=this.version,t=0;12>t;t++)e=e<<1^7973*(e>>>11);var n=this.version<<12|e;s(n>>>18==0);for(var r=0;18>r;r++){var i=o(n,r),a=this.size-11+r%3,u=Math.floor(r/3);this.setFunctionModule(a,u,i),this.setFunctionModule(u,a,i)}}}},{key:"drawFinderPattern",value:function(e,t){for(var n=-4;4>=n;n++)for(var r=-4;4>=r;r++){var i=Math.max(Math.abs(r),Math.abs(n)),o=e+r,a=t+n;o>=0&&this.size>o&&a>=0&&this.size>a&&this.setFunctionModule(o,a,2!=i&&4!=i)}}},{key:"drawAlignmentPattern",value:function(e,t){for(var n=-2;2>=n;n++)for(var r=-2;2>=r;r++)this.setFunctionModule(e+r,t+n,1!=Math.max(Math.abs(r),Math.abs(n)))}},{key:"setFunctionModule",value:function(e,t,n){this.modules[t][e]=n,this.isFunction[t][e]=!0}},{key:"addEccAndInterleave",value:function(e){var t=this.version,r=this.errorCorrectionLevel;if(e.length!=n.getNumDataCodewords(t,r))throw new RangeError("Invalid argument");for(var i=n.NUM_ERROR_CORRECTION_BLOCKS[r.ordinal][t],o=n.ECC_CODEWORDS_PER_BLOCK[r.ordinal][t],a=Math.floor(n.getNumRawDataModules(t)/8),u=i-a%i,l=Math.floor(a/i),h=[],c=n.reedSolomonComputeDivisor(o),f=0,d=0;i>f;f++){var v=e.slice(d,d+l-o+(u>f?0:1));d+=v.length;var m=n.reedSolomonComputeRemainder(v,c);u>f&&v.push(0),h.push(v.concat(m))}for(var g=[],y=function(e){h.forEach((function(t,n){e==l-o&&u>n||g.push(t[e])}))},E=0;h[0].length>E;E++)y(E);return s(g.length==a),g}},{key:"drawCodewords",value:function(e){if(e.length!=Math.floor(n.getNumRawDataModules(this.version)/8))throw new RangeError("Invalid argument");for(var t=0,r=this.size-1;r>=1;r-=2){6==r&&(r=5);for(var i=0;this.size>i;i++)for(var a=0;2>a;a++){var u=r-a,l=0==(r+1&2)?this.size-1-i:i;!this.isFunction[l][u]&&8*e.length>t&&(this.modules[l][u]=o(e[t>>>3],7-(7&t)),t++)}}s(t==8*e.length)}},{key:"applyMask",value:function(e){if(0>e||e>7)throw new RangeError("Mask value out of range");for(var t=0;this.size>t;t++)for(var n=0;this.size>n;n++){var r=void 0;switch(e){case 0:r=(n+t)%2==0;break;case 1:r=t%2==0;break;case 2:r=n%3==0;break;case 3:r=(n+t)%3==0;break;case 4:r=(Math.floor(n/3)+Math.floor(t/2))%2==0;break;case 5:r=n*t%2+n*t%3==0;break;case 6:r=(n*t%2+n*t%3)%2==0;break;case 7:r=((n+t)%2+n*t%3)%2==0;break;default:throw Error("Unreachable")}!this.isFunction[t][n]&&r&&(this.modules[t][n]=!this.modules[t][n])}}},{key:"getPenaltyScore",value:function(){for(var e=0,t=0;this.size>t;t++){for(var r=!1,i=0,o=[0,0,0,0,0,0,0],u=0;this.size>u;u++)this.modules[t][u]==r?5==++i?e+=n.PENALTY_N1:i>5&&e++:(this.finderPenaltyAddHistory(i,o),r||(e+=this.finderPenaltyCountPatterns(o)*n.PENALTY_N3),r=this.modules[t][u],i=1);e+=this.finderPenaltyTerminateAndCount(r,i,o)*n.PENALTY_N3}for(var l=0;this.size>l;l++){for(var h=!1,c=0,f=[0,0,0,0,0,0,0],d=0;this.size>d;d++)this.modules[d][l]==h?5==++c?e+=n.PENALTY_N1:c>5&&e++:(this.finderPenaltyAddHistory(c,f),h||(e+=this.finderPenaltyCountPatterns(f)*n.PENALTY_N3),h=this.modules[d][l],c=1);e+=this.finderPenaltyTerminateAndCount(h,c,f)*n.PENALTY_N3}for(var v=0;this.size-1>v;v++)for(var m=0;this.size-1>m;m++){var g=this.modules[v][m];g==this.modules[v][m+1]&&g==this.modules[v+1][m]&&g==this.modules[v+1][m+1]&&(e+=n.PENALTY_N2)}var y,E=0,w=a(this.modules);try{for(w.s();!(y=w.n()).done;){E=y.value.reduce((function(e,t){return e+(t?1:0)}),E)}}catch(e){w.e(e)}finally{w.f()}var M=this.size*this.size,C=Math.ceil(Math.abs(20*E-10*M)/M)-1;return s(C>=0&&9>=C),s((e+=C*n.PENALTY_N4)>=0&&2568888>=e),e}},{key:"getAlignmentPatternPositions",value:function(){if(1==this.version)return[];for(var e=Math.floor(this.version/7)+2,t=32==this.version?26:2*Math.ceil((4*this.version+4)/(2*e-2)),n=[6],r=this.size-7;e>n.length;r-=t)n.splice(1,0,r);return n}},{key:"finderPenaltyCountPatterns",value:function(e){var t=e[1];s(3*this.size>=t);var n=t>0&&e[2]==t&&e[3]==3*t&&e[4]==t&&e[5]==t;return(!n||4*t>e[0]||t>e[6]?0:1)+(!n||4*t>e[6]||t>e[0]?0:1)}},{key:"finderPenaltyTerminateAndCount",value:function(e,t,n){return e&&(this.finderPenaltyAddHistory(t,n),t=0),this.finderPenaltyAddHistory(t+=this.size,n),this.finderPenaltyCountPatterns(n)}},{key:"finderPenaltyAddHistory",value:function(e,t){0==t[0]&&(e+=this.size),t.pop(),t.unshift(e)}}],[{key:"encodeText",value:function(t,r){var i=e.QrSegment.makeSegments(t);return n.encodeSegments(i,r)}},{key:"encodeBinary",value:function(t,r){var i=e.QrSegment.makeBytes(t);return n.encodeSegments([i],r)}},{key:"encodeSegments",value:function(e,t){var r,o,l=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1,h=arguments.length>3&&void 0!==arguments[3]?arguments[3]:40,c=arguments.length>4&&void 0!==arguments[4]?arguments[4]:-1,f=5>=arguments.length||void 0===arguments[5]||arguments[5];if(n.MIN_VERSION>l||l>h||h>n.MAX_VERSION||-1>c||c>7)throw new RangeError("Invalid value");for(r=l;;r++){var d=8*n.getNumDataCodewords(r,t),v=u.getTotalBits(e,r);if(d>=v){o=v;break}if(r>=h)throw new RangeError("Data too long")}for(var m=0,g=[n.Ecc.MEDIUM,n.Ecc.QUARTILE,n.Ecc.HIGH];g.length>m;m++){var y=g[m];f&&o<=8*n.getNumDataCodewords(r,y)&&(t=y)}var E,w=[],M=a(e);try{for(M.s();!(E=M.n()).done;){var C=E.value;i(C.mode.modeBits,4,w),i(C.numChars,C.mode.numCharCountBits(r),w);var R,p=a(C.getData());try{for(p.s();!(R=p.n()).done;){var A=R.value;w.push(A)}}catch(e){p.e(e)}finally{p.f()}}}catch(e){M.e(e)}finally{M.f()}s(w.length==o);var P=8*n.getNumDataCodewords(r,t);s(P>=w.length),i(0,Math.min(4,P-w.length),w),i(0,(8-w.length%8)%8,w),s(w.length%8==0);for(var N=236;P>w.length;N^=253)i(N,8,w);for(var k=[];w.length>8*k.length;)k.push(0);return w.forEach((function(e,t){return k[t>>>3]|=e<<7-(7&t)})),new n(r,t,k,c)}},{key:"getNumRawDataModules",value:function(e){if(n.MIN_VERSION>e||e>n.MAX_VERSION)throw new RangeError("Version number out of range");var t=(16*e+128)*e+64;if(e>=2){var r=Math.floor(e/7)+2;t-=(25*r-10)*r-55,7>e||(t-=36)}return s(t>=208&&29648>=t),t}},{key:"getNumDataCodewords",value:function(e,t){return Math.floor(n.getNumRawDataModules(e)/8)-n.ECC_CODEWORDS_PER_BLOCK[t.ordinal][e]*n.NUM_ERROR_CORRECTION_BLOCKS[t.ordinal][e]}},{key:"reedSolomonComputeDivisor",value:function(e){if(1>e||e>255)throw new RangeError("Degree out of range");for(var t=[],r=0;e-1>r;r++)t.push(0);t.push(1);for(var i=1,o=0;e>o;o++){for(var a=0;t.length>a;a++)t[a]=n.reedSolomonMultiply(t[a],i),t.length>a+1&&(t[a]^=t[a+1]);i=n.reedSolomonMultiply(i,2)}return t}},{key:"reedSolomonComputeRemainder",value:function(e,t){var r,i=t.map((function(e){return 0})),o=a(e);try{var s=function(){var e=r.value^i.shift();i.push(0),t.forEach((function(t,r){return i[r]^=n.reedSolomonMultiply(t,e)}))};for(o.s();!(r=o.n()).done;)s()}catch(e){o.e(e)}finally{o.f()}return i}},{key:"reedSolomonMultiply",value:function(e,t){if(e>>>8!=0||t>>>8!=0)throw new RangeError("Byte out of range");for(var n=0,r=7;r>=0;r--)n=n<<1^285*(n>>>7),n^=(t>>>r&1)*e;return s(n>>>8==0),n}}]),n}();function i(e,t,n){if(0>t||t>31||e>>>t!=0)throw new RangeError("Value out of range");for(var r=t-1;r>=0;r--)n.push(e>>>r&1)}function o(e,t){return 0!=(e>>>t&1)}function s(e){if(!e)throw Error("Assertion error")}n.MIN_VERSION=1,n.MAX_VERSION=40,n.PENALTY_N1=3,n.PENALTY_N2=3,n.PENALTY_N3=40,n.PENALTY_N4=10,n.ECC_CODEWORDS_PER_BLOCK=[[-1,7,10,15,20,26,18,20,24,30,18,20,24,26,30,22,24,28,30,28,28,28,28,30,30,26,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,10,16,26,18,24,16,18,22,22,26,30,22,22,24,24,28,28,26,26,26,26,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28],[-1,13,22,18,26,18,24,18,22,20,24,28,26,24,20,30,24,28,28,26,30,28,30,30,30,30,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,17,28,22,16,22,28,26,26,24,28,24,28,22,24,24,30,28,28,26,28,30,24,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30]],n.NUM_ERROR_CORRECTION_BLOCKS=[[-1,1,1,1,1,1,2,2,2,2,4,4,4,4,4,6,6,6,6,7,8,8,9,9,10,12,12,12,13,14,15,16,17,18,19,19,20,21,22,24,25],[-1,1,1,1,2,2,4,4,4,5,5,5,8,9,9,10,10,11,13,14,16,17,17,18,20,21,23,25,26,28,29,31,33,35,37,38,40,43,45,47,49],[-1,1,1,2,2,4,4,6,6,8,8,8,10,12,16,12,17,16,18,21,20,23,23,25,27,29,34,34,35,38,40,43,45,48,51,53,56,59,62,65,68],[-1,1,1,2,4,4,4,5,6,8,8,11,11,16,16,18,16,19,21,25,25,25,34,30,32,35,37,40,42,45,48,51,54,57,60,63,66,70,74,77,81]],e.QrCode=n;var u=function(){function e(n,r,i){if(t(this,e),this.mode=void 0,this.numChars=void 0,this.bitData=void 0,this.mode=n,this.numChars=r,this.bitData=i,0>r)throw new RangeError("Invalid argument");this.bitData=i.slice()}return r(e,[{key:"getData",value:function(){return this.bitData.slice()}}],[{key:"makeBytes",value:function(t){var n,r=[],o=a(t);try{for(o.s();!(n=o.n()).done;){i(n.value,8,r)}}catch(e){o.e(e)}finally{o.f()}return new e(e.Mode.BYTE,t.length,r)}},{key:"makeNumeric",value:function(t){if(!e.isNumeric(t))throw new RangeError("String contains non-numeric characters");for(var n=[],r=0;t.length>r;){var o=Math.min(t.length-r,3);i(parseInt(t.substring(r,r+o),10),3*o+1,n),r+=o}return new e(e.Mode.NUMERIC,t.length,n)}},{key:"makeAlphanumeric",value:function(t){if(!e.isAlphanumeric(t))throw new RangeError("String contains unencodable characters in alphanumeric mode");var n,r=[];for(n=0;t.length>=n+2;n+=2){var o=45*e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(n));i(o+=e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(n+1)),11,r)}return t.length>n&&i(e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(n)),6,r),new e(e.Mode.ALPHANUMERIC,t.length,r)}},{key:"makeSegments",value:function(t){return""==t?[]:e.isNumeric(t)?[e.makeNumeric(t)]:e.isAlphanumeric(t)?[e.makeAlphanumeric(t)]:[e.makeBytes(e.toUtf8ByteArray(t))]}},{key:"makeEci",value:function(t){var n=[];if(0>t)throw new RangeError("ECI assignment value out of range");if(128>t)i(t,8,n);else if(16384>t)i(2,2,n),i(t,14,n);else{if(t>=1e6)throw new RangeError("ECI assignment value out of range");i(6,3,n),i(t,21,n)}return new e(e.Mode.ECI,0,n)}},{key:"isNumeric",value:function(t){return e.NUMERIC_REGEX.test(t)}},{key:"isAlphanumeric",value:function(t){return e.ALPHANUMERIC_REGEX.test(t)}},{key:"getTotalBits",value:function(e,t){var n,r=0,i=a(e);try{for(i.s();!(n=i.n()).done;){var o=n.value,s=o.mode.numCharCountBits(t);if(o.numChars>=1<<s)return 1/0;r+=4+s+o.bitData.length}}catch(e){i.e(e)}finally{i.f()}return r}},{key:"toUtf8ByteArray",value:function(e){e=encodeURI(e);for(var t=[],n=0;e.length>n;n++)"%"!=e.charAt(n)?t.push(e.charCodeAt(n)):(t.push(parseInt(e.substring(n+1,n+3),16)),n+=2);return t}}]),e}();u.NUMERIC_REGEX=/^[0-9]*$/,u.ALPHANUMERIC_REGEX=/^[A-Z0-9 $%*+.\/:-]*$/,u.ALPHANUMERIC_CHARSET="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:",e.QrSegment=u}(s||(s={})),function(e){!function(e){var n=r((function e(n,r){t(this,e),this.ordinal=void 0,this.formatBits=void 0,this.ordinal=n,this.formatBits=r}));n.LOW=new n(0,1),n.MEDIUM=new n(1,0),n.QUARTILE=new n(2,3),n.HIGH=new n(3,2),e.Ecc=n}(e.QrCode||(e.QrCode={}))}(s||(s={})),function(e){!function(e){var n=function(){function e(n,r){t(this,e),this.modeBits=void 0,this.numBitsCharCount=void 0,this.modeBits=n,this.numBitsCharCount=r}return r(e,[{key:"numCharCountBits",value:function(e){return this.numBitsCharCount[Math.floor((e+7)/17)]}}]),e}();n.NUMERIC=new n(1,[10,12,14]),n.ALPHANUMERIC=new n(2,[9,11,13]),n.BYTE=new n(4,[8,16,16]),n.KANJI=new n(8,[8,10,12]),n.ECI=new n(7,[0,0,0]),e.Mode=n}(e.QrSegment||(e.QrSegment={}))}(s||(s={}));var u=s,l=["value","size","level","bgColor","fgColor","includeMargin","marginSize","imageSettings"],h={L:u.QrCode.Ecc.LOW,M:u.QrCode.Ecc.MEDIUM,Q:u.QrCode.Ecc.QUARTILE,H:u.QrCode.Ecc.HIGH};function c(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=[];return e.forEach((function(e,r){var i=null;e.forEach((function(o,a){if(!o&&null!==i)return n.push("M".concat(i+t," ").concat(r+t,"h").concat(a-i,"v1H").concat(i+t,"z")),void(i=null);if(a!==e.length-1)o&&null===i&&(i=a);else{if(!o)return;n.push(null===i?"M".concat(a+t,",").concat(r+t," h1v1H").concat(a+t,"z"):"M".concat(i+t,",").concat(r+t," h").concat(a+1-i,"v1H").concat(i+t,"z"))}}))})),n.join("")}function f(e,t){return e.slice().map((function(e,n){return t.y>n||n>=t.y+t.h?e:e.map((function(e,n){return(t.x>n||n>=t.x+t.w)&&e}))}))}var d=function(){function e(n,r){var i=this;for(var o in t(this,e),this.canvas=void 0,this.pixelRatio="undefined"!=typeof window?window.devicePixelRatio:1,this.path2D=!0,this.SUPPORTS_PATH2D=void 0,this.createImage=function(){return new Image},this.createPath2D=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0,t="createPath2D";return i.canvas&&t in i.canvas?i.canvas[t](e):new Path2D(e)},this.canvas=n,r)o in this&&(this[o]=r[o]);this.SUPPORTS_PATH2D=function(){try{i.createPath2D()}catch(e){return!1}return!0}()}return r(e,[{key:"render",value:function(e,t){var n=this,r=e.value,o=e.size,a=void 0===o?128:o,s=e.level,d=void 0===s?"L":s,v=e.bgColor,m=void 0===v?"#FFFFFF":v,g=e.fgColor,y=void 0===g?"#000000":g,E=e.includeMargin,w=void 0!==E&&E,M=e.marginSize,C=e.imageSettings,R=(i(e,l),null==C?void 0:C.src),p=this.canvas,A=p.getContext("2d");if(A&&r){var P=u.QrCode.encodeText(r,h[d]).getModules(),N=function(e,t){return null!=t?Math.floor(t):e?4:0}(w,M),k=P.length+2*N,S=function(e,t,n,r){if(null==r)return null;var i=e.length+2*n,o=Math.floor(.1*t),a=i/t,s=(r.width||o)*a,u=(r.height||o)*a,l=null==r.x?e.length/2-s/2:r.x*a,h=null==r.y?e.length/2-u/2:r.y*a,c=null;if(r.excavate){var f=Math.floor(l),d=Math.floor(h);c={x:f,y:d,w:Math.ceil(s+l-f),h:Math.ceil(u+h-d)}}return{x:l,y:h,h:u,w:s,excavation:c}}(P,a,N,C),I=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,r=null!=S&&null!==e&&e.complete&&0!==e.naturalHeight&&0!==e.naturalWidth;r&&null!=S.excavation&&(P=f(P,S.excavation)),t&&t(P);var i=n.pixelRatio;A.clearRect(0,0,p.width,p.height),A.setTransform(1,0,0,1,0,0),p.height=p.width=a*i;var o=a/k*i;if(A.scale(o,o),A.fillStyle=m,A.fillRect(0,0,k,k),A.fillStyle=y,n.SUPPORTS_PATH2D&&n.path2D){var s=n.createPath2D(c(P,N));A.fill(s)}else P.forEach((function(e,t){e.forEach((function(e,n){e&&A.fillRect(n+N,t+N,1,1)}))}));var u=(null==e?void 0:e.path)||e;r&&A.drawImage(u,S.x+N,S.y+N,S.w,S.h),"draw"in A&&A.draw()};if(R){var b=this.createImage(p);b.onload=function(){I(b)},b.onerror=function(e){I(),console.warn(e)},b.src=R}else I()}}}]),e}();e.QRCodeCanvas=d,Object.defineProperty(e,"__esModule",{value:!0})})); diff --git a/uni_modules/lime-qrcode/hybrid/html/uni.webview.1.5.3.js b/uni_modules/lime-qrcode/hybrid/html/uni.webview.1.5.3.js new file mode 100644 index 0000000..d6524fa --- /dev/null +++ b/uni_modules/lime-qrcode/hybrid/html/uni.webview.1.5.3.js @@ -0,0 +1 @@ +!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e=e||self).uni=n()}(this,(function(){"use strict";try{var e={};Object.defineProperty(e,"passive",{get:function(){!0}}),window.addEventListener("test-passive",null,e)}catch(e){}var n=Object.prototype.hasOwnProperty;function i(e,i){return n.call(e,i)}var t=[];function o(){return window.__dcloud_weex_postMessage||window.__dcloud_weex_}var r=function(e,n){var i={options:{timestamp:+new Date},name:e,arg:n};if(o()){if("postMessage"===e){var r={data:[n]};return window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessage(r):window.__dcloud_weex_.postMessage(JSON.stringify(r))}var a={type:"WEB_INVOKE_APPSERVICE",args:{data:i,webviewIds:t}};window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessageToService(a):window.__dcloud_weex_.postMessageToService(JSON.stringify(a))}if(!window.plus)return window.parent.postMessage({type:"WEB_INVOKE_APPSERVICE",data:i,pageId:""},"*");if(0===t.length){var d=plus.webview.currentWebview();if(!d)throw new Error("plus.webview.currentWebview() is undefined");var s=d.parent(),w="";w=s?s.id:d.id,t.push(w)}if(plus.webview.getWebviewById("__uniapp__service"))plus.webview.postMessageToUniNView({type:"WEB_INVOKE_APPSERVICE",args:{data:i,webviewIds:t}},"__uniapp__service");else{var u=JSON.stringify(i);plus.webview.getLaunchWebview().evalJS('UniPlusBridge.subscribeHandler("'.concat("WEB_INVOKE_APPSERVICE",'",').concat(u,",").concat(JSON.stringify(t),");"))}},a={navigateTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("navigateTo",{url:encodeURI(n)})},navigateBack:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.delta;r("navigateBack",{delta:parseInt(n)||1})},switchTab:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("switchTab",{url:encodeURI(n)})},reLaunch:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("reLaunch",{url:encodeURI(n)})},redirectTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("redirectTo",{url:encodeURI(n)})},getEnv:function(e){o()?e({nvue:!0}):window.plus?e({plus:!0}):e({h5:!0})},postMessage:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};r("postMessage",e.data||{})}},d=/uni-app/i.test(navigator.userAgent),s=/Html5Plus/i.test(navigator.userAgent),w=/complete|loaded|interactive/;var u=window.my&&navigator.userAgent.indexOf("AlipayClient")>-1;var g=window.swan&&window.swan.webView&&/swan/i.test(navigator.userAgent);var c=window.qq&&window.qq.miniProgram&&/QQ/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var v=window.tt&&window.tt.miniProgram&&/toutiaomicroapp/i.test(navigator.userAgent);var m=window.wx&&window.wx.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var p=window.qa&&/quickapp/i.test(navigator.userAgent);var f=window.ks&&window.ks.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var l=window.tt&&window.tt.miniProgram&&/Lark|Feishu/i.test(navigator.userAgent);var _=window.jd&&window.jd.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);for(var E,b=function(){window.UniAppJSBridge=!0,document.dispatchEvent(new CustomEvent("UniAppJSBridgeReady",{bubbles:!0,cancelable:!0}))},h=[function(e){if(d||s)return window.__dcloud_weex_postMessage||window.__dcloud_weex_?document.addEventListener("DOMContentLoaded",e):window.plus&&w.test(document.readyState)?setTimeout(e,0):document.addEventListener("plusready",e),a},function(e){if(m)return window.WeixinJSBridge&&window.WeixinJSBridge.invoke?setTimeout(e,0):document.addEventListener("WeixinJSBridgeReady",e),window.wx.miniProgram},function(e){if(c)return window.QQJSBridge&&window.QQJSBridge.invoke?setTimeout(e,0):document.addEventListener("QQJSBridgeReady",e),window.qq.miniProgram},function(e){if(u){document.addEventListener("DOMContentLoaded",e);var n=window.my;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){if(g)return document.addEventListener("DOMContentLoaded",e),window.swan.webView},function(e){if(v)return document.addEventListener("DOMContentLoaded",e),window.tt.miniProgram},function(e){if(p){window.QaJSBridge&&window.QaJSBridge.invoke?setTimeout(e,0):document.addEventListener("QaJSBridgeReady",e);var n=window.qa;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){if(f)return window.WeixinJSBridge&&window.WeixinJSBridge.invoke?setTimeout(e,0):document.addEventListener("WeixinJSBridgeReady",e),window.ks.miniProgram},function(e){if(l)return document.addEventListener("DOMContentLoaded",e),window.tt.miniProgram},function(e){if(_)return window.JDJSBridgeReady&&window.JDJSBridgeReady.invoke?setTimeout(e,0):document.addEventListener("JDJSBridgeReady",e),window.jd.miniProgram},function(e){return document.addEventListener("DOMContentLoaded",e),a}],y=0;y<h.length&&!(E=h[y](b));y++);E||(E={});var B="undefined"!=typeof uni?uni:{};if(!B.navigateTo)for(var S in E)i(E,S)&&(B[S]=E[S]);return B.webView=E,B})); diff --git a/uni_modules/lime-qrcode/package.json b/uni_modules/lime-qrcode/package.json new file mode 100644 index 0000000..c8446a3 --- /dev/null +++ b/uni_modules/lime-qrcode/package.json @@ -0,0 +1,91 @@ +{ + "id": "lime-qrcode", + "displayName": "qrcode 二维码生成", + "version": "0.1.8", + "description": "一款全平台通用的二维码生成插件,支持uniapp/uniappx(web,ios,安卓)", + "keywords": [ + "qrcode", + "qr", + "uvue", + "生成图片", + "二维码" + ], + "repository": "", + "engines": { + }, + "dcloudext": { + "type": "component-vue", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "305716444" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [ + "lime-shared" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "Vue": { + "vue2": "y", + "vue3": "y" + }, + "App": { + "app-vue": "y", + "app-nvue": "y", + "app-uvue": "y", + "app-android": { + "minVersion": "19" + } + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "y", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u", + "钉钉": "u", + "快手": "u", + "飞书": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/lime-qrcode/readme.md b/uni_modules/lime-qrcode/readme.md new file mode 100644 index 0000000..320874a --- /dev/null +++ b/uni_modules/lime-qrcode/readme.md @@ -0,0 +1,152 @@ +# lime-qrcode 二维码 +- 一款全平台通用的二维码生成插件,支持uniapp/uniappx(web,ios,安卓) +- uvue 需要导入[lime-qrcodegen](https://ext.dcloud.net.cn/plugin?id=15838) + +## 使用 +- 导入插件后直接使用 +- uvue 需要导入**[lime-qrcodegen](https://ext.dcloud.net.cn/plugin?id=15838)** + +#### 基础使用 + +```html +<l-qrcode value="http://lime.qcoon.cn" /> +``` + + +#### ICON +- 带 Icon 的二维码 + +```html +<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" icon="/static/logo.png" iconSize="70rpx"></l-qrcode> +``` + +#### 颜色 +- 通过设置 `color` 自定义二维码颜色,通过设置 `bgColor` 自定义背景颜色。 + +```html +<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(82,196,26)"></l-qrcode> +<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(22,119,255)" bgColor="rgb(245,245,245)"></l-qrcode> +``` + +#### 纠错比例 +- 通过设置 `errorLevel` 调整不同的容错等级。 + +```html +<l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" errorLevel="H"></l-qrcode> +``` + +#### 生成图片 +- 1、通过调用插件的`canvasToTempFilePath`方法生成图片。 + +```html +<image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image> +<l-qrcode ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode> +<button @click="onClick">生成图片</button> +``` +```js +// vue3 +const qrcodeRef = ref(null) +const onClick = () => { + if(!qrcodeRef.value) return + qrcodeRef.value.canvasToTempFilePath({ + success(res) { + image.value = res.tempFilePath + console.log('success:::', res) + }, + fail(err) { + console.log('err:::', err) + } + }) +} + +// vue2 +const el = this.$refs['qrcodeRef'] +el.canvasToTempFilePath({ + success:(res)=>{ + this.image = res.tempFilePath + }, + fail(err) { + console.log('err:::', err) + } +}) + +// uvue +const el:LQrcodeComponentPublicInstance = this.$refs['qrcodeRef'] as LQrcodeComponentPublicInstance +el.canvasToTempFilePath({ + success:(res: TakeSnapshotSuccess)=>{ + this.image = res.tempFilePath + }, + fail(err: TakeSnapshotFail) { + console.log('err:::', err) + } +}) +``` + +- 2、通过设置`useCanvasToTempFilePath`在`success`事件里接收图片地址 + +```html +<image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image> +<l-qrcode useCanvasToTempFilePath @success="success" value="https://limeui.qcoon.cn"></l-qrcode> +``` +```js +const image = ref(null) +const success = (img) => { + image.value = img +} +``` + +### 关于vue2的使用方式 +- 插件使用了`composition-api`, 如果你希望在vue2中使用请按官方的教程[vue-composition-api](https://uniapp.dcloud.net.cn/tutorial/vue-composition-api.html)配置 +- 关键代码是: 在main.js中 在vue2部分加上这一段即可 + +```js +// main.js vue2 +import Vue from 'vue' +import VueCompositionAPI from '@vue/composition-api' +Vue.use(VueCompositionAPI) +``` +另外插件也用到了TS,vue2可能会遇过官方的TS版本过低的问题,找到HX目录下的`compile-typescript`目录 +```cmd +// \HBuilderX\plugins\compile-typescript +yarn add typescript -D +- or - +npm install typescript -D +``` + +### 查看示例 +- 导入后直接使用这个标签查看演示效果 + +```html +// 代码位于 uni_modules/lime-qrcode/compoents/lime-qrcode +<lime-qrcode /> +``` + +### 插件标签 +- 默认 l-qrcode 为 component +- 默认 lime-qrcode 为 demo + + + +## API + +### Props + +| 参数 | 说明 | 类型 | 默认值 | +| --------------------------| ------------------------------------------------------------ | ---------------- | ------------ | +| value | 扫描后的文本 | <em>string</em> | `-` | +| icon | 二维码中图片的地址 | <em>string</em> | `-` | +| size | 二维码大小 | <em>number,string</em> | `160` | +| iconSize | 二维码中图片的大小 | <em>number,string</em> | `40` | +| color | 二维码颜色 | <em>string</em> | `-` | +| bgColor | 二维码背景颜色 | <em>string</em> | `-` | +| errorLevel | 二维码纠错等级 | `'L' | 'M' | 'Q' | 'H' ` | `M` | +| marginSize | 边距码大小,默认为0码点 | <em>number</em> | `0` | + +### 常见问题 +- icon 是网络地址时,H5和Nvue需要解决跨域问题,小程序需要配置download + +## 打赏 + +如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。 + + \ No newline at end of file diff --git a/uni_modules/lime-shared/addUnit/index.ts b/uni_modules/lime-shared/addUnit/index.ts new file mode 100644 index 0000000..78476c9 --- /dev/null +++ b/uni_modules/lime-shared/addUnit/index.ts @@ -0,0 +1,42 @@ +// @ts-nocheck +import {isNumeric} from '../isNumeric' +import {isDef} from '../isDef' +/** + * 给一个值添加单位(像素 px) + * @param value 要添加单位的值,可以是字符串或数字 + * @returns 添加了单位的值,如果值为 null 则返回 null + */ + +// #ifndef UNI-APP-X && APP +export function addUnit(value?: string | number): string | null { + if (!isDef(value)) { + return null; + } + value = String(value); // 将值转换为字符串 + // 如果值是数字,则在后面添加单位 "px",否则保持原始值 + return isNumeric(value) ? `${value}px` : value; +} +// #endif + + +// #ifdef UNI-APP-X && APP +function addUnit(value: string): string +function addUnit(value: number): string +function addUnit(value: any|null): string|null { + if (!isDef(value)) { + return null; + } + value = `${value}` //value.toString(); // 将值转换为字符串 + + // 如果值是数字,则在后面添加单位 "px",否则保持原始值 + return isNumeric(value) ? `${value}px` : value; +} +export {addUnit} +// #endif + + +// console.log(addUnit(100)); // 输出: "100px" +// console.log(addUnit("200")); // 输出: "200px" +// console.log(addUnit("300px")); // 输出: "300px"(已经包含单位) +// console.log(addUnit()); // 输出: undefined(值为 undefined) +// console.log(addUnit(null)); // 输出: undefined(值为 null) \ No newline at end of file diff --git a/uni_modules/lime-shared/animation/bezier.ts b/uni_modules/lime-shared/animation/bezier.ts new file mode 100644 index 0000000..b4239e1 --- /dev/null +++ b/uni_modules/lime-shared/animation/bezier.ts @@ -0,0 +1,82 @@ +export function cubicBezier(p1x : number, p1y : number, p2x : number, p2y : number):(x: number)=> number { + const ZERO_LIMIT = 1e-6; + // Calculate the polynomial coefficients, + // implicit first and last control points are (0,0) and (1,1). + const ax = 3 * p1x - 3 * p2x + 1; + const bx = 3 * p2x - 6 * p1x; + const cx = 3 * p1x; + + const ay = 3 * p1y - 3 * p2y + 1; + const by = 3 * p2y - 6 * p1y; + const cy = 3 * p1y; + + function sampleCurveDerivativeX(t : number) : number { + // `ax t^3 + bx t^2 + cx t` expanded using Horner's rule + return (3 * ax * t + 2 * bx) * t + cx; + } + + function sampleCurveX(t : number) : number { + return ((ax * t + bx) * t + cx) * t; + } + + function sampleCurveY(t : number) : number { + return ((ay * t + by) * t + cy) * t; + } + + // Given an x value, find a parametric value it came from. + function solveCurveX(x : number) : number { + let t2 = x; + let derivative : number; + let x2 : number; + + // https://trac.webkit.org/browser/trunk/Source/WebCore/platform/animation + // first try a few iterations of Newton's method -- normally very fast. + // http://en.wikipedia.org/wikiNewton's_method + for (let i = 0; i < 8; i++) { + // f(t) - x = 0 + x2 = sampleCurveX(t2) - x; + if (Math.abs(x2) < ZERO_LIMIT) { + return t2; + } + derivative = sampleCurveDerivativeX(t2); + // == 0, failure + /* istanbul ignore if */ + if (Math.abs(derivative) < ZERO_LIMIT) { + break; + } + t2 -= x2 / derivative; + } + + // Fall back to the bisection method for reliability. + // bisection + // http://en.wikipedia.org/wiki/Bisection_method + let t1 = 1; + /* istanbul ignore next */ + let t0 = 0; + + /* istanbul ignore next */ + t2 = x; + /* istanbul ignore next */ + while (t1 > t0) { + x2 = sampleCurveX(t2) - x; + if (Math.abs(x2) < ZERO_LIMIT) { + return t2; + } + if (x2 > 0) { + t1 = t2; + } else { + t0 = t2; + } + t2 = (t1 + t0) / 2; + } + + // Failure + return t2; + } + + return function (x : number) : number { + return sampleCurveY(solveCurveX(x)); + } + + // return solve; +} \ No newline at end of file diff --git a/uni_modules/lime-shared/animation/ease.ts b/uni_modules/lime-shared/animation/ease.ts new file mode 100644 index 0000000..0af156e --- /dev/null +++ b/uni_modules/lime-shared/animation/ease.ts @@ -0,0 +1,3 @@ +import {cubicBezier} from './bezier'; +export let ease = cubicBezier(0.25, 0.1, 0.25, 1); +export let linear = cubicBezier(0,0,1,1); \ No newline at end of file diff --git a/uni_modules/lime-shared/animation/index.ts b/uni_modules/lime-shared/animation/index.ts new file mode 100644 index 0000000..5979064 --- /dev/null +++ b/uni_modules/lime-shared/animation/index.ts @@ -0,0 +1,10 @@ +// @ts-nocheck +// #ifdef UNI-APP-X && APP +export * from './uvue.uts' +// #endif + + + +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif diff --git a/uni_modules/lime-shared/animation/useTransition.ts b/uni_modules/lime-shared/animation/useTransition.ts new file mode 100644 index 0000000..02b0f4a --- /dev/null +++ b/uni_modules/lime-shared/animation/useTransition.ts @@ -0,0 +1,98 @@ +// @ts-nocheck +import { ComponentPublicInstance } from 'vue' +import { ease, linear } from './ease'; +import { Timeline, Animation } from './'; +export type UseTransitionOptions = { + duration ?: number + immediate ?: boolean + context ?: ComponentPublicInstance +} +// #ifndef UNI-APP-X && APP +import { ref, watch, Ref } from '@/uni_modules/lime-shared/vue' + +export function useTransition(percent : Ref<number>|(() => number), options : UseTransitionOptions) : Ref<number> { + const current = ref(0) + const { immediate, duration = 300 } = options + let tl:Timeline|null = null; + let timer = -1 + const isFunction = typeof percent === 'function' + watch(isFunction ? percent : () => percent.value, (v) => { + if(tl == null){ + tl = new Timeline() + } + tl.start(); + tl.add( + new Animation( + current.value, + v, + duration, + 0, + ease, + nowValue => { + current.value = nowValue + clearTimeout(timer) + if(current.value == v){ + timer = setTimeout(()=>{ + tl?.pause(); + tl = null + }, duration) + } + } + ) + ); + }, { immediate }) + + return current +} + +// #endif + +// #ifdef UNI-APP-X && APP +type UseTransitionReturnType = Ref<number> +// export function useTransition<T>(source : any, options : UseTransitionOptions) : UseTransitionReturnType +export function useTransition(source : any, options : UseTransitionOptions) : UseTransitionReturnType { + const outputRef : Ref<number> = ref(0) + const immediate = options.immediate ?? false + const duration = options.duration ?? 300 + const context = options.context //as ComponentPublicInstance | null + let tl:Timeline|null = null; + let timer = -1 + const watchFunc = (v : number) => { + if(tl == null){ + tl = new Timeline() + } + tl!.start(); + tl!.add( + new Animation( + outputRef.value, + v, + duration, + 0, + ease, + nowValue => { + outputRef.value = nowValue //nowValue < 0.0001 ? 0 : Math.abs(v - nowValue) < 0.00001 ? v : nowValue; + clearTimeout(timer) + if(outputRef.value == v){ + timer = setTimeout(()=>{ + tl?.pause(); + tl = null + }, duration) + } + } + ), null + ); + } + + if (context != null && typeof source == 'string') { + context.$watch(source, watchFunc, { immediate } as WatchOptions) + } else { + watch(source, watchFunc, { immediate } as WatchOptions) + } + + const stop = ()=>{ + + } + return outputRef //as UseTransitionReturnType +} + +// #endif \ No newline at end of file diff --git a/uni_modules/lime-shared/animation/uvue.uts b/uni_modules/lime-shared/animation/uvue.uts new file mode 100644 index 0000000..9b6936c --- /dev/null +++ b/uni_modules/lime-shared/animation/uvue.uts @@ -0,0 +1,117 @@ +// @ts-nocheck +import {raf, cancelRaf} from '../raf' +export class Timeline { + state : string + animations : Set<Animation> = new Set<Animation>() + delAnimations : Animation[] = [] + startTimes : Map<Animation, number> = new Map<Animation, number>() + pauseTime : number = 0 + pauseStart : number = Date.now() + tickHandler : number = 0 + tickHandlers : number[] = [] + tick : (() => void) | null = null + constructor() { + this.state = 'Initiated'; + } + start() { + if (!(this.state === 'Initiated')) return; + this.state = 'Started'; + + let startTime = Date.now(); + this.pauseTime = 0; + this.tick = () => { + let now = Date.now(); + this.animations.forEach((animation : Animation) => { + let t:number; + const ani = this.startTimes.get(animation) + if (ani == null) return + if (ani < startTime) { + t = now - startTime - animation.delay - this.pauseTime; + } else { + t = now - ani - animation.delay - this.pauseTime; + } + if (t > animation.duration) { + this.delAnimations.push(animation) + // 不能在 foreach 里面 对 集合进行删除操作 + // this.animations.delete(animation); + t = animation.duration; + } + if (t > 0) animation.run(t); + }) + // 不能在 foreach 里面 对 集合进行删除操作 + while (this.delAnimations.length > 0) { + const animation = this.delAnimations.pop(); + if (animation == null) return + this.animations.delete(animation); + } + clearTimeout(this.tickHandler); + if (this.state != 'Started') return + // this.tickHandler = setTimeout(() => { + // this.tick!() + // }, 1000 / 60) + this.tickHandler = raf(()=> { + this.tick!() + }) + // this.tickHandlers.push(this.tickHandler) + } + this.tick!() + } + pause() { + if (!(this.state === 'Started')) return; + this.state = 'Paused'; + this.pauseStart = Date.now(); + // clearTimeout(this.tickHandler); + cancelRaf(this.tickHandler); + } + resume() { + if (!(this.state === 'Paused')) return; + this.state = 'Started'; + this.pauseTime += Date.now() - this.pauseStart; + this.tick!(); + } + reset() { + this.pause(); + this.state = 'Initiated'; + this.pauseTime = 0; + this.pauseStart = 0; + this.animations.clear() + this.delAnimations.clear() + this.startTimes.clear() + this.tickHandler = 0; + } + add(animation : Animation, startTime ?: number | null) { + if (startTime == null) startTime = Date.now(); + this.animations.add(animation); + this.startTimes.set(animation, startTime); + } +} + +export class Animation { + startValue : number + endValue : number + duration : number + timingFunction : (t : number) => number + delay : number + template : (t : number) => void + constructor( + startValue : number, + endValue : number, + duration : number, + delay : number, + timingFunction : (t : number) => number, + template : (v : number) => void) { + this.startValue = startValue; + this.endValue = endValue; + this.duration = duration; + this.timingFunction = timingFunction; + this.delay = delay; + this.template = template; + } + + run(time : number) { + let range = this.endValue - this.startValue; + let progress = time / this.duration + if(progress != 1) progress = this.timingFunction(progress) + this.template(this.startValue + range * progress) + } +} \ No newline at end of file diff --git a/uni_modules/lime-shared/animation/vue.ts b/uni_modules/lime-shared/animation/vue.ts new file mode 100644 index 0000000..30f89e5 --- /dev/null +++ b/uni_modules/lime-shared/animation/vue.ts @@ -0,0 +1,123 @@ +// @ts-nocheck +const TICK = Symbol('tick'); +const TICK_HANDLER = Symbol('tick-handler'); +const ANIMATIONS = Symbol('animations'); +const START_TIMES = Symbol('start-times'); +const PAUSE_START = Symbol('pause-start'); +const PAUSE_TIME = Symbol('pause-time'); +const _raf = typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame : function(cb: Function) {return setTimeout(cb, 1000/60)} +const _caf = typeof cancelAnimationFrame !== 'undefined' ? cancelAnimationFrame: function(id: any) {clearTimeout(id)} + +// const TICK = 'tick'; +// const TICK_HANDLER = 'tick-handler'; +// const ANIMATIONS = 'animations'; +// const START_TIMES = 'start-times'; +// const PAUSE_START = 'pause-start'; +// const PAUSE_TIME = 'pause-time'; +// const _raf = function(callback):number|null {return setTimeout(callback, 1000/60)} +// const _caf = function(id: number):void {clearTimeout(id)} + +export class Timeline { + state: string + constructor() { + this.state = 'Initiated'; + this[ANIMATIONS] = new Set(); + this[START_TIMES] = new Map(); + } + start() { + if (!(this.state === 'Initiated')) return; + this.state = 'Started'; + + let startTime = Date.now(); + this[PAUSE_TIME] = 0; + this[TICK] = () => { + let now = Date.now(); + this[ANIMATIONS].forEach((animation) => { + let t: number; + if (this[START_TIMES].get(animation) < startTime) { + t = now - startTime - animation.delay - this[PAUSE_TIME]; + } else { + t = now - this[START_TIMES].get(animation) - animation.delay - this[PAUSE_TIME]; + } + + if (t > animation.duration) { + this[ANIMATIONS].delete(animation); + t = animation.duration; + } + if (t > 0) animation.run(t); + }) + // for (let animation of this[ANIMATIONS]) { + // let t: number; + // console.log('animation', animation) + // if (this[START_TIMES].get(animation) < startTime) { + // t = now - startTime - animation.delay - this[PAUSE_TIME]; + // } else { + // t = now - this[START_TIMES].get(animation) - animation.delay - this[PAUSE_TIME]; + // } + + // if (t > animation.duration) { + // this[ANIMATIONS].delete(animation); + // t = animation.duration; + // } + // if (t > 0) animation.run(t); + // } + this[TICK_HANDLER] = _raf(this[TICK]); + }; + this[TICK](); + } + pause() { + if (!(this.state === 'Started')) return; + this.state = 'Paused'; + + this[PAUSE_START] = Date.now(); + _caf(this[TICK_HANDLER]); + } + resume() { + if (!(this.state === 'Paused')) return; + this.state = 'Started'; + + this[PAUSE_TIME] += Date.now() - this[PAUSE_START]; + this[TICK](); + } + reset() { + this.pause(); + this.state = 'Initiated'; + this[PAUSE_TIME] = 0; + this[PAUSE_START] = 0; + this[ANIMATIONS] = new Set(); + this[START_TIMES] = new Map(); + this[TICK_HANDLER] = null; + } + add(animation: any, startTime?: number) { + if (arguments.length < 2) startTime = Date.now(); + this[ANIMATIONS].add(animation); + this[START_TIMES].set(animation, startTime); + } +} + +export class Animation { + startValue: number + endValue: number + duration: number + timingFunction: (t: number) => number + delay: number + template: (t: number) => void + constructor(startValue: number, endValue: number, duration: number, delay: number, timingFunction: (t: number) => number, template: (v: number) => void) { + timingFunction = timingFunction || (v => v); + template = template || (v => v); + + this.startValue = startValue; + this.endValue = endValue; + this.duration = duration; + this.timingFunction = timingFunction; + this.delay = delay; + this.template = template; + } + + run(time: number) { + let range = this.endValue - this.startValue; + let progress = time / this.duration + if(progress != 1) progress = this.timingFunction(progress) + this.template(this.startValue + range * progress) + } +} \ No newline at end of file diff --git a/uni_modules/lime-shared/arrayBufferToFile/index.ts b/uni_modules/lime-shared/arrayBufferToFile/index.ts new file mode 100644 index 0000000..49ee530 --- /dev/null +++ b/uni_modules/lime-shared/arrayBufferToFile/index.ts @@ -0,0 +1,8 @@ +// @ts-nocheck +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif + +// #ifdef UNI-APP-X && APP +export * from './uvue.uts' +// #endif \ No newline at end of file diff --git a/uni_modules/lime-shared/arrayBufferToFile/uvue.uts b/uni_modules/lime-shared/arrayBufferToFile/uvue.uts new file mode 100644 index 0000000..65c7b14 --- /dev/null +++ b/uni_modules/lime-shared/arrayBufferToFile/uvue.uts @@ -0,0 +1,10 @@ +// @ts-nocheck +// import {platform} from '../platform' +/** + * buffer转路径 + * @param {Object} buffer + */ +// @ts-nocheck +export function arrayBufferToFile(buffer: ArrayBuffer, name?: string, format?:string):Promise<(File|string)> { + console.error('[arrayBufferToFile] 当前环境不支持') +} \ No newline at end of file diff --git a/uni_modules/lime-shared/arrayBufferToFile/vue.ts b/uni_modules/lime-shared/arrayBufferToFile/vue.ts new file mode 100644 index 0000000..9760b20 --- /dev/null +++ b/uni_modules/lime-shared/arrayBufferToFile/vue.ts @@ -0,0 +1,63 @@ +// @ts-nocheck +import {platform} from '../platform' +/** + * buffer转路径 + * @param {Object} buffer + */ +// @ts-nocheck +export function arrayBufferToFile(buffer: ArrayBuffer | Blob, name?: string, format?:string):Promise<(File|string)> { + return new Promise((resolve, reject) => { + // #ifdef MP + const fs = uni.getFileSystemManager() + //自定义文件名 + if (!name && !format) { + reject(new Error('ERROR_NAME_PARSE')) + } + const fileName = `${name || new Date().getTime()}.${format.replace(/(.+)?\//,'')}`; + let pre = platform() + const filePath = `${pre.env.USER_DATA_PATH}/${fileName}` + fs.writeFile({ + filePath, + data: buffer, + success() { + resolve(filePath) + }, + fail(err) { + console.error(err) + reject(err) + } + }) + // #endif + + // #ifdef H5 + const file = new File([buffer], name, { + type: format, + }); + resolve(file) + // #endif + + // #ifdef APP-PLUS + const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now()) + const base64 = uni.arrayBufferToBase64(buffer) + bitmap.loadBase64Data(base64, () => { + if (!name && !format) { + reject(new Error('ERROR_NAME_PARSE')) + } + const fileNmae = `${name || new Date().getTime()}.${format.replace(/(.+)?\//,'')}`; + const filePath = `_doc/uniapp_temp/${fileNmae}` + bitmap.save(filePath, {}, + () => { + bitmap.clear() + resolve(filePath) + }, + (error) => { + bitmap.clear() + reject(error) + }) + }, (error) => { + bitmap.clear() + reject(error) + }) + // #endif + }) +} \ No newline at end of file diff --git a/uni_modules/lime-shared/base64ToArrayBuffer/index.ts b/uni_modules/lime-shared/base64ToArrayBuffer/index.ts new file mode 100644 index 0000000..f83b640 --- /dev/null +++ b/uni_modules/lime-shared/base64ToArrayBuffer/index.ts @@ -0,0 +1,13 @@ +// @ts-nocheck +// 未完成 +export function base64ToArrayBuffer(base64 : string) { + const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || []; + if (!format) { + new Error('ERROR_BASE64SRC_PARSE') + } + if(uni.base64ToArrayBuffer) { + return uni.base64ToArrayBuffer(bodyData) + } else { + + } +} \ No newline at end of file diff --git a/uni_modules/lime-shared/base64ToPath/index.ts b/uni_modules/lime-shared/base64ToPath/index.ts new file mode 100644 index 0000000..af9a9d2 --- /dev/null +++ b/uni_modules/lime-shared/base64ToPath/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif + + +// #ifdef UNI-APP-X && APP +export * from './uvue.uts' +// #endif diff --git a/uni_modules/lime-shared/base64ToPath/uvue.uts b/uni_modules/lime-shared/base64ToPath/uvue.uts new file mode 100644 index 0000000..7019ecb --- /dev/null +++ b/uni_modules/lime-shared/base64ToPath/uvue.uts @@ -0,0 +1,22 @@ +// @ts-nocheck +import { processFile, ProcessFileOptions } from '@/uni_modules/lime-file-utils' + +/** + * base64转路径 + * @param {Object} base64 + */ +export function base64ToPath(base64: string, filename: string | null = null):Promise<string> { + return new Promise((resolve,reject) => { + processFile({ + type: 'toDataURL', + path: base64, + filename, + success(res: string){ + resolve(res) + }, + fail(err){ + reject(err) + } + } as ProcessFileOptions) + }) +} \ No newline at end of file diff --git a/uni_modules/lime-shared/base64ToPath/vue.ts b/uni_modules/lime-shared/base64ToPath/vue.ts new file mode 100644 index 0000000..735000f --- /dev/null +++ b/uni_modules/lime-shared/base64ToPath/vue.ts @@ -0,0 +1,75 @@ +// @ts-nocheck +import {platform} from '../platform' +/** + * base64转路径 + * @param {Object} base64 + */ +export function base64ToPath(base64: string, filename?: string):Promise<string> { + const [, format] = /^data:image\/(\w+);base64,/.exec(base64) || []; + return new Promise((resolve, reject) => { + // #ifdef MP + const fs = uni.getFileSystemManager() + //自定义文件名 + if (!filename && !format) { + reject(new Error('ERROR_BASE64SRC_PARSE')) + } + // const time = new Date().getTime(); + const name = filename || `${new Date().getTime()}.${format}`; + let pre = platform() + const filePath = `${pre.env.USER_DATA_PATH}/${name}` + fs.writeFile({ + filePath, + data: base64.split(',')[1], + encoding: 'base64', + success() { + resolve(filePath) + }, + fail(err) { + console.error(err) + reject(err) + } + }) + // #endif + + // #ifdef H5 + // mime类型 + let mimeString = base64.split(',')[0].split(':')[1].split(';')[0]; + //base64 解码 + let byteString = atob(base64.split(',')[1]); + //创建缓冲数组 + let arrayBuffer = new ArrayBuffer(byteString.length); + //创建视图 + let intArray = new Uint8Array(arrayBuffer); + for (let i = 0; i < byteString.length; i++) { + intArray[i] = byteString.charCodeAt(i); + } + resolve(URL.createObjectURL(new Blob([intArray], { + type: mimeString + }))) + // #endif + + // #ifdef APP-PLUS + const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now()) + bitmap.loadBase64Data(base64, () => { + if (!filename && !format) { + reject(new Error('ERROR_BASE64SRC_PARSE')) + } + // const time = new Date().getTime(); + const name = filename || `${new Date().getTime()}.${format}`; + const filePath = `_doc/uniapp_temp/${name}` + bitmap.save(filePath, {}, + () => { + bitmap.clear() + resolve(filePath) + }, + (error) => { + bitmap.clear() + reject(error) + }) + }, (error) => { + bitmap.clear() + reject(error) + }) + // #endif + }) +} \ No newline at end of file diff --git a/uni_modules/lime-shared/camelCase/index.ts b/uni_modules/lime-shared/camelCase/index.ts new file mode 100644 index 0000000..dd470ab --- /dev/null +++ b/uni_modules/lime-shared/camelCase/index.ts @@ -0,0 +1,21 @@ +/** + * 将字符串转换为 camelCase 或 PascalCase 风格的命名约定 + * @param str 要转换的字符串 + * @param isPascalCase 指示是否转换为 PascalCase 的布尔值,默认为 false + * @returns 转换后的字符串 + */ +export function camelCase(str: string, isPascalCase: boolean = false): string { + // 将字符串分割成单词数组 + let words: string[] = str.split(/[\s_-]+/); + + // 将数组中的每个单词首字母大写(除了第一个单词) + let camelCased: string[] = words.map((word, index):string => { + if (index == 0 && !isPascalCase) { + return word.toLowerCase(); // 第一个单词全小写 + } + return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); + }); + + // 将数组中的单词拼接成一个字符串 + return camelCased.join(''); +}; \ No newline at end of file diff --git a/uni_modules/lime-shared/canIUseCanvas2d/index.ts b/uni_modules/lime-shared/canIUseCanvas2d/index.ts new file mode 100644 index 0000000..2d60e65 --- /dev/null +++ b/uni_modules/lime-shared/canIUseCanvas2d/index.ts @@ -0,0 +1,67 @@ +// @ts-nocheck + +// #ifndef UNI-APP-X && APP + +// #ifdef MP-ALIPAY +interface My { + SDKVersion: string +} +declare var my: My +// #endif + +function compareVersion(v1:string, v2:string) { + let a1 = v1.split('.'); + let a2 = v2.split('.'); + const len = Math.max(a1.length, a2.length); + + while (a1.length < len) { + a1.push('0'); + } + while (a2.length < len) { + a2.push('0'); + } + + for (let i = 0; i < len; i++) { + const num1 = parseInt(a1[i], 10); + const num2 = parseInt(a2[i], 10); + + if (num1 > num2) { + return 1; + } + if (num1 < num2) { + return -1; + } + } + + return 0; +} + +function gte(version: string) { + let {SDKVersion} = uni.getSystemInfoSync(); + // #ifdef MP-ALIPAY + SDKVersion = my.SDKVersion + // #endif + return compareVersion(SDKVersion, version) >= 0; +} +// #endif + + +/** 环境是否支持canvas 2d */ +export function canIUseCanvas2d(): boolean { + // #ifdef MP-WEIXIN + return gte('2.9.0'); + // #endif + // #ifdef MP-ALIPAY + return gte('2.7.0'); + // #endif + // #ifdef MP-TOUTIAO + return gte('1.78.0'); + // #endif + // #ifndef MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO + return false + // #endif + + // #ifdef UNI-APP-X && APP || APP-NVUE || APP-VUE + return false; + // #endif +} diff --git a/uni_modules/lime-shared/capitalizedAmount/index.ts b/uni_modules/lime-shared/capitalizedAmount/index.ts new file mode 100644 index 0000000..9a0c1f4 --- /dev/null +++ b/uni_modules/lime-shared/capitalizedAmount/index.ts @@ -0,0 +1,111 @@ +// @ts-nocheck +import { isString } from "../isString"; +import { isNumber } from "../isNumber"; +/** + * 将金额转换为中文大写形式 + * @param {number | string} amount - 需要转换的金额,可以是数字或字符串 + * @returns {string} 转换后的中文大写金额 + */ +function capitalizedAmount(amount : number) : string +function capitalizedAmount(amount : string) : string +function capitalizedAmount(amount : any | null) : string { + try { + let _amountStr :string; + let _amountNum :number = 0; + // 如果输入是字符串,先将其转换为数字,并去除逗号 + if (typeof amount == 'string') { + _amountNum = parseFloat((amount as string).replace(/,/g, '')); + } + if(isNumber(amount)) { + _amountNum = amount as number + } + // 判断输入是否为有效的金额 || isNaN(amount) + if (amount == null) throw new Error('不是有效的金额!'); + + let result = ''; + + // 处理负数情况 + if (_amountNum < 0) { + result = '欠'; + _amountNum = Math.abs(_amountNum); + } + + // 金额不能超过千亿以上 + if (_amountNum >= 10e11) throw new Error('计算金额过大!'); + + // 保留两位小数并转换为字符串 + _amountStr = _amountNum.toFixed(2); + + // 定义数字、单位和小数单位的映射 + const digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']; + const units = ['', '拾', '佰', '仟']; + const bigUnits = ['', '万', '亿']; + const decimalUnits = ['角', '分']; + + // 分离整数部分和小数部分 + const amountArray = _amountStr.split('.'); + let integerPart = amountArray[0]; // string| number[] + const decimalPart = amountArray[1]; + + // 处理整数部分 + if (integerPart != '0') { + let _integerPart = integerPart.split('').map((item):number => parseInt(item)); + + // 将整数部分按四位一级进行分组 + const levels = _integerPart.reverse().reduce((prev:string[][], item, index):string[][] => { + // const level = prev?.[0]?.length < 4 ? prev[0] : []; + const level = prev.length > 0 && prev[0].length < 4 ? prev[0]: [] + + const value = item == 0 ? digits[item] : digits[item] + units[index % 4]; + + level.unshift(value); + + if (level.length == 1) { + prev.unshift(level); + } else { + prev[0] = level; + } + + return prev; + }, [] as string[][]); + // 将分组后的整数部分转换为中文大写形式 + result += levels.reduce((prev, item, index):string => { + let _level = bigUnits[levels.length - index - 1]; + let _item = item.join('').replace(/(零)\1+/g, '$1'); + + if (_item == '零') { + _level = ''; + _item = ''; + } else if (_item.endsWith('零')) { + _item = _item.slice(0, _item.length - 1); + } + + return prev + _item + _level; + }, ''); + } else { + result += '零'; + } + + // 添加元 + result += '元'; + + // 处理小数部分 + if (decimalPart != '00') { + if (result == '零元') result = ''; + + for (let i = 0; i < decimalPart.length; i++) { + const digit = parseInt(decimalPart.charAt(i)); + + if (digit != 0) { + result += digits[digit] + decimalUnits[i]; + } + } + } else { + result += '整'; + } + + return result; + } catch (error : Error) { + return error.message; + } +}; \ No newline at end of file diff --git a/uni_modules/lime-shared/changelog.md b/uni_modules/lime-shared/changelog.md new file mode 100644 index 0000000..f791344 --- /dev/null +++ b/uni_modules/lime-shared/changelog.md @@ -0,0 +1,40 @@ +## 0.1.8(2024-10-08) +- fix: vue2 条件编译 // #ifdef APP-IOS || APP-ANDROID 会生效 +## 0.1.7(2024-09-23) +- fix: raf 类型跟随版本变更 +## 0.1.6(2024-07-24) +- fix: vue2 app ts需要明确的后缀,所有补全 +- chore: 减少依赖 +## 0.1.5(2024-07-21) +- feat: 删除 Hooks +- feat: 兼容uniappx +## 0.1.4(2023-09-05) +- feat: 增加 Hooks `useIntersectionObserver` +- feat: 增加 `floatAdd` +- feat: 因为本人插件兼容 vue2 需要使用 `composition-api`,故增加vue文件代码插件的条件编译 +## 0.1.3(2023-08-13) +- feat: 增加 `camelCase` +## 0.1.2(2023-07-17) +- feat: 增加 `getClassStr` +## 0.1.1(2023-07-06) +- feat: 增加 `isNumeric`, 区别于 `isNumber` +## 0.1.0(2023-06-30) +- fix: `clamp`忘记导出了 +## 0.0.9(2023-06-27) +- feat: 增加`arrayBufferToFile` +## 0.0.8(2023-06-19) +- feat: 增加`createAnimation`、`clamp` +## 0.0.7(2023-06-08) +- chore: 更新注释 +## 0.0.6(2023-06-08) +- chore: 增加`createImage`为`lime-watermark`和`lime-qrcode`提供依赖 +## 0.0.5(2023-06-03) +- chore: 更新注释 +## 0.0.4(2023-05-22) +- feat: 增加`range`,`exif`,`selectComponent` +## 0.0.3(2023-05-08) +- feat: 增加`fillZero`,`debounce`,`throttle`,`random` +## 0.0.2(2023-05-05) +- chore: 更新文档 +## 0.0.1(2023-05-05) +- 无 diff --git a/uni_modules/lime-shared/clamp/index.ts b/uni_modules/lime-shared/clamp/index.ts new file mode 100644 index 0000000..0e16358 --- /dev/null +++ b/uni_modules/lime-shared/clamp/index.ts @@ -0,0 +1,16 @@ +// @ts-nocheck +/** + * 将一个值限制在指定的范围内 + * @param val 要限制的值 + * @param min 最小值 + * @param max 最大值 + * @returns 限制后的值 + */ +export function clamp(val: number, min: number, max: number): number { + return Math.max(min, Math.min(max, val)); +} + + +// console.log(clamp(5 ,0, 10)); // 输出: 5(在范围内,不做更改) +// console.log(clamp(-5 ,0, 10)); // 输出: 0(小于最小值,被限制为最小值) +// console.log(clamp(15 ,0, 10)); // 输出: 10(大于最大值,被限制为最大值) \ No newline at end of file diff --git a/uni_modules/lime-shared/cloneDeep/index.ts b/uni_modules/lime-shared/cloneDeep/index.ts new file mode 100644 index 0000000..3428c49 --- /dev/null +++ b/uni_modules/lime-shared/cloneDeep/index.ts @@ -0,0 +1,10 @@ +// @ts-nocheck + +// #ifdef UNI-APP-X && APP +export * from './uvue.ts' +// #endif + + +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif \ No newline at end of file diff --git a/uni_modules/lime-shared/cloneDeep/uvue.ts b/uni_modules/lime-shared/cloneDeep/uvue.ts new file mode 100644 index 0000000..7929bc2 --- /dev/null +++ b/uni_modules/lime-shared/cloneDeep/uvue.ts @@ -0,0 +1,17 @@ +// @ts-nocheck +/** + * 深度克隆一个对象或数组 + * @param obj 要克隆的对象或数组 + * @returns 克隆后的对象或数组 + */ +export function cloneDeep<T>(obj: any): T { + // 如果传入的对象是基本数据类型(如字符串、数字等),则直接返回 + // if(['number', 'string'].includes(typeof obj) || Array.isArray(obj)){ + // return obj as T + // } + if(typeof obj == 'object'){ + return JSON.parse(JSON.stringify(obj as T)) as T; + } + return obj as T +} + diff --git a/uni_modules/lime-shared/cloneDeep/vue.ts b/uni_modules/lime-shared/cloneDeep/vue.ts new file mode 100644 index 0000000..ded334d --- /dev/null +++ b/uni_modules/lime-shared/cloneDeep/vue.ts @@ -0,0 +1,103 @@ +// @ts-nocheck +/** + * 深度克隆一个对象或数组 + * @param obj 要克隆的对象或数组 + * @returns 克隆后的对象或数组 + */ +export function cloneDeep<T>(obj: any): T { + // 如果传入的对象为空,返回空 + if (obj === null) { + return null as unknown as T; + } + + // 如果传入的对象是 Set 类型,则将其转换为数组,并通过新的 Set 构造函数创建一个新的 Set 对象 + if (obj instanceof Set) { + return new Set([...obj]) as unknown as T; + } + + // 如果传入的对象是 Map 类型,则将其转换为数组,并通过新的 Map 构造函数创建一个新的 Map 对象 + if (obj instanceof Map) { + return new Map([...obj]) as unknown as T; + } + + // 如果传入的对象是 WeakMap 类型,则直接用传入的 WeakMap 对象进行赋值 + if (obj instanceof WeakMap) { + let weakMap = new WeakMap(); + weakMap = obj; + return weakMap as unknown as T; + } + + // 如果传入的对象是 WeakSet 类型,则直接用传入的 WeakSet 对象进行赋值 + if (obj instanceof WeakSet) { + let weakSet = new WeakSet(); + weakSet = obj; + return weakSet as unknown as T; + } + + // 如果传入的对象是 RegExp 类型,则通过新的 RegExp 构造函数创建一个新的 RegExp 对象 + if (obj instanceof RegExp) { + return new RegExp(obj) as unknown as T; + } + + // 如果传入的对象是 undefined 类型,则返回 undefined + if (typeof obj === 'undefined') { + return undefined as unknown as T; + } + + // 如果传入的对象是数组,则递归调用 cloneDeep 函数对数组中的每个元素进行克隆 + if (Array.isArray(obj)) { + return obj.map(cloneDeep) as unknown as T; + } + + // 如果传入的对象是 Date 类型,则通过新的 Date 构造函数创建一个新的 Date 对象 + if (obj instanceof Date) { + return new Date(obj.getTime()) as unknown as T; + } + + // 如果传入的对象是普通对象,则使用递归调用 cloneDeep 函数对对象的每个属性进行克隆 + if (typeof obj === 'object') { + const newObj: any = {}; + for (const [key, value] of Object.entries(obj)) { + newObj[key] = cloneDeep(value); + } + const symbolKeys = Object.getOwnPropertySymbols(obj); + for (const key of symbolKeys) { + newObj[key] = cloneDeep(obj[key]); + } + return newObj; + } + + // 如果传入的对象是基本数据类型(如字符串、数字等),则直接返回 + return obj; +} + +// 示例使用 + +// // 克隆一个对象 +// const obj = { name: 'John', age: 30 }; +// const clonedObj = cloneDeep(obj); + +// console.log(clonedObj); // 输出: { name: 'John', age: 30 } +// console.log(clonedObj === obj); // 输出: false (副本与原对象是独立的) + +// // 克隆一个数组 +// const arr = [1, 2, 3]; +// const clonedArr = cloneDeep(arr); + +// console.log(clonedArr); // 输出: [1, 2, 3] +// console.log(clonedArr === arr); // 输出: false (副本与原数组是独立的) + +// // 克隆一个包含嵌套对象的对象 +// const person = { +// name: 'Alice', +// age: 25, +// address: { +// city: 'New York', +// country: 'USA', +// }, +// }; +// const clonedPerson = cloneDeep(person); + +// console.log(clonedPerson); // 输出: { name: 'Alice', age: 25, address: { city: 'New York', country: 'USA' } } +// console.log(clonedPerson === person); // 输出: false (副本与原对象是独立的) +// console.log(clonedPerson.address === person.address); // 输出: false (嵌套对象的副本也是独立的) \ No newline at end of file diff --git a/uni_modules/lime-shared/closest/index.ts b/uni_modules/lime-shared/closest/index.ts new file mode 100644 index 0000000..e6e79c2 --- /dev/null +++ b/uni_modules/lime-shared/closest/index.ts @@ -0,0 +1,22 @@ +// @ts-nocheck + +/** + * 在给定数组中找到最接近目标数字的元素。 + * @param arr 要搜索的数字数组。 + * @param target 目标数字。 + * @returns 最接近目标数字的数组元素。 + */ +export function closest(arr: number[], target: number):number { + return arr.reduce((pre: number, cur: number):number => + Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur + ); +} + +// 示例 +// // 定义一个数字数组 +// const numbers = [1, 3, 5, 7, 9]; + +// // 在数组中找到最接近目标数字 6 的元素 +// const closestNumber = closest(numbers, 6); + +// console.log(closestNumber); // 输出结果: 5 \ No newline at end of file diff --git a/uni_modules/lime-shared/components/lime-shared/lime-shared.vue b/uni_modules/lime-shared/components/lime-shared/lime-shared.vue new file mode 100644 index 0000000..e2d7fe2 --- /dev/null +++ b/uni_modules/lime-shared/components/lime-shared/lime-shared.vue @@ -0,0 +1,156 @@ +<template> + <view id="shared" style="height: 500px; width: 300px; background-color: aqua;"> + + </view> +</template> + +<script lang="ts"> + import { getRect, getAllRect } from '@/uni_modules/lime-shared/getRect' + + import { camelCase } from '@/uni_modules/lime-shared/camelCase' + import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d' + import { clamp } from '@/uni_modules/lime-shared/clamp' + import { cloneDeep } from '@/uni_modules/lime-shared/cloneDeep' + import { closest } from '@/uni_modules/lime-shared/closest' + import { debounce } from '@/uni_modules/lime-shared/debounce' + import { fillZero } from '@/uni_modules/lime-shared/fillZero' + import { floatAdd } from '@/uni_modules/lime-shared/floatAdd' + import { floatMul } from '@/uni_modules/lime-shared/floatMul' + import { floatDiv } from '@/uni_modules/lime-shared/floatDiv' + import { floatSub } from '@/uni_modules/lime-shared/floatSub' + import { getClassStr } from '@/uni_modules/lime-shared/getClassStr' + import { getCurrentPage } from '@/uni_modules/lime-shared/getCurrentPage' + import { getStyleStr } from '@/uni_modules/lime-shared/getStyleStr' + import { hasOwn } from '@/uni_modules/lime-shared/hasOwn' + import { isBase64 } from '@/uni_modules/lime-shared/isBase64' + import { isBrowser } from '@/uni_modules/lime-shared/isBrowser' + import { isDef } from '@/uni_modules/lime-shared/isDef' + import { isEmpty } from '@/uni_modules/lime-shared/isEmpty' + import { isFunction } from '@/uni_modules/lime-shared/isFunction' + import { isNumber } from '@/uni_modules/lime-shared/isNumber' + import { isNumeric } from '@/uni_modules/lime-shared/isNumeric' + import { isObject } from '@/uni_modules/lime-shared/isObject' + import { isPromise } from '@/uni_modules/lime-shared/isPromise' + import { isString } from '@/uni_modules/lime-shared/isString' + import { kebabCase } from '@/uni_modules/lime-shared/kebabCase' + import { raf, doubleRaf } from '@/uni_modules/lime-shared/raf' + import { random } from '@/uni_modules/lime-shared/random' + import { range } from '@/uni_modules/lime-shared/range' + import { sleep } from '@/uni_modules/lime-shared/sleep' + import { throttle } from '@/uni_modules/lime-shared/throttle' + import { toArray } from '@/uni_modules/lime-shared/toArray' + import { toBoolean } from '@/uni_modules/lime-shared/toBoolean' + import { toNumber } from '@/uni_modules/lime-shared/toNumber' + import { unitConvert } from '@/uni_modules/lime-shared/unitConvert' + import { getCurrentInstance } from '@/uni_modules/lime-shared/vue' + import { capitalizedAmount } from '@/uni_modules/lime-shared/capitalizedAmount' + + // #ifdef VUE2 + type UTSJSONObject = any + // #endif + + const context = getCurrentInstance() + // getRect('#shared', context!).then(res =>{ + // console.log('res', res.bottom) + // }) + // getAllRect('#shared', context).then(res =>{ + // console.log('res', res) + // }) + + + // console.log('camelCase::', camelCase("hello world")); + // console.log('camelCase::', camelCase("my_name_is_john", true)); + // console.log('canIUseCanvas2d::', canIUseCanvas2d()); + // console.log('clamp::', clamp(5 ,0, 10)); + // console.log('cloneDeep::', cloneDeep<UTSJSONObject>({a:5})); + // console.log('closest::', closest([1, 3, 5, 7, 9], 6)); + + + + + // const saveData = (data: any) => { + // // 模拟保存数据的操作 + // console.log(`Saving data: ${data}`); + // } + + // const debouncedSaveData = debounce(saveData, 500); + // debouncedSaveData('Data 1'); + // debouncedSaveData('Data 2'); + + // console.log('fillZero', fillZero(1)) + // console.log('floatAdd', floatAdd(0.1, 0.2), floatAdd(1.05, 0.05), floatAdd(0.1, 0.7), floatAdd(0.0001, 0.0002), floatAdd(123.456 , 789.012)) + // console.log('floatMul', floatMul(0.1, 0.02), floatMul(1.0255, 100)) + // console.log('floatDiv', floatDiv(10.44, 100), floatDiv(1.0255, 100), floatDiv(5.419909340994699, 0.2)) + // console.log('floatSub', floatSub(0.4, 0.1), floatSub(1.0255, 100)) + const now = () : number => System.nanoTime() / 1_000_000.0 + console.log('capitalizedAmount', capitalizedAmount(0.4)) + console.log('capitalizedAmount', capitalizedAmount(100)) + console.log('capitalizedAmount', capitalizedAmount(100000000)) + console.log('capitalizedAmount', capitalizedAmount('2023.04')) + console.log('capitalizedAmount', capitalizedAmount(-1024)) + console.log('now', now(), Date.now()) + // console.log('getClassStr', getClassStr({hover: true})) + // console.log('getStyleStr', getStyleStr({ color: 'red', fontSize: '16px', backgroundColor: '', border: null })) + // console.log('hasOwn', hasOwn({a: true}, 'key')) + // console.log('isBase64::', isBase64("SGVsbG8sIFdvcmxkIQ==")); + // console.log('isBrowser::', isBrowser); + // console.log('isDef::', isDef('6')); + // console.log('isEmpty::', isEmpty({a: true})); + + // const b = () =>{} + // console.log('isFunction::', isFunction(b)); + // console.log('isNumber::', isNumber('6')); + // console.log('isNumeric::', isNumeric('6')); + // console.log('isObject::', isObject({})); + + // const promise = ():Promise<boolean> => { + // return new Promise((resolve) => { + // resolve(true) + // }) + // } + // const a = promise() + // console.log('isPromise::', isPromise(a)); + // console.log('isString::', isString('null')); + // console.log('kebabCase::', kebabCase('my love')); + // console.log('raf::', raf(()=>{ + // console.log('raf:::1') + // })); + // console.log('doubleRaf::', doubleRaf(()=>{ + // console.log('doubleRaf:::1') + // })); + // console.log('random', random(0, 10)) + // console.log('random', random(0, 1, 2)) + // console.log('range', range(0, 10, 2)) + // console.log('sleep', sleep(300).then(res => { + // console.log('log') + // })) + + // const handleScroll = (a: string) => { + // console.log("Scroll event handled!", a); + // } + + // // // 使用节流函数对 handleScroll 进行节流,间隔时间为 500 毫秒 + // const throttledScroll = throttle(handleScroll, 500); + // throttledScroll('5'); + // const page = getCurrentPage() + // console.log('getCurrentPage::', page) + + // console.log('toArray', toArray<number>(5)) + // console.log('toBoolean', toBoolean(5)) + // console.log('toNumber', toNumber('5')) + // console.log('unitConvert', unitConvert('5')) + + // uni.getImageInfo({ + // src: '/static/logo.png', + // success(res) { + // console.log('res', res) + // } + // }) + export default { + + } +</script> + +<style> + +</style> \ No newline at end of file diff --git a/uni_modules/lime-shared/createAnimation/index.ts b/uni_modules/lime-shared/createAnimation/index.ts new file mode 100644 index 0000000..999ec1c --- /dev/null +++ b/uni_modules/lime-shared/createAnimation/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +// #ifndef UNI-APP-X +export * from './type.ts' +export * from './vue.ts' +// #endif + +// #ifdef UNI-APP-X +export * from './uvue.ts' +// #endif \ No newline at end of file diff --git a/uni_modules/lime-shared/createAnimation/type.ts b/uni_modules/lime-shared/createAnimation/type.ts new file mode 100644 index 0000000..0f8ad34 --- /dev/null +++ b/uni_modules/lime-shared/createAnimation/type.ts @@ -0,0 +1,25 @@ +export type CreateAnimationOptions = { + /** + * 动画持续时间,单位ms + */ + duration ?: number; + /** + * 定义动画的效果 + * - linear: 动画从头到尾的速度是相同的 + * - ease: 动画以低速开始,然后加快,在结束前变慢 + * - ease-in: 动画以低速开始 + * - ease-in-out: 动画以低速开始和结束 + * - ease-out: 动画以低速结束 + * - step-start: 动画第一帧就跳至结束状态直到结束 + * - step-end: 动画一直保持开始状态,最后一帧跳到结束状态 + */ + timingFunction ?: string //'linear' | 'ease' | 'ease-in' | 'ease-in-out' | 'ease-out' | 'step-start' | 'step-end'; + /** + * 动画延迟时间,单位 ms + */ + delay ?: number; + /** + * 设置transform-origin + */ + transformOrigin ?: string; +} \ No newline at end of file diff --git a/uni_modules/lime-shared/createAnimation/uvue.ts b/uni_modules/lime-shared/createAnimation/uvue.ts new file mode 100644 index 0000000..96d8263 --- /dev/null +++ b/uni_modules/lime-shared/createAnimation/uvue.ts @@ -0,0 +1,5 @@ +// @ts-nocheck +// export * from '@/uni_modules/lime-animateIt' +export function createAnimation() { + console.error('当前环境不支持,请使用:lime-animateIt') +} \ No newline at end of file diff --git a/uni_modules/lime-shared/createAnimation/vue.ts b/uni_modules/lime-shared/createAnimation/vue.ts new file mode 100644 index 0000000..d6223d8 --- /dev/null +++ b/uni_modules/lime-shared/createAnimation/vue.ts @@ -0,0 +1,148 @@ +// @ts-nocheck +// nvue 需要在节点上设置ref或在export里传入 +// const animation = createAnimation({ +// ref: this.$refs['xxx'], +// duration: 0, +// timingFunction: 'linear' +// }) +// animation.opacity(1).translate(x, y).step({duration}) +// animation.export(ref) + +// 抹平nvue 与 uni.createAnimation的使用差距 +// 但是nvue动画太慢 + + + +import { CreateAnimationOptions } from './type' +// #ifdef APP-NVUE +const nvueAnimation = uni.requireNativePlugin('animation') + +type AnimationTypes = 'matrix' | 'matrix3d' | 'rotate' | 'rotate3d' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scale3d' | 'scaleX' | 'scaleY' | 'scaleZ' | 'skew' | 'skewX' | 'skewY' | 'translate' | 'translate3d' | 'translateX' | 'translateY' | 'translateZ' + | 'opacity' | 'backgroundColor' | 'width' | 'height' | 'left' | 'right' | 'top' | 'bottom' + +interface Styles { + [key : string] : any +} + +interface StepConfig { + duration?: number + timingFunction?: string + delay?: number + needLayout?: boolean + transformOrigin?: string +} +interface StepAnimate { + styles?: Styles + config?: StepConfig +} +interface StepAnimates { + [key: number]: StepAnimate +} +// export interface CreateAnimationOptions extends UniApp.CreateAnimationOptions { +// ref?: string +// } + +type Callback = (time: number) => void +const animateTypes1 : AnimationTypes[] = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d', + 'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY', + 'translateZ' +] +const animateTypes2 : AnimationTypes[] = ['opacity', 'backgroundColor'] +const animateTypes3 : AnimationTypes[] = ['width', 'height', 'left', 'right', 'top', 'bottom'] + +class LimeAnimation { + ref : any + context : any + options : UniApp.CreateAnimationOptions + // stack : any[] = [] + next : number = 0 + currentStepAnimates : StepAnimates = {} + duration : number = 0 + constructor(options : CreateAnimationOptions) { + const {ref} = options + this.ref = ref + this.options = options + } + addAnimate(type : AnimationTypes, args: (string | number)[]) { + let aniObj = this.currentStepAnimates[this.next] + let stepAnimate:StepAnimate = {} + if (!aniObj) { + stepAnimate = {styles: {}, config: {}} + } else { + stepAnimate = aniObj + } + + if (animateTypes1.includes(type)) { + if (!stepAnimate.styles.transform) { + stepAnimate.styles.transform = '' + } + let unit = '' + if (type === 'rotate') { + unit = 'deg' + } + stepAnimate.styles.transform += `${type}(${args.map((v: number) => v + unit).join(',')}) ` + } else { + stepAnimate.styles[type] = `${args.join(',')}` + } + this.currentStepAnimates[this.next] = stepAnimate + } + animateRun(styles: Styles = {}, config:StepConfig = {}, ref: any) { + const el = ref || this.ref + if (!el) return + return new Promise((resolve) => { + const time = +new Date() + nvueAnimation.transition(el, { + styles, + ...config + }, () => { + resolve(+new Date() - time) + }) + }) + } + nextAnimate(animates: StepAnimates, step: number = 0, ref: any, cb: Callback) { + let obj = animates[step] + if (obj) { + let { styles, config } = obj + // this.duration += config.duration + this.animateRun(styles, config, ref).then((time: number) => { + step += 1 + this.duration += time + this.nextAnimate(animates, step, ref, cb) + }) + } else { + this.currentStepAnimates = {} + cb && cb(this.duration) + } + } + step(config:StepConfig = {}) { + this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config) + this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin + this.next++ + return this + } + export(ref: any, cb?: Callback) { + ref = ref || this.ref + if(!ref) return + this.duration = 0 + this.next = 0 + this.nextAnimate(this.currentStepAnimates, 0, ref, cb) + return null + } +} + + +animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => { + LimeAnimation.prototype[type] = function(...args: (string | number)[]) { + this.addAnimate(type, args) + return this + } +}) +// #endif +export function createAnimation(options : CreateAnimationOptions) { + // #ifndef APP-NVUE + return uni.createAnimation({ ...options }) + // #endif + // #ifdef APP-NVUE + return new LimeAnimation(options) + // #endif +} diff --git a/uni_modules/lime-shared/createCanvas/index.ts b/uni_modules/lime-shared/createCanvas/index.ts new file mode 100644 index 0000000..987be92 --- /dev/null +++ b/uni_modules/lime-shared/createCanvas/index.ts @@ -0,0 +1,73 @@ + +// @ts-nocheck +// #ifndef UNI-APP-X && APP +import type { ComponentInternalInstance } from '@/uni_modules/lime-shared/vue' +import { getRect } from '@/uni_modules/lime-shared/getRect' +import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d' +export const isCanvas2d = canIUseCanvas2d() +// #endif + + +export function createCanvas(canvasId : string, component : ComponentInternalInstance) { + // #ifdef UNI-APP-X + uni.createCanvasContextAsync({ + canvasId, + component, + success(context : CanvasContext) { + + }, + fail(error : UniError) { + + } + }) + // #endif + // #ifndef UNI-APP-X + const isCanvas2d = canIUseCanvas2d() + getRect('#' + canvasId, context, isCanvas2d).then(res => { + if (res.node) { + res.node.width = res.width + res.node.height = res.height + return res.node + } else { + const ctx = uni.createCanvasContext(canvasId, context) + if (!ctx._drawImage) { + ctx._drawImage = ctx.drawImage + ctx.drawImage = function (...args) { + const { path } = args.shift() + ctx._drawImage(path, ...args) + } + } + if (!ctx.getImageData) { + ctx.getImageData = function () { + return new Promise((resolve, reject) => { + uni.canvasGetImageData({ + canvasId, + x: parseInt(arguments[0]), + y: parseInt(arguments[1]), + width: parseInt(arguments[2]), + height: parseInt(arguments[3]), + success(res) { + resolve(res) + }, + fail(err) { + reject(err) + } + }, context) + }) + + } + return { + getContext(type: string) { + if(type == '2d') { + return ctx + } + }, + width: res.width, + height: res.height, + createImage + } + } + } + }) + // #endif +} \ No newline at end of file diff --git a/uni_modules/lime-shared/createImage/index.ts b/uni_modules/lime-shared/createImage/index.ts new file mode 100644 index 0000000..c9e72fe --- /dev/null +++ b/uni_modules/lime-shared/createImage/index.ts @@ -0,0 +1,71 @@ +// @ts-nocheck +// #ifndef UNI-APP-X && APP +import {isBrowser} from '../isBrowser' +class Image { + currentSrc: string | null = null + naturalHeight: number = 0 + naturalWidth: number = 0 + width: number = 0 + height: number = 0 + tagName: string = 'IMG' + path: string = '' + crossOrigin: string = '' + referrerPolicy: string = '' + onload: () => void = () => {} + onerror: () => void = () => {} + complete: boolean = false + constructor() {} + set src(src: string) { + console.log('src', src) + if(!src) { + return this.onerror() + } + src = src.replace(/^@\//,'/') + this.currentSrc = src + uni.getImageInfo({ + src, + success: (res) => { + const localReg = /^\.|^\/(?=[^\/])/; + // #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO + res.path = localReg.test(src) ? `/${res.path}` : res.path; + // #endif + this.complete = true + this.path = res.path + this.naturalWidth = this.width = res.width + this.naturalHeight = this.height = res.height + this.onload() + }, + fail: () => { + this.onerror() + } + }) + } + get src() { + return this.currentSrc + } +} +interface UniImage extends WechatMiniprogram.Image { + complete?: boolean + naturalHeight?: number + naturalWidth?: number +} +/** 创建用于 canvas 的 img */ +export function createImage(canvas?: any): HTMLImageElement | UniImage { + if(canvas && canvas.createImage) { + return (canvas as WechatMiniprogram.Canvas).createImage() + } else if(this && this['tagName'] == 'canvas' && !('toBlob' in this) || canvas && !('toBlob' in canvas)){ + return new Image() + } else if(isBrowser) { + return new window.Image() + } + return new Image() +} +// #endif + + +// #ifdef UNI-APP-X && APP +export function createImage():Image{ + // console.error('当前环境不支持') + return new Image() +} +// #endif diff --git a/uni_modules/lime-shared/debounce/index.ts b/uni_modules/lime-shared/debounce/index.ts new file mode 100644 index 0000000..a168ca3 --- /dev/null +++ b/uni_modules/lime-shared/debounce/index.ts @@ -0,0 +1,10 @@ +// @ts-nocheck + +// #ifdef UNI-APP-X && APP +export * from './uvue.ts' + +// #endif + +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif \ No newline at end of file diff --git a/uni_modules/lime-shared/debounce/uvue.ts b/uni_modules/lime-shared/debounce/uvue.ts new file mode 100644 index 0000000..f1fc29d --- /dev/null +++ b/uni_modules/lime-shared/debounce/uvue.ts @@ -0,0 +1,36 @@ +// @ts-nocheck +/** + * 防抖函数,通过延迟一定时间来限制函数的执行频率。 + * @param fn 要防抖的函数。 + * @param wait 触发防抖的等待时间,单位为毫秒。 + * @returns 防抖函数。 + */ +export function debounce<A extends any>(fn : (args: A)=> void, wait = 300): (args: A)=> void { + let timer = -1 + + return (args: A) => { + if (timer >-1) {clearTimeout(timer)}; + + timer = setTimeout(()=>{ + fn(args) + }, wait) + } +}; + + + +// 示例 +// 定义一个函数 +// function saveData(data: string) { +// // 模拟保存数据的操作 +// console.log(`Saving data: ${data}`); +// } + +// // 创建一个防抖函数,延迟 500 毫秒后调用 saveData 函数 +// const debouncedSaveData = debounce(saveData, 500); + +// // 连续调用防抖函数 +// debouncedSaveData('Data 1'); // 不会立即调用 saveData 函数 +// debouncedSaveData('Data 2'); // 不会立即调用 saveData 函数 + +// 在 500 毫秒后,只会调用一次 saveData 函数,输出结果为 "Saving data: Data 2" \ No newline at end of file diff --git a/uni_modules/lime-shared/debounce/vue.ts b/uni_modules/lime-shared/debounce/vue.ts new file mode 100644 index 0000000..694b44d --- /dev/null +++ b/uni_modules/lime-shared/debounce/vue.ts @@ -0,0 +1,40 @@ +// @ts-nocheck +type Timeout = ReturnType<typeof setTimeout> | null; +/** + * 防抖函数,通过延迟一定时间来限制函数的执行频率。 + * @param fn 要防抖的函数。 + * @param wait 触发防抖的等待时间,单位为毫秒。 + * @returns 防抖函数。 + */ +export function debounce<A extends any, R>( + fn : (...args : A) => R, + wait : number = 300) : (...args : A) => void { + let timer : Timeout = null; + + return function (...args : A) { + if (timer) clearTimeout(timer); // 如果上一个 setTimeout 存在,则清除它 + + // 设置一个新的 setTimeout,在指定的等待时间后调用防抖函数 + timer = setTimeout(() => { + fn.apply(this, args); // 使用提供的参数调用原始函数 + }, wait); + }; +}; + + + +// 示例 +// 定义一个函数 +// function saveData(data: string) { +// // 模拟保存数据的操作 +// console.log(`Saving data: ${data}`); +// } + +// // 创建一个防抖函数,延迟 500 毫秒后调用 saveData 函数 +// const debouncedSaveData = debounce(saveData, 500); + +// // 连续调用防抖函数 +// debouncedSaveData('Data 1'); // 不会立即调用 saveData 函数 +// debouncedSaveData('Data 2'); // 不会立即调用 saveData 函数 + +// 在 500 毫秒后,只会调用一次 saveData 函数,输出结果为 "Saving data: Data 2" \ No newline at end of file diff --git a/uni_modules/lime-shared/exif/index.ts b/uni_modules/lime-shared/exif/index.ts new file mode 100644 index 0000000..c1e7e3f --- /dev/null +++ b/uni_modules/lime-shared/exif/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif + + +// #ifdef UNI-APP-X && APP +export * from './uvue.ts' +// #endif diff --git a/uni_modules/lime-shared/exif/uvue.ts b/uni_modules/lime-shared/exif/uvue.ts new file mode 100644 index 0000000..01d21a2 --- /dev/null +++ b/uni_modules/lime-shared/exif/uvue.ts @@ -0,0 +1,7 @@ +class EXIF { + constructor(){ + console.error('当前环境不支持') + } +} + +export const exif = new EXIF() \ No newline at end of file diff --git a/uni_modules/lime-shared/exif/vue.ts b/uni_modules/lime-shared/exif/vue.ts new file mode 100644 index 0000000..86f2454 --- /dev/null +++ b/uni_modules/lime-shared/exif/vue.ts @@ -0,0 +1,1057 @@ +// @ts-nocheck +import { base64ToArrayBuffer } from '../base64ToArrayBuffer'; +import { pathToBase64 } from '../pathToBase64'; +// import { isBase64 } from '../isBase64'; +import { isBase64DataUri } from '../isBase64'; +import { isString } from '../isString'; + +interface File { + exifdata : any + iptcdata : any + xmpdata : any + src : string +} +class EXIF { + isXmpEnabled = false + debug = false + Tags = { + // version tags + 0x9000: "ExifVersion", // EXIF version + 0xA000: "FlashpixVersion", // Flashpix format version + + // colorspace tags + 0xA001: "ColorSpace", // Color space information tag + + // image configuration + 0xA002: "PixelXDimension", // Valid width of meaningful image + 0xA003: "PixelYDimension", // Valid height of meaningful image + 0x9101: "ComponentsConfiguration", // Information about channels + 0x9102: "CompressedBitsPerPixel", // Compressed bits per pixel + + // user information + 0x927C: "MakerNote", // Any desired information written by the manufacturer + 0x9286: "UserComment", // Comments by user + + // related file + 0xA004: "RelatedSoundFile", // Name of related sound file + + // date and time + 0x9003: "DateTimeOriginal", // Date and time when the original image was generated + 0x9004: "DateTimeDigitized", // Date and time when the image was stored digitally + 0x9290: "SubsecTime", // Fractions of seconds for DateTime + 0x9291: "SubsecTimeOriginal", // Fractions of seconds for DateTimeOriginal + 0x9292: "SubsecTimeDigitized", // Fractions of seconds for DateTimeDigitized + + // picture-taking conditions + 0x829A: "ExposureTime", // Exposure time (in seconds) + 0x829D: "FNumber", // F number + 0x8822: "ExposureProgram", // Exposure program + 0x8824: "SpectralSensitivity", // Spectral sensitivity + 0x8827: "ISOSpeedRatings", // ISO speed rating + 0x8828: "OECF", // Optoelectric conversion factor + 0x9201: "ShutterSpeedValue", // Shutter speed + 0x9202: "ApertureValue", // Lens aperture + 0x9203: "BrightnessValue", // Value of brightness + 0x9204: "ExposureBias", // Exposure bias + 0x9205: "MaxApertureValue", // Smallest F number of lens + 0x9206: "SubjectDistance", // Distance to subject in meters + 0x9207: "MeteringMode", // Metering mode + 0x9208: "LightSource", // Kind of light source + 0x9209: "Flash", // Flash status + 0x9214: "SubjectArea", // Location and area of main subject + 0x920A: "FocalLength", // Focal length of the lens in mm + 0xA20B: "FlashEnergy", // Strobe energy in BCPS + 0xA20C: "SpatialFrequencyResponse", // + 0xA20E: "FocalPlaneXResolution", // Number of pixels in width direction per FocalPlaneResolutionUnit + 0xA20F: "FocalPlaneYResolution", // Number of pixels in height direction per FocalPlaneResolutionUnit + 0xA210: "FocalPlaneResolutionUnit", // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution + 0xA214: "SubjectLocation", // Location of subject in image + 0xA215: "ExposureIndex", // Exposure index selected on camera + 0xA217: "SensingMethod", // Image sensor type + 0xA300: "FileSource", // Image source (3 == DSC) + 0xA301: "SceneType", // Scene type (1 == directly photographed) + 0xA302: "CFAPattern", // Color filter array geometric pattern + 0xA401: "CustomRendered", // Special processing + 0xA402: "ExposureMode", // Exposure mode + 0xA403: "WhiteBalance", // 1 = auto white balance, 2 = manual + 0xA404: "DigitalZoomRation", // Digital zoom ratio + 0xA405: "FocalLengthIn35mmFilm", // Equivalent foacl length assuming 35mm film camera (in mm) + 0xA406: "SceneCaptureType", // Type of scene + 0xA407: "GainControl", // Degree of overall image gain adjustment + 0xA408: "Contrast", // Direction of contrast processing applied by camera + 0xA409: "Saturation", // Direction of saturation processing applied by camera + 0xA40A: "Sharpness", // Direction of sharpness processing applied by camera + 0xA40B: "DeviceSettingDescription", // + 0xA40C: "SubjectDistanceRange", // Distance to subject + + // other tags + 0xA005: "InteroperabilityIFDPointer", + 0xA420: "ImageUniqueID" // Identifier assigned uniquely to each image + } + TiffTags = { + 0x0100: "ImageWidth", + 0x0101: "ImageHeight", + 0x8769: "ExifIFDPointer", + 0x8825: "GPSInfoIFDPointer", + 0xA005: "InteroperabilityIFDPointer", + 0x0102: "BitsPerSample", + 0x0103: "Compression", + 0x0106: "PhotometricInterpretation", + 0x0112: "Orientation", + 0x0115: "SamplesPerPixel", + 0x011C: "PlanarConfiguration", + 0x0212: "YCbCrSubSampling", + 0x0213: "YCbCrPositioning", + 0x011A: "XResolution", + 0x011B: "YResolution", + 0x0128: "ResolutionUnit", + 0x0111: "StripOffsets", + 0x0116: "RowsPerStrip", + 0x0117: "StripByteCounts", + 0x0201: "JPEGInterchangeFormat", + 0x0202: "JPEGInterchangeFormatLength", + 0x012D: "TransferFunction", + 0x013E: "WhitePoint", + 0x013F: "PrimaryChromaticities", + 0x0211: "YCbCrCoefficients", + 0x0214: "ReferenceBlackWhite", + 0x0132: "DateTime", + 0x010E: "ImageDescription", + 0x010F: "Make", + 0x0110: "Model", + 0x0131: "Software", + 0x013B: "Artist", + 0x8298: "Copyright" + } + GPSTags = { + 0x0000: "GPSVersionID", + 0x0001: "GPSLatitudeRef", + 0x0002: "GPSLatitude", + 0x0003: "GPSLongitudeRef", + 0x0004: "GPSLongitude", + 0x0005: "GPSAltitudeRef", + 0x0006: "GPSAltitude", + 0x0007: "GPSTimeStamp", + 0x0008: "GPSSatellites", + 0x0009: "GPSStatus", + 0x000A: "GPSMeasureMode", + 0x000B: "GPSDOP", + 0x000C: "GPSSpeedRef", + 0x000D: "GPSSpeed", + 0x000E: "GPSTrackRef", + 0x000F: "GPSTrack", + 0x0010: "GPSImgDirectionRef", + 0x0011: "GPSImgDirection", + 0x0012: "GPSMapDatum", + 0x0013: "GPSDestLatitudeRef", + 0x0014: "GPSDestLatitude", + 0x0015: "GPSDestLongitudeRef", + 0x0016: "GPSDestLongitude", + 0x0017: "GPSDestBearingRef", + 0x0018: "GPSDestBearing", + 0x0019: "GPSDestDistanceRef", + 0x001A: "GPSDestDistance", + 0x001B: "GPSProcessingMethod", + 0x001C: "GPSAreaInformation", + 0x001D: "GPSDateStamp", + 0x001E: "GPSDifferential" + } + // EXIF 2.3 Spec + IFD1Tags = { + 0x0100: "ImageWidth", + 0x0101: "ImageHeight", + 0x0102: "BitsPerSample", + 0x0103: "Compression", + 0x0106: "PhotometricInterpretation", + 0x0111: "StripOffsets", + 0x0112: "Orientation", + 0x0115: "SamplesPerPixel", + 0x0116: "RowsPerStrip", + 0x0117: "StripByteCounts", + 0x011A: "XResolution", + 0x011B: "YResolution", + 0x011C: "PlanarConfiguration", + 0x0128: "ResolutionUnit", + 0x0201: "JpegIFOffset", // When image format is JPEG, this value show offset to JPEG data stored.(aka "ThumbnailOffset" or "JPEGInterchangeFormat") + 0x0202: "JpegIFByteCount", // When image format is JPEG, this value shows data size of JPEG image (aka "ThumbnailLength" or "JPEGInterchangeFormatLength") + 0x0211: "YCbCrCoefficients", + 0x0212: "YCbCrSubSampling", + 0x0213: "YCbCrPositioning", + 0x0214: "ReferenceBlackWhite" + } + StringValues = { + ExposureProgram: { + 0: "Not defined", + 1: "Manual", + 2: "Normal program", + 3: "Aperture priority", + 4: "Shutter priority", + 5: "Creative program", + 6: "Action program", + 7: "Portrait mode", + 8: "Landscape mode" + }, + MeteringMode: { + 0: "Unknown", + 1: "Average", + 2: "CenterWeightedAverage", + 3: "Spot", + 4: "MultiSpot", + 5: "Pattern", + 6: "Partial", + 255: "Other" + }, + LightSource: { + 0: "Unknown", + 1: "Daylight", + 2: "Fluorescent", + 3: "Tungsten (incandescent light)", + 4: "Flash", + 9: "Fine weather", + 10: "Cloudy weather", + 11: "Shade", + 12: "Daylight fluorescent (D 5700 - 7100K)", + 13: "Day white fluorescent (N 4600 - 5400K)", + 14: "Cool white fluorescent (W 3900 - 4500K)", + 15: "White fluorescent (WW 3200 - 3700K)", + 17: "Standard light A", + 18: "Standard light B", + 19: "Standard light C", + 20: "D55", + 21: "D65", + 22: "D75", + 23: "D50", + 24: "ISO studio tungsten", + 255: "Other" + }, + Flash: { + 0x0000: "Flash did not fire", + 0x0001: "Flash fired", + 0x0005: "Strobe return light not detected", + 0x0007: "Strobe return light detected", + 0x0009: "Flash fired, compulsory flash mode", + 0x000D: "Flash fired, compulsory flash mode, return light not detected", + 0x000F: "Flash fired, compulsory flash mode, return light detected", + 0x0010: "Flash did not fire, compulsory flash mode", + 0x0018: "Flash did not fire, auto mode", + 0x0019: "Flash fired, auto mode", + 0x001D: "Flash fired, auto mode, return light not detected", + 0x001F: "Flash fired, auto mode, return light detected", + 0x0020: "No flash function", + 0x0041: "Flash fired, red-eye reduction mode", + 0x0045: "Flash fired, red-eye reduction mode, return light not detected", + 0x0047: "Flash fired, red-eye reduction mode, return light detected", + 0x0049: "Flash fired, compulsory flash mode, red-eye reduction mode", + 0x004D: "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected", + 0x004F: "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected", + 0x0059: "Flash fired, auto mode, red-eye reduction mode", + 0x005D: "Flash fired, auto mode, return light not detected, red-eye reduction mode", + 0x005F: "Flash fired, auto mode, return light detected, red-eye reduction mode" + }, + SensingMethod: { + 1: "Not defined", + 2: "One-chip color area sensor", + 3: "Two-chip color area sensor", + 4: "Three-chip color area sensor", + 5: "Color sequential area sensor", + 7: "Trilinear sensor", + 8: "Color sequential linear sensor" + }, + SceneCaptureType: { + 0: "Standard", + 1: "Landscape", + 2: "Portrait", + 3: "Night scene" + }, + SceneType: { + 1: "Directly photographed" + }, + CustomRendered: { + 0: "Normal process", + 1: "Custom process" + }, + WhiteBalance: { + 0: "Auto white balance", + 1: "Manual white balance" + }, + GainControl: { + 0: "None", + 1: "Low gain up", + 2: "High gain up", + 3: "Low gain down", + 4: "High gain down" + }, + Contrast: { + 0: "Normal", + 1: "Soft", + 2: "Hard" + }, + Saturation: { + 0: "Normal", + 1: "Low saturation", + 2: "High saturation" + }, + Sharpness: { + 0: "Normal", + 1: "Soft", + 2: "Hard" + }, + SubjectDistanceRange: { + 0: "Unknown", + 1: "Macro", + 2: "Close view", + 3: "Distant view" + }, + FileSource: { + 3: "DSC" + }, + + Components: { + 0: "", + 1: "Y", + 2: "Cb", + 3: "Cr", + 4: "R", + 5: "G", + 6: "B" + } + } + enableXmp() { + this.isXmpEnabled = true + } + disableXmp() { + this.isXmpEnabled = false; + } + /** + * 获取图片数据 + * @param img 图片地址 + * @param callback 回调 返回图片数据 + * */ + getData(img : any, callback : Function) { + // if (((self.Image && img instanceof self.Image) || (self.HTMLImageElement && img instanceof self.HTMLImageElement)) && !img.complete) + // return false; + let file : File = { + src: '', + exifdata: null, + iptcdata: null, + xmpdata: null, + } + if (isBase64(img)) { + file.src = img + } else if (img.path) { + file.src = img.path + } else if (isString(img)) { + file.src = img + } else { + return false; + } + + + if (!imageHasData(file)) { + getImageData(file, callback); + } else { + if (callback) { + callback.call(file); + } + } + return true; + } + /** + * 获取图片tag + * @param img 图片数据 + * @param tag tag 类型 + * */ + getTag(img : File, tag : string) { + if (!imageHasData(img)) return; + return img.exifdata[tag]; + } + getIptcTag(img : File, tag : string) { + if (!imageHasData(img)) return; + return img.iptcdata[tag]; + } + getAllTags(img : File) { + if (!imageHasData(img)) return {}; + let a, + data = img.exifdata, + tags = {}; + for (a in data) { + if (data.hasOwnProperty(a)) { + tags[a] = data[a]; + } + } + return tags; + } + getAllIptcTags(img : File) { + if (!imageHasData(img)) return {}; + let a, + data = img.iptcdata, + tags = {}; + for (a in data) { + if (data.hasOwnProperty(a)) { + tags[a] = data[a]; + } + } + return tags; + } + pretty(img : File) { + if (!imageHasData(img)) return ""; + let a, + data = img.exifdata, + strPretty = ""; + for (a in data) { + if (data.hasOwnProperty(a)) { + if (typeof data[a] == "object") { + if (data[a] instanceof Number) { + strPretty += a + " : " + data[a] + " [" + data[a].numerator + "/" + data[a] + .denominator + "]\r\n"; + } else { + strPretty += a + " : [" + data[a].length + " values]\r\n"; + } + } else { + strPretty += a + " : " + data[a] + "\r\n"; + } + } + } + return strPretty; + } + readFromBinaryFile(file: ArrayBuffer) { + return findEXIFinJPEG(file); + } +} + +export const exif = new EXIF() +// export function getData(img, callback) { +// const exif = new EXIF() +// exif.getData(img, callback) +// } + +// export default {getData} +const ExifTags = exif.Tags +const TiffTags = exif.TiffTags +const IFD1Tags = exif.IFD1Tags +const GPSTags = exif.GPSTags +const StringValues = exif.StringValues + + +function imageHasData(img : File) : boolean { + return !!(img.exifdata); +} + +function objectURLToBlob(url : string, callback : Function) { + try { + const http = new XMLHttpRequest(); + http.open("GET", url, true); + http.responseType = "blob"; + http.onload = function (e) { + if (this.status == 200 || this.status === 0) { + callback(this.response); + } + }; + http.send(); + } catch (e) { + console.warn(e) + } +} + + +function getImageData(img : File, callback : Function) { + function handleBinaryFile(binFile: ArrayBuffer) { + const data = findEXIFinJPEG(binFile); + img.exifdata = data ?? {}; + const iptcdata = findIPTCinJPEG(binFile); + img.iptcdata = iptcdata ?? {}; + if (exif.isXmpEnabled) { + const xmpdata = findXMPinJPEG(binFile); + img.xmpdata = xmpdata ?? {}; + } + if (callback) { + callback.call(img); + } + } + + if (img.src) { + if (/^data\:/i.test(img.src)) { // Data URI + // var arrayBuffer = base64ToArrayBuffer(img.src); + handleBinaryFile(base64ToArrayBuffer(img.src)); + + } else if (/^blob\:/i.test(img.src) && typeof FileReader !== 'undefined') { // Object URL + var fileReader = new FileReader(); + fileReader.onload = function (e) { + handleBinaryFile(e.target.result); + }; + objectURLToBlob(img.src, function (blob : Blob) { + fileReader.readAsArrayBuffer(blob); + }); + } else if (typeof XMLHttpRequest !== 'undefined') { + var http = new XMLHttpRequest(); + http.onload = function () { + if (this.status == 200 || this.status === 0) { + handleBinaryFile(http.response); + } else { + throw "Could not load image"; + } + http = null; + }; + http.open("GET", img.src, true); + http.responseType = "arraybuffer"; + http.send(null); + } else { + pathToBase64(img.src).then(res => { + handleBinaryFile(base64ToArrayBuffer(res)); + }) + } + } else if (typeof FileReader !== 'undefined' && self.FileReader && (img instanceof self.Blob || img instanceof self.File)) { + var fileReader = new FileReader(); + fileReader.onload = function (e : any) { + if (exif.debug) console.log("Got file of length " + e.target.result.byteLength); + handleBinaryFile(e.target.result); + }; + + fileReader.readAsArrayBuffer(img); + } +} + +function findEXIFinJPEG(file: ArrayBuffer) { + const dataView = new DataView(file); + + if (exif.debug) console.log("Got file of length " + file.byteLength); + if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) { + if (exif.debug) console.log("Not a valid JPEG"); + return false; // not a valid jpeg + } + + let offset = 2, + length = file.byteLength, + marker; + + while (offset < length) { + if (dataView.getUint8(offset) != 0xFF) { + if (exif.debug) console.log("Not a valid marker at offset " + offset + ", found: " + dataView.getUint8( + offset)); + return false; // not a valid marker, something is wrong + } + + marker = dataView.getUint8(offset + 1); + if (exif.debug) console.log(marker); + + // we could implement handling for other markers here, + // but we're only looking for 0xFFE1 for EXIF data + + if (marker == 225) { + if (exif.debug) console.log("Found 0xFFE1 marker"); + + return readEXIFData(dataView, offset + 4, dataView.getUint16(offset + 2) - 2); + + // offset += 2 + file.getShortAt(offset+2, true); + + } else { + offset += 2 + dataView.getUint16(offset + 2); + } + + } + +} + +function findIPTCinJPEG(file: ArrayBuffer) { + const dataView = new DataView(file); + + if (exif.debug) console.log("Got file of length " + file.byteLength); + if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) { + if (exif.debug) console.log("Not a valid JPEG"); + return false; // not a valid jpeg + } + + let offset = 2, + length = file.byteLength; + + + const isFieldSegmentStart = function (dataView, offset: number) { + return ( + dataView.getUint8(offset) === 0x38 && + dataView.getUint8(offset + 1) === 0x42 && + dataView.getUint8(offset + 2) === 0x49 && + dataView.getUint8(offset + 3) === 0x4D && + dataView.getUint8(offset + 4) === 0x04 && + dataView.getUint8(offset + 5) === 0x04 + ); + }; + + while (offset < length) { + + if (isFieldSegmentStart(dataView, offset)) { + + // Get the length of the name header (which is padded to an even number of bytes) + var nameHeaderLength = dataView.getUint8(offset + 7); + if (nameHeaderLength % 2 !== 0) nameHeaderLength += 1; + // Check for pre photoshop 6 format + if (nameHeaderLength === 0) { + // Always 4 + nameHeaderLength = 4; + } + + var startOffset = offset + 8 + nameHeaderLength; + var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength); + + return readIPTCData(file, startOffset, sectionLength); + + break; + + } + + + // Not the marker, continue searching + offset++; + + } + +} + +const IptcFieldMap = { + 0x78: 'caption', + 0x6E: 'credit', + 0x19: 'keywords', + 0x37: 'dateCreated', + 0x50: 'byline', + 0x55: 'bylineTitle', + 0x7A: 'captionWriter', + 0x69: 'headline', + 0x74: 'copyright', + 0x0F: 'category' +}; + +function readIPTCData(file: ArrayBuffer, startOffset: number, sectionLength: number) { + const dataView = new DataView(file); + let data = {}; + let fieldValue, fieldName, dataSize, segmentType, segmentSize; + let segmentStartPos = startOffset; + while (segmentStartPos < startOffset + sectionLength) { + if (dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos + 1) === 0x02) { + segmentType = dataView.getUint8(segmentStartPos + 2); + if (segmentType in IptcFieldMap) { + dataSize = dataView.getInt16(segmentStartPos + 3); + segmentSize = dataSize + 5; + fieldName = IptcFieldMap[segmentType]; + fieldValue = getStringFromDB(dataView, segmentStartPos + 5, dataSize); + // Check if we already stored a value with this name + if (data.hasOwnProperty(fieldName)) { + // Value already stored with this name, create multivalue field + if (data[fieldName] instanceof Array) { + data[fieldName].push(fieldValue); + } else { + data[fieldName] = [data[fieldName], fieldValue]; + } + } else { + data[fieldName] = fieldValue; + } + } + + } + segmentStartPos++; + } + return data; +} + +function readTags(file: DataView, tiffStart: number, dirStart: number, strings: any[], bigEnd: number) { + let entries = file.getUint16(dirStart, !bigEnd), + tags = {}, + entryOffset, tag; + + for (let i = 0; i < entries; i++) { + entryOffset = dirStart + i * 12 + 2; + tag = strings[file.getUint16(entryOffset, !bigEnd)]; + if (!tag && exif.debug) console.log("Unknown tag: " + file.getUint16(entryOffset, !bigEnd)); + tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd); + } + return tags; +} + +function readTagValue(file: DataView, entryOffset: number, tiffStart: number, dirStart: number, bigEnd: number) { + let type = file.getUint16(entryOffset + 2, !bigEnd), + numValues = file.getUint32(entryOffset + 4, !bigEnd), + valueOffset = file.getUint32(entryOffset + 8, !bigEnd) + tiffStart, + offset, + vals, val, n, + numerator, denominator; + + switch (type) { + case 1: // byte, 8-bit unsigned int + case 7: // undefined, 8-bit byte, value depending on field + if (numValues == 1) { + return file.getUint8(entryOffset + 8, !bigEnd); + } else { + offset = numValues > 4 ? valueOffset : (entryOffset + 8); + vals = []; + for (n = 0; n < numValues; n++) { + vals[n] = file.getUint8(offset + n); + } + return vals; + } + + case 2: // ascii, 8-bit byte + offset = numValues > 4 ? valueOffset : (entryOffset + 8); + return getStringFromDB(file, offset, numValues - 1); + + case 3: // short, 16 bit int + if (numValues == 1) { + return file.getUint16(entryOffset + 8, !bigEnd); + } else { + offset = numValues > 2 ? valueOffset : (entryOffset + 8); + vals = []; + for (n = 0; n < numValues; n++) { + vals[n] = file.getUint16(offset + 2 * n, !bigEnd); + } + return vals; + } + + case 4: // long, 32 bit int + if (numValues == 1) { + return file.getUint32(entryOffset + 8, !bigEnd); + } else { + vals = []; + for (n = 0; n < numValues; n++) { + vals[n] = file.getUint32(valueOffset + 4 * n, !bigEnd); + } + return vals; + } + + case 5: // rational = two long values, first is numerator, second is denominator + if (numValues == 1) { + numerator = file.getUint32(valueOffset, !bigEnd); + denominator = file.getUint32(valueOffset + 4, !bigEnd); + val = new Number(numerator / denominator); + val.numerator = numerator; + val.denominator = denominator; + return val; + } else { + vals = []; + for (n = 0; n < numValues; n++) { + numerator = file.getUint32(valueOffset + 8 * n, !bigEnd); + denominator = file.getUint32(valueOffset + 4 + 8 * n, !bigEnd); + vals[n] = new Number(numerator / denominator); + vals[n].numerator = numerator; + vals[n].denominator = denominator; + } + return vals; + } + + case 9: // slong, 32 bit signed int + if (numValues == 1) { + return file.getInt32(entryOffset + 8, !bigEnd); + } else { + vals = []; + for (n = 0; n < numValues; n++) { + vals[n] = file.getInt32(valueOffset + 4 * n, !bigEnd); + } + return vals; + } + + case 10: // signed rational, two slongs, first is numerator, second is denominator + if (numValues == 1) { + return file.getInt32(valueOffset, !bigEnd) / file.getInt32(valueOffset + 4, !bigEnd); + } else { + vals = []; + for (n = 0; n < numValues; n++) { + vals[n] = file.getInt32(valueOffset + 8 * n, !bigEnd) / file.getInt32(valueOffset + 4 + 8 * + n, !bigEnd); + } + return vals; + } + } +} +/** + * Given an IFD (Image File Directory) start offset + * returns an offset to next IFD or 0 if it's the last IFD. + */ +function getNextIFDOffset(dataView: DataView, dirStart: number, bigEnd: number) { + //the first 2bytes means the number of directory entries contains in this IFD + var entries = dataView.getUint16(dirStart, !bigEnd); + + // After last directory entry, there is a 4bytes of data, + // it means an offset to next IFD. + // If its value is '0x00000000', it means this is the last IFD and there is no linked IFD. + + return dataView.getUint32(dirStart + 2 + entries * 12, !bigEnd); // each entry is 12 bytes long +} + +function readThumbnailImage(dataView: DataView, tiffStart: number, firstIFDOffset: number, bigEnd: number) { + // get the IFD1 offset + const IFD1OffsetPointer = getNextIFDOffset(dataView, tiffStart + firstIFDOffset, bigEnd); + + if (!IFD1OffsetPointer) { + // console.log('******** IFD1Offset is empty, image thumb not found ********'); + return {}; + } else if (IFD1OffsetPointer > dataView.byteLength) { // this should not happen + // console.log('******** IFD1Offset is outside the bounds of the DataView ********'); + return {}; + } + // console.log('******* thumbnail IFD offset (IFD1) is: %s', IFD1OffsetPointer); + + let thumbTags : any = readTags(dataView, tiffStart, tiffStart + IFD1OffsetPointer, IFD1Tags, bigEnd) + + // EXIF 2.3 specification for JPEG format thumbnail + + // If the value of Compression(0x0103) Tag in IFD1 is '6', thumbnail image format is JPEG. + // Most of Exif image uses JPEG format for thumbnail. In that case, you can get offset of thumbnail + // by JpegIFOffset(0x0201) Tag in IFD1, size of thumbnail by JpegIFByteCount(0x0202) Tag. + // Data format is ordinary JPEG format, starts from 0xFFD8 and ends by 0xFFD9. It seems that + // JPEG format and 160x120pixels of size are recommended thumbnail format for Exif2.1 or later. + + if (thumbTags['Compression'] && typeof Blob !== 'undefined') { + // console.log('Thumbnail image found!'); + + switch (thumbTags['Compression']) { + case 6: + // console.log('Thumbnail image format is JPEG'); + if (thumbTags.JpegIFOffset && thumbTags.JpegIFByteCount) { + // extract the thumbnail + var tOffset = tiffStart + thumbTags.JpegIFOffset; + var tLength = thumbTags.JpegIFByteCount; + thumbTags['blob'] = new Blob([new Uint8Array(dataView.buffer, tOffset, tLength)], { + type: 'image/jpeg' + }); + } + break; + + case 1: + console.log("Thumbnail image format is TIFF, which is not implemented."); + break; + default: + console.log("Unknown thumbnail image format '%s'", thumbTags['Compression']); + } + } else if (thumbTags['PhotometricInterpretation'] == 2) { + console.log("Thumbnail image format is RGB, which is not implemented."); + } + return thumbTags; +} + +function getStringFromDB(buffer: DataView, start: number, length: number) { + let outstr = ""; + for (let n = start; n < start + length; n++) { + outstr += String.fromCharCode(buffer.getUint8(n)); + } + return outstr; +} + +function readEXIFData(file: DataView, start: number) { + if (getStringFromDB(file, start, 4) != "Exif") { + if (exif.debug) console.log("Not valid EXIF data! " + getStringFromDB(file, start, 4)); + return false; + } + + let bigEnd, + tags, tag, + exifData, gpsData, + tiffOffset = start + 6; + + // test for TIFF validity and endianness + if (file.getUint16(tiffOffset) == 0x4949) { + bigEnd = false; + } else if (file.getUint16(tiffOffset) == 0x4D4D) { + bigEnd = true; + } else { + if (exif.debug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)"); + return false; + } + + if (file.getUint16(tiffOffset + 2, !bigEnd) != 0x002A) { + if (exif.debug) console.log("Not valid TIFF data! (no 0x002A)"); + return false; + } + + const firstIFDOffset = file.getUint32(tiffOffset + 4, !bigEnd); + + if (firstIFDOffset < 0x00000008) { + if (exif.debug) console.log("Not valid TIFF data! (First offset less than 8)", file.getUint32(tiffOffset + 4, + !bigEnd)); + return false; + } + + tags = readTags(file, tiffOffset, tiffOffset + firstIFDOffset, TiffTags, bigEnd); + + if (tags.ExifIFDPointer) { + exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd); + for (tag in exifData) { + switch (tag) { + case "LightSource": + case "Flash": + case "MeteringMode": + case "ExposureProgram": + case "SensingMethod": + case "SceneCaptureType": + case "SceneType": + case "CustomRendered": + case "WhiteBalance": + case "GainControl": + case "Contrast": + case "Saturation": + case "Sharpness": + case "SubjectDistanceRange": + case "FileSource": + exifData[tag] = StringValues[tag][exifData[tag]]; + break; + + case "ExifVersion": + case "FlashpixVersion": + exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2], + exifData[tag][3]); + break; + + case "ComponentsConfiguration": + exifData[tag] = + StringValues.Components[exifData[tag][0]] + + StringValues.Components[exifData[tag][1]] + + StringValues.Components[exifData[tag][2]] + + StringValues.Components[exifData[tag][3]]; + break; + } + tags[tag] = exifData[tag]; + } + } + + if (tags.GPSInfoIFDPointer) { + gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd); + for (tag in gpsData) { + switch (tag) { + case "GPSVersionID": + gpsData[tag] = gpsData[tag][0] + + "." + gpsData[tag][1] + + "." + gpsData[tag][2] + + "." + gpsData[tag][3]; + break; + } + tags[tag] = gpsData[tag]; + } + } + + // extract thumbnail + tags['thumbnail'] = readThumbnailImage(file, tiffOffset, firstIFDOffset, bigEnd); + + return tags; +} + +function findXMPinJPEG(file: ArrayBuffer) { + + if (!('DOMParser' in self)) { + // console.warn('XML parsing not supported without DOMParser'); + return; + } + const dataView = new DataView(file); + + if (exif.debug) console.log("Got file of length " + file.byteLength); + if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) { + if (exif.debug) console.log("Not a valid JPEG"); + return false; // not a valid jpeg + } + + let offset = 2, + length = file.byteLength, + dom = new DOMParser(); + + while (offset < (length - 4)) { + if (getStringFromDB(dataView, offset, 4) == "http") { + const startOffset = offset - 1; + const sectionLength = dataView.getUint16(offset - 2) - 1; + let xmpString = getStringFromDB(dataView, startOffset, sectionLength) + const xmpEndIndex = xmpString.indexOf('xmpmeta>') + 8; + xmpString = xmpString.substring(xmpString.indexOf('<x:xmpmeta'), xmpEndIndex); + + const indexOfXmp = xmpString.indexOf('x:xmpmeta') + 10 + //Many custom written programs embed xmp/xml without any namespace. Following are some of them. + //Without these namespaces, XML is thought to be invalid by parsers + xmpString = xmpString.slice(0, indexOfXmp) + + 'xmlns:Iptc4xmpCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/" ' + + 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' + + 'xmlns:tiff="http://ns.adobe.com/tiff/1.0/" ' + + 'xmlns:plus="http://schemas.android.com/apk/lib/com.google.android.gms.plus" ' + + 'xmlns:ext="http://www.gettyimages.com/xsltExtension/1.0" ' + + 'xmlns:exif="http://ns.adobe.com/exif/1.0/" ' + + 'xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" ' + + 'xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" ' + + 'xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/" ' + + 'xmlns:xapGImg="http://ns.adobe.com/xap/1.0/g/img/" ' + + 'xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/" ' + + xmpString.slice(indexOfXmp) + + var domDocument = dom.parseFromString(xmpString, 'text/xml'); + return xml2Object(domDocument); + } else { + offset++; + } + } +} + + +function xml2json(xml: any) { + var json = {}; + + if (xml.nodeType == 1) { // element node + if (xml.attributes.length > 0) { + json['@attributes'] = {}; + for (var j = 0; j < xml.attributes.length; j++) { + var attribute = xml.attributes.item(j); + json['@attributes'][attribute.nodeName] = attribute.nodeValue; + } + } + } else if (xml.nodeType == 3) { // text node + return xml.nodeValue; + } + + // deal with children + if (xml.hasChildNodes()) { + for (var i = 0; i < xml.childNodes.length; i++) { + var child = xml.childNodes.item(i); + var nodeName = child.nodeName; + if (json[nodeName] == null) { + json[nodeName] = xml2json(child); + } else { + if (json[nodeName].push == null) { + var old = json[nodeName]; + json[nodeName] = []; + json[nodeName].push(old); + } + json[nodeName].push(xml2json(child)); + } + } + } + + return json; +} + +function xml2Object(xml: any) { + try { + var obj = {}; + if (xml.children.length > 0) { + for (var i = 0; i < xml.children.length; i++) { + var item = xml.children.item(i); + var attributes = item.attributes; + for (var idx in attributes) { + var itemAtt = attributes[idx]; + var dataKey = itemAtt.nodeName; + var dataValue = itemAtt.nodeValue; + + if (dataKey !== undefined) { + obj[dataKey] = dataValue; + } + } + var nodeName = item.nodeName; + + if (typeof (obj[nodeName]) == "undefined") { + obj[nodeName] = xml2json(item); + } else { + if (typeof (obj[nodeName].push) == "undefined") { + var old = obj[nodeName]; + + obj[nodeName] = []; + obj[nodeName].push(old); + } + obj[nodeName].push(xml2json(item)); + } + } + } else { + obj = xml.textContent; + } + return obj; + } catch (e) { + console.log(e.message); + } +} \ No newline at end of file diff --git a/uni_modules/lime-shared/fillZero/index.ts b/uni_modules/lime-shared/fillZero/index.ts new file mode 100644 index 0000000..9952c45 --- /dev/null +++ b/uni_modules/lime-shared/fillZero/index.ts @@ -0,0 +1,11 @@ +// @ts-nocheck +/** + * 在数字前填充零,返回字符串形式的结果 + * @param number 要填充零的数字 + * @param length 填充零后的字符串长度,默认为2 + * @returns 填充零后的字符串 + */ +export function fillZero(number: number, length: number = 2): string { + // 将数字转换为字符串,然后使用 padStart 方法填充零到指定长度 + return `${number}`.padStart(length, '0'); +} \ No newline at end of file diff --git a/uni_modules/lime-shared/floatAdd/index.ts b/uni_modules/lime-shared/floatAdd/index.ts new file mode 100644 index 0000000..aad3929 --- /dev/null +++ b/uni_modules/lime-shared/floatAdd/index.ts @@ -0,0 +1,36 @@ +import { isNumber } from '../isNumber' +/** + * 返回两个浮点数相加的结果 + * @param num1 第一个浮点数 + * @param num2 第二个浮点数 + * @returns 两个浮点数的相加结果 + */ +export function floatAdd(num1 : number, num2 : number) : number { + // 检查 num1 和 num2 是否为数字类型 + if (!(isNumber(num1) || isNumber(num2))) { + console.warn('Please pass in the number type'); + return NaN; + } + + let r1 : number, r2 : number, m : number; + + try { + // 获取 num1 小数点后的位数 + r1 = num1.toString().split('.')[1].length; + } catch (error) { + r1 = 0; + } + + try { + // 获取 num2 小数点后的位数 + r2 = num2.toString().split('.')[1].length; + } catch (error) { + r2 = 0; + } + + // 计算需要扩大的倍数 + m = Math.pow(10, Math.max(r1, r2)); + + // 返回相加结果 + return (num1 * m + num2 * m) / m; +} \ No newline at end of file diff --git a/uni_modules/lime-shared/floatDiv/index.ts b/uni_modules/lime-shared/floatDiv/index.ts new file mode 100644 index 0000000..195d4ab --- /dev/null +++ b/uni_modules/lime-shared/floatDiv/index.ts @@ -0,0 +1,45 @@ +import { floatMul } from '../floatMul'; +import { isNumber } from '../isNumber'; + +/** + * 除法函数,用于处理浮点数除法并保持精度。 + * @param {number} num1 - 被除数。 + * @param {number} num2 - 除数。 + * @returns {number} 除法运算的结果,保留正确的精度。 + */ +export function floatDiv(num1:number, num2:number):number { + // 如果传入的不是数字类型,则打印警告并返回NaN + if (!isNumber(num1) || !isNumber(num2)) { + console.warn('请传入数字类型'); + return NaN; + } + + let m1 = 0, // 被除数小数点后的位数 + m2 = 0, // 除数小数点后的位数 + s1 = num1.toString(), // 将被除数转换为字符串 + s2 = num2.toString(); // 将除数转换为字符串 + + // 计算被除数小数点后的位数 + try { + m1 += s1.split('.')[1].length; + } catch (error) {} + + // 计算除数小数点后的位数 + try { + m2 += s2.split('.')[1].length; + } catch (error) {} + + // 进行除法运算并处理小数点后的位数,使用之前定义的乘法函数保持精度 + // #ifdef APP-ANDROID + return floatMul( + parseFloat(s1.replace('.', '')) / parseFloat(s2.replace('.', '')), + Math.pow(10, m2 - m1), + ); + // #endif + // #ifndef APP-ANDROID + return floatMul( + Number(s1.replace('.', '')) / Number(s2.replace('.', '')), + Math.pow(10, m2 - m1), + ); + // #endif +} diff --git a/uni_modules/lime-shared/floatMul/index.ts b/uni_modules/lime-shared/floatMul/index.ts new file mode 100644 index 0000000..51a867e --- /dev/null +++ b/uni_modules/lime-shared/floatMul/index.ts @@ -0,0 +1,44 @@ +// @ts-nocheck +import {isNumber} from '../isNumber'; +// #ifdef APP-ANDROID +import BigDecimal from 'java.math.BigDecimal' +// import BigDecimal from 'java.math.BigDecimal' +// import StringBuilder from 'java.lang.StringBuilder' +// import java.math.BigDecimal; +// #endif + +/** + * 乘法函数,用于处理浮点数乘法并保持精度。 + * @param {number} num1 - 第一个乘数。 + * @param {number} num2 - 第二个乘数。 + * @returns {number} 乘法运算的结果,保留正确的精度。 + */ +export function floatMul(num1 : number, num2 : number) : number { + if (!(isNumber(num1) || isNumber(num2))) { + console.warn('Please pass in the number type'); + return NaN; + } + let m = 0; + // #ifdef APP-ANDROID + let s1 = BigDecimal.valueOf(num1.toDouble()).toPlainString(); //new UTSNumber(num1).toString() // //`${num1.toFloat()}`// num1.toString(), + let s2 = BigDecimal.valueOf(num2.toDouble()).toPlainString(); //new UTSNumber(num2).toString() //`${num2.toFloat()}`//.toString(); + // #endif + // #ifndef APP-ANDROID + let s1:string = `${num1}`// num1.toString(), + let s2:string = `${num2}`//.toString(); + // #endif + + try { + m += s1.split('.')[1].length; + } catch (error) { } + try { + m += s2.split('.')[1].length; + } catch (error) { } + + // #ifdef APP-ANDROID + return parseFloat(s1.replace('.', '')) * parseFloat(s2.replace('.', '')) / Math.pow(10, m); + // #endif + // #ifndef APP-ANDROID + return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m); + // #endif +} diff --git a/uni_modules/lime-shared/floatSub/index.ts b/uni_modules/lime-shared/floatSub/index.ts new file mode 100644 index 0000000..9bc25cb --- /dev/null +++ b/uni_modules/lime-shared/floatSub/index.ts @@ -0,0 +1,32 @@ +import { isNumber } from '../isNumber'; +/** + * 减法函数,用于处理浮点数减法并保持精度。 + * @param {number} num1 - 被减数。 + * @param {number} num2 - 减数。 + * @returns {number} 减法运算的结果,保留正确的精度。 + */ +export function floatSub(num1 : number, num2 : number) : number { + if (!(isNumber(num1) || isNumber(num2))) { + console.warn('Please pass in the number type'); + return NaN; + } + let r1:number, r2:number, m:number, n:number; + try { + r1 = num1.toString().split('.')[1].length; + } catch (error) { + r1 = 0; + } + try { + r2 = num2.toString().split('.')[1].length; + } catch (error) { + r2 = 0; + } + m = Math.pow(10, Math.max(r1, r2)); + n = r1 >= r2 ? r1 : r2; + // #ifndef APP-ANDROID + return Number(((num1 * m - num2 * m) / m).toFixed(n)); + // #endif + // #ifdef APP-ANDROID + return parseFloat(((num1 * m - num2 * m) / m).toFixed(n)); + // #endif +} \ No newline at end of file diff --git a/uni_modules/lime-shared/getClassStr/index.ts b/uni_modules/lime-shared/getClassStr/index.ts new file mode 100644 index 0000000..ef8c7d6 --- /dev/null +++ b/uni_modules/lime-shared/getClassStr/index.ts @@ -0,0 +1,53 @@ +// @ts-nocheck + +// #ifdef UNI-APP-X && APP +import { isNumber } from '../isNumber' +import { isString } from '../isString' +import { isDef } from '../isDef' +// #endif + +/** + * 获取对象的类名字符串 + * @param obj - 需要处理的对象 + * @returns 由对象属性作为类名组成的字符串 + */ +export function getClassStr<T>(obj : T) : string { + let classNames : string[] = []; + // #ifdef UNI-APP-X && APP + if (obj instanceof UTSJSONObject) { + (obj as UTSJSONObject).toMap().forEach((value, key) => { + if (isDef(value)) { + if (isNumber(value)) { + classNames.push(key); + } + if (isString(value) && value !== '') { + classNames.push(key); + } + if (typeof value == 'boolean' && (value as boolean)) { + classNames.push(key); + } + } + }) + } + // #endif + // #ifndef UNI-APP-X && APP + // 遍历对象的属性 + for (let key in obj) { + // 检查属性确实属于对象自身且其值为true + if ((obj as any).hasOwnProperty(key) && obj[key]) { + // 将属性名添加到类名数组中 + classNames.push(key); + } + } + // #endif + + + // 将类名数组用空格连接成字符串并返回 + return classNames.join(' '); +} + + +// 示例 +// const obj = { foo: true, bar: false, baz: true }; +// const classNameStr = getClassStr(obj); +// console.log(classNameStr); // 输出: "foo baz" \ No newline at end of file diff --git a/uni_modules/lime-shared/getCurrentPage/index.ts b/uni_modules/lime-shared/getCurrentPage/index.ts new file mode 100644 index 0000000..af9a9d2 --- /dev/null +++ b/uni_modules/lime-shared/getCurrentPage/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif + + +// #ifdef UNI-APP-X && APP +export * from './uvue.uts' +// #endif diff --git a/uni_modules/lime-shared/getCurrentPage/uvue.uts b/uni_modules/lime-shared/getCurrentPage/uvue.uts new file mode 100644 index 0000000..9e96d2b --- /dev/null +++ b/uni_modules/lime-shared/getCurrentPage/uvue.uts @@ -0,0 +1,5 @@ +// @ts-nocheck +export const getCurrentPage = ():Page => { + const pages = getCurrentPages(); + return pages[pages.length - 1] +}; \ No newline at end of file diff --git a/uni_modules/lime-shared/getCurrentPage/vue.ts b/uni_modules/lime-shared/getCurrentPage/vue.ts new file mode 100644 index 0000000..79ecac8 --- /dev/null +++ b/uni_modules/lime-shared/getCurrentPage/vue.ts @@ -0,0 +1,6 @@ +// @ts-nocheck +/** 获取当前页 */ +export const getCurrentPage = () => { + const pages = getCurrentPages(); + return pages[pages.length - 1] //as T & WechatMiniprogram.Page.TrivialInstance; +}; \ No newline at end of file diff --git a/uni_modules/lime-shared/getLocalFilePath/index.ts b/uni_modules/lime-shared/getLocalFilePath/index.ts new file mode 100644 index 0000000..c496a07 --- /dev/null +++ b/uni_modules/lime-shared/getLocalFilePath/index.ts @@ -0,0 +1,62 @@ +// @ts-nocheck +// #ifdef APP-NVUE || APP-VUE +export const getLocalFilePath = (path : string) => { + if (typeof plus == 'undefined') return path + if (/^(_www|_doc|_documents|_downloads|file:\/\/|\/storage\/emulated\/0\/)/.test(path)) return path + if (/^\//.test(path)) { + const localFilePath = plus.io.convertAbsoluteFileSystem(path) + if (localFilePath !== path) { + return localFilePath + } else { + path = path.slice(1) + } + } + return '_www/' + path +} +// #endif + + +// #ifdef UNI-APP-X && APP +export { getResourcePath as getLocalFilePath } from '@/uni_modules/lime-file-utils' +// export const getLocalFilePath = (path : string) : string => { +// let uri = path +// if (uri.startsWith("http") || uri.startsWith("<svg") || uri.startsWith("data:image/svg+xml")) { +// return uri +// } +// if (uri.startsWith("file://")) { +// uri = uri.substring("file://".length) +// } else if (uri.startsWith("unifile://")) { +// uri = UTSAndroid.convert2AbsFullPath(uri) +// } else { +// uri = UTSAndroid.convert2AbsFullPath(uri) +// if (uri.startsWith("/android_asset/")) { +// uri = uri.replace("/android_asset/", "") +// } +// } +// if (new File(uri).exists()) { +// return uri +// } else { +// return null +// } +// // return UTSAndroid.convert2AbsFullPath(path) +// } +// #endif +// #ifdef APP-IOS +// export const getLocalFilePath = (path : string) : string => { +// try { +// let uri = path +// if (uri.startsWith("http") || uri.startsWith("<svg") || uri.startsWith("data:image/svg+xml")) { +// return uri +// } +// if (uri.startsWith("file://")) { +// return uri.substring("file://".length) +// } else if (path.startsWith("/var/")) { +// return path +// } +// return UTSiOS.getResourcePath(path) +// } catch (e) { +// return null +// } +// // return UTSiOS.getResourcePath(path) +// } +// #endif diff --git a/uni_modules/lime-shared/getRect/index.ts b/uni_modules/lime-shared/getRect/index.ts new file mode 100644 index 0000000..4a82c3c --- /dev/null +++ b/uni_modules/lime-shared/getRect/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +// #ifdef UNI-APP-X && APP +export * from './uvue.uts' +// #endif + + +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif \ No newline at end of file diff --git a/uni_modules/lime-shared/getRect/uvue.uts b/uni_modules/lime-shared/getRect/uvue.uts new file mode 100644 index 0000000..abceb2d --- /dev/null +++ b/uni_modules/lime-shared/getRect/uvue.uts @@ -0,0 +1,16 @@ +// @ts-nocheck +export function getRect(selector : string, context: ComponentInternalInstance):Promise<NodeInfo> { + return new Promise((resolve)=>{ + uni.createSelectorQuery().in(context).select(selector).boundingClientRect(res =>{ + resolve(res as NodeInfo) + }).exec(); + }) +} + +export function getAllRect(selector : string, context: ComponentInternalInstance):Promise<NodeInfo[]> { + return new Promise((resolve)=>{ + uni.createSelectorQuery().in(context).selectAll(selector).boundingClientRect(res =>{ + resolve(res as NodeInfo[]) + }).exec(); + }) +} \ No newline at end of file diff --git a/uni_modules/lime-shared/getRect/vue.ts b/uni_modules/lime-shared/getRect/vue.ts new file mode 100644 index 0000000..992ad09 --- /dev/null +++ b/uni_modules/lime-shared/getRect/vue.ts @@ -0,0 +1,117 @@ +// @ts-nocheck + +// #ifdef APP-NVUE +// 当编译环境是 APP-NVUE 时,引入 uni.requireNativePlugin('dom'),具体插件用途未知 +const dom = uni.requireNativePlugin('dom') +// #endif + +/** + * 获取节点信息 + * @param selector 选择器字符串 + * @param context ComponentInternalInstance 对象 + * @param node 是否获取node + * @returns 包含节点信息的 Promise 对象 + */ +export function getRect(selector : string, context : ComponentInternalInstance, node: boolean = false) { + // 之前是个对象,现在改成实例,防止旧版会报错 + if(context== null) { + return Promise.reject('context is null') + } + if(context.context){ + context = context.context + } + // #ifdef MP || VUE2 + if (context.proxy) context = context.proxy + // #endif + return new Promise<UniNamespace.NodeInfo>((resolve, reject) => { + // #ifndef APP-NVUE + const dom = uni.createSelectorQuery().in(context).select(selector); + const result = (rect: UniNamespace.NodeInfo) => { + if (rect) { + resolve(rect) + } else { + reject('no rect') + } + } + + if (!node) { + dom.boundingClientRect(result).exec() + } else { + dom.fields({ + node: true, + size: true, + rect: true + }, result).exec() + } + // #endif + // #ifdef APP-NVUE + let { context } = options + if (/#|\./.test(selector) && context.refs) { + selector = selector.replace(/#|\./, '') + if (context.refs[selector]) { + selector = context.refs[selector] + if(Array.isArray(selector)) { + selector = selector[0] + } + } + } + dom.getComponentRect(selector, (res) => { + if (res.size) { + resolve(res.size) + } else { + reject('no rect') + } + }) + // #endif + }); +}; + + +export function getAllRect(selector : string, context: ComponentInternalInstance, node:boolean = false) { + if(context== null) { + return Promise.reject('context is null') + } + // #ifdef MP || VUE2 + if (context.proxy) context = context.proxy + // #endif + return new Promise<UniNamespace.NodeInfo>((resolve, reject) => { + // #ifndef APP-NVUE + const dom = uni.createSelectorQuery().in(context).selectAll(selector); + const result = (rect: UniNamespace.NodeInfo[]) => { + if (rect) { + resolve(rect) + } else { + reject('no rect') + } + } + if (!node) { + dom.boundingClientRect(result).exec() + } else { + dom.fields({ + node: true, + size: true, + rect: true + }, result).exec() + } + // #endif + // #ifdef APP-NVUE + let { context } = options + if (/#|\./.test(selector) && context.refs) { + selector = selector.replace(/#|\./, '') + if (context.refs[selector]) { + selector = context.refs[selector] + if(Array.isArray(selector)) { + selector = selector[0] + } + } + } + dom.getComponentRect(selector, (res) => { + if (res.size) { + resolve([res.size]) + } else { + reject('no rect') + } + }) + // #endif + }); +}; \ No newline at end of file diff --git a/uni_modules/lime-shared/getStyleStr/index.ts b/uni_modules/lime-shared/getStyleStr/index.ts new file mode 100644 index 0000000..665941c --- /dev/null +++ b/uni_modules/lime-shared/getStyleStr/index.ts @@ -0,0 +1,54 @@ +// @ts-nocheck +// #ifndef UNI-APP-X && APP +interface CSSProperties { + [key : string] : string | number | null +} +// #endif + +// #ifdef VUE3 +// #ifdef UNI-APP-X && APP +type CSSProperties = UTSJSONObject +// #endif +// #endif +/** + * 将字符串转换为带有连字符分隔的小写形式 + * @param key - 要转换的字符串 + * @returns 转换后的字符串 + */ +export function toLowercaseSeparator(key : string):string { + return key.replace(/([A-Z])/g, '-$1').toLowerCase(); +} + +/** + * 获取样式对象对应的样式字符串 + * @param style - CSS样式对象 + * @returns 由非空有效样式属性键值对组成的字符串 + */ +export function getStyleStr(style : CSSProperties) : string { + + // #ifdef UNI-APP-X && APP + let styleStr = ''; + style.toMap().forEach((value, key) => { + if(value !== null && value != '') { + styleStr += `${toLowercaseSeparator(key as string)}: ${value};` + } + }) + return styleStr + // #endif + // #ifndef UNI-APP-X && APP + return Object.keys(style) + .filter( + (key) => + style[key] !== undefined && + style[key] !== null && + style[key] !== '') + .map((key : string) => `${toLowercaseSeparator(key)}: ${style[key]};`) + .join(' '); + // #endif +} + +// 示例 +// const style = { color: 'red', fontSize: '16px', backgroundColor: '', border: null }; +// const styleStr = getStyleStr(style); +// console.log(styleStr); +// 输出: "color: red; font-size: 16px;" \ No newline at end of file diff --git a/uni_modules/lime-shared/getStyleStr/index_.uts b/uni_modules/lime-shared/getStyleStr/index_.uts new file mode 100644 index 0000000..a4c4494 --- /dev/null +++ b/uni_modules/lime-shared/getStyleStr/index_.uts @@ -0,0 +1,39 @@ +// @ts-nocheck +// #ifndef UNI-APP-X +interface CSSProperties { + [key : string] : string | number +} +// #endif + +// #ifdef UNI-APP-X +type CSSProperties = UTSJSONObject +// #endif +/** + * 将字符串转换为带有连字符分隔的小写形式 + * @param key - 要转换的字符串 + * @returns 转换后的字符串 + */ +export function toLowercaseSeparator(key : string) : string { + return key.replace(/([A-Z])/g, '-$1').toLowerCase(); +} + +/** + * 获取样式对象对应的样式字符串 + * @param style - CSS样式对象 + * @returns 由非空有效样式属性键值对组成的字符串 + */ +export function getStyleStr(style : CSSProperties) : string { + let styleStr = ''; + style.toMap().forEach((value, key) => { + if(value !== null && value != '') { + styleStr += `${toLowercaseSeparator(key as string)}: ${value};` + } + }) + return styleStr +} + +// 示例 +// const style = { color: 'red', fontSize: '16px', backgroundColor: '', border: null }; +// const styleStr = getStyleStr(style); +// console.log(styleStr); +// 输出: "color: red; font-size: 16px;" \ No newline at end of file diff --git a/uni_modules/lime-shared/hasOwn/index.ts b/uni_modules/lime-shared/hasOwn/index.ts new file mode 100644 index 0000000..5e2b6da --- /dev/null +++ b/uni_modules/lime-shared/hasOwn/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +// #ifdef UNI-APP-X && APP +export * from './uvue.ts' +// #endif + + +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif \ No newline at end of file diff --git a/uni_modules/lime-shared/hasOwn/uvue.ts b/uni_modules/lime-shared/hasOwn/uvue.ts new file mode 100644 index 0000000..50345bf --- /dev/null +++ b/uni_modules/lime-shared/hasOwn/uvue.ts @@ -0,0 +1,39 @@ +// @ts-nocheck +/** + * 检查对象或数组是否具有指定的属性或键 + * @param obj 要检查的对象或数组 + * @param key 指定的属性或键 + * @returns 如果对象或数组具有指定的属性或键,则返回true;否则返回false + */ +function hasOwn(obj: UTSJSONObject, key: string): boolean +function hasOwn(obj: Map<string, unknown>, key: string): boolean +function hasOwn(obj: any, key: string): boolean { + if(obj instanceof UTSJSONObject){ + return obj[key] != null + } + if(obj instanceof Map<string, unknown>){ + return (obj as Map<string, unknown>).has(key) + } + return false +} +export { + hasOwn +} +// 示例 +// const obj = { name: 'John', age: 30 }; + +// if (hasOwn(obj, 'name')) { +// console.log("对象具有 'name' 属性"); +// } else { +// console.log("对象不具有 'name' 属性"); +// } +// // 输出: 对象具有 'name' 属性 + +// const arr = [1, 2, 3]; + +// if (hasOwn(arr, 'length')) { +// console.log("数组具有 'length' 属性"); +// } else { +// console.log("数组不具有 'length' 属性"); +// } +// 输出: 数组具有 'length' 属性 \ No newline at end of file diff --git a/uni_modules/lime-shared/hasOwn/vue.ts b/uni_modules/lime-shared/hasOwn/vue.ts new file mode 100644 index 0000000..7317879 --- /dev/null +++ b/uni_modules/lime-shared/hasOwn/vue.ts @@ -0,0 +1,30 @@ +// @ts-nocheck +const hasOwnProperty = Object.prototype.hasOwnProperty +/** + * 检查对象或数组是否具有指定的属性或键 + * @param obj 要检查的对象或数组 + * @param key 指定的属性或键 + * @returns 如果对象或数组具有指定的属性或键,则返回true;否则返回false + */ +export function hasOwn(obj: Object | Array<any>, key: string): boolean { + return hasOwnProperty.call(obj, key); +} + +// 示例 +// const obj = { name: 'John', age: 30 }; + +// if (hasOwn(obj, 'name')) { +// console.log("对象具有 'name' 属性"); +// } else { +// console.log("对象不具有 'name' 属性"); +// } +// // 输出: 对象具有 'name' 属性 + +// const arr = [1, 2, 3]; + +// if (hasOwn(arr, 'length')) { +// console.log("数组具有 'length' 属性"); +// } else { +// console.log("数组不具有 'length' 属性"); +// } +// 输出: 数组具有 'length' 属性 \ No newline at end of file diff --git a/uni_modules/lime-shared/index.ts b/uni_modules/lime-shared/index.ts new file mode 100644 index 0000000..2eb1d94 --- /dev/null +++ b/uni_modules/lime-shared/index.ts @@ -0,0 +1,43 @@ +// @ts-nocheck +// validator +// export * from './isString' +// export * from './isNumber' +// export * from './isNumeric' +// export * from './isDef' +// export * from './isFunction' +// export * from './isObject' +// export * from './isPromise' +// export * from './isBase64' + +// export * from './hasOwn' + +// // 单位转换 +// export * from './addUnit' +// export * from './unitConvert' +// export * from './toNumber' + +// export * from './random' +// export * from './range' +// export * from './fillZero' + +// // image +// export * from './base64ToPath' +// export * from './pathToBase64' +// export * from './exif' + +// // canvas +// export * from './canIUseCanvas2d' + +// // page +// export * from './getCurrentPage' + +// // dom +// export * from './getRect' +// export * from './selectComponent' +// export * from './createAnimation' + +// // delay +// export * from './sleep' +// export * from './debounce' +// export * from './throttle' + diff --git a/uni_modules/lime-shared/isBase64/index.ts b/uni_modules/lime-shared/isBase64/index.ts new file mode 100644 index 0000000..dcb8f3e --- /dev/null +++ b/uni_modules/lime-shared/isBase64/index.ts @@ -0,0 +1,23 @@ +// @ts-nocheck + +/** + * 判断一个字符串是否为Base64编码。 + * Base64编码的字符串只包含A-Z、a-z、0-9、+、/ 和 = 这些字符。 + * @param {string} str - 要检查的字符串。 + * @returns {boolean} 如果字符串是Base64编码,返回true,否则返回false。 + */ +export function isBase64(str: string): boolean { + const base64Regex = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; + return base64Regex.test(str); +} + +/** + * 判断一个字符串是否为Base64编码的data URI。 + * Base64编码的data URI通常以"data:"开头,后面跟着MIME类型和编码信息,然后是Base64编码的数据。 + * @param {string} str - 要检查的字符串。 + * @returns {boolean} 如果字符串是Base64编码的data URI,返回true,否则返回false。 + */ +export function isBase64DataUri(str: string): boolean { + const dataUriRegex = /^data:([a-zA-Z]+\/[a-zA-Z0-9-+.]+)(;base64)?,([a-zA-Z0-9+/]+={0,2})$/; + return dataUriRegex.test(str); +} \ No newline at end of file diff --git a/uni_modules/lime-shared/isBrowser/index.ts b/uni_modules/lime-shared/isBrowser/index.ts new file mode 100644 index 0000000..6baee2b --- /dev/null +++ b/uni_modules/lime-shared/isBrowser/index.ts @@ -0,0 +1,8 @@ +// @ts-nocheck +// #ifdef WEB +export const isBrowser = typeof window !== 'undefined'; +// #endif + +// #ifndef WEB +export const isBrowser = false; +// #endif \ No newline at end of file diff --git a/uni_modules/lime-shared/isDef/index.ts b/uni_modules/lime-shared/isDef/index.ts new file mode 100644 index 0000000..f8ca9bc --- /dev/null +++ b/uni_modules/lime-shared/isDef/index.ts @@ -0,0 +1,23 @@ +// @ts-nocheck +/** + * 检查一个值是否已定义(不为 undefined)且不为 null + * @param value 要检查的值 + * @returns 如果值已定义且不为 null,则返回 true;否则返回 false + */ +// #ifndef UNI-APP-X +export function isDef(value: unknown): boolean { + return value !== undefined && value !== null; +} +// #endif + + +// #ifdef UNI-APP-X +export function isDef(value : any|null) : boolean { + // #ifdef UNI-APP-X && APP + return value != null; + // #endif + // #ifndef UNI-APP-X && APP + return value != null && value != undefined; + // #endif +} +// #endif \ No newline at end of file diff --git a/uni_modules/lime-shared/isEmpty/index.ts b/uni_modules/lime-shared/isEmpty/index.ts new file mode 100644 index 0000000..74dbc35 --- /dev/null +++ b/uni_modules/lime-shared/isEmpty/index.ts @@ -0,0 +1,83 @@ +// @ts-nocheck +import {isDef} from '../isDef' +import {isString} from '../isString' +import {isNumber} from '../isNumber' +/** + * 判断一个值是否为空。 + * + * 对于字符串,去除首尾空格后判断长度是否为0。 + * 对于数组,判断长度是否为0。 + * 对于对象,判断键的数量是否为0。 + * 对于null或undefined,直接返回true。 + * 其他类型(如数字、布尔值等)默认不为空。 + * + * @param {any} value - 要检查的值。 + * @returns {boolean} 如果值为空,返回true,否则返回false。 + */ + + +// #ifdef UNI-APP-X && APP +export function isEmpty(value : any | null) : boolean { + // 为null + if(!isDef(value)){ + return true + } + // 为空字符 + if(isString(value)){ + return value.toString().trim().length == 0 + } + // 为数值 + if(isNumber(value)){ + return false + } + + if(typeof value == 'object'){ + // 数组 + if(Array.isArray(value)){ + return (value as Array<unknown>).length == 0 + } + // Map + if(value instanceof Map<unknown, unknown>) { + return value.size == 0 + } + // Set + if(value instanceof Set<unknown>) { + return value.size == 0 + } + if(value instanceof UTSJSONObject) { + return value.toMap().size == 0 + } + return JSON.stringify(value) == '{}' + } + + return true +} +// #endif + + +// #ifndef UNI-APP-X && APP +export function isEmpty(value: any): boolean { + // 检查是否为null或undefined + if (value == null) { + return true; + } + + // 检查字符串是否为空 + if (typeof value === 'string') { + return value.trim().length === 0; + } + + // 检查数组是否为空 + if (Array.isArray(value)) { + return value.length === 0; + } + + // 检查对象是否为空 + if (typeof value === 'object') { + return Object.keys(value).length === 0; + } + + // 其他类型(如数字、布尔值等)不为空 + return false; +} +// #endif \ No newline at end of file diff --git a/uni_modules/lime-shared/isFunction/index.ts b/uni_modules/lime-shared/isFunction/index.ts new file mode 100644 index 0000000..12605c3 --- /dev/null +++ b/uni_modules/lime-shared/isFunction/index.ts @@ -0,0 +1,16 @@ +// @ts-nocheck +/** + * 检查一个值是否为函数类型 + * @param val 要检查的值 + * @returns 如果值的类型是函数类型,则返回 true;否则返回 false + */ + +// #ifdef UNI-APP-X && APP +export const isFunction = (val: any):boolean => typeof val == 'function'; + // #endif + + +// #ifndef UNI-APP-X && APP +export const isFunction = (val: unknown): val is Function => + typeof val === 'function'; +// #endif diff --git a/uni_modules/lime-shared/isNumber/index.ts b/uni_modules/lime-shared/isNumber/index.ts new file mode 100644 index 0000000..cf8dd9b --- /dev/null +++ b/uni_modules/lime-shared/isNumber/index.ts @@ -0,0 +1,26 @@ +// @ts-nocheck +/** + * 检查一个值是否为数字类型 + * @param value 要检查的值,可以是 number 类型或 string 类型的数字 + * @returns 如果值是数字类型且不是 NaN,则返回 true;否则返回 false + */ + +// #ifndef UNI-APP-X +export function isNumber(value: number | string | null): boolean { + return typeof value === 'number' && !isNaN(value); +} +// #endif + +// #ifdef UNI-APP-X +export function isNumber(value: any|null): boolean { + // #ifdef APP-ANDROID + return ['Byte', 'UByte','Short','UShort','Int','UInt','Long','ULong','Float','Double','number'].includes(typeof value) + // #endif + // #ifdef APP-IOS + return ['Int8', 'UInt8','Int16','UInt16','Int32','UInt32','Int64','UInt64','Int','UInt','Float','Float16','Float32','Float64','Double', 'number'].includes(typeof value) + // #endif + // #ifndef UNI-APP-X && APP + return typeof value === 'number' && !isNaN(value); + // #endif +} +// #endif \ No newline at end of file diff --git a/uni_modules/lime-shared/isNumeric/index.ts b/uni_modules/lime-shared/isNumeric/index.ts new file mode 100644 index 0000000..af5020e --- /dev/null +++ b/uni_modules/lime-shared/isNumeric/index.ts @@ -0,0 +1,33 @@ +// @ts-nocheck + +/** + * 检查一个值是否为数字类型或表示数字的字符串 + * @param value 要检查的值,可以是 string 类型或 number 类型 + * @returns 如果值是数字类型或表示数字的字符串,则返回 true;否则返回 false + */ + +// #ifndef UNI-APP-X && APP +export function isNumeric(value: string | number | undefined | null): boolean { + return /^(-)?\d+(\.\d+)?$/.test(value); +} +// #endif + + +// #ifdef UNI-APP-X && APP +import {isNumber} from '../isNumber'; +import {isString} from '../isString'; +export function isNumeric(value : any|null) : boolean { + if(value == null) { + return false + } + if(isNumber(value)) { + return true + } else if(isString(value)) { + // const regex = "-?\\d+(\\.\\d+)?".toRegex() + const regex = new RegExp("^(-)?\\d+(\\.\\d+)?$") + return regex.test(value as string) //regex.matches(value as string) + } + return false + // return /^(-)?\d+(\.\d+)?$/.test(value); +} +// #endif diff --git a/uni_modules/lime-shared/isObject/index.ts b/uni_modules/lime-shared/isObject/index.ts new file mode 100644 index 0000000..d06c15b --- /dev/null +++ b/uni_modules/lime-shared/isObject/index.ts @@ -0,0 +1,19 @@ +// @ts-nocheck +/** + * 检查一个值是否为对象类型 + * @param val 要检查的值 + * @returns 如果值的类型是对象类型,则返回 true;否则返回 false + */ + +// #ifndef UNI-APP-X && APP +export const isObject = (val : unknown) : val is Record<any, any> => + val !== null && typeof val === 'object'; + +// #endif + + +// #ifdef UNI-APP-X && APP +export const isObject = (val : any | null) : boolean =>{ + return val !== null && typeof val === 'object'; +} +// #endif diff --git a/uni_modules/lime-shared/isPromise/index.ts b/uni_modules/lime-shared/isPromise/index.ts new file mode 100644 index 0000000..b13c10b --- /dev/null +++ b/uni_modules/lime-shared/isPromise/index.ts @@ -0,0 +1,22 @@ +// @ts-nocheck +import {isFunction} from '../isFunction' +import {isObject} from '../isObject' +/** + * 检查一个值是否为 Promise 类型 + * @param val 要检查的值 + * @returns 如果值的类型是 Promise 类型,则返回 true;否则返回 false + */ +// #ifndef APP-ANDROID +export const isPromise = <T = any>(val: unknown): val is Promise<T> => { + // 使用 isObject 函数判断值是否为对象类型 + // 使用 isFunction 函数判断值是否具有 then 方法和 catch 方法 + return isObject(val) && isFunction(val.then) && isFunction(val.catch); +}; +// #endif + + +// #ifdef APP-ANDROID +export const isPromise = (val: any): boolean => { + return val instanceof Promise<unknown> +}; +// #endif \ No newline at end of file diff --git a/uni_modules/lime-shared/isString/index.ts b/uni_modules/lime-shared/isString/index.ts new file mode 100644 index 0000000..143f876 --- /dev/null +++ b/uni_modules/lime-shared/isString/index.ts @@ -0,0 +1,19 @@ +// @ts-nocheck +/** + * 检查一个值是否为字符串类型 + * @param str 要检查的值 + * @returns 如果值的类型是字符串类型,则返回 true;否则返回 false + */ +// #ifndef UNI-APP-X && APP +// export const isString = (str: unknown): str is string => typeof str === 'string'; +export function isString (str: unknown): str is string { + return typeof str == 'string' +} +// #endif + + +// #ifdef UNI-APP-X && APP +export function isString (str: any|null): boolean { + return typeof str == 'string' +} +// #endif diff --git a/uni_modules/lime-shared/kebabCase/index.ts b/uni_modules/lime-shared/kebabCase/index.ts new file mode 100644 index 0000000..114c52c --- /dev/null +++ b/uni_modules/lime-shared/kebabCase/index.ts @@ -0,0 +1,24 @@ +// @ts-nocheck +// export function toLowercaseSeparator(key: string) { +// return key.replace(/([A-Z])/g, '-$1').toLowerCase(); +// } + +/** + * 将字符串转换为指定连接符的命名约定 + * @param str 要转换的字符串 + * @param separator 指定的连接符,默认为 "-" + * @returns 转换后的字符串 + */ +export function kebabCase(str : string, separator : string = "-") : string { + return str + + // #ifdef UNI-APP-X && APP + .replace(/[A-Z]/g, (match : string, _ : number, _ : string) : string => `${separator}${match.toLowerCase()}`) // 将大写字母替换为连接符加小写字母 + // #endif + // #ifndef UNI-APP-X && APP + .replace(/[A-Z]/g, (match : string) : string => `${separator}${match.toLowerCase()}`) // 将大写字母替换为连接符加小写字母 + // #endif + .replace(/[\s_-]+/g, separator) // 将空格、下划线和短横线替换为指定连接符 + .replace(new RegExp(`^${separator}|${separator}$`, "g"), "") // 删除开头和结尾的连接符 + .toLowerCase(); // 将结果转换为全小写 +} \ No newline at end of file diff --git a/uni_modules/lime-shared/package.json b/uni_modules/lime-shared/package.json new file mode 100644 index 0000000..a307b41 --- /dev/null +++ b/uni_modules/lime-shared/package.json @@ -0,0 +1,86 @@ +{ + "id": "lime-shared", + "displayName": "lime-shared", + "version": "0.1.8", + "description": "本人插件的几个公共函数,获取当前页,图片的base64转临时路径,图片的exif信息等", + "keywords": [ + "lime-shared", + "exif" +], + "repository": "", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "type": "sdk-js", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [ + + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "Vue": { + "vue2": "y", + "vue3": "y" + }, + "App": { + "app-vue": "y", + "app-uvue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y", + "钉钉": "y", + "快手": "y", + "飞书": "y", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/lime-shared/pathToBase64/index.ts b/uni_modules/lime-shared/pathToBase64/index.ts new file mode 100644 index 0000000..af9a9d2 --- /dev/null +++ b/uni_modules/lime-shared/pathToBase64/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif + + +// #ifdef UNI-APP-X && APP +export * from './uvue.uts' +// #endif diff --git a/uni_modules/lime-shared/pathToBase64/uvue.uts b/uni_modules/lime-shared/pathToBase64/uvue.uts new file mode 100644 index 0000000..d5bbdb1 --- /dev/null +++ b/uni_modules/lime-shared/pathToBase64/uvue.uts @@ -0,0 +1,17 @@ +// @ts-nocheck +// import { processFile, ProcessFileOptions } from '@/uni_modules/lime-file-utils' +export function pathToBase64(path : string) : Promise<string> { + console.error('pathToBase64: 当前环境不支持,请使用 【lime-file-utils】') + // return new Promise((resolve, reject) => { + // processFile({ + // type: 'toDataURL', + // path, + // success(res : string) { + // resolve(res) + // }, + // fail(err: any){ + // reject(err) + // } + // } as ProcessFileOptions) + // }) +} \ No newline at end of file diff --git a/uni_modules/lime-shared/pathToBase64/vue.ts b/uni_modules/lime-shared/pathToBase64/vue.ts new file mode 100644 index 0000000..8167f88 --- /dev/null +++ b/uni_modules/lime-shared/pathToBase64/vue.ts @@ -0,0 +1,121 @@ +// @ts-nocheck + +// #ifdef APP-PLUS +import { getLocalFilePath } from '../getLocalFilePath' +// #endif +function isImage(extension : string) { + const imageExtensions = ["jpg", "jpeg", "png", "gif", "bmp", "svg"]; + return imageExtensions.includes(extension.toLowerCase()); +} +// #ifdef H5 +function getSVGFromURL(url: string) { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.responseType = 'text'; + + xhr.onload = function () { + if (xhr.status === 200) { + const svg = xhr.responseText; + resolve(svg); + } else { + reject(new Error(xhr.statusText)); + } + }; + + xhr.onerror = function () { + reject(new Error('Network error')); + }; + + xhr.send(); + }); +} +// #endif +/** + * 路径转base64 + * @param {Object} string + */ +export function pathToBase64(path : string) : Promise<string> { + if (/^data:/.test(path)) return path + let extension = path.substring(path.lastIndexOf('.') + 1); + const isImageFile = isImage(extension) + let prefix = '' + if (isImageFile) { + prefix = 'image/'; + if(extension == 'svg') { + extension += '+xml' + } + } else if (extension === 'pdf') { + prefix = 'application/pdf'; + } else if (extension === 'txt') { + prefix = 'text/plain'; + } else { + // 添加更多文件类型的判断 + // 如果不是图片、PDF、文本等类型,可以设定默认的前缀或采取其他处理 + prefix = 'application/octet-stream'; + } + return new Promise((resolve, reject) => { + // #ifdef H5 + if (isImageFile) { + if(extension == 'svg') { + getSVGFromURL(path).then(svg => { + const base64 = btoa(svg); + resolve(`data:image/svg+xml;base64,${base64}`); + }) + } else { + let image = new Image(); + image.setAttribute("crossOrigin", 'Anonymous'); + image.onload = function () { + let canvas = document.createElement('canvas'); + canvas.width = this.naturalWidth; + canvas.height = this.naturalHeight; + canvas.getContext('2d').drawImage(image, 0, 0); + let result = canvas.toDataURL(`${prefix}${extension}`) + resolve(result); + canvas.height = canvas.width = 0 + } + image.src = path + '?v=' + Math.random() + image.onerror = (error) => { + reject(error); + }; + } + + } else { + reject('not image'); + } + + // #endif + + // #ifdef MP + if (uni.canIUse('getFileSystemManager')) { + uni.getFileSystemManager().readFile({ + filePath: path, + encoding: 'base64', + success: (res) => { + resolve(`data:${prefix}${extension};base64,${res.data}`) + }, + fail: (error) => { + console.error({ error, path }) + reject(error) + } + }) + } + // #endif + + // #ifdef APP-PLUS + plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), (entry) => { + entry.file((file : any) => { + const fileReader = new plus.io.FileReader() + fileReader.onload = (data) => { + resolve(data.target.result) + } + fileReader.onerror = (error) => { + console.error({ error, path }) + reject(error) + } + fileReader.readAsDataURL(file) + }, reject) + }, reject) + // #endif + }) +} \ No newline at end of file diff --git a/uni_modules/lime-shared/platform/index.ts b/uni_modules/lime-shared/platform/index.ts new file mode 100644 index 0000000..55fd412 --- /dev/null +++ b/uni_modules/lime-shared/platform/index.ts @@ -0,0 +1,34 @@ +// @ts-nocheck +export function getPlatform():Uni { + // #ifdef MP-WEIXIN + return wx + // #endif + // #ifdef MP-BAIDU + return swan + // #endif + // #ifdef MP-ALIPAY + return my + // #endif + // #ifdef MP-JD + return jd + // #endif + // #ifdef MP-QQ + return qq + // #endif + // #ifdef MP-360 + return qh + // #endif + // #ifdef MP-KUAISHOU + return ks + // #endif + // #ifdef MP-LARK||MP-TOUTIAO + return tt + // #endif + // #ifdef MP-DINGTALK + return dd + // #endif + // #ifdef QUICKAPP-WEBVIEW || QUICKAPP-WEBVIEW-UNION || QUICKAPP-WEBVIEW-HUAWEI + return qa + // #endif + return uni +} \ No newline at end of file diff --git a/uni_modules/lime-shared/raf/index.ts b/uni_modules/lime-shared/raf/index.ts new file mode 100644 index 0000000..2da0808 --- /dev/null +++ b/uni_modules/lime-shared/raf/index.ts @@ -0,0 +1,10 @@ +// @ts-nocheck + +// #ifdef UNI-APP-X && APP +export * from './uvue.ts' +// #endif + + +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif diff --git a/uni_modules/lime-shared/raf/uvue.ts b/uni_modules/lime-shared/raf/uvue.ts new file mode 100644 index 0000000..1b0d9f2 --- /dev/null +++ b/uni_modules/lime-shared/raf/uvue.ts @@ -0,0 +1,48 @@ +// @ts-nocheck +// 是否支持被动事件监听 +export const supportsPassive = true; + +// #ifdef uniVersion < 4.25 +// 请求动画帧 +export function raf(fn: TimerCallback): number { + return setTimeout(fn, 1000 / 60); +} + +// 取消动画帧 +export function cancelRaf(id: number) { + clearTimeout(id); +} + + +// 双倍动画帧 +export function doubleRaf(fn: TimerCallback): void { + raf(():number => raf(fn)); // 在下一帧回调中再次请求动画帧,实现双倍动画帧效果 +} +// #endif + + +// #ifdef uniVersion >= 4.25 +// 请求动画帧 +export function raf(fn: UniAnimationFrameCallback): number +export function raf(fn: UniAnimationFrameCallbackWithNoArgument): number +export function raf(fn: any): number { + if(typeof fn == 'UniAnimationFrameCallback') { + return requestAnimationFrame(fn as UniAnimationFrameCallback); + } else { + return requestAnimationFrame(fn as UniAnimationFrameCallbackWithNoArgument); + } +} + +// 取消动画帧 +export function cancelRaf(id: number) { + cancelAnimationFrame(id); +} + +// 双倍动画帧 +export function doubleRaf(fn: UniAnimationFrameCallback): void +export function doubleRaf(fn: UniAnimationFrameCallbackWithNoArgument): void +export function doubleRaf(fn: any): void { + raf(():number => raf(fn)); // 在下一帧回调中再次请求动画帧,实现双倍动画帧效果 +} +// #endif + diff --git a/uni_modules/lime-shared/raf/vue.ts b/uni_modules/lime-shared/raf/vue.ts new file mode 100644 index 0000000..98a364e --- /dev/null +++ b/uni_modules/lime-shared/raf/vue.ts @@ -0,0 +1,32 @@ +// @ts-nocheck +type Callback = () => void//Function +// 是否支持被动事件监听 +export const supportsPassive = true; + +// 请求动画帧 +export function raf(fn : Callback) : number { + // #ifndef WEB + return setTimeout(fn, 1000 / 60); // 请求动画帧 + // #endif + // #ifdef WEB + return requestAnimationFrame(fn); // 请求动画帧 + // #endif +} + +// 取消动画帧 +export function cancelRaf(id : number) { + // 如果是在浏览器环境下,使用 cancelAnimationFrame 方法 + // #ifdef WEB + cancelAnimationFrame(id); // 取消动画帧 + // #endif + // #ifndef WEB + clearTimeout(id); // 取消动画帧 + // #endif +} + +// 双倍动画帧 +export function doubleRaf(fn : Callback) : void { + raf(() => { + raf(fn) + }); // 在下一帧回调中再次请求动画帧,实现双倍动画帧效果 +} \ No newline at end of file diff --git a/uni_modules/lime-shared/random/index.ts b/uni_modules/lime-shared/random/index.ts new file mode 100644 index 0000000..49a21ed --- /dev/null +++ b/uni_modules/lime-shared/random/index.ts @@ -0,0 +1,24 @@ +// @ts-nocheck +/** + * 生成一个指定范围内的随机数 + * @param min 随机数的最小值 + * @param max 随机数的最大值 + * @param fixed 随机数的小数位数,默认为 0 + * @returns 生成的随机数 + */ + +export function random(min: number, max: number, fixed: number = 0):number { + // 将 min 和 max 转换为数字类型 + // min = +min || 0; + // max = +max || 0; + // 计算随机数范围内的一个随机数 + const num = Math.random() * (max - min) + min; + // 如果 fixed 参数为 0,则返回四舍五入的整数随机数;否则保留固定小数位数 + // Number + return fixed == 0 ? Math.round(num) : parseFloat(num.toFixed(fixed)); +} + +// 示例 +// console.log(random(0, 10)); // 输出:在 0 和 10 之间的一个整数随机数 +// console.log(random(0, 1, 2)); // 输出:在 0 和 1 之间的一个保留两位小数的随机数 +// console.log(random(1, 100, 3)); // 输出:在 1 和 100 之间的一个保留三位小数的随机数 \ No newline at end of file diff --git a/uni_modules/lime-shared/range/index.ts b/uni_modules/lime-shared/range/index.ts new file mode 100644 index 0000000..483b7d1 --- /dev/null +++ b/uni_modules/lime-shared/range/index.ts @@ -0,0 +1,36 @@ +// @ts-nocheck +/** + * 生成一个数字范围的数组 + * @param start 范围的起始值 + * @param end 范围的结束值 + * @param step 步长,默认为 1 + * @param fromRight 是否从右侧开始生成,默认为 false + * @returns 生成的数字范围数组 + */ +export function range(start : number, end : number, step : number = 1, fromRight : boolean = false) : number[] { + let index = -1; + // 计算范围的长度 + let length = Math.max(Math.ceil((end - start) / step), 0); + // 创建一个长度为 length 的数组 + // #ifdef APP-ANDROID + const result = Array.fromNative(new IntArray(length.toInt())); + // #endif + // #ifndef APP-ANDROID + const result = new Array(length); + // #endif + + // 使用循环生成数字范围数组 + let _start = start + while (length-- > 0) { + // 根据 fromRight 参数决定从左侧还是右侧开始填充数组 + result[fromRight ? length : ++index] = _start; + _start += step; + } + return result; +} + + +// 示例 +// console.log(range(0, 5)); // 输出: [0, 1, 2, 3, 4] +// console.log(range(1, 10, 2, true)); // 输出: [9, 7, 5, 3, 1] +// console.log(range(5, 0, -1)); // 输出: [5, 4, 3, 2, 1] \ No newline at end of file diff --git a/uni_modules/lime-shared/readme.md b/uni_modules/lime-shared/readme.md new file mode 100644 index 0000000..3dc8c22 --- /dev/null +++ b/uni_modules/lime-shared/readme.md @@ -0,0 +1,445 @@ +# lime-shared 工具库 +- 本人插件的几个公共函数 +- 按需引入 + + +## 引入 +按需引入只会引入相关的方法,不要看着 插件函数列表多 而占空间,只要不引用不会被打包 +```js +import {getRect} from '@/uni_modules/lime-shared/getRect' +``` + +## 目录 ++ [getRect](#api_getRect): 获取节点尺寸信息 ++ [addUnit](#api_addUnit): 将未带单位的数值添加px,如果有单位则返回原值 ++ [unitConvert](#api_unitConvert): 将带有rpx|px的字符转成number,若本身是number则直接返回 ++ [canIUseCanvas2d](#api_canIUseCanvas2d): 环境是否支持使用 canvas 2d ++ [getCurrentPage](#api_getCurrentPage): 获取当前页 ++ [base64ToPath](#api_base64ToPath): 把base64的图片转成临时路径 ++ [pathToBase64](#api_pathToBase64): 把图片的临时路径转成base64 ++ [sleep](#api_sleep): async 内部程序等待一定时间后再执行 ++ [throttle](#api_throttle): 节流 ++ [debounce](#api_debounce): 防抖 ++ [random](#api_random): 返回指定范围的随机数 ++ [range](#api_range): 生成区间数组 ++ [clamp](#api_clamp): 夹在min和max之间的数值 ++ [floatAdd](#api_floatAdd): 返回两个浮点数相加的结果 ++ [fillZero](#api_fillZero): 补零,如果传入的是个位数则在前面补0 ++ [exif](#api_exif): 获取图片exif ++ [selectComponent](#api_selectComponent): 获取页面或当前实例的指定组件 ++ [createAnimation](#api_createAnimation): uni.createAnimation ++ [animation](#api_animation): 数值从一个值到另一个值的过渡 ++ [camelCase](#api_camelCase): 字符串转换为 camelCase 或 PascalCase 风格的命名约定 ++ [kebabCase](#api_kebabCase): 将字符串转换为指定连接符的命名约定 ++ [closest](#api_closest): 在给定数组中找到最接近目标数字的元素 ++ [isBase64](#api_isBase64): 判断字符串是否为base64 ++ [isNumber](#api_isNumber): 检查一个值是否为数字类型 ++ [isNumeric](#api_isNumeric): 检查一个值是否为数字类型或表示数字的字符串 ++ [isString](#api_isString): 检查一个值是否为字符串类型 ++ [composition-api](#api_composition-api): 为兼容vue2 + +## Utils + + +### getRect <a id="api_getRect"></a> +- 返回节点尺寸信息 + +```js +// 组件内需要传入上下文 +// 如果是nvue 则需要在节点上加与id或class同名的ref +getRect('#id',{context: this}).then(res => {}) +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + + +### addUnit <a id="api_addUnit"></a> +- 将未带单位的数值添加px,如果有单位则返回原值 + +```js +addUnit(10) +// 10px +``` + +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + + +### unitConvert <a id="api_unitConvert"></a> +- 将带有rpx|px的字符转成number,若本身是number则直接返回 + +```js +unitConvert('10rpx') +// 5 设备不同 返回的值也不同 +unitConvert('10px') +// 10 +unitConvert(10) +// 10 +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +### canIUseCanvas2d <a id="api_canIUseCanvas2d"></a> +- 环境是否支持使用 canvas 2d + +```js +canIUseCanvas2d() +// 若支持返回 true 否则 false +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +### getCurrentPage <a id="api_getCurrentPage"></a> +- 获取当前页 + +```js +const page = getCurrentPage() +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +### base64ToPath <a id="api_base64ToPath"></a> +- 把base64的图片转成临时路径 + +```js +base64ToPath(`xxxxx`).then(res => {}) +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +### pathToBase64 <a id="api_pathToBase64"></a> +- 把图片的临时路径转成base64 + +```js +pathToBase64(`xxxxx/xxx.png`).then(res => {}) +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +### sleep <a id="api_sleep"></a> +- 睡眠,让 async 内部程序等待一定时间后再执行 + +```js +async next () => { + await sleep(300) + console.log('limeui'); +} +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### throttle <a id="api_throttle"></a> +- 节流 + +```js +throttle((nama) => {console.log(nama)}, 200)('limeui'); +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### debounce <a id="api_debounce"></a> +- 防抖 + +```js +debounce((nama) => {console.log(nama)}, 200)('limeui'); +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### random <a id="api_random"></a> +- 返回指定范围的随机数 + +```js +random(1, 5); +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### range <a id="api_range"></a> +- 生成区间数组 + +```js +range(0, 5) +// [0,1,2,3,4,5] +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +### clamp <a id="api_clamp"></a> +- 夹在min和max之间的数值,如小于min,返回min, 如大于max,返回max,否侧原值返回 + +```js +clamp(0, 10, -1) +// 0 +clamp(0, 10, 11) +// 10 +clamp(0, 10, 9) +// 9 +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +### floatAdd <a id="api_floatAdd"></a> +- 返回两个浮点数相加的结果 + +```js +floatAdd(0.1, 0.2) // 0.3 +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### fillZero <a id="api_fillZero"></a> +- 补零,如果传入的是`个位数`则在前面补0 + +```js +fillZero(9); +// 09 +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### exif <a id="api_exif"></a> +- 获取图片exif +- 支持临时路径、base64 + +```js +uni.chooseImage({ + count: 1, //最多可以选择的图片张数 + sizeType: "original", + success: (res) => { + exif.getData(res.tempFiles[0], function() { + let tagj = exif.getTag(this, "GPSLongitude"); + let Orientation = exif.getTag(this, 'Orientation'); + console.log(tagj, Orientation) + }) + } +}) +``` + +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | x | + + +### selectComponent <a id="api_selectComponent"></a> +- 获取页面或当前实例的指定组件,会在页面或实例向所有的节点查找(包括子组件或子子组件) +- 仅vue3,vue2没有测试过 + +```js +// 当前页面 +const page = getCurrentPage() +selectComponent('.custom', {context: page}).then(res => { +}) +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | x | + + + +### createAnimation <a id="api_createAnimation"></a> +- 创建动画,与uni.createAnimation使用方法一致,只为了抹平nvue + +```html +<view ref="ball" :animation="animationData"></view> +``` +```js +const ball = ref(null) +const animation = createAnimation({ + transformOrigin: "50% 50%", + duration: 1000, + timingFunction: "ease", + delay: 0 +}) + +animation.scale(2,2).rotate(45).step() +// nvue 无导出数据,这样写只为了平台一致, +// nvue 需要把 ref 传入,其它平台不需要 +const animationData = animation.export(ball.value) +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +### camelCase <a id="api_camelCase"></a> +- 将字符串转换为 camelCase 或 PascalCase 风格的命名约定 + +```js +camelCase("hello world") // helloWorld +camelCase("hello world", true) // HelloWorld +``` + +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### kebabCase <a id="api_kebabCase"></a> +- 将字符串转换为指定连接符的命名约定 + +```js +kebabCase("helloWorld") // hello-world +kebabCase("hello world_example") // hello-world-example +kebabCase("helloWorld", "_") // hello_world +``` + +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + + +### closest <a id="api_closest"></a> +- 在给定数组中找到最接近目标数字的元素 + +```js +closest([1, 3, 5, 7, 9], 6) // 5 +``` + +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +### isBase64 <a id="api_isBase64"></a> +- 判断字符串是否为base64 + +```js +isBase64('xxxxx') +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### isNumber <a id="api_isNumber"></a> +- 检查一个值是否为数字类型 + +```js +isNumber('0') // false +isNumber(0) // true +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### isNumeric <a id="api_isNumeric"></a> +- 检查一个值是否为数字类型或表示数字的字符串 + +```js +isNumeric('0') // true +isNumeric(0) // true +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + +### isString <a id="api_isString"></a> +- 检查一个值是否为数字类型或表示数字的字符串 + +```js +isString('0') // true +isString(0) // false +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + + +## composition-api <a id="api_composition-api"></a> +- 因本人插件需要兼容vue2/vue3,故增加一个vue文件,代替条件编译 +- vue2需要在main.js加上这一段 +```js +// vue2 +import Vue from 'vue' +import VueCompositionAPI from '@vue/composition-api' +Vue.use(VueCompositionAPI) +``` + +```js +//使用 +import {computed, onMounted, watch, reactive} from '@/uni_modules/lime-shared/vue' +``` + +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | x | diff --git a/uni_modules/lime-shared/selectAllComponent/index.ts b/uni_modules/lime-shared/selectAllComponent/index.ts new file mode 100644 index 0000000..17c3a3c --- /dev/null +++ b/uni_modules/lime-shared/selectAllComponent/index.ts @@ -0,0 +1,8 @@ +// @ts-nocheck +// #ifdef UNI-APP-X +export * from './uvue.uts' +// #endif + +// #ifndef UNI-APP-X +export * from './vue.ts' +// #endif \ No newline at end of file diff --git a/uni_modules/lime-shared/selectAllComponent/uvue.uts b/uni_modules/lime-shared/selectAllComponent/uvue.uts new file mode 100644 index 0000000..07c9fcd --- /dev/null +++ b/uni_modules/lime-shared/selectAllComponent/uvue.uts @@ -0,0 +1,39 @@ +// @ts-nocheck +import { type ComponentPublicInstance } from 'vue'; + +type SelectOptions = { + context : ComponentPublicInstance, + needAll : boolean | null, + +} + +export function selectAllComponent(selector : string, options : UTSJSONObject) : ComponentPublicInstance[]|null { + const context = options.get('context')! as ComponentPublicInstance; + let needAll = options.get('needAll') as boolean; + let result:ComponentPublicInstance[] = [] + + if(needAll == null) { needAll = true }; + + if(context.$children.length > 0) { + const queue:ComponentPublicInstance[] = [...context.$children]; + while(queue.length > 0) { + const child = queue.shift(); + const name = child?.$options?.name; + if(name == selector) { + result.push(child as ComponentPublicInstance) + } else { + const children = child?.$children + if(children !== null) { + queue.push(...children) + } + } + if(result.length > 0 && !needAll) { + break; + } + } + } + if(result.length > 0) { + return result + } + return null +} \ No newline at end of file diff --git a/uni_modules/lime-shared/selectAllComponent/vue.ts b/uni_modules/lime-shared/selectAllComponent/vue.ts new file mode 100644 index 0000000..380bd7a --- /dev/null +++ b/uni_modules/lime-shared/selectAllComponent/vue.ts @@ -0,0 +1,151 @@ +// @ts-nocheck +interface SelectOptions { + context?: any + needAll?: boolean + node?: boolean +} +// #ifdef MP +function selectMPComponent(key: string, name: string, context: any, needAll: boolean) { + const {proxy, $vm} = context + context = $vm || proxy + if(!['ref','component'].includes(key)) { + const queue = [context] + let result = null + const selector = (key == 'id' ? '#': '.') + name; + while(queue.length > 0) { + const child = queue.shift(); + const flag = child?.selectComponent(selector) + if(flag) { + if(!needAll) {return result = flag.$vm} + return result = child.selectAllComponents(selector).map(item => item.$vm) + } else { + child.$children && (queue.push(...child.$children)); + } + } + return result + } else { + const {$templateRefs} = context.$ + const nameMap = {} + for (var i = 0; i < $templateRefs.length; i++) { + const item = $templateRefs[i] + nameMap[item.i] = item.r + } + let result = [] + if(context.$children.length) { + const queue = [...context.$children] + while(queue.length > 0) { + const child = queue.shift(); + if(key == 'component' && (child.type?.name === name || child.$?.type?.name === name)) { + result.push(child) + } else if(child.$refs && child.$refs[name]) { + result = child.$refs[name] + } else if(nameMap[child.id] === name){ + result.push(child) + } else { + child.$children && (queue.push(...child.$children)); + } + if(result.length && !needAll) { + return; + } + } + } + return needAll ? result : result[0] + } +} +// #endif +// #ifdef H5 +function selectH5Component(key: string, name: string, context: any, needAll: boolean) { + const {_, component } = context + const child = {component: _ || component || context, children: null , subTree: null, props: null} + let result = [] + let queue = [child] + while(queue.length > 0 ) { + const child = queue.shift() + const {component, children , props, subTree} = child + if(key === 'component' && component?.type?.name == name) { + result.push(component) + } else if(key === 'ref' && component && (props?.ref == name || component[key][name])) { + if(props?.ref == name) { + //exposed + result.push(component) + } else if(component[key][name]) { + result.push(component[key][name]) + } + } else if(key !== 'ref' && component?.exposed && new RegExp(`\\b${name}\\b`).test(component.attrs[key])) { + // exposed + result.push(component) + } else if(children && Array.isArray(children)) { + queue.push(...children) + } else if(!component && subTree) { + queue.push(subTree) + } else if(component?.subTree) { + queue.push(component.subTree) + } + if(result.length && !needAll) { + break + } + } + return needAll ? result : result[0] +} +// #endif +// #ifdef APP +function selectAPPComponent(key: string, name: string, context: any, needAll: boolean, node: boolean) { + let result = [] + // const {_, component} = context + // const child = {component: _ || component || context, children: null, props: null, subTree: null} + const queue = [context] + while(queue.length > 0) { + const child = queue.shift() + const {component, children, props, subTree} = child + const isComp = component && props && component.exposed && !node + if(key == 'component' && child.type && child.type.name === name) { + result.push(component) + } else if(props?.[key] === name && node) { + result.push(child) + } else if(key === 'ref' && isComp && (props.ref === name || props.ref_key === name)) { + // exposed + result.push(component) + } else if(key !== 'ref' && isComp && new RegExp(`\\b${name}\\b`).test(props[key])) { + // exposed + result.push(component) + } + // else if(component && component.subTree && Array.isArray(component.subTree.children)){ + // queue.push(...component.subTree.children) + // } + else if(subTree) { + queue.push(subTree) + } else if(component && component.subTree){ + queue.push(component.subTree) + } + else if(children && Array.isArray(children)) { + queue.push(...children) + } + if(result.length && !needAll) { + break; + } + } + return needAll ? result : result[0] +} +// #endif +export function selectAllComponent(selector: string, options: SelectOptions = {}) { + // . class + // # id + // $ ref + // @ component name + const reg = /^(\.|#|@|\$)([a-zA-Z_0-9\-]+)$/; + if(!reg.test(selector)) return null + let { context, needAll = true, node} = options + const [,prefix, name] = selector.match(reg) + const symbolMappings = {'.': 'class', '#': 'id', '$':'ref', '@':'component'} + + const key = symbolMappings [prefix] //prefix === '.' ? 'class' : prefix === '#' ? 'id' : 'ref'; + // #ifdef MP + return selectMPComponent(key, name, context, needAll) + // #endif + // #ifdef H5 + return selectH5Component(key, name, context, needAll) + // #endif + // #ifdef APP + return selectAPPComponent(key, name, context, needAll, node) + // #endif +} \ No newline at end of file diff --git a/uni_modules/lime-shared/selectComponent/index.ts b/uni_modules/lime-shared/selectComponent/index.ts new file mode 100644 index 0000000..52454fb --- /dev/null +++ b/uni_modules/lime-shared/selectComponent/index.ts @@ -0,0 +1,7 @@ +// @ts-nocheck +// #ifndef UNI-APP-X +export * from './vue.ts' +// #endif +// #ifdef UNI-APP-X +export * from './uvue.uts' +// #endif \ No newline at end of file diff --git a/uni_modules/lime-shared/selectComponent/uvue.uts b/uni_modules/lime-shared/selectComponent/uvue.uts new file mode 100644 index 0000000..c2aa2bc --- /dev/null +++ b/uni_modules/lime-shared/selectComponent/uvue.uts @@ -0,0 +1,75 @@ +// @ts-nocheck +import { type ComponentPublicInstance } from 'vue'; +// #ifdef APP +function findChildren(selector: string, context: ComponentPublicInstance, needAll: boolean): ComponentPublicInstance [] | null{ + let result:ComponentPublicInstance[] = [] + + if(context !== null && context.$children.length > 0) { + const queue:ComponentPublicInstance[] = [...context.$children]; + while(queue.length > 0) { + const child = queue.shift(); + const name = child?.$options?.name; + if(name == selector) { + result.push(child as ComponentPublicInstance) + } else { + const children = child?.$children + if(children !== null) { + queue.push(...children) + } + } + if(result.length > 0 && !needAll) { + break; + } + } + } + if(result.length > 0) { + return result + } + return null +} + +class Query { + context : ComponentPublicInstance | null = null + selector : string = '' + // components : ComponentPublicInstance[] = [] + constructor(selector : string, context : ComponentPublicInstance | null) { + this.selector = selector + this.context = context + } + in(context : ComponentPublicInstance) : Query { + return new Query(this.selector, context) + } + find(): ComponentPublicInstance | null { + const selector = this.selector + if(selector == '') return null + const component = findChildren(selector, this.context!, false) + return component != null ? component[0]: null + } + findAll():ComponentPublicInstance[] | null { + const selector = this.selector + if(selector == '') return null + return findChildren(selector, this.context!, true) + } + closest(): ComponentPublicInstance | null { + const selector = this.selector + if(selector == '') return null + let parent = this.context!.$parent + let name = parent?.$options?.name; + while (parent != null && (name == null || selector != name)) { + parent = parent.$parent + if (parent != null) { + name = parent.$options.name + } + } + return parent + } +} + +export function selectComponent(selector: string): Query{ + return new Query(selector, null) +} +// #endif + +// selectComponent('selector').in(this).find() +// selectComponent('selector').in(this).findAll() +// selectComponent('selector').in(this).closest() diff --git a/uni_modules/lime-shared/selectComponent/vue.ts b/uni_modules/lime-shared/selectComponent/vue.ts new file mode 100644 index 0000000..9fca0cd --- /dev/null +++ b/uni_modules/lime-shared/selectComponent/vue.ts @@ -0,0 +1,149 @@ +// @ts-nocheck +// #ifdef MP +function findChildren(selector : string, context : ComponentPublicInstance, needAll : boolean) { + const { proxy, $vm } = context + context = $vm || proxy + if ((selector.startsWith('.') || selector.startsWith('#'))) { + const queue = [context] + let result = null + while (queue.length > 0) { + const child = queue.shift(); + const flag = child?.selectComponent(selector) + if (flag) { + if (!needAll) { return result = flag.$vm } + return result = child.selectAllComponents(selector).map(item => item.$vm) + } else { + child.$children && (queue.push(...child.$children)); + } + } + return result + } else { + const { $templateRefs } = context.$ + const selectorValue = /#|\.|@|$/.test(selector) ? selector.substring(1) : selector + const nameMap = {} + for (var i = 0; i < $templateRefs.length; i++) { + const item = $templateRefs[i] + nameMap[item.i] = item.r + } + let result = [] + if (context.$children.length) { + const queue = [...context.$children] + while (queue.length > 0) { + const child = queue.shift(); + if (child.type?.name === selectorValue || child.$?.type?.name === selectorValue) { + result.push(child) + } else if (child.$refs && child.$refs[selectorValue]) { + result = child.$refs[selectorValue] + } else if (nameMap[child.id] === selectorValue) { + result.push(child) + } else { + child.$children && (queue.push(...child.$children)); + } + if (result.length && !needAll) { + return; + } + } + } + return needAll ? result : result[0] + } +} +// #endif + +// #ifdef H5 +function findChildren(selector : string, context : ComponentPublicInstance, needAll : boolean){ + const {_, component } = context + const child = {component: _ || component || context, children: null , subTree: null, props: null} + let result = [] + let queue = [child] + const selectorValue = /#|\.|@|$/.test(selector) ? selector.substring(1) : selector + while(queue.length > 0 ) { + const child = queue.shift() + const {component, children , props, subTree} = child + if(component?.type?.name == selectorValue) { + result.push(component) + } else if(selector.startsWith('$') && component && (props?.ref == selectorValue || component[key][selectorValue])) { + if(props?.ref == selectorValue) { + //exposed + result.push(component) + } else if(component[key][selectorValue]) { + result.push(component[key][selectorValue]) + } + } else if(!selector.startsWith('$') && component?.exposed && new RegExp(`\\b${selectorValue}\\b`).test(component.attrs[key])) { + // exposed + result.push(component) + } else if(children && Array.isArray(children)) { + queue.push(...children) + } else if(!component && subTree) { + queue.push(subTree) + } else if(component?.subTree) { + queue.push(component.subTree) + } + if(result.length && !needAll) { + break + } + } + return needAll ? result : result[0] +} +// #endif + +// #ifdef APP +function findChildren(selector : string, context : ComponentPublicInstance, needAll : boolean){ + let result = [] + const selectorValue = /#|\.|@|$/.test(selector) ? selector.substring(1) : selector + const queue = [context] + while(queue.length > 0) { + const child = queue.shift() + const {component, children, props, subTree} = child + const isComp = component && props && component.exposed && !node + if(child.type && child.type.name === selectorValue) { + result.push(component) + } else if(props?.[key] === selectorValue && node) { + result.push(child) + } else if(selector.startsWith('$') && isComp && (props.ref === selectorValue || props.ref_key === selectorValue)) { + // exposed + result.push(component) + } else if(!selector.startsWith('$') && isComp && new RegExp(`\\b${selectorValue}\\b`).test(props[key])) { + // exposed + result.push(component) + } + else if(subTree) { + queue.push(subTree) + } else if(component && component.subTree){ + queue.push(component.subTree) + } + else if(children && Array.isArray(children)) { + queue.push(...children) + } + if(result.length && !needAll) { + break; + } + } + return needAll ? result : result[0] +} +// #endif + +class Query { + context : ComponentPublicInstance | null = null + selector : string = '' + // components : ComponentPublicInstance[] = [] + constructor(selector : string, context : ComponentPublicInstance | null) { + this.selector = selector + this.context = context + } + in(context : ComponentPublicInstance) : Query { + return new Query(this.selector, context) + } + find() : ComponentPublicInstance | null { + return findChildren(this.selector, this.context, false) + } + findAll() : ComponentPublicInstance[] | null { + return findChildren(this.selector, this.context, true) + } + closest() : ComponentPublicInstance | null { + return null + } +} + +export function selectComponent(selector: string) { + return new Query(selector) +} \ No newline at end of file diff --git a/uni_modules/lime-shared/selectElement/index.uts b/uni_modules/lime-shared/selectElement/index.uts new file mode 100644 index 0000000..d189583 --- /dev/null +++ b/uni_modules/lime-shared/selectElement/index.uts @@ -0,0 +1,275 @@ +// @ts-nocheck +import {isDef} from '../isDef' +import {ComponentPublicInstance} from 'vue' + +type HasSelectorFunc = (selector : string, element : UniElement) => boolean + +const hasSelectorClassName : HasSelectorFunc = (selector : string, element : UniElement) : boolean => { + return element.classList.includes(selector) +} +const hasSelectorId : HasSelectorFunc = (selector : string, element : UniElement) : boolean => { + return element.getAttribute("id") == selector +} +const hasSelectorTagName : HasSelectorFunc = (selector : string, element : UniElement) : boolean => { + return element.tagName!.toLowerCase() == selector.toLowerCase() +} + +type ProcessSelectorResult = { + selectorValue : string + hasSelector : HasSelectorFunc +} +const processSelector = (selector : string) : ProcessSelectorResult => { + + const selectorValue = /#|\./.test(selector) ? selector.substring(1) : selector + let hasSelector : HasSelectorFunc + + if (selector.startsWith('.')) { + hasSelector = hasSelectorClassName + } else if (selector.startsWith('#')) { + hasSelector = hasSelectorId + } else { + hasSelector = hasSelectorTagName + } + + return { + selectorValue, + hasSelector + } as ProcessSelectorResult +} + + +function isNotEmptyString(str:string): boolean { + return str.length > 0; +} + +function isElement(element:UniElement|null):boolean { + return isDef(element) && element?.tagName != 'COMMENT'; +} + +type ElementArray = Array<UniElement|null> +class Query { + context : ComponentPublicInstance | null = null + selector : string = '' + elements : ElementArray = [] + constructor(selector : string | null, context : ComponentPublicInstance | null) { + this.context = context + if(selector != null){ + this.selector = selector + } + this.find(this.selector) + } + in(context : ComponentPublicInstance) : Query { + return new Query(this.selector, context) + } + findAll(selector : string): Query { + if (isDef(this.context)) { + const root = this.context?.$el //as Element | null; + if (isDef(root)) { + this.elements = [root!] //as ElementArray + } + const { selectorValue, hasSelector } = processSelector(selector) + const foundElements : ElementArray = []; + + function findChildren(element : UniElement) { + element.children.forEach((child : UniElement) => { + if (hasSelector(selectorValue, child)) { + foundElements.push(child) + } + }) + } + this.elements.forEach(el => { + findChildren(el!); + }); + this.elements = foundElements + } else if (selector.startsWith('#')) { + const element = uni.getElementById(selector) + if (isElement(element!)) { + this.elements = [element] + } + } + return this; + } + /** + * 在当前元素集合中查找匹配的元素 + */ + find(selector : string) : Query { + if (isDef(this.context)) { + const root = this.context?.$el //as Element | null; + if (isElement(root)) { + this.elements = [root] //as ElementArray + } + if(isNotEmptyString(selector) && this.elements.length > 0){ + const { selectorValue, hasSelector } = processSelector(selector) + const foundElements : ElementArray = []; + function findChildren(element : UniElement) { + element.children.forEach((child : UniElement) => { + if (hasSelector(selectorValue, child) && foundElements.length < 1) { + foundElements.push(child) + } + if (foundElements.length < 1) { + findChildren(child); + } + }) + } + this.elements.forEach(el => { + findChildren(el!); + }); + this.elements = foundElements + } + + } else if (selector.startsWith('#')) { + const element = uni.getElementById(selector) + if (isElement(element!)) { + this.elements = [element] + } + } + return this; + } + /** + * 获取当前元素集合的直接子元素 + */ + children() : Query { + // if (this.elements.length > 0) { + // const children = this.elements.reduce((acc, el) => [...acc, ...Array.from(el.children)], []); + // this.elements = children; + // } + return this; + } + /** + * 获取当前元素集合的父元素 + */ + parent() : Query { + // if (this.elements.length > 0) { + // const parents = this.elements.map(el => el.parentElement).filter(parent => parent !== null) as ElementArray; + // this.elements = parents + // // this.elements = Array.from(new Set(parents)); + // } + return this; + } + /** + * 获取当前元素集合的兄弟元素 + */ + siblings() : Query { + // if (this.elements.length > 0) { + // const siblings = this.elements.reduce((acc, el) => [...acc, ...Array.from(el.parentElement?.children || [])], []); + // this.elements = siblings.filter(sibling => sibling !== null && !this.elements?.includes(sibling)); + // } + return this; + } + /** + * 获取当前元素集合的下一个兄弟元素 + */ + next() : Query { + // if (this.elements.length > 0) { + // const nextElements = this.elements.map(el => el.nextElementSibling).filter(next => next !== null) as ElementArray; + // this.elements = nextElements; + // } + return this; + } + /** + * 获取当前元素集合的上一个兄弟元素 + */ + prev() : Query { + // if (this.elements.length > 0) { + // const prevElements = this.elements.map(el => el.previousElementSibling).filter(prev => prev !== null) as ElementArray; + // this.elements = prevElements; + // } + return this; + } + /** + * 从当前元素开始向上查找匹配的元素 + */ + closest(selector : string) : Query { + if (isDef(this.context)) { + // && this.context.$parent != null && this.context.$parent.$el !== null + if(this.elements.length == 0 && isDef(this.context?.$parent) && isElement(this.context!.$parent?.$el)){ + this.elements = [this.context!.$parent?.$el!] + } + + const selectorsArray = selector.split(',') + // const { selectorValue, hasSelector } = processSelector(selector) + const processedSelectors = selectorsArray.map((selector: string):ProcessSelectorResult => processSelector(selector)) + const closestElements = this.elements.map((el) : UniElement | null => { + let closestElement : UniElement | null = el + while (closestElement !== null) { + // if (hasSelector(selectorValue, closestElement)) { + // break; + // } + const isMatchingSelector = processedSelectors.some(({selectorValue, hasSelector}):boolean => { + return hasSelector(selectorValue, closestElement!) + }) + if(isMatchingSelector){ + break; + } + closestElement = closestElement.parentElement; + } + return closestElement + }) + this.elements = closestElements.filter((closest : UniElement | null) : boolean => isDef(closest))// as ElementArray + + } + return this; + } + + /** + * 从当前元素集合中过滤出匹配的元素 + */ + filter() : Query { + + return this; + } + /** + * 从当前元素集合中排除匹配的元素 + */ + not() { } + /** + * 从当前元素集合中查找包含匹配元素的元素 + */ + has() { } + /** + * 获取当前元素集合的第一个 + */ + first() : Query { + if (this.elements.length > 0) { + // this.elements = [this.elements[0]]; + } + return this; + } + /** + * 最后一个元素 + */ + last() : Query { + if (this.elements.length > 0) { + // this.elements = [this.elements[this.elements.length - 1]]; + } + return this; + } + /** + * 获取当前元素在其兄弟元素中的索引 + */ + index() : number | null { + // if (this.elements.length > 0 && this.elements.length > 0 && this.elements[0].parentElement !== null) { + // return Array.from(this.elements[0].parentElement.children).indexOf(this.elements[0]); + // } + return null; + } + get(index : number) : UniElement | null { + if (this.elements.length > index) { + return this.elements[index] //as Element + } + return null + } +} + +export function selectElement(selector : string | null = null) : Query { + // if(typeof selector == 'string' || selector == null){ + // return new Query(selector as string | null, null) + // } + // else if(selector instanceof ComponentPublicInstance){ + // return new Query(null, selector) + // } + return new Query(selector, null) +} + +// $('xxx').in(this).find('xxx') +// $('xxx').in(this).get() \ No newline at end of file diff --git a/uni_modules/lime-shared/sleep/index.ts b/uni_modules/lime-shared/sleep/index.ts new file mode 100644 index 0000000..a01cdfd --- /dev/null +++ b/uni_modules/lime-shared/sleep/index.ts @@ -0,0 +1,44 @@ +// @ts-nocheck +/** + * 延迟指定时间后解析的 Promise + * @param delay 延迟的时间(以毫秒为单位),默认为 300 毫秒 + * @returns 一个 Promise,在延迟结束后解析 + */ + + +// #ifdef UNI-APP-X && APP +function sleep(delay: number = 300):Promise<boolean> { + return new Promise((resolve):void => {setTimeout(() => {resolve(true)}, delay)}); +} +export { + sleep +} + +// #endif + +// #ifndef UNI-APP-X && APP +export const sleep = (delay: number = 300) => + new Promise(resolve => setTimeout(resolve, delay)); + +// #endif + +// 示例 +// async function example() { +// console.log("Start"); + +// // 延迟 1 秒后执行 +// await sleep(1000); +// console.log("1 second later"); + +// // 延迟 500 毫秒后执行 +// await sleep(500); +// console.log("500 milliseconds later"); + +// // 延迟 2 秒后执行 +// await sleep(2000); +// console.log("2 seconds later"); + +// console.log("End"); +// } + +// example(); \ No newline at end of file diff --git a/uni_modules/lime-shared/throttle/index.ts b/uni_modules/lime-shared/throttle/index.ts new file mode 100644 index 0000000..fff1d82 --- /dev/null +++ b/uni_modules/lime-shared/throttle/index.ts @@ -0,0 +1,77 @@ +// @ts-nocheck +/** + * 节流函数,用于限制函数的调用频率 + * @param fn 要进行节流的函数 + * @param delay 两次调用之间的最小间隔时间 + * @returns 节流后的函数 + */ + +// #ifndef UNI-APP-X && APP +export function throttle(fn: (...args: any[]) => void, delay: number) { + let flag = true; // 标记是否可以执行函数 + + return (...args: any[]) => { + if (flag) { + flag = false; // 设置为不可执行状态 + fn(...args); // 执行传入的函数 + + setTimeout(() => { + flag = true; // 经过指定时间后,设置为可执行状态 + }, delay); + } + }; +} +// #endif + + +// #ifdef UNI-APP-X && APP +// type Rfun = (...args: any[]) => void +// type Rfun = (...args: any[]) => void + +export function throttle<T extends any|null>( + fn: (args : T) => void, + delay: number):(args : T) => void { + let flag = true; // 标记是否可以执行函数 + + return (args : T) =>{ + if(flag){ + flag = false; + fn(args); + + setTimeout(()=>{ + flag = true; + }, delay) + } + } + // return (...args: any[]) => { + // // if (flag) { + // // flag = false; // 设置为不可执行状态 + // // fn(...args); // 执行传入的函数 + + // // setTimeout(() => { + // // flag = true; // 经过指定时间后,设置为可执行状态 + // // }, delay); + // // } + // }; +} + +// #endif + +// // 示例 +// // 定义一个被节流的函数 +// function handleScroll() { +// console.log("Scroll event handled!"); +// } + +// // 使用节流函数对 handleScroll 进行节流,间隔时间为 500 毫秒 +// const throttledScroll = throttle(handleScroll, 500); + +// // 模拟多次调用 handleScroll +// throttledScroll(); // 输出 "Scroll event handled!" +// throttledScroll(); // 不会输出 +// throttledScroll(); // 不会输出 + +// // 经过 500 毫秒后,再次调用 handleScroll +// setTimeout(() => { +// throttledScroll(); // 输出 "Scroll event handled!" +// }, 500); \ No newline at end of file diff --git a/uni_modules/lime-shared/toArray/index.ts b/uni_modules/lime-shared/toArray/index.ts new file mode 100644 index 0000000..e0840d2 --- /dev/null +++ b/uni_modules/lime-shared/toArray/index.ts @@ -0,0 +1,21 @@ +// @ts-nocheck +/** + * 将一个或多个元素转换为数组 + * @param item 要转换为数组的元素 + * @returns 转换后的数组 + */ +// #ifndef UNI-APP-X && APP +export const toArray = <T>(item: T | T[]): T[] => Array.isArray(item) ? item : [item]; +// #endif + + +// #ifdef UNI-APP-X && APP +export function toArray<T extends any>(item: any): T[] { + return Array.isArray(item) ? item as T[] : [item as T]// as T[] +}; +// #endif +// 示例 +// console.log(toArray(5)); // 输出: [5] +// console.log(toArray("hello")); // 输出: ["hello"] +// console.log(toArray([1, 2, 3])); // 输出: [1, 2, 3] +// console.log(toArray(["apple", "banana"])); // 输出: ["apple", "banana"] \ No newline at end of file diff --git a/uni_modules/lime-shared/toBoolean/index.ts b/uni_modules/lime-shared/toBoolean/index.ts new file mode 100644 index 0000000..f61d49d --- /dev/null +++ b/uni_modules/lime-shared/toBoolean/index.ts @@ -0,0 +1,40 @@ +// @ts-nocheck +import { isNumber } from '../isNumber' +import { isString } from '../isString' +// 函数重载,定义多个函数签名 +// function toBoolean(value : any) : boolean; +// function toBoolean(value : string) : boolean; +// function toBoolean(value : number) : boolean; +// function toBoolean(value : boolean) : boolean; + +// #ifdef UNI-APP-X && APP +function toBoolean(value : any | null) : boolean { + // 根据输入值的类型,返回相应的布尔值 + // if (isNumber(value)) { + // return (value as number) != 0; + // } + // if (isString(value)) { + // return `${value}`.length > 0; + // } + // if (typeof value == 'boolean') { + // return value as boolean; + // } + // #ifdef APP-IOS + return value != null && value != undefined + // #endif + // #ifdef APP-ANDROID + return value != null + // #endif +} +// #endif + + +// #ifndef UNI-APP-X && APP +function toBoolean(value : any | null) : value is NonNullable<typeof value> { + return !!value//value !== null && value !== undefined; +} +// #endif + +export { + toBoolean +} \ No newline at end of file diff --git a/uni_modules/lime-shared/toNumber/index.ts b/uni_modules/lime-shared/toNumber/index.ts new file mode 100644 index 0000000..a8e1b1d --- /dev/null +++ b/uni_modules/lime-shared/toNumber/index.ts @@ -0,0 +1,28 @@ +// @ts-nocheck +/** + * 将字符串转换为数字 + * @param val 要转换的字符串 + * @returns 转换后的数字或原始字符串 + */ + +// #ifdef UNI-APP-X && APP +// function toNumber(val: string): number +// function toNumber(val: string): string +function toNumber(val: string): number|null { + const n = parseFloat(val); // 使用 parseFloat 函数将字符串转换为浮点数 + return isNaN(n) ? null : n; // 使用 isNaN 函数判断是否为非数字,返回转换后的数字或原始字符串 +} +export {toNumber} +// #endif + +// #ifndef UNI-APP-X && APP +export function toNumber(val: string): number | string { + const n = parseFloat(val); // 使用 parseFloat 函数将字符串转换为浮点数 + return isNaN(n) ? val : n; // 使用 isNaN 函数判断是否为非数字,返回转换后的数字或原始字符串 +} +// #endif + +// 示例 +// console.log(toNumber("123")); // 输出: 123 +// console.log(toNumber("3.14")); // 输出: 3.14 +// console.log(toNumber("hello")); // 输出: "hello" \ No newline at end of file diff --git a/uni_modules/lime-shared/unitConvert/index.ts b/uni_modules/lime-shared/unitConvert/index.ts new file mode 100644 index 0000000..cf6063a --- /dev/null +++ b/uni_modules/lime-shared/unitConvert/index.ts @@ -0,0 +1,79 @@ +// @ts-nocheck +import { isString } from '../isString' +import { isNumeric } from '../isNumeric' + +/** + * 单位转换函数,将字符串数字或带有单位的字符串转换为数字 + * @param value 要转换的值,可以是字符串数字或带有单位的字符串 + * @returns 转换后的数字,如果无法转换则返回0 + */ +// #ifndef UNI-APP-X && APP +export function unitConvert(value : string | number, base: number = 0) : number { + // 如果是字符串数字 + if (isNumeric(value)) { + return Number(value); + } + // 如果有单位 + if (isString(value)) { + const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g; + const results = reg.exec(value); + if (!value || !results) { + return 0; + } + const unit = results[3]; + const _value = parseFloat(value); + if (unit === 'rpx') { + return uni.upx2px(_value); + } + if (unit === 'px') { + return _value * 1; + } + if(unit == '%') { + return _value / 100 * base + } + // 如果是其他单位,可以继续添加对应的转换逻辑 + } + return 0; +} +// #endif + + +// #ifdef UNI-APP-X && APP +import { isNumber } from '../isNumber' +export function unitConvert(value : any | null, base: number = 0) : number { + if (isNumber(value)) { + return value as number + } + // 如果是字符串数字 + if (isNumeric(value)) { + return parseFloat(value as string); + } + // 如果有单位 + if (isString(value)) { + const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g; + const results = reg.exec(value as string); + if (results == null) { + return 0; + } + const unit = results[3]; + const _value = parseFloat(value); + if (unit == 'rpx') { + const { windowWidth } = uni.getWindowInfo() + return windowWidth / 750 * _value; + } + if (unit == 'px') { + return _value; + } + if(unit == '%') { + return _value / 100 * base + } + // 如果是其他单位,可以继续添加对应的转换逻辑 + } + return 0; +} +// #endif +// 示例 +// console.log(unitConvert("123")); // 输出: 123 (字符串数字转换为数字) +// console.log(unitConvert("3.14em")); // 输出: 0 (无法识别的单位) +// console.log(unitConvert("20rpx")); // 输出: 根据具体情况而定 (根据单位进行转换) +// console.log(unitConvert(10)); // 输出: 10 (数字不需要转换) \ No newline at end of file diff --git a/uni_modules/lime-shared/vue/index.ts b/uni_modules/lime-shared/vue/index.ts new file mode 100644 index 0000000..07f7135 --- /dev/null +++ b/uni_modules/lime-shared/vue/index.ts @@ -0,0 +1,16 @@ +// @ts-nocheck + +// #ifdef VUE3 +export * from 'vue'; +// #endif + +// #ifndef VUE3 +export * from '@vue/composition-api'; + +// #ifdef APP-NVUE +import Vue from 'vue' +import VueCompositionAPI from '@vue/composition-api' +Vue.use(VueCompositionAPI) +// #endif + +// #endif diff --git a/uni_modules/uni-badge/changelog.md b/uni_modules/uni-badge/changelog.md new file mode 100644 index 0000000..e352c60 --- /dev/null +++ b/uni_modules/uni-badge/changelog.md @@ -0,0 +1,33 @@ +## 1.2.2(2023-01-28) +- 修复 运行/打包 控制台警告问题 +## 1.2.1(2022-09-05) +- 修复 当 text 超过 max-num 时,badge 的宽度计算是根据 text 的长度计算,更改为 css 计算实际展示宽度,详见:[https://ask.dcloud.net.cn/question/150473](https://ask.dcloud.net.cn/question/150473) +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-badge](https://uniapp.dcloud.io/component/uniui/uni-badge) +## 1.1.7(2021-11-08) +- 优化 升级ui +- 修改 size 属性默认值调整为 small +- 修改 type 属性,默认值调整为 error,info 替换 default +## 1.1.6(2021-09-22) +- 修复 在字节小程序上样式不生效的 bug +## 1.1.5(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.4(2021-07-29) +- 修复 去掉 nvue 不支持css 的 align-self 属性,nvue 下不暂支持 absolute 属性 +## 1.1.3(2021-06-24) +- 优化 示例项目 +## 1.1.1(2021-05-12) +- 新增 组件示例地址 +## 1.1.0(2021-05-12) +- 新增 uni-badge 的 absolute 属性,支持定位 +- 新增 uni-badge 的 offset 属性,支持定位偏移 +- 新增 uni-badge 的 is-dot 属性,支持仅显示有一个小点 +- 新增 uni-badge 的 max-num 属性,支持自定义封顶的数字值,超过 99 显示99+ +- 优化 uni-badge 属性 custom-style, 支持以对象形式自定义样式 +## 1.0.7(2021-05-07) +- 修复 uni-badge 在 App 端,数字小于10时不是圆形的bug +- 修复 uni-badge 在父元素不是 flex 布局时,宽度缩小的bug +- 新增 uni-badge 属性 custom-style, 支持自定义样式 +## 1.0.6(2021-02-04) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-badge/components/uni-badge/uni-badge.vue b/uni_modules/uni-badge/components/uni-badge/uni-badge.vue new file mode 100644 index 0000000..956354b --- /dev/null +++ b/uni_modules/uni-badge/components/uni-badge/uni-badge.vue @@ -0,0 +1,268 @@ +<template> + <view class="uni-badge--x"> + <slot /> + <text v-if="text" :class="classNames" :style="[positionStyle, customStyle, dotStyle]" + class="uni-badge" @click="onClick()">{{displayValue}}</text> + </view> +</template> + +<script> + /** + * Badge 数字角标 + * @description 数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景 + * @tutorial https://ext.dcloud.net.cn/plugin?id=21 + * @property {String} text 角标内容 + * @property {String} size = [normal|small] 角标内容 + * @property {String} type = [info|primary|success|warning|error] 颜色类型 + * @value info 灰色 + * @value primary 蓝色 + * @value success 绿色 + * @value warning 黄色 + * @value error 红色 + * @property {String} inverted = [true|false] 是否无需背景颜色 + * @property {Number} maxNum 展示封顶的数字值,超过 99 显示 99+ + * @property {String} absolute = [rightTop|rightBottom|leftBottom|leftTop] 开启绝对定位, 角标将定位到其包裹的标签的四角上 + * @value rightTop 右上 + * @value rightBottom 右下 + * @value leftTop 左上 + * @value leftBottom 左下 + * @property {Array[number]} offset 距定位角中心点的偏移量,只有存在 absolute 属性时有效,例如:[-10, -10] 表示向外偏移 10px,[10, 10] 表示向 absolute 指定的内偏移 10px + * @property {String} isDot = [true|false] 是否显示为一个小点 + * @event {Function} click 点击 Badge 触发事件 + * @example <uni-badge text="1"></uni-badge> + */ + + export default { + name: 'UniBadge', + emits: ['click'], + props: { + type: { + type: String, + default: 'error' + }, + inverted: { + type: Boolean, + default: false + }, + isDot: { + type: Boolean, + default: false + }, + maxNum: { + type: Number, + default: 99 + }, + absolute: { + type: String, + default: '' + }, + offset: { + type: Array, + default () { + return [0, 0] + } + }, + text: { + type: [String, Number], + default: '' + }, + size: { + type: String, + default: 'small' + }, + customStyle: { + type: Object, + default () { + return {} + } + } + }, + data() { + return {}; + }, + computed: { + width() { + return String(this.text).length * 8 + 12 + }, + classNames() { + const { + inverted, + type, + size, + absolute + } = this + return [ + inverted ? 'uni-badge--' + type + '-inverted' : '', + 'uni-badge--' + type, + 'uni-badge--' + size, + absolute ? 'uni-badge--absolute' : '' + ].join(' ') + }, + positionStyle() { + if (!this.absolute) return {} + let w = this.width / 2, + h = 10 + if (this.isDot) { + w = 5 + h = 5 + } + const x = `${- w + this.offset[0]}px` + const y = `${- h + this.offset[1]}px` + + const whiteList = { + rightTop: { + right: x, + top: y + }, + rightBottom: { + right: x, + bottom: y + }, + leftBottom: { + left: x, + bottom: y + }, + leftTop: { + left: x, + top: y + } + } + const match = whiteList[this.absolute] + return match ? match : whiteList['rightTop'] + }, + dotStyle() { + if (!this.isDot) return {} + return { + width: '10px', + minWidth: '0', + height: '10px', + padding: '0', + borderRadius: '10px' + } + }, + displayValue() { + const { + isDot, + text, + maxNum + } = this + return isDot ? '' : (Number(text) > maxNum ? `${maxNum}+` : text) + } + }, + methods: { + onClick() { + this.$emit('click'); + } + } + }; +</script> + +<style lang="scss" > + $uni-primary: #2979ff !default; + $uni-success: #4cd964 !default; + $uni-warning: #f0ad4e !default; + $uni-error: #dd524d !default; + $uni-info: #909399 !default; + + + $bage-size: 12px; + $bage-small: scale(0.8); + + .uni-badge--x { + /* #ifdef APP-NVUE */ + // align-self: flex-start; + /* #endif */ + /* #ifndef APP-NVUE */ + display: inline-block; + /* #endif */ + position: relative; + } + + .uni-badge--absolute { + position: absolute; + } + + .uni-badge--small { + transform: $bage-small; + transform-origin: center center; + } + + .uni-badge { + /* #ifndef APP-NVUE */ + display: flex; + overflow: hidden; + box-sizing: border-box; + font-feature-settings: "tnum"; + min-width: 20px; + /* #endif */ + justify-content: center; + flex-direction: row; + height: 20px; + padding: 0 4px; + line-height: 18px; + color: #fff; + border-radius: 100px; + background-color: $uni-info; + background-color: transparent; + border: 1px solid #fff; + text-align: center; + font-family: 'Helvetica Neue', Helvetica, sans-serif; + font-size: $bage-size; + /* #ifdef H5 */ + z-index: 999; + cursor: pointer; + /* #endif */ + + &--info { + color: #fff; + background-color: $uni-info; + } + + &--primary { + background-color: $uni-primary; + } + + &--success { + background-color: $uni-success; + } + + &--warning { + background-color: $uni-warning; + } + + &--error { + background-color: $uni-error; + } + + &--inverted { + padding: 0 5px 0 0; + color: $uni-info; + } + + &--info-inverted { + color: $uni-info; + background-color: transparent; + } + + &--primary-inverted { + color: $uni-primary; + background-color: transparent; + } + + &--success-inverted { + color: $uni-success; + background-color: transparent; + } + + &--warning-inverted { + color: $uni-warning; + background-color: transparent; + } + + &--error-inverted { + color: $uni-error; + background-color: transparent; + } + + } +</style> diff --git a/uni_modules/uni-badge/package.json b/uni_modules/uni-badge/package.json new file mode 100644 index 0000000..b0bac93 --- /dev/null +++ b/uni_modules/uni-badge/package.json @@ -0,0 +1,85 @@ +{ + "id": "uni-badge", + "displayName": "uni-badge 数字角标", + "version": "1.2.2", + "description": "数字角标(徽章)组件,在元素周围展示消息提醒,一般用于列表、九宫格、按钮等地方。", + "keywords": [ + "", + "badge", + "uni-ui", + "uniui", + "数字角标", + "徽章" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "y", + "联盟": "y" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-badge/readme.md b/uni_modules/uni-badge/readme.md new file mode 100644 index 0000000..bdf175d --- /dev/null +++ b/uni_modules/uni-badge/readme.md @@ -0,0 +1,10 @@ +## Badge 数字角标 +> **组件名:uni-badge** +> 代码块: `uBadge` + +数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景, + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-badge) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-calendar/changelog.md b/uni_modules/uni-calendar/changelog.md new file mode 100644 index 0000000..30ca0df --- /dev/null +++ b/uni_modules/uni-calendar/changelog.md @@ -0,0 +1,30 @@ +## 1.4.12(2024-09-21) +- 修复 calendar在选择日期范围后重新选择日期需要点两次的Bug +## 1.4.11(2024-01-10) +- 修复 回到今天时,月份显示不一致问题 +## 1.4.10(2023-04-10) +- 修复 某些情况 monthSwitch 未触发的Bug +## 1.4.9(2023-02-02) +- 修复 某些情况切换月份错误的Bug +## 1.4.8(2023-01-30) +- 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/161964) +## 1.4.7(2022-09-16) +- 优化 支持使用 uni-scss 控制主题色 +## 1.4.6(2022-09-08) +- 修复 表头年月切换,导致改变当前日期为选择月1号,且未触发change事件的Bug +## 1.4.5(2022-02-25) +- 修复 条件编译 nvue 不支持的 css 样式的Bug +## 1.4.4(2022-02-25) +- 修复 条件编译 nvue 不支持的 css 样式的Bug +## 1.4.3(2021-09-22) +- 修复 startDate、 endDate 属性失效的Bug +## 1.4.2(2021-08-24) +- 新增 支持国际化 +## 1.4.1(2021-08-05) +- 修复 弹出层被 tabbar 遮盖的Bug +## 1.4.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.3.16(2021-05-12) +- 新增 组件示例地址 +## 1.3.15(2021-02-04) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-calendar/components/uni-calendar/calendar.js b/uni_modules/uni-calendar/components/uni-calendar/calendar.js new file mode 100644 index 0000000..55eed81 --- /dev/null +++ b/uni_modules/uni-calendar/components/uni-calendar/calendar.js @@ -0,0 +1,544 @@ +/** +* @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 + case 30: + s = '\u4e09\u5341'; break + default : + s = this.nStr2[Math.floor(d / 10)] + s += this.nStr1[d % 10] + } + return (s) + }, + + /** + * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春” + * @param y year + * @return Cn string + * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔' + */ + getAnimal: function (y) { + return this.Animals[(y - 4) % 12] + }, + + /** + * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON + * @param y solar year + * @param m solar month + * @param d solar day + * @return JSON object + * @eg:console.log(calendar.solar2lunar(1987,11,01)); + */ + solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31 + // 年份限定、上限 + if (y < 1900 || y > 2100) { + return -1// undefined转换为数字变为NaN + } + // 公历传参最下限 + if (y == 1900 && m == 1 && d < 31) { + return -1 + } + // 未传参 获得当天 + if (!y) { + var objDate = new Date() + } else { + var objDate = new Date(y, parseInt(m) - 1, d) + } + var i; var leap = 0; var temp = 0 + // 修正ymd参数 + var y = objDate.getFullYear() + var m = objDate.getMonth() + 1 + var d = objDate.getDate() + var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000 + for (i = 1900; i < 2101 && offset > 0; i++) { + temp = this.lYearDays(i) + offset -= temp + } + if (offset < 0) { + offset += temp; i-- + } + + // 是否今天 + var isTodayObj = new Date() + var isToday = false + if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) { + isToday = true + } + // 星期几 + var nWeek = objDate.getDay() + var cWeek = this.nStr1[nWeek] + // 数字表示周几顺应天朝周一开始的惯例 + if (nWeek == 0) { + nWeek = 7 + } + // 农历年 + var year = i + var leap = this.leapMonth(i) // 闰哪个月 + var isLeap = false + + // 效验闰月 + for (i = 1; i < 13 && offset > 0; i++) { + // 闰月 + if (leap > 0 && i == (leap + 1) && isLeap == false) { + --i + isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数 + } else { + temp = this.monthDays(year, i)// 计算农历普通月天数 + } + // 解除闰月 + if (isLeap == true && i == (leap + 1)) { isLeap = false } + offset -= temp + } + // 闰月导致数组下标重叠取反 + if (offset == 0 && leap > 0 && i == leap + 1) { + if (isLeap) { + isLeap = false + } else { + isLeap = true; --i + } + } + if (offset < 0) { + offset += temp; --i + } + // 农历月 + var month = i + // 农历日 + var day = offset + 1 + // 天干地支处理 + var sm = m - 1 + var gzY = this.toGanZhiYear(year) + + // 当月的两个节气 + // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year` + var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始 + var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始 + + // 依据12节气修正干支月 + var gzM = this.toGanZhi((y - 1900) * 12 + m + 11) + if (d >= firstNode) { + gzM = this.toGanZhi((y - 1900) * 12 + m + 12) + } + + // 传入的日期的节气与否 + var isTerm = false + var Term = null + if (firstNode == d) { + isTerm = true + Term = this.solarTerm[m * 2 - 2] + } + if (secondNode == d) { + isTerm = true + Term = this.solarTerm[m * 2 - 1] + } + // 日柱 当月一日与 1900/1/1 相差天数 + var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10 + var gzD = this.toGanZhi(dayCyclical + d - 1) + // 该日期所属的星座 + var astro = this.toAstro(m, d) + + return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro } + }, + + /** + * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON + * @param y lunar year + * @param m lunar month + * @param d lunar day + * @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可] + * @return JSON object + * @eg:console.log(calendar.lunar2solar(1987,9,10)); + */ + lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1 + var isLeapMonth = !!isLeapMonth + var leapOffset = 0 + var leapMonth = this.leapMonth(y) + var leapDay = this.leapDays(y) + if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同 + if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值 + var day = this.monthDays(y, m) + var _day = day + // bugFix 2016-9-25 + // if month is leap, _day use leapDays method + if (isLeapMonth) { + _day = this.leapDays(y, m) + } + if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验 + + // 计算农历的时间差 + var offset = 0 + for (var i = 1900; i < y; i++) { + offset += this.lYearDays(i) + } + var leap = 0; var isAdd = false + for (var i = 1; i < m; i++) { + leap = this.leapMonth(y) + if (!isAdd) { // 处理闰月 + if (leap <= i && leap > 0) { + offset += this.leapDays(y); isAdd = true + } + } + offset += this.monthDays(y, i) + } + // 转换闰月农历 需补充该年闰月的前一个月的时差 + if (isLeapMonth) { offset += day } + // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点) + var stmap = Date.UTC(1900, 1, 30, 0, 0, 0) + var calObj = new Date((offset + d - 31) * 86400000 + stmap) + var cY = calObj.getUTCFullYear() + var cM = calObj.getUTCMonth() + 1 + var cD = calObj.getUTCDate() + + return this.solar2lunar(cY, cM, cD) + } +} + +export default calendar diff --git a/uni_modules/uni-calendar/components/uni-calendar/i18n/en.json b/uni_modules/uni-calendar/components/uni-calendar/i18n/en.json new file mode 100644 index 0000000..fcbd13c --- /dev/null +++ b/uni_modules/uni-calendar/components/uni-calendar/i18n/en.json @@ -0,0 +1,12 @@ +{ + "uni-calender.ok": "ok", + "uni-calender.cancel": "cancel", + "uni-calender.today": "today", + "uni-calender.MON": "MON", + "uni-calender.TUE": "TUE", + "uni-calender.WED": "WED", + "uni-calender.THU": "THU", + "uni-calender.FRI": "FRI", + "uni-calender.SAT": "SAT", + "uni-calender.SUN": "SUN" +} diff --git a/uni_modules/uni-calendar/components/uni-calendar/i18n/index.js b/uni_modules/uni-calendar/components/uni-calendar/i18n/index.js new file mode 100644 index 0000000..de7509c --- /dev/null +++ b/uni_modules/uni-calendar/components/uni-calendar/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hans.json b/uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hans.json new file mode 100644 index 0000000..1ca43de --- /dev/null +++ b/uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hans.json @@ -0,0 +1,12 @@ +{ + "uni-calender.ok": "确定", + "uni-calender.cancel": "取消", + "uni-calender.today": "今日", + "uni-calender.SUN": "日", + "uni-calender.MON": "一", + "uni-calender.TUE": "二", + "uni-calender.WED": "三", + "uni-calender.THU": "四", + "uni-calender.FRI": "五", + "uni-calender.SAT": "六" +} diff --git a/uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hant.json b/uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hant.json new file mode 100644 index 0000000..e0fe33b --- /dev/null +++ b/uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hant.json @@ -0,0 +1,12 @@ +{ + "uni-calender.ok": "確定", + "uni-calender.cancel": "取消", + "uni-calender.today": "今日", + "uni-calender.SUN": "日", + "uni-calender.MON": "一", + "uni-calender.TUE": "二", + "uni-calender.WED": "三", + "uni-calender.THU": "四", + "uni-calender.FRI": "五", + "uni-calender.SAT": "六" +} diff --git a/uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue b/uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue new file mode 100644 index 0000000..a54135e --- /dev/null +++ b/uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue @@ -0,0 +1,187 @@ +<template> + <view class="uni-calendar-item__weeks-box" :class="{ + 'uni-calendar-item--disable':weeks.disable, + 'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, + 'uni-calendar-item--checked':(calendar.fullDate === weeks.fullDate && !weeks.isDay) , + 'uni-calendar-item--before-checked':weeks.beforeMultiple, + 'uni-calendar-item--multiple': weeks.multiple, + 'uni-calendar-item--after-checked':weeks.afterMultiple, + }" + @click="choiceDate(weeks)"> + <view class="uni-calendar-item__weeks-box-item"> + <text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text> + <text class="uni-calendar-item__weeks-box-text" :class="{ + 'uni-calendar-item--isDay-text': weeks.isDay, + 'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, + 'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay, + 'uni-calendar-item--before-checked':weeks.beforeMultiple, + 'uni-calendar-item--multiple': weeks.multiple, + 'uni-calendar-item--after-checked':weeks.afterMultiple, + 'uni-calendar-item--disable':weeks.disable, + }">{{weeks.date}}</text> + <text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{ + 'uni-calendar-item--isDay-text':weeks.isDay, + 'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, + 'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay, + 'uni-calendar-item--before-checked':weeks.beforeMultiple, + 'uni-calendar-item--multiple': weeks.multiple, + 'uni-calendar-item--after-checked':weeks.afterMultiple, + }">{{todayText}}</text> + <text v-if="lunar&&!weeks.extraInfo" class="uni-calendar-item__weeks-lunar-text" :class="{ + 'uni-calendar-item--isDay-text':weeks.isDay, + 'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, + 'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay, + 'uni-calendar-item--before-checked':weeks.beforeMultiple, + 'uni-calendar-item--multiple': weeks.multiple, + 'uni-calendar-item--after-checked':weeks.afterMultiple, + 'uni-calendar-item--disable':weeks.disable, + }">{{weeks.isDay ? todayText : (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text> + <text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{ + 'uni-calendar-item--extra':weeks.extraInfo.info, + 'uni-calendar-item--isDay-text':weeks.isDay, + 'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, + 'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay, + 'uni-calendar-item--before-checked':weeks.beforeMultiple, + 'uni-calendar-item--multiple': weeks.multiple, + 'uni-calendar-item--after-checked':weeks.afterMultiple, + 'uni-calendar-item--disable':weeks.disable, + }">{{weeks.extraInfo.info}}</text> + </view> + </view> +</template> + +<script> + 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 + } + }, + computed: { + todayText() { + return t("uni-calender.today") + }, + }, + methods: { + choiceDate(weeks) { + this.$emit('change', weeks) + } + } + } +</script> + +<style lang="scss" scoped> + $uni-font-size-base:14px; + $uni-text-color:#333; + $uni-font-size-sm:12px; + $uni-color-error: #e43d33; + $uni-opacity-disabled: 0.3; + $uni-text-color-disable:#c0c0c0; + $uni-primary: #2979ff !default; + .uni-calendar-item__weeks-box { + flex: 1; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + justify-content: center; + align-items: center; + } + + .uni-calendar-item__weeks-box-text { + font-size: $uni-font-size-base; + color: $uni-text-color; + } + + .uni-calendar-item__weeks-lunar-text { + font-size: $uni-font-size-sm; + color: $uni-text-color; + } + + .uni-calendar-item__weeks-box-item { + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + justify-content: center; + align-items: center; + width: 100rpx; + height: 100rpx; + } + + .uni-calendar-item__weeks-box-circle { + position: absolute; + top: 5px; + right: 5px; + width: 8px; + height: 8px; + border-radius: 8px; + background-color: $uni-color-error; + + } + + .uni-calendar-item--disable { + background-color: rgba(249, 249, 249, $uni-opacity-disabled); + color: $uni-text-color-disable; + } + + .uni-calendar-item--isDay-text { + color: $uni-primary; + } + + .uni-calendar-item--isDay { + background-color: $uni-primary; + opacity: 0.8; + color: #fff; + } + + .uni-calendar-item--extra { + color: $uni-color-error; + opacity: 0.8; + } + + .uni-calendar-item--checked { + background-color: $uni-primary; + color: #fff; + opacity: 0.8; + } + + .uni-calendar-item--multiple { + background-color: $uni-primary; + color: #fff; + opacity: 0.8; + } + .uni-calendar-item--before-checked { + background-color: #ff5a5f; + color: #fff; + } + .uni-calendar-item--after-checked { + background-color: #ff5a5f; + color: #fff; + } +</style> diff --git a/uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue b/uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue new file mode 100644 index 0000000..0beebfb --- /dev/null +++ b/uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue @@ -0,0 +1,567 @@ +<template> + <view class="uni-calendar"> + <view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" @click="clean"></view> + <view v-if="insert || show" class="uni-calendar__content" :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}"> + <view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top"> + <view class="uni-calendar__header-btn-box" @click="close"> + <text class="uni-calendar__header-text uni-calendar--fixed-width">{{cancelText}}</text> + </view> + <view class="uni-calendar__header-btn-box" @click="confirm"> + <text class="uni-calendar__header-text uni-calendar--fixed-width">{{okText}}</text> + </view> + </view> + <view class="uni-calendar__header"> + <view class="uni-calendar__header-btn-box" @click.stop="pre"> + <view class="uni-calendar__header-btn uni-calendar--left"></view> + </view> + <picker mode="date" :value="date" fields="month" @change="bindDateChange"> + <text class="uni-calendar__header-text">{{ (nowDate.year||'') +' / '+( nowDate.month||'')}}</text> + </picker> + <view class="uni-calendar__header-btn-box" @click.stop="next"> + <view class="uni-calendar__header-btn uni-calendar--right"></view> + </view> + <text class="uni-calendar__backtoday" @click="backToday">{{todayText}}</text> + + </view> + <view class="uni-calendar__box"> + <view v-if="showMonth" class="uni-calendar__box-bg"> + <text class="uni-calendar__box-bg-text">{{nowDate.month}}</text> + </view> + <view class="uni-calendar__weeks"> + <view class="uni-calendar__weeks-day"> + <text class="uni-calendar__weeks-day-text">{{SUNText}}</text> + </view> + <view class="uni-calendar__weeks-day"> + <text class="uni-calendar__weeks-day-text">{{monText}}</text> + </view> + <view class="uni-calendar__weeks-day"> + <text class="uni-calendar__weeks-day-text">{{TUEText}}</text> + </view> + <view class="uni-calendar__weeks-day"> + <text class="uni-calendar__weeks-day-text">{{WEDText}}</text> + </view> + <view class="uni-calendar__weeks-day"> + <text class="uni-calendar__weeks-day-text">{{THUText}}</text> + </view> + <view class="uni-calendar__weeks-day"> + <text class="uni-calendar__weeks-day-text">{{FRIText}}</text> + </view> + <view class="uni-calendar__weeks-day"> + <text class="uni-calendar__weeks-day-text">{{SATText}}</text> + </view> + </view> + <view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex"> + <view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex"> + <calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></calendar-item> + </view> + </view> + </view> + </view> + </view> +</template> + +<script> + import Calendar from './util.js'; + import CalendarItem from './uni-calendar-item.vue' + + import { initVueI18n } from '@dcloudio/uni-i18n' + import i18nMessages from './i18n/index.js' + const { t } = initVueI18n(i18nMessages) + + /** + * Calendar 日历 + * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等 + * @tutorial https://ext.dcloud.net.cn/plugin?id=56 + * @property {String} date 自定义当前时间,默认为今天 + * @property {Boolean} lunar 显示农历 + * @property {String} startDate 日期选择范围-开始日期 + * @property {String} endDate 日期选择范围-结束日期 + * @property {Boolean} 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 {Boolean} showMonth 是否选择月份为背景 + * @event {Function} change 日期改变,`insert :ture` 时生效 + * @event {Function} confirm 确认选择`insert :false` 时生效 + * @event {Function} monthSwitch 切换月份时触发 + * @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" /> + */ + export default { + components: { + CalendarItem + }, + emits:['close','confirm','change','monthSwitch'], + props: { + date: { + type: String, + default: '' + }, + selected: { + type: Array, + default () { + return [] + } + }, + lunar: { + type: Boolean, + default: false + }, + startDate: { + type: String, + default: '' + }, + endDate: { + type: String, + default: '' + }, + range: { + type: Boolean, + default: false + }, + insert: { + type: Boolean, + default: true + }, + showMonth: { + type: Boolean, + default: true + }, + clearDate: { + type: Boolean, + default: true + } + }, + data() { + return { + show: false, + weeks: [], + calendar: {}, + nowDate: '', + aniMaskShow: false + } + }, + computed:{ + /** + * for i18n + */ + + okText() { + return t("uni-calender.ok") + }, + cancelText() { + return t("uni-calender.cancel") + }, + todayText() { + return t("uni-calender.today") + }, + monText() { + return t("uni-calender.MON") + }, + TUEText() { + return t("uni-calender.TUE") + }, + WEDText() { + return t("uni-calender.WED") + }, + THUText() { + return t("uni-calender.THU") + }, + FRIText() { + return t("uni-calender.FRI") + }, + SATText() { + return t("uni-calender.SAT") + }, + SUNText() { + return t("uni-calender.SUN") + }, + }, + watch: { + date(newVal) { + // this.cale.setDate(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.cale = new Calendar({ + selected: this.selected, + startDate: this.startDate, + endDate: this.endDate, + range: this.range, + }) + this.init(this.date) + }, + methods: { + // 取消穿透 + clean() {}, + 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) { + this.cale.setDate(date) + this.weeks = this.cale.weeks + this.nowDate = this.calendar = this.cale.getInfo(date) + }, + /** + * 打开日历弹窗 + */ + open() { + // 弹窗模式并且清理数据 + if (this.clearDate && !this.insert) { + this.cale.cleanMultipleStatus() + // this.cale.setDate(this.date) + this.init(this.date) + } + this.show = true + this.$nextTick(() => { + setTimeout(() => { + this.aniMaskShow = true + }, 50) + }) + }, + /** + * 关闭日历弹窗 + */ + close() { + this.aniMaskShow = false + this.$nextTick(() => { + setTimeout(() => { + this.show = false + this.$emit('close') + }, 300) + }) + }, + /** + * 确认按钮 + */ + confirm() { + this.setEmit('confirm') + this.close() + }, + /** + * 变化触发 + */ + change() { + if (!this.insert) return + this.setEmit('change') + }, + /** + * 选择月份触发 + */ + 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.multipleStatus, + year, + month, + date, + fulldate: fullDate, + lunar, + extraInfo: extraInfo || {} + }) + }, + /** + * 选择天触发 + * @param {Object} weeks + */ + choiceDate(weeks) { + if (weeks.disable) return + this.calendar = weeks + // 设置多选 + 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 lang="scss" scoped> + $uni-bg-color-mask: rgba($color: #000000, $alpha: 0.4); + $uni-border-color: #EDEDED; + $uni-text-color: #333; + $uni-bg-color-hover:#f1f1f1; + $uni-font-size-base:14px; + $uni-text-color-placeholder: #808080; + $uni-color-subtitle: #555555; + $uni-text-color-grey:#999; + .uni-calendar { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + } + + .uni-calendar__mask { + position: fixed; + bottom: 0; + top: 0; + left: 0; + right: 0; + background-color: $uni-bg-color-mask; + transition-property: opacity; + transition-duration: 0.3s; + opacity: 0; + /* #ifndef APP-NVUE */ + z-index: 99; + /* #endif */ + } + + .uni-calendar--mask-show { + opacity: 1 + } + + .uni-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 */ + } + + .uni-calendar--ani-show { + transform: translateY(0); + } + + .uni-calendar__content { + background-color: #fff; + } + + .uni-calendar__header { + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + justify-content: center; + align-items: center; + height: 50px; + border-bottom-color: $uni-border-color; + border-bottom-style: solid; + border-bottom-width: 1px; + } + + .uni-calendar--fixed-top { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + justify-content: space-between; + border-top-color: $uni-border-color; + border-top-style: solid; + border-top-width: 1px; + } + + .uni-calendar--fixed-width { + width: 50px; + } + + .uni-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: $uni-text-color; + background-color: $uni-bg-color-hover; + } + + .uni-calendar__header-text { + text-align: center; + width: 100px; + font-size: $uni-font-size-base; + color: $uni-text-color; + } + + .uni-calendar__header-btn-box { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + align-items: center; + justify-content: center; + width: 50px; + height: 50px; + } + + .uni-calendar__header-btn { + width: 10px; + height: 10px; + border-left-color: $uni-text-color-placeholder; + border-left-style: solid; + border-left-width: 2px; + border-top-color: $uni-color-subtitle; + border-top-style: solid; + border-top-width: 2px; + } + + .uni-calendar--left { + transform: rotate(-45deg); + } + + .uni-calendar--right { + transform: rotate(135deg); + } + + + .uni-calendar__weeks { + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + } + + .uni-calendar__weeks-item { + flex: 1; + } + + .uni-calendar__weeks-day { + flex: 1; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + justify-content: center; + align-items: center; + height: 45px; + border-bottom-color: #F5F5F5; + border-bottom-style: solid; + border-bottom-width: 1px; + } + + .uni-calendar__weeks-day-text { + font-size: 14px; + } + + .uni-calendar__box { + position: relative; + } + + .uni-calendar__box-bg { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + justify-content: center; + align-items: center; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + } + + .uni-calendar__box-bg-text { + font-size: 200px; + font-weight: bold; + color: $uni-text-color-grey; + opacity: 0.1; + text-align: center; + /* #ifndef APP-NVUE */ + line-height: 1; + /* #endif */ + } +</style> diff --git a/uni_modules/uni-calendar/components/uni-calendar/util.js b/uni_modules/uni-calendar/components/uni-calendar/util.js new file mode 100644 index 0000000..e0c5103 --- /dev/null +++ b/uni_modules/uni-calendar/components/uni-calendar/util.js @@ -0,0 +1,360 @@ +import CALENDAR from './calendar.js' + +class Calendar { + constructor({ + date, + selected, + startDate, + endDate, + range + } = {}) { + // 当前日期 + this.date = this.getDate(new Date()) // 当前初入日期 + // 打点信息 + this.selected = selected || []; + // 范围开始 + this.startDate = startDate + // 范围结束 + this.endDate = endDate + this.range = range + // 多选状态 + this.cleanMultipleStatus() + // 每周日期 + this.weeks = {} + // this._getWeek(this.date.fullDate) + } + /** + * 设置日期 + * @param {Object} date + */ + setDate(date) { + this.selectDate = this.getDate(date) + this._getWeek(this.selectDate.fullDate) + } + + /** + * 清理多选状态 + */ + cleanMultipleStatus() { + this.multipleStatus = { + before: '', + after: '', + 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 multiples = this.multipleStatus.data + let checked = false + let multiplesStatus = -1 + if (this.range) { + if (multiples) { + multiplesStatus = multiples.findIndex((item) => { + return this.dateEqual(item, nowDate) + }) + } + if (multiplesStatus !== -1) { + checked = true + } + } + let data = { + fullDate: nowDate, + year: full.year, + date: i, + multiple: this.range ? checked : false, + beforeMultiple: this.dateEqual(this.multipleStatus.before, nowDate), + afterMultiple: this.dateEqual(this.multipleStatus.after, nowDate), + 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() + } + 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 + } + } + + + /** + * 获取日期范围内所有日期 + * @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) { + let { + before, + after + } = this.multipleStatus + + if (!this.range) return + if (before && after) { + this.multipleStatus.before = fullDate + this.multipleStatus.after = '' + this.multipleStatus.data = [] + } else { + if (!before) { + this.multipleStatus.before = fullDate + } else { + this.multipleStatus.after = fullDate + if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) { + this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after); + } else { + this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.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 diff --git a/uni_modules/uni-calendar/package.json b/uni_modules/uni-calendar/package.json new file mode 100644 index 0000000..ec924e9 --- /dev/null +++ b/uni_modules/uni-calendar/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-calendar", + "displayName": "uni-calendar 日历", + "version": "1.4.12", + "description": "日历组件", + "keywords": [ + "uni-ui", + "uniui", + "日历", + "", + "打卡", + "日历选择" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-calendar/readme.md b/uni_modules/uni-calendar/readme.md new file mode 100644 index 0000000..4e1748c --- /dev/null +++ b/uni_modules/uni-calendar/readme.md @@ -0,0 +1,103 @@ + + +## Calendar 日历 +> **组件名:uni-calendar** +> 代码块: `uCalendar` + + +日历组件 + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - 本组件农历转换使用的js是 [@1900-2100区间内的公历、农历互转](https://github.com/jjonline/calendar.js) +> - 仅支持自定义组件模式 +> - `date`属性传入的应该是一个 String ,如: 2019-06-27 ,而不是 new Date() +> - 通过 `insert` 属性来确定当前的事件是 @change 还是 @confirm 。理应合并为一个事件,但是为了区分模式,现使用两个事件,这里需要注意 +> - 弹窗模式下无法阻止后面的元素滚动,如有需要阻止,请在弹窗弹出后,手动设置滚动元素为不可滚动 + + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +在 ``template`` 中使用组件 + +```html +<view> + <uni-calendar + :insert="true" + :lunar="true" + :start-date="'2019-3-2'" + :end-date="'2019-5-20'" + @change="change" + /> +</view> +``` + +### 通过方法打开日历 + +需要设置 `insert` 为 `false` + +```html +<view> + <uni-calendar + ref="calendar" + :insert="false" + @confirm="confirm" + /> + <button @click="open">打开日历</button> +</view> +``` + +```javascript + +export default { + data() { + return {}; + }, + methods: { + open(){ + this.$refs.calendar.open(); + }, + confirm(e) { + console.log(e); + } + } +}; + +``` + + +## API + +### Calendar Props + +| 属性名 | 类型 | 默认值| 说明 | +| - | - | - | - | +| date | String |- | 自定义当前时间,默认为今天 | +| lunar | Boolean | false | 显示农历 | +| startDate | String |- | 日期选择范围-开始日期 | +| endDate | String |- | 日期选择范围-结束日期 | +| range | Boolean | false | 范围选择 | +| insert | Boolean | false | 插入模式,可选值,ture:插入模式;false:弹窗模式;默认为插入模式 | +|clearDate |Boolean |true |弹窗模式是否清空上次选择内容 | +| selected | Array |- | 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] | +|showMonth | Boolean | true | 是否显示月份为背景 | + +### Calendar Events + +| 事件名 | 说明 |返回值| +| - | - | - | +| open | 弹出日历组件,`insert :false` 时生效|- | + + + + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar](https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar) diff --git a/uni_modules/uni-card/changelog.md b/uni_modules/uni-card/changelog.md new file mode 100644 index 0000000..c3cd8c4 --- /dev/null +++ b/uni_modules/uni-card/changelog.md @@ -0,0 +1,26 @@ +## 1.3.1(2021-12-20) +- 修复 在vue页面下略缩图显示不正常的bug +## 1.3.0(2021-11-19) +- 重构插槽的用法 ,header 替换为 title +- 新增 actions 插槽 +- 新增 cover 封面图属性和插槽 +- 新增 padding 内容默认内边距离 +- 新增 margin 卡片默认外边距离 +- 新增 spacing 卡片默认内边距 +- 新增 shadow 卡片阴影属性 +- 取消 mode 属性,可使用组合插槽代替 +- 取消 note 属性 ,使用actions插槽代替 +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-card](https://uniapp.dcloud.io/component/uniui/uni-card) +## 1.2.1(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.2.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.8(2021-07-01) +- 优化 图文卡片无图片加载时,提供占位图标 +- 新增 header 插槽,自定义卡片头部( 图文卡片 mode="style" 时,不支持) +- 修复 thumbnail 不存在仍然占位的 bug +## 1.1.7(2021-05-12) +- 新增 组件示例地址 +## 1.1.6(2021-02-04) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-card/components/uni-card/uni-card.vue b/uni_modules/uni-card/components/uni-card/uni-card.vue new file mode 100644 index 0000000..38cf594 --- /dev/null +++ b/uni_modules/uni-card/components/uni-card/uni-card.vue @@ -0,0 +1,270 @@ +<template> + <view class="uni-card" :class="{ 'uni-card--full': isFull, 'uni-card--shadow': isShadow,'uni-card--border':border}" + :style="{'margin':isFull?0:margin,'padding':spacing,'box-shadow':isShadow?shadow:''}"> + <!-- 封面 --> + <slot name="cover"> + <view v-if="cover" class="uni-card__cover"> + <image class="uni-card__cover-image" mode="widthFix" @click="onClick('cover')" :src="cover"></image> + </view> + </slot> + <slot name="title"> + <view v-if="title || extra" class="uni-card__header"> + <!-- 卡片标题 --> + <view class="uni-card__header-box" @click="onClick('title')"> + <view v-if="thumbnail" class="uni-card__header-avatar"> + <image class="uni-card__header-avatar-image" :src="thumbnail" mode="aspectFit" /> + </view> + <view class="uni-card__header-content"> + <text class="uni-card__header-content-title uni-ellipsis">{{ title }}</text> + <text v-if="title&&subTitle" + class="uni-card__header-content-subtitle uni-ellipsis">{{ subTitle }}</text> + </view> + </view> + <view class="uni-card__header-extra" @click="onClick('extra')"> + <text class="uni-card__header-extra-text">{{ extra }}</text> + </view> + </view> + </slot> + <!-- 卡片内容 --> + <view class="uni-card__content" :style="{padding:padding}" @click="onClick('content')"> + <slot></slot> + </view> + <view class="uni-card__actions" @click="onClick('actions')"> + <slot name="actions"></slot> + </view> + </view> +</template> + +<script> + /** + * Card 卡片 + * @description 卡片视图组件 + * @tutorial https://ext.dcloud.net.cn/plugin?id=22 + * @property {String} title 标题文字 + * @property {String} subTitle 副标题 + * @property {Number} padding 内容内边距 + * @property {Number} margin 卡片外边距 + * @property {Number} spacing 卡片内边距 + * @property {String} extra 标题额外信息 + * @property {String} cover 封面图(本地路径需要引入) + * @property {String} thumbnail 标题左侧缩略图 + * @property {Boolean} is-full = [true | false] 卡片内容是否通栏,为 true 时将去除padding值 + * @property {Boolean} is-shadow = [true | false] 卡片内容是否开启阴影 + * @property {String} shadow 卡片阴影 + * @property {Boolean} border 卡片边框 + * @event {Function} click 点击 Card 触发事件 + */ + export default { + name: 'UniCard', + emits: ['click'], + props: { + title: { + type: String, + default: '' + }, + subTitle: { + type: String, + default: '' + }, + padding: { + type: String, + default: '10px' + }, + margin: { + type: String, + default: '15px' + }, + spacing: { + type: String, + default: '0 10px' + }, + extra: { + type: String, + default: '' + }, + cover: { + type: String, + default: '' + }, + thumbnail: { + type: String, + default: '' + }, + isFull: { + // 内容区域是否通栏 + type: Boolean, + default: false + }, + isShadow: { + // 是否开启阴影 + type: Boolean, + default: true + }, + shadow: { + type: String, + default: '0px 0px 3px 1px rgba(0, 0, 0, 0.08)' + }, + border: { + type: Boolean, + default: true + } + }, + methods: { + onClick(type) { + this.$emit('click', type) + } + } + } +</script> + +<style lang="scss"> + $uni-border-3: #EBEEF5 !default; + $uni-shadow-base:0 0px 6px 1px rgba($color: #a5a5a5, $alpha: 0.2) !default; + $uni-main-color: #3a3a3a !default; + $uni-base-color: #6a6a6a !default; + $uni-secondary-color: #909399 !default; + $uni-spacing-sm: 8px !default; + $uni-border-color:$uni-border-3; + $uni-shadow: $uni-shadow-base; + $uni-card-title: 15px; + $uni-cart-title-color:$uni-main-color; + $uni-card-subtitle: 12px; + $uni-cart-subtitle-color:$uni-secondary-color; + $uni-card-spacing: 10px; + $uni-card-content-color: $uni-base-color; + + .uni-card { + margin: $uni-card-spacing; + padding: 0 $uni-spacing-sm; + border-radius: 4px; + overflow: hidden; + font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif; + background-color: #fff; + flex: 1; + + .uni-card__cover { + position: relative; + margin-top: $uni-card-spacing; + flex-direction: row; + overflow: hidden; + border-radius: 4px; + .uni-card__cover-image { + flex: 1; + // width: 100%; + /* #ifndef APP-PLUS */ + vertical-align: middle; + /* #endif */ + } + } + + .uni-card__header { + display: flex; + border-bottom: 1px $uni-border-color solid; + flex-direction: row; + align-items: center; + padding: $uni-card-spacing; + overflow: hidden; + + .uni-card__header-box { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex: 1; + flex-direction: row; + align-items: center; + overflow: hidden; + } + + .uni-card__header-avatar { + width: 40px; + height: 40px; + overflow: hidden; + border-radius: 5px; + margin-right: $uni-card-spacing; + .uni-card__header-avatar-image { + flex: 1; + width: 40px; + height: 40px; + } + } + + .uni-card__header-content { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + justify-content: center; + flex: 1; + // height: 40px; + overflow: hidden; + + .uni-card__header-content-title { + font-size: $uni-card-title; + color: $uni-cart-title-color; + // line-height: 22px; + } + + .uni-card__header-content-subtitle { + font-size: $uni-card-subtitle; + margin-top: 5px; + color: $uni-cart-subtitle-color; + } + } + + .uni-card__header-extra { + line-height: 12px; + + .uni-card__header-extra-text { + font-size: 12px; + color: $uni-cart-subtitle-color; + } + } + } + + .uni-card__content { + padding: $uni-card-spacing; + font-size: 14px; + color: $uni-card-content-color; + line-height: 22px; + } + + .uni-card__actions { + font-size: 12px; + } + } + + .uni-card--border { + border: 1px solid $uni-border-color; + } + + .uni-card--shadow { + position: relative; + /* #ifndef APP-NVUE */ + box-shadow: $uni-shadow; + /* #endif */ + } + + .uni-card--full { + margin: 0; + border-left-width: 0; + border-left-width: 0; + border-radius: 0; + } + + /* #ifndef APP-NVUE */ + .uni-card--full:after { + border-radius: 0; + } + + /* #endif */ + .uni-ellipsis { + /* #ifndef APP-NVUE */ + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + /* #endif */ + /* #ifdef APP-NVUE */ + lines: 1; + /* #endif */ + } +</style> diff --git a/uni_modules/uni-card/package.json b/uni_modules/uni-card/package.json new file mode 100644 index 0000000..f16224d --- /dev/null +++ b/uni_modules/uni-card/package.json @@ -0,0 +1,90 @@ +{ + "id": "uni-card", + "displayName": "uni-card 卡片", + "version": "1.3.1", + "description": "Card 组件,提供常见的卡片样式。", + "keywords": [ + "uni-ui", + "uniui", + "card", + "", + "卡片" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-icons", + "uni-scss" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-card/readme.md b/uni_modules/uni-card/readme.md new file mode 100644 index 0000000..7434e71 --- /dev/null +++ b/uni_modules/uni-card/readme.md @@ -0,0 +1,12 @@ + + +## Card 卡片 +> **组件名:uni-card** +> 代码块: `uCard` + +卡片视图组件。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-card) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-collapse/changelog.md b/uni_modules/uni-collapse/changelog.md new file mode 100644 index 0000000..455308a --- /dev/null +++ b/uni_modules/uni-collapse/changelog.md @@ -0,0 +1,38 @@ +## 1.4.4(2024-03-20) +- 修复 titleBorder类型修正 +## 1.4.3(2022-01-25) +- 修复 初始化的时候 ,open 属性失效的bug +## 1.4.2(2022-01-21) +- 修复 微信小程序resize后组件收起的bug +## 1.4.1(2021-11-22) +- 修复 vue3中个别scss变量无法找到的问题 +## 1.4.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-collapse](https://uniapp.dcloud.io/component/uniui/uni-collapse) +## 1.3.3(2021-08-17) +- 优化 show-arrow 属性默认为true +## 1.3.2(2021-08-17) +- 新增 show-arrow 属性,控制是否显示右侧箭头 +## 1.3.1(2021-07-30) +- 优化 vue3下小程序事件警告的问题 +## 1.3.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.2.2(2021-07-21) +- 修复 由1.2.0版本引起的 change 事件返回 undefined 的Bug +## 1.2.1(2021-07-21) +- 优化 组件示例 +## 1.2.0(2021-07-21) +- 新增 组件折叠动画 +- 新增 value\v-model 属性 ,动态修改面板折叠状态 +- 新增 title 插槽 ,可定义面板标题 +- 新增 border 属性 ,显示隐藏面板内容分隔线 +- 新增 title-border 属性 ,显示隐藏面板标题分隔线 +- 修复 resize 方法失效的Bug +- 修复 change 事件返回参数不正确的Bug +- 优化 H5、App 平台自动更具内容更新高度,无需调用 reszie() 方法 +## 1.1.7(2021-05-12) +- 新增 组件示例地址 +## 1.1.6(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 1.1.5(2021-02-05) +- 调整为uni_modules目录规范 \ No newline at end of file diff --git a/uni_modules/uni-collapse/components/uni-collapse-item/uni-collapse-item.vue b/uni_modules/uni-collapse/components/uni-collapse-item/uni-collapse-item.vue new file mode 100644 index 0000000..2f0862e --- /dev/null +++ b/uni_modules/uni-collapse/components/uni-collapse-item/uni-collapse-item.vue @@ -0,0 +1,402 @@ +<template> + <view class="uni-collapse-item"> + <!-- onClick(!isOpen) --> + <view @click="onClick(!isOpen)" class="uni-collapse-item__title" + :class="{'is-open':isOpen &&titleBorder === 'auto' ,'uni-collapse-item-border':titleBorder !== 'none'}"> + <view class="uni-collapse-item__title-wrap"> + <slot name="title"> + <view class="uni-collapse-item__title-box" :class="{'is-disabled':disabled}"> + <image v-if="thumb" :src="thumb" class="uni-collapse-item__title-img" /> + <text class="uni-collapse-item__title-text">{{ title }}</text> + </view> + </slot> + </view> + <view v-if="showArrow" + :class="{ 'uni-collapse-item__title-arrow-active': isOpen, 'uni-collapse-item--animation': showAnimation === true }" + class="uni-collapse-item__title-arrow"> + <uni-icons :color="disabled?'#ddd':'#bbb'" size="14" type="bottom" /> + </view> + </view> + <view class="uni-collapse-item__wrap" :class="{'is--transition':showAnimation}" + :style="{height: (isOpen?height:0) +'px'}"> + <view :id="elId" ref="collapse--hook" class="uni-collapse-item__wrap-content" + :class="{open:isheight,'uni-collapse-item--border':border&&isOpen}"> + <slot></slot> + </view> + </view> + + </view> +</template> + +<script> + // #ifdef APP-NVUE + const dom = weex.requireModule('dom') + // #endif + /** + * CollapseItem 折叠面板子组件 + * @description 折叠面板子组件 + * @property {String} title 标题文字 + * @property {String} thumb 标题左侧缩略图 + * @property {String} name 唯一标志符 + * @property {Boolean} open = [true|false] 是否展开组件 + * @property {Boolean} titleBorder = [true|false] 是否显示标题分隔线 + * @property {String} border = ['auto'|'show'|'none'] 是否显示分隔线 + * @property {Boolean} disabled = [true|false] 是否展开面板 + * @property {Boolean} showAnimation = [true|false] 开启动画 + * @property {Boolean} showArrow = [true|false] 是否显示右侧箭头 + */ + export default { + name: 'uniCollapseItem', + props: { + // 列表标题 + title: { + type: String, + default: '' + }, + name: { + type: [Number, String], + default: '' + }, + // 是否禁用 + disabled: { + type: Boolean, + default: false + }, + // #ifdef APP-PLUS + // 是否显示动画,app 端默认不开启动画,卡顿严重 + showAnimation: { + type: Boolean, + default: false + }, + // #endif + // #ifndef APP-PLUS + // 是否显示动画 + showAnimation: { + type: Boolean, + default: true + }, + // #endif + // 是否展开 + open: { + type: Boolean, + default: false + }, + // 缩略图 + thumb: { + type: String, + default: '' + }, + // 标题分隔线显示类型 + titleBorder: { + type: String, + default: 'auto' + }, + border: { + type: Boolean, + default: true + }, + showArrow: { + type: Boolean, + default: true + } + }, + data() { + // TODO 随机生生元素ID,解决百度小程序获取同一个元素位置信息的bug + const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}` + return { + isOpen: false, + isheight: null, + height: 0, + elId, + nameSync: 0 + } + }, + watch: { + open(val) { + this.isOpen = val + this.onClick(val, 'init') + } + }, + updated(e) { + this.$nextTick(() => { + this.init(true) + }) + }, + created() { + this.collapse = this.getCollapse() + this.oldHeight = 0 + this.onClick(this.open, 'init') + }, + // #ifndef VUE3 + // TODO vue2 + destroyed() { + if (this.__isUnmounted) return + this.uninstall() + }, + // #endif + // #ifdef VUE3 + // TODO vue3 + unmounted() { + this.__isUnmounted = true + this.uninstall() + }, + // #endif + mounted() { + if (!this.collapse) return + if (this.name !== '') { + this.nameSync = this.name + } else { + this.nameSync = this.collapse.childrens.length + '' + } + if (this.collapse.names.indexOf(this.nameSync) === -1) { + this.collapse.names.push(this.nameSync) + } else { + console.warn(`name 值 ${this.nameSync} 重复`); + } + if (this.collapse.childrens.indexOf(this) === -1) { + this.collapse.childrens.push(this) + } + this.init() + }, + methods: { + init(type) { + // #ifndef APP-NVUE + this.getCollapseHeight(type) + // #endif + // #ifdef APP-NVUE + this.getNvueHwight(type) + // #endif + }, + uninstall() { + if (this.collapse) { + this.collapse.childrens.forEach((item, index) => { + if (item === this) { + this.collapse.childrens.splice(index, 1) + } + }) + this.collapse.names.forEach((item, index) => { + if (item === this.nameSync) { + this.collapse.names.splice(index, 1) + } + }) + } + }, + onClick(isOpen, type) { + if (this.disabled) return + this.isOpen = isOpen + if (this.isOpen && this.collapse) { + this.collapse.setAccordion(this) + } + if (type !== 'init') { + this.collapse.onChange(isOpen, this) + } + }, + getCollapseHeight(type, index = 0) { + const views = uni.createSelectorQuery().in(this) + views + .select(`#${this.elId}`) + .fields({ + size: true + }, data => { + // TODO 百度中可能获取不到节点信息 ,需要循环获取 + if (index >= 10) return + if (!data) { + index++ + this.getCollapseHeight(false, index) + return + } + // #ifdef APP-NVUE + this.height = data.height + 1 + // #endif + // #ifndef APP-NVUE + this.height = data.height + // #endif + this.isheight = true + if (type) return + this.onClick(this.isOpen, 'init') + }) + .exec() + }, + getNvueHwight(type) { + const result = dom.getComponentRect(this.$refs['collapse--hook'], option => { + if (option && option.result && option.size) { + // #ifdef APP-NVUE + this.height = option.size.height + 1 + // #endif + // #ifndef APP-NVUE + this.height = option.size.height + // #endif + this.isheight = true + if (type) return + this.onClick(this.open, 'init') + } + }) + }, + /** + * 获取父元素实例 + */ + getCollapse(name = 'uniCollapse') { + 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; + } + } + } +</script> + +<style lang="scss"> + .uni-collapse-item { + /* #ifndef APP-NVUE */ + box-sizing: border-box; + + /* #endif */ + &__title { + /* #ifndef APP-NVUE */ + display: flex; + width: 100%; + box-sizing: border-box; + /* #endif */ + flex-direction: row; + align-items: center; + transition: border-bottom-color .3s; + + // transition-property: border-bottom-color; + // transition-duration: 5s; + &-wrap { + width: 100%; + flex: 1; + + } + + &-box { + padding: 0 15px; + /* #ifndef APP-NVUE */ + display: flex; + width: 100%; + box-sizing: border-box; + /* #endif */ + flex-direction: row; + justify-content: space-between; + align-items: center; + height: 48px; + line-height: 48px; + background-color: #fff; + color: #303133; + font-size: 13px; + font-weight: 500; + /* #ifdef H5 */ + cursor: pointer; + outline: none; + + /* #endif */ + &.is-disabled { + .uni-collapse-item__title-text { + color: #999; + } + } + + } + + &.uni-collapse-item-border { + border-bottom: 1px solid #ebeef5; + } + + &.is-open { + border-bottom-color: transparent; + } + + &-img { + height: 22px; + width: 22px; + margin-right: 10px; + } + + &-text { + flex: 1; + font-size: 14px; + /* #ifndef APP-NVUE */ + white-space: nowrap; + color: inherit; + /* #endif */ + /* #ifdef APP-NVUE */ + lines: 1; + /* #endif */ + overflow: hidden; + text-overflow: ellipsis; + } + + &-arrow { + /* #ifndef APP-NVUE */ + display: flex; + box-sizing: border-box; + /* #endif */ + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + margin-right: 10px; + transform: rotate(0deg); + + &-active { + transform: rotate(-180deg); + } + } + + + } + + &__wrap { + /* #ifndef APP-NVUE */ + will-change: height; + box-sizing: border-box; + /* #endif */ + background-color: #fff; + overflow: hidden; + position: relative; + height: 0; + + &.is--transition { + // transition: all 0.3s; + transition-property: height, border-bottom-width; + transition-duration: 0.3s; + /* #ifndef APP-NVUE */ + will-change: height; + /* #endif */ + } + + + + &-content { + position: absolute; + font-size: 13px; + color: #303133; + // transition: height 0.3s; + border-bottom-color: transparent; + border-bottom-style: solid; + border-bottom-width: 0; + + &.uni-collapse-item--border { + border-bottom-width: 1px; + border-bottom-color: red; + border-bottom-color: #ebeef5; + } + + &.open { + position: relative; + } + } + } + + &--animation { + transition-property: transform; + transition-duration: 0.3s; + transition-timing-function: ease; + } + + } +</style> diff --git a/uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue b/uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue new file mode 100644 index 0000000..384c39a --- /dev/null +++ b/uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue @@ -0,0 +1,147 @@ +<template> + <view class="uni-collapse"> + <slot /> + </view> +</template> +<script> + /** + * Collapse 折叠面板 + * @description 展示可以折叠 / 展开的内容区域 + * @tutorial https://ext.dcloud.net.cn/plugin?id=23 + * @property {String|Array} value 当前激活面板改变时触发(如果是手风琴模式,参数类型为string,否则为array) + * @property {Boolean} accordion = [true|false] 是否开启手风琴效果是否开启手风琴效果 + * @event {Function} change 切换面板时触发,如果是手风琴模式,返回类型为string,否则为array + */ + export default { + name: 'uniCollapse', + emits:['change','activeItem','input','update:modelValue'], + props: { + value: { + type: [String, Array], + default: '' + }, + modelValue: { + type: [String, Array], + default: '' + }, + accordion: { + // 是否开启手风琴效果 + type: [Boolean, String], + default: false + }, + }, + data() { + return {} + }, + computed: { + // TODO 兼容 vue2 和 vue3 + dataValue() { + let value = (typeof this.value === 'string' && this.value === '') || + (Array.isArray(this.value) && this.value.length === 0) + let modelValue = (typeof this.modelValue === 'string' && this.modelValue === '') || + (Array.isArray(this.modelValue) && this.modelValue.length === 0) + if (value) { + return this.modelValue + } + if (modelValue) { + return this.value + } + + return this.value + } + }, + watch: { + dataValue(val) { + this.setOpen(val) + } + }, + created() { + this.childrens = [] + this.names = [] + }, + mounted() { + this.$nextTick(()=>{ + this.setOpen(this.dataValue) + }) + }, + methods: { + setOpen(val) { + let str = typeof val === 'string' + let arr = Array.isArray(val) + this.childrens.forEach((vm, index) => { + if (str) { + if (val === vm.nameSync) { + if (!this.accordion) { + console.warn('accordion 属性为 false ,v-model 类型应该为 array') + return + } + vm.isOpen = true + } + } + if (arr) { + val.forEach(v => { + if (v === vm.nameSync) { + if (this.accordion) { + console.warn('accordion 属性为 true ,v-model 类型应该为 string') + return + } + vm.isOpen = true + } + }) + } + }) + this.emit(val) + }, + setAccordion(self) { + if (!this.accordion) return + this.childrens.forEach((vm, index) => { + if (self !== vm) { + vm.isOpen = false + } + }) + }, + resize() { + this.childrens.forEach((vm, index) => { + // #ifndef APP-NVUE + vm.getCollapseHeight() + // #endif + // #ifdef APP-NVUE + vm.getNvueHwight() + // #endif + }) + }, + onChange(isOpen, self) { + let activeItem = [] + + if (this.accordion) { + activeItem = isOpen ? self.nameSync : '' + } else { + this.childrens.forEach((vm, index) => { + if (vm.isOpen) { + activeItem.push(vm.nameSync) + } + }) + } + this.$emit('change', activeItem) + this.emit(activeItem) + }, + emit(val){ + this.$emit('input', val) + this.$emit('update:modelValue', val) + } + } + } +</script> +<style lang="scss" > + .uni-collapse { + /* #ifndef APP-NVUE */ + width: 100%; + display: flex; + /* #endif */ + /* #ifdef APP-NVUE */ + flex: 1; + /* #endif */ + flex-direction: column; + background-color: #fff; + } +</style> diff --git a/uni_modules/uni-collapse/package.json b/uni_modules/uni-collapse/package.json new file mode 100644 index 0000000..65c5c2e --- /dev/null +++ b/uni_modules/uni-collapse/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-collapse", + "displayName": "uni-collapse 折叠面板", + "version": "1.4.4", + "description": "Collapse 组件,可以折叠 / 展开的内容区域。", + "keywords": [ + "uni-ui", + "折叠", + "折叠面板", + "手风琴" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-collapse/readme.md b/uni_modules/uni-collapse/readme.md new file mode 100644 index 0000000..bc758eb --- /dev/null +++ b/uni_modules/uni-collapse/readme.md @@ -0,0 +1,12 @@ + + +## Collapse 折叠面板 +> **组件名:uni-collapse** +> 代码块: `uCollapse` +> 关联组件:`uni-collapse-item`、`uni-icons`。 + + +折叠面板用来折叠/显示过长的内容或者是列表。通常是在多内容分类项使用,折叠不重要的内容,显示重要内容。点击可以展开折叠部分。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-collapse) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-combox/changelog.md b/uni_modules/uni-combox/changelog.md new file mode 100644 index 0000000..a9c0f2c --- /dev/null +++ b/uni_modules/uni-combox/changelog.md @@ -0,0 +1,17 @@ +## 1.0.2(2024-09-21) +- 新增 clearAble属性 +## 1.0.1(2021-11-23) +- 优化 label、label-width 属性 +## 1.0.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-combox](https://uniapp.dcloud.io/component/uniui/uni-combox) +## 0.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 0.0.6(2021-05-12) +- 新增 组件示例地址 +## 0.0.5(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 0.0.4(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 0.0.3(2021-02-04) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-combox/components/uni-combox/uni-combox.vue b/uni_modules/uni-combox/components/uni-combox/uni-combox.vue new file mode 100644 index 0000000..cc702c2 --- /dev/null +++ b/uni_modules/uni-combox/components/uni-combox/uni-combox.vue @@ -0,0 +1,284 @@ +<template> + <view class="uni-combox" :class="border ? '' : 'uni-combox__no-border'"> + <view v-if="label" class="uni-combox__label" :style="labelStyle"> + <text>{{label}}</text> + </view> + <view class="uni-combox__input-box"> + <input class="uni-combox__input" type="text" :placeholder="placeholder" placeholder-class="uni-combox__input-plac" + v-model="inputVal" @input="onInput" @focus="onFocus" @blur="onBlur" /> + <uni-icons v-if="!inputVal || !clearAble" :type="showSelector? 'top' : 'bottom'" size="14" color="#999" @click="toggleSelector"> + </uni-icons> + <uni-icons v-if="inputVal && clearAble" type="clear" size="24" color="#999" @click="clean"> + </uni-icons> + </view> + <view class="uni-combox__selector" v-if="showSelector"> + <view class="uni-popper__arrow"></view> + <scroll-view scroll-y="true" class="uni-combox__selector-scroll"> + <view class="uni-combox__selector-empty" v-if="filterCandidatesLength === 0"> + <text>{{emptyTips}}</text> + </view> + <view class="uni-combox__selector-item" v-for="(item,index) in filterCandidates" :key="index" + @click="onSelectorClick(index)"> + <text>{{item}}</text> + </view> + </scroll-view> + </view> + </view> +</template> + +<script> + /** + * Combox 组合输入框 + * @description 组合输入框一般用于既可以输入也可以选择的场景 + * @tutorial https://ext.dcloud.net.cn/plugin?id=1261 + * @property {String} label 左侧文字 + * @property {String} labelWidth 左侧内容宽度 + * @property {String} placeholder 输入框占位符 + * @property {Array} candidates 候选项列表 + * @property {String} emptyTips 筛选结果为空时显示的文字 + * @property {String} value 组合框的值 + */ + export default { + name: 'uniCombox', + emits: ['input', 'update:modelValue'], + props: { + clearAble: { + type: Boolean, + default: false + }, + border: { + type: Boolean, + default: true + }, + label: { + type: String, + default: '' + }, + labelWidth: { + type: String, + default: 'auto' + }, + placeholder: { + type: String, + default: '' + }, + candidates: { + type: Array, + default () { + return [] + } + }, + emptyTips: { + type: String, + default: '无匹配项' + }, + // #ifndef VUE3 + value: { + type: [String, Number], + default: '' + }, + // #endif + // #ifdef VUE3 + modelValue: { + type: [String, Number], + default: '' + }, + // #endif + }, + data() { + return { + showSelector: false, + inputVal: '' + } + }, + computed: { + labelStyle() { + if (this.labelWidth === 'auto') { + return "" + } + return `width: ${this.labelWidth}` + }, + filterCandidates() { + return this.candidates.filter((item) => { + return item.toString().indexOf(this.inputVal) > -1 + }) + }, + filterCandidatesLength() { + return this.filterCandidates.length + } + }, + watch: { + // #ifndef VUE3 + value: { + handler(newVal) { + this.inputVal = newVal + }, + immediate: true + }, + // #endif + // #ifdef VUE3 + modelValue: { + handler(newVal) { + this.inputVal = newVal + }, + immediate: true + }, + // #endif + }, + methods: { + toggleSelector() { + this.showSelector = !this.showSelector + }, + onFocus() { + this.showSelector = true + }, + onBlur() { + setTimeout(() => { + this.showSelector = false + }, 153) + }, + onSelectorClick(index) { + this.inputVal = this.filterCandidates[index] + this.showSelector = false + this.$emit('input', this.inputVal) + this.$emit('update:modelValue', this.inputVal) + }, + onInput() { + setTimeout(() => { + this.$emit('input', this.inputVal) + this.$emit('update:modelValue', this.inputVal) + }) + }, + clean() { + this.inputVal = '' + this.onInput() + } + } + } +</script> + +<style lang="scss" scoped> + .uni-combox { + font-size: 14px; + border: 1px solid #DCDFE6; + border-radius: 4px; + padding: 6px 10px; + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + // height: 40px; + flex-direction: row; + align-items: center; + // border-bottom: solid 1px #DDDDDD; + } + + .uni-combox__label { + font-size: 16px; + line-height: 22px; + padding-right: 10px; + color: #999999; + } + + .uni-combox__input-box { + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex: 1; + flex-direction: row; + align-items: center; + } + + .uni-combox__input { + flex: 1; + font-size: 14px; + height: 22px; + line-height: 22px; + } + + .uni-combox__input-plac { + font-size: 14px; + color: #999; + } + + .uni-combox__selector { + /* #ifndef APP-NVUE */ + box-sizing: border-box; + /* #endif */ + position: absolute; + top: calc(100% + 12px); + left: 0; + width: 100%; + background-color: #FFFFFF; + border: 1px solid #EBEEF5; + border-radius: 6px; + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); + z-index: 2; + padding: 4px 0; + } + + .uni-combox__selector-scroll { + /* #ifndef APP-NVUE */ + max-height: 200px; + box-sizing: border-box; + /* #endif */ + } + + .uni-combox__selector-empty, + .uni-combox__selector-item { + /* #ifndef APP-NVUE */ + display: flex; + cursor: pointer; + /* #endif */ + line-height: 36px; + font-size: 14px; + text-align: center; + // border-bottom: solid 1px #DDDDDD; + padding: 0px 10px; + } + + .uni-combox__selector-item:hover { + background-color: #f9f9f9; + } + + .uni-combox__selector-empty:last-child, + .uni-combox__selector-item:last-child { + /* #ifndef APP-NVUE */ + border-bottom: none; + /* #endif */ + } + + // picker 弹出层通用的指示小三角 + .uni-popper__arrow, + .uni-popper__arrow::after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + border-width: 6px; + } + + .uni-popper__arrow { + filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); + top: -6px; + left: 10%; + margin-right: 3px; + border-top-width: 0; + border-bottom-color: #EBEEF5; + } + + .uni-popper__arrow::after { + content: " "; + top: 1px; + margin-left: -6px; + border-top-width: 0; + border-bottom-color: #fff; + } + + .uni-combox__no-border { + border: none; + } +</style> diff --git a/uni_modules/uni-combox/package.json b/uni_modules/uni-combox/package.json new file mode 100644 index 0000000..7af24fc --- /dev/null +++ b/uni_modules/uni-combox/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-combox", + "displayName": "uni-combox 组合框", + "version": "1.0.2", + "description": "可以选择也可以输入的表单项 ", + "keywords": [ + "uni-ui", + "uniui", + "combox", + "组合框", + "select" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "n" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-combox/readme.md b/uni_modules/uni-combox/readme.md new file mode 100644 index 0000000..ffa2cc8 --- /dev/null +++ b/uni_modules/uni-combox/readme.md @@ -0,0 +1,11 @@ + + +## Combox 组合框 +> **组件名:uni-combox** +> 代码块: `uCombox` + + +组合框组件。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-combox) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-countdown/changelog.md b/uni_modules/uni-countdown/changelog.md new file mode 100644 index 0000000..1337418 --- /dev/null +++ b/uni_modules/uni-countdown/changelog.md @@ -0,0 +1,28 @@ +## 1.2.4(2024-09-21) +- 新增 支持控制显示位数 默认显示2位 +## 1.2.3(2024-02-20) +- 新增 支持控制小时,分钟的显隐:showHour showMinute +## 1.2.2(2022-01-19) +- 修复 在微信小程序中样式不生效的bug +## 1.2.1(2022-01-18) +- 新增 update 方法 ,在动态更新时间后,刷新组件 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-countdown](https://uniapp.dcloud.io/component/uniui/uni-countdown) +## 1.1.3(2021-10-18) +- 重构 +- 新增 font-size 支持自定义字体大小 +## 1.1.2(2021-08-24) +- 新增 支持国际化 +## 1.1.1(2021-07-30) +- 优化 vue3下小程序事件警告的问题 +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.5(2021-06-18) +- 修复 uni-countdown 重复赋值跳两秒的 bug +## 1.0.4(2021-05-12) +- 新增 组件示例地址 +## 1.0.3(2021-05-08) +- 修复 uni-countdown 不能控制倒计时的 bug +## 1.0.2(2021-02-04) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-countdown/components/uni-countdown/i18n/en.json b/uni_modules/uni-countdown/components/uni-countdown/i18n/en.json new file mode 100644 index 0000000..06309cb --- /dev/null +++ b/uni_modules/uni-countdown/components/uni-countdown/i18n/en.json @@ -0,0 +1,6 @@ +{ + "uni-countdown.day": "day", + "uni-countdown.h": "h", + "uni-countdown.m": "m", + "uni-countdown.s": "s" +} diff --git a/uni_modules/uni-countdown/components/uni-countdown/i18n/index.js b/uni_modules/uni-countdown/components/uni-countdown/i18n/index.js new file mode 100644 index 0000000..de7509c --- /dev/null +++ b/uni_modules/uni-countdown/components/uni-countdown/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hans.json b/uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hans.json new file mode 100644 index 0000000..358cdd1 --- /dev/null +++ b/uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hans.json @@ -0,0 +1,6 @@ +{ + "uni-countdown.day": "天", + "uni-countdown.h": "时", + "uni-countdown.m": "分", + "uni-countdown.s": "秒" +} diff --git a/uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hant.json b/uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hant.json new file mode 100644 index 0000000..e5a63de --- /dev/null +++ b/uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hant.json @@ -0,0 +1,6 @@ +{ + "uni-countdown.day": "天", + "uni-countdown.h": "時", + "uni-countdown.m": "分", + "uni-countdown.s": "秒" +} diff --git a/uni_modules/uni-countdown/components/uni-countdown/uni-countdown.vue b/uni_modules/uni-countdown/components/uni-countdown/uni-countdown.vue new file mode 100644 index 0000000..94a90ae --- /dev/null +++ b/uni_modules/uni-countdown/components/uni-countdown/uni-countdown.vue @@ -0,0 +1,276 @@ +<template> + <view class="uni-countdown"> + <text v-if="showDay" :style="[timeStyle]" class="uni-countdown__number">{{ d }}</text> + <text v-if="showDay" :style="[splitorStyle]" class="uni-countdown__splitor">{{dayText}}</text> + <text v-if="showHour" :style="[timeStyle]" class="uni-countdown__number">{{ h }}</text> + <text v-if="showHour" :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : hourText }}</text> + <text v-if="showMinute" :style="[timeStyle]" class="uni-countdown__number">{{ i }}</text> + <text v-if="showMinute" :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : minuteText }}</text> + <text :style="[timeStyle]" class="uni-countdown__number">{{ s }}</text> + <text v-if="!showColon" :style="[splitorStyle]" class="uni-countdown__splitor">{{secondText}}</text> + </view> +</template> +<script> + import { + initVueI18n + } from '@dcloudio/uni-i18n' + import messages from './i18n/index.js' + const { + t + } = initVueI18n(messages) + /** + * Countdown 倒计时 + * @description 倒计时组件 + * @tutorial https://ext.dcloud.net.cn/plugin?id=25 + * @property {String} backgroundColor 背景色 + * @property {String} color 文字颜色 + * @property {Number} day 天数 + * @property {Number} hour 小时 + * @property {Number} minute 分钟 + * @property {Number} second 秒 + * @property {Number} timestamp 时间戳 + * @property {Boolean} showDay = [true|false] 是否显示天数 + * @property {Boolean} showHour = [true|false] 是否显示小时 + * @property {Boolean} showMinute = [true|false] 是否显示分钟 + * @property {Boolean} show-colon = [true|false] 是否以冒号为分隔符 + * @property {String} splitorColor 分割符号颜色 + * @event {Function} timeup 倒计时时间到触发事件 + * @example <uni-countdown :day="1" :hour="1" :minute="12" :second="40"></uni-countdown> + */ + export default { + name: 'UniCountdown', + emits: ['timeup'], + props: { + showDay: { + type: Boolean, + default: true + }, + showHour: { + type: Boolean, + default: true + }, + showMinute: { + type: Boolean, + default: true + }, + showColon: { + type: Boolean, + default: true + }, + start: { + type: Boolean, + default: true + }, + backgroundColor: { + type: String, + default: '' + }, + color: { + type: String, + default: '#333' + }, + fontSize: { + type: Number, + default: 14 + }, + splitorColor: { + type: String, + default: '#333' + }, + day: { + type: Number, + default: 0 + }, + hour: { + type: Number, + default: 0 + }, + minute: { + type: Number, + default: 0 + }, + second: { + type: Number, + default: 0 + }, + timestamp: { + type: Number, + default: 0 + }, + filterShow : { + type:Object, + default:{} + } + }, + data() { + return { + timer: null, + syncFlag: false, + d: '00', + h: '00', + i: '00', + s: '00', + leftTime: 0, + seconds: 0 + } + }, + computed: { + dayText() { + return t("uni-countdown.day") + }, + hourText(val) { + return t("uni-countdown.h") + }, + minuteText(val) { + return t("uni-countdown.m") + }, + secondText(val) { + return t("uni-countdown.s") + }, + timeStyle() { + const { + color, + backgroundColor, + fontSize + } = this + return { + color, + backgroundColor, + fontSize: `${fontSize}px`, + width: `${fontSize * 22 / 14}px`, // 按字体大小为 14px 时的比例缩放 + lineHeight: `${fontSize * 20 / 14}px`, + borderRadius: `${fontSize * 3 / 14}px`, + } + }, + splitorStyle() { + const { splitorColor, fontSize, backgroundColor } = this + return { + color: splitorColor, + fontSize: `${fontSize * 12 / 14}px`, + margin: backgroundColor ? `${fontSize * 4 / 14}px` : '' + } + } + }, + watch: { + day(val) { + this.changeFlag() + }, + hour(val) { + this.changeFlag() + }, + minute(val) { + this.changeFlag() + }, + second(val) { + this.changeFlag() + }, + start: { + immediate: true, + handler(newVal, oldVal) { + if (newVal) { + this.startData(); + } else { + if (!oldVal) return + clearInterval(this.timer) + } + } + + } + }, + created: function(e) { + this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second) + this.countDown() + }, + // #ifndef VUE3 + destroyed() { + clearInterval(this.timer) + }, + // #endif + // #ifdef VUE3 + unmounted() { + clearInterval(this.timer) + }, + // #endif + methods: { + toSeconds(timestamp, day, hours, minutes, seconds) { + if (timestamp) { + return timestamp - parseInt(new Date().getTime() / 1000, 10) + } + return day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds + }, + timeUp() { + clearInterval(this.timer) + this.$emit('timeup') + }, + countDown() { + let seconds = this.seconds + let [day, hour, minute, second] = [0, 0, 0, 0] + if (seconds > 0) { + day = Math.floor(seconds / (60 * 60 * 24)) + hour = Math.floor(seconds / (60 * 60)) - (day * 24) + minute = Math.floor(seconds / 60) - (day * 24 * 60) - (hour * 60) + second = Math.floor(seconds) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60) + } else { + this.timeUp() + } + this.d = String(day).padStart(this.validFilterShow(this.filterShow.d), '0') + this.h = String(hour).padStart(this.validFilterShow(this.filterShow.h), '0') + this.i = String(minute).padStart(this.validFilterShow(this.filterShow.m), '0') + this.s = String(second).padStart(this.validFilterShow(this.filterShow.s), '0') + }, + validFilterShow(filter){ + return (filter && filter > 0) ? filter : 2; + }, + startData() { + this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second) + if (this.seconds <= 0) { + this.seconds = this.toSeconds(0, 0, 0, 0, 0) + this.countDown() + return + } + clearInterval(this.timer) + this.countDown() + this.timer = setInterval(() => { + this.seconds-- + if (this.seconds < 0) { + this.timeUp() + return + } + this.countDown() + }, 1000) + }, + update(){ + this.startData(); + }, + changeFlag() { + if (!this.syncFlag) { + this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second) + this.startData(); + this.syncFlag = true; + } + } + } + } +</script> +<style lang="scss" scoped> + $font-size: 14px; + + .uni-countdown { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + + &__splitor { + margin: 0 2px; + font-size: $font-size; + color: #333; + } + + &__number { + border-radius: 3px; + text-align: center; + font-size: $font-size; + } + } +</style> diff --git a/uni_modules/uni-countdown/package.json b/uni_modules/uni-countdown/package.json new file mode 100644 index 0000000..9ec970b --- /dev/null +++ b/uni_modules/uni-countdown/package.json @@ -0,0 +1,84 @@ +{ + "id": "uni-countdown", + "displayName": "uni-countdown 倒计时", + "version": "1.2.4", + "description": "CountDown 倒计时组件", + "keywords": [ + "uni-ui", + "uniui", + "countdown", + "倒计时" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-countdown/readme.md b/uni_modules/uni-countdown/readme.md new file mode 100644 index 0000000..4bcb1aa --- /dev/null +++ b/uni_modules/uni-countdown/readme.md @@ -0,0 +1,10 @@ + + +## CountDown 倒计时 +> **组件名:uni-countdown** +> 代码块: `uCountDown` + +倒计时组件。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-countdown) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-data-checkbox/changelog.md b/uni_modules/uni-data-checkbox/changelog.md new file mode 100644 index 0000000..b475fcb --- /dev/null +++ b/uni_modules/uni-data-checkbox/changelog.md @@ -0,0 +1,49 @@ +## 1.0.5(2024-03-20) +- 修复 单选模式下选中样式不生效的bug +## 1.0.4(2024-01-27) +- 修复 修复错别字chagne为change +## 1.0.3(2022-09-16) +- 可以使用 uni-scss 控制主题色 +## 1.0.2(2022-06-30) +- 优化 在 uni-forms 中的依赖注入方式 +## 1.0.1(2022-02-07) +- 修复 multiple 为 true 时,v-model 的值为 null 报错的 bug +## 1.0.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-checkbox](https://uniapp.dcloud.io/component/uniui/uni-data-checkbox) +## 0.2.5(2021-08-23) +- 修复 在uni-forms中 modelValue 中不存在当前字段,当前字段必填写也不参与校验的问题 +## 0.2.4(2021-08-17) +- 修复 单选 list 模式下 ,icon 为 left 时,选中图标不显示的问题 +## 0.2.3(2021-08-11) +- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题 +## 0.2.2(2021-07-30) +- 优化 在uni-forms组件,与label不对齐的问题 +## 0.2.1(2021-07-27) +- 修复 单选默认值为0不能选中的Bug +## 0.2.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 0.1.11(2021-07-06) +- 优化 删除无用日志 +## 0.1.10(2021-07-05) +- 修复 由 0.1.9 引起的非 nvue 端图标不显示的问题 +## 0.1.9(2021-07-05) +- 修复 nvue 黑框样式问题 +## 0.1.8(2021-06-28) +- 修复 selectedTextColor 属性不生效的Bug +## 0.1.7(2021-06-02) +- 新增 map 属性,可以方便映射text/value属性 +## 0.1.6(2021-05-26) +- 修复 不关联服务空间的情况下组件报错的Bug +## 0.1.5(2021-05-12) +- 新增 组件示例地址 +## 0.1.4(2021-04-09) +- 修复 nvue 下无法选中的问题 +## 0.1.3(2021-03-22) +- 新增 disabled属性 +## 0.1.2(2021-02-24) +- 优化 默认颜色显示 +## 0.1.1(2021-02-24) +- 新增 支持nvue +## 0.1.0(2021-02-18) +- “暂无数据”显示居中 diff --git a/uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue b/uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue new file mode 100644 index 0000000..81d3f07 --- /dev/null +++ b/uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue @@ -0,0 +1,849 @@ +<template> + <view class="uni-data-checklist" :style="{'margin-top':isTop+'px'}"> + <template v-if="!isLocal"> + <view class="uni-data-loading"> + <uni-load-more v-if="!mixinDatacomErrorMessage" status="loading" iconType="snow" :iconSize="18" + :content-text="contentText"></uni-load-more> + <text v-else>{{mixinDatacomErrorMessage}}</text> + </view> + </template> + <template v-else> + <checkbox-group v-if="multiple" class="checklist-group" :class="{'is-list':mode==='list' || wrap}" + @change="change"> + <label class="checklist-box" + :class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']" + :style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index"> + <checkbox class="hidden" hidden :disabled="disabled || !!item.disabled" :value="item[map.value]+''" + :checked="item.selected" /> + <view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" + class="checkbox__inner" :style="item.styleIcon"> + <view class="checkbox__inner-icon"></view> + </view> + <view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}"> + <text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text> + <view v-if="mode === 'list' && icon === 'right'" class="checkobx__list" :style="item.styleBackgroud"></view> + </view> + </label> + </checkbox-group> + <radio-group v-else class="checklist-group" :class="{'is-list':mode==='list','is-wrap':wrap}" @change="change"> + <label class="checklist-box" + :class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']" + :style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index"> + <radio class="hidden" hidden :disabled="disabled || item.disabled" :value="item[map.value]+''" + :checked="item.selected" /> + <view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" class="radio__inner" + :style="item.styleBackgroud"> + <view class="radio__inner-icon" :style="item.styleIcon"></view> + </view> + <view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}"> + <text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text> + <view v-if="mode === 'list' && icon === 'right'" :style="item.styleRightIcon" class="checkobx__list"></view> + </view> + </label> + </radio-group> + </template> + </view> +</template> + +<script> + /** + * DataChecklist 数据选择器 + * @description 通过数据渲染 checkbox 和 radio + * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx + * @property {String} mode = [default| list | button | tag] 显示模式 + * @value default 默认横排模式 + * @value list 列表模式 + * @value button 按钮模式 + * @value tag 标签模式 + * @property {Boolean} multiple = [true|false] 是否多选 + * @property {Array|String|Number} value 默认值 + * @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}] + * @property {Number|String} min 最小选择个数 ,multiple为true时生效 + * @property {Number|String} max 最大选择个数 ,multiple为true时生效 + * @property {Boolean} wrap 是否换行显示 + * @property {String} icon = [left|right] list 列表模式下icon显示位置 + * @property {Boolean} selectedColor 选中颜色 + * @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效 + * @property {Boolean} selectedTextColor 选中文本颜色,如不填写则自动显示 + * @property {Object} map 字段映射, 默认 map={text:'text',value:'value'} + * @value left 左侧显示 + * @value right 右侧显示 + * @event {Function} change 选中发生变化触发 + */ + + export default { + name: 'uniDataChecklist', + mixins: [uniCloud.mixinDatacom || {}], + emits: ['input', 'update:modelValue', 'change'], + props: { + mode: { + type: String, + default: 'default' + }, + + multiple: { + type: Boolean, + default: false + }, + value: { + type: [Array, String, Number], + default () { + return '' + } + }, + // TODO vue3 + modelValue: { + type: [Array, String, Number], + default () { + return ''; + } + }, + localdata: { + type: Array, + default () { + return [] + } + }, + min: { + type: [Number, String], + default: '' + }, + max: { + type: [Number, String], + default: '' + }, + wrap: { + type: Boolean, + default: false + }, + icon: { + type: String, + default: 'left' + }, + selectedColor: { + type: String, + default: '' + }, + selectedTextColor: { + type: String, + default: '' + }, + emptyText: { + type: String, + default: '暂无数据' + }, + disabled: { + type: Boolean, + default: false + }, + map: { + type: Object, + default () { + return { + text: 'text', + value: 'value' + } + } + } + }, + watch: { + localdata: { + handler(newVal) { + this.range = newVal + this.dataList = this.getDataList(this.getSelectedValue(newVal)) + }, + deep: true + }, + mixinDatacomResData(newVal) { + this.range = newVal + this.dataList = this.getDataList(this.getSelectedValue(newVal)) + }, + value(newVal) { + this.dataList = this.getDataList(newVal) + // fix by mehaotian is_reset 在 uni-forms 中定义 + // if(!this.is_reset){ + // this.is_reset = false + // this.formItem && this.formItem.setValue(newVal) + // } + }, + modelValue(newVal) { + this.dataList = this.getDataList(newVal); + // if(!this.is_reset){ + // this.is_reset = false + // this.formItem && this.formItem.setValue(newVal) + // } + } + }, + data() { + return { + dataList: [], + range: [], + contentText: { + contentdown: '查看更多', + contentrefresh: '加载中', + contentnomore: '没有更多' + }, + isLocal: true, + styles: { + selectedColor: '#2979ff', + selectedTextColor: '#666', + }, + isTop: 0 + }; + }, + computed: { + dataValue() { + if (this.value === '') return this.modelValue + if (this.modelValue === '') return this.value + return this.value + } + }, + created() { + // this.form = this.getForm('uniForms') + // this.formItem = this.getForm('uniFormsItem') + // this.formItem && this.formItem.setValue(this.value) + + // if (this.formItem) { + // this.isTop = 6 + // if (this.formItem.name) { + // // 如果存在name添加默认值,否则formData 中不存在这个字段不校验 + // if(!this.is_reset){ + // this.is_reset = false + // this.formItem.setValue(this.dataValue) + // } + // this.rename = this.formItem.name + // this.form.inputChildrens.push(this) + // } + // } + + if (this.localdata && this.localdata.length !== 0) { + this.isLocal = true + this.range = this.localdata + this.dataList = this.getDataList(this.getSelectedValue(this.range)) + } else { + if (this.collection) { + this.isLocal = false + this.loadData() + } + } + }, + methods: { + loadData() { + this.mixinDatacomGet().then(res => { + this.mixinDatacomResData = res.result.data + if (this.mixinDatacomResData.length === 0) { + this.isLocal = false + this.mixinDatacomErrorMessage = this.emptyText + } else { + this.isLocal = true + } + }).catch(err => { + this.mixinDatacomErrorMessage = err.message + }) + }, + /** + * 获取父元素实例 + */ + getForm(name = 'uniForms') { + 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; + }, + change(e) { + const values = e.detail.value + + let detail = { + value: [], + data: [] + } + + if (this.multiple) { + this.range.forEach(item => { + + if (values.includes(item[this.map.value] + '')) { + detail.value.push(item[this.map.value]) + detail.data.push(item) + } + }) + } else { + const range = this.range.find(item => (item[this.map.value] + '') === values) + if (range) { + detail = { + value: range[this.map.value], + data: range + } + } + } + // this.formItem && this.formItem.setValue(detail.value) + // TODO 兼容 vue2 + this.$emit('input', detail.value); + // // TOTO 兼容 vue3 + this.$emit('update:modelValue', detail.value); + this.$emit('change', { + detail + }) + if (this.multiple) { + // 如果 v-model 没有绑定 ,则走内部逻辑 + // if (this.value.length === 0) { + this.dataList = this.getDataList(detail.value, true) + // } + } else { + this.dataList = this.getDataList(detail.value) + } + }, + + /** + * 获取渲染的新数组 + * @param {Object} value 选中内容 + */ + getDataList(value) { + // 解除引用关系,破坏原引用关系,避免污染源数据 + let dataList = JSON.parse(JSON.stringify(this.range)) + let list = [] + if (this.multiple) { + if (!Array.isArray(value)) { + value = [] + } + } + dataList.forEach((item, index) => { + item.disabled = item.disable || item.disabled || false + if (this.multiple) { + if (value.length > 0) { + let have = value.find(val => val === item[this.map.value]) + item.selected = have !== undefined + } else { + item.selected = false + } + } else { + item.selected = value === item[this.map.value] + } + + list.push(item) + }) + return this.setRange(list) + }, + /** + * 处理最大最小值 + * @param {Object} list + */ + setRange(list) { + let selectList = list.filter(item => item.selected) + let min = Number(this.min) || 0 + let max = Number(this.max) || '' + list.forEach((item, index) => { + if (this.multiple) { + if (selectList.length <= min) { + let have = selectList.find(val => val[this.map.value] === item[this.map.value]) + if (have !== undefined) { + item.disabled = true + } + } + + if (selectList.length >= max && max !== '') { + let have = selectList.find(val => val[this.map.value] === item[this.map.value]) + if (have === undefined) { + item.disabled = true + } + } + } + this.setStyles(item, index) + list[index] = item + }) + return list + }, + /** + * 设置 class + * @param {Object} item + * @param {Object} index + */ + setStyles(item, index) { + // 设置自定义样式 + item.styleBackgroud = this.setStyleBackgroud(item) + item.styleIcon = this.setStyleIcon(item) + item.styleIconText = this.setStyleIconText(item) + item.styleRightIcon = this.setStyleRightIcon(item) + }, + + /** + * 获取选中值 + * @param {Object} range + */ + getSelectedValue(range) { + if (!this.multiple) return this.dataValue + let selectedArr = [] + range.forEach((item) => { + if (item.selected) { + selectedArr.push(item[this.map.value]) + } + }) + return this.dataValue.length > 0 ? this.dataValue : selectedArr + }, + + /** + * 设置背景样式 + */ + setStyleBackgroud(item) { + let styles = {} + let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff' + if (this.selectedColor) { + if (this.mode !== 'list') { + styles['border-color'] = item.selected ? selectedColor : '#DCDFE6' + } + if (this.mode === 'tag') { + styles['background-color'] = item.selected ? selectedColor : '#f5f5f5' + } + } + let classles = '' + for (let i in styles) { + classles += `${i}:${styles[i]};` + } + return classles + }, + setStyleIcon(item) { + let styles = {} + let classles = '' + if (this.selectedColor) { + let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff' + styles['background-color'] = item.selected ? selectedColor : '#fff' + styles['border-color'] = item.selected ? selectedColor : '#DCDFE6' + + if (!item.selected && item.disabled) { + styles['background-color'] = '#F2F6FC' + styles['border-color'] = item.selected ? selectedColor : '#DCDFE6' + } + } + for (let i in styles) { + classles += `${i}:${styles[i]};` + } + return classles + }, + setStyleIconText(item) { + let styles = {} + let classles = '' + if (this.selectedColor) { + let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff' + if (this.mode === 'tag') { + styles.color = item.selected ? (this.selectedTextColor ? this.selectedTextColor : '#fff') : '#666' + } else { + styles.color = item.selected ? (this.selectedTextColor ? this.selectedTextColor : selectedColor) : '#666' + } + if (!item.selected && item.disabled) { + styles.color = '#999' + } + } + for (let i in styles) { + classles += `${i}:${styles[i]};` + } + return classles + }, + setStyleRightIcon(item) { + let styles = {} + let classles = '' + if (this.mode === 'list') { + styles['border-color'] = item.selected ? this.styles.selectedColor : '#DCDFE6' + } + for (let i in styles) { + classles += `${i}:${styles[i]};` + } + + return classles + } + } + } +</script> + +<style lang="scss"> + $uni-primary: #2979ff !default; + $border-color: #DCDFE6; + $disable: 0.4; + + @mixin flex { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + } + + .uni-data-loading { + @include flex; + flex-direction: row; + justify-content: center; + align-items: center; + height: 36px; + padding-left: 10px; + color: #999; + } + + .uni-data-checklist { + position: relative; + z-index: 0; + flex: 1; + + // 多选样式 + .checklist-group { + @include flex; + flex-direction: row; + flex-wrap: wrap; + + &.is-list { + flex-direction: column; + } + + .checklist-box { + @include flex; + flex-direction: row; + align-items: center; + position: relative; + margin: 5px 0; + margin-right: 25px; + + .hidden { + position: absolute; + opacity: 0; + } + + // 文字样式 + .checklist-content { + @include flex; + flex: 1; + flex-direction: row; + align-items: center; + justify-content: space-between; + + .checklist-text { + font-size: 14px; + color: #666; + margin-left: 5px; + line-height: 14px; + } + + .checkobx__list { + border-right-width: 1px; + border-right-color: #007aff; + border-right-style: solid; + border-bottom-width: 1px; + border-bottom-color: #007aff; + border-bottom-style: solid; + height: 12px; + width: 6px; + left: -5px; + transform-origin: center; + transform: rotate(45deg); + opacity: 0; + } + } + + // 多选样式 + .checkbox__inner { + /* #ifndef APP-NVUE */ + flex-shrink: 0; + box-sizing: border-box; + /* #endif */ + position: relative; + width: 16px; + height: 16px; + border: 1px solid $border-color; + border-radius: 4px; + background-color: #fff; + z-index: 1; + + .checkbox__inner-icon { + position: absolute; + /* #ifdef APP-NVUE */ + top: 2px; + /* #endif */ + /* #ifndef APP-NVUE */ + top: 1px; + /* #endif */ + left: 5px; + height: 8px; + width: 4px; + border-right-width: 1px; + border-right-color: #fff; + border-right-style: solid; + border-bottom-width: 1px; + border-bottom-color: #fff; + border-bottom-style: solid; + opacity: 0; + transform-origin: center; + transform: rotate(40deg); + } + } + + // 单选样式 + .radio__inner { + @include flex; + /* #ifndef APP-NVUE */ + flex-shrink: 0; + box-sizing: border-box; + /* #endif */ + justify-content: center; + align-items: center; + position: relative; + width: 16px; + height: 16px; + border: 1px solid $border-color; + border-radius: 16px; + background-color: #fff; + z-index: 1; + + .radio__inner-icon { + width: 8px; + height: 8px; + border-radius: 10px; + opacity: 0; + } + } + + // 默认样式 + &.is--default { + + // 禁用 + &.is-disable { + /* #ifdef H5 */ + cursor: not-allowed; + + /* #endif */ + .checkbox__inner { + background-color: #F2F6FC; + border-color: $border-color; + /* #ifdef H5 */ + cursor: not-allowed; + /* #endif */ + } + + .radio__inner { + background-color: #F2F6FC; + border-color: $border-color; + } + + .checklist-text { + color: #999; + } + } + + // 选中 + &.is-checked { + .checkbox__inner { + border-color: $uni-primary; + background-color: $uni-primary; + + .checkbox__inner-icon { + opacity: 1; + transform: rotate(45deg); + } + } + + .radio__inner { + border-color: $uni-primary; + + .radio__inner-icon { + opacity: 1; + background-color: $uni-primary; + } + } + + .checklist-text { + color: $uni-primary; + } + + // 选中禁用 + &.is-disable { + .checkbox__inner { + opacity: $disable; + } + + .checklist-text { + opacity: $disable; + } + + .radio__inner { + opacity: $disable; + } + } + } + } + + // 按钮样式 + &.is--button { + margin-right: 10px; + padding: 5px 10px; + border: 1px $border-color solid; + border-radius: 3px; + transition: border-color 0.2s; + + // 禁用 + &.is-disable { + /* #ifdef H5 */ + cursor: not-allowed; + /* #endif */ + border: 1px #eee solid; + opacity: $disable; + + .checkbox__inner { + background-color: #F2F6FC; + border-color: $border-color; + /* #ifdef H5 */ + cursor: not-allowed; + /* #endif */ + } + + .radio__inner { + background-color: #F2F6FC; + border-color: $border-color; + /* #ifdef H5 */ + cursor: not-allowed; + /* #endif */ + } + + .checklist-text { + color: #999; + } + } + + &.is-checked { + border-color: $uni-primary; + + .checkbox__inner { + border-color: $uni-primary; + background-color: $uni-primary; + + .checkbox__inner-icon { + opacity: 1; + transform: rotate(45deg); + } + } + + .radio__inner { + border-color: $uni-primary; + + .radio__inner-icon { + opacity: 1; + background-color: $uni-primary; + } + } + + .checklist-text { + color: $uni-primary; + } + + // 选中禁用 + &.is-disable { + opacity: $disable; + } + } + } + + // 标签样式 + &.is--tag { + margin-right: 10px; + padding: 5px 10px; + border: 1px $border-color solid; + border-radius: 3px; + background-color: #f5f5f5; + + .checklist-text { + margin: 0; + color: #666; + } + + // 禁用 + &.is-disable { + /* #ifdef H5 */ + cursor: not-allowed; + /* #endif */ + opacity: $disable; + } + + &.is-checked { + background-color: $uni-primary; + border-color: $uni-primary; + + .checklist-text { + color: #fff; + } + } + } + + // 列表样式 + &.is--list { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + padding: 10px 15px; + padding-left: 0; + margin: 0; + + &.is-list-border { + border-top: 1px #eee solid; + } + + // 禁用 + &.is-disable { + /* #ifdef H5 */ + cursor: not-allowed; + + /* #endif */ + .checkbox__inner { + background-color: #F2F6FC; + border-color: $border-color; + /* #ifdef H5 */ + cursor: not-allowed; + /* #endif */ + } + + .checklist-text { + color: #999; + } + } + + &.is-checked { + .checkbox__inner { + border-color: $uni-primary; + background-color: $uni-primary; + + .checkbox__inner-icon { + opacity: 1; + transform: rotate(45deg); + } + } + + .radio__inner { + border-color: $uni-primary; + .radio__inner-icon { + opacity: 1; + background-color: $uni-primary; + } + } + + .checklist-text { + color: $uni-primary; + } + + .checklist-content { + .checkobx__list { + opacity: 1; + border-color: $uni-primary; + } + } + + // 选中禁用 + &.is-disable { + .checkbox__inner { + opacity: $disable; + } + + .checklist-text { + opacity: $disable; + } + } + } + } + } + } + } +</style> diff --git a/uni_modules/uni-data-checkbox/package.json b/uni_modules/uni-data-checkbox/package.json new file mode 100644 index 0000000..fc15e8b --- /dev/null +++ b/uni_modules/uni-data-checkbox/package.json @@ -0,0 +1,84 @@ +{ + "id": "uni-data-checkbox", + "displayName": "uni-data-checkbox 数据选择器", + "version": "1.0.5", + "description": "通过数据驱动的单选框和复选框", + "keywords": [ + "uni-ui", + "checkbox", + "单选", + "多选", + "单选多选" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "^3.1.1" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-load-more","uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-data-checkbox/readme.md b/uni_modules/uni-data-checkbox/readme.md new file mode 100644 index 0000000..6eb253d --- /dev/null +++ b/uni_modules/uni-data-checkbox/readme.md @@ -0,0 +1,18 @@ + + +## DataCheckbox 数据驱动的单选复选框 +> **组件名:uni-data-checkbox** +> 代码块: `uDataCheckbox` + + +本组件是基于uni-app基础组件checkbox的封装。本组件要解决问题包括: + +1. 数据绑定型组件:给本组件绑定一个data,会自动渲染一组候选内容。再以往,开发者需要编写不少代码实现类似功能 +2. 自动的表单校验:组件绑定了data,且符合[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)组件的表单校验规范,搭配使用会自动实现表单校验 +3. 本组件合并了单选多选 +4. 本组件有若干风格选择,如普通的单选多选框、并列button风格、tag风格。开发者可以快速选择需要的风格。但作为一个封装组件,样式代码虽然不用自己写了,却会牺牲一定的样式自定义性 + +在uniCloud开发中,`DB Schema`中配置了enum枚举等类型后,在web控制台的[自动生成表单](https://uniapp.dcloud.io/uniCloud/schema?id=autocode)功能中,会自动生成``uni-data-checkbox``组件并绑定好data + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-checkbox) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-data-picker/changelog.md b/uni_modules/uni-data-picker/changelog.md new file mode 100644 index 0000000..e095788 --- /dev/null +++ b/uni_modules/uni-data-picker/changelog.md @@ -0,0 +1,79 @@ +## 2.0.1(2024-08-22) +- 修复 uni-app-x v-model 没有更新传入值的 bug +## 2.0.0(2023-12-11) +- 新增 支持 uni-app-x +## 1.1.2(2023-04-11) +- 修复 更改 modelValue 报错的 bug +- 修复 v-for 未使用 key 值控制台 warning +## 1.1.1(2023-02-21) +- 修复代码合并时引发 value 属性为空时不渲染数据的问题 +## 1.1.0(2023-02-15) +- 修复 localdata 不支持动态更新的bug +## 1.0.9(2023-02-15) +- 修复 localdata 不支持动态更新的bug +## 1.0.8(2022-09-16) +- 可以使用 uni-scss 控制主题色 +## 1.0.7(2022-07-06) +- 优化 pc端图标位置不正确的问题 +## 1.0.6(2022-07-05) +- 优化 显示样式 +## 1.0.5(2022-07-04) +- 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug +## 1.0.4(2022-04-19) +- 修复 字节小程序 本地数据无法选择下一级的Bug +## 1.0.3(2022-02-25) +- 修复 nvue 不支持的 v-show 的 bug +## 1.0.2(2022-02-25) +- 修复 条件编译 nvue 不支持的 css 样式 +## 1.0.1(2021-11-23) +- 修复 由上个版本引发的map、v-model等属性不生效的bug +## 1.0.0(2021-11-19) +- 优化 组件 UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-picker](https://uniapp.dcloud.io/component/uniui/uni-data-picker) +## 0.4.9(2021-10-28) +- 修复 VUE2 v-model 概率无效的 bug +## 0.4.8(2021-10-27) +- 修复 v-model 概率无效的 bug +## 0.4.7(2021-10-25) +- 新增 属性 spaceInfo 服务空间配置 HBuilderX 3.2.11+ +- 修复 树型 uniCloud 数据类型为 int 时报错的 bug +## 0.4.6(2021-10-19) +- 修复 非 VUE3 v-model 为 0 时无法选中的 bug +## 0.4.5(2021-09-26) +- 新增 清除已选项的功能(通过 clearIcon 属性配置是否显示按钮),同时提供 clear 方法以供调用,二者等效 +- 修复 readonly 为 true 时报错的 bug +## 0.4.4(2021-09-26) +- 修复 上一版本造成的 map 属性失效的 bug +- 新增 ellipsis 属性,支持配置 tab 选项长度过长时是否自动省略 +## 0.4.3(2021-09-24) +- 修复 某些情况下级联未触发的 bug +## 0.4.2(2021-09-23) +- 新增 提供 show 和 hide 方法,开发者可以通过 ref 调用 +- 新增 选项内容过长自动添加省略号 +## 0.4.1(2021-09-15) +- 新增 map 属性 字段映射,将 text/value 映射到数据中的其他字段 +## 0.4.0(2021-07-13) +- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 0.3.5(2021-06-04) +- 修复 无法加载云端数据的问题 +## 0.3.4(2021-05-28) +- 修复 v-model 无效问题 +- 修复 loaddata 为空数据组时加载时间过长问题 +- 修复 上个版本引出的本地数据无法选择带有 children 的 2 级节点 +## 0.3.3(2021-05-12) +- 新增 组件示例地址 +## 0.3.2(2021-04-22) +- 修复 非树形数据有 where 属性查询报错的问题 +## 0.3.1(2021-04-15) +- 修复 本地数据概率无法回显时问题 +## 0.3.0(2021-04-07) +- 新增 支持云端非树形表结构数据 +- 修复 根节点 parent_field 字段等于 null 时选择界面错乱问题 +## 0.2.0(2021-03-15) +- 修复 nodeclick、popupopened、popupclosed 事件无法触发的问题 +## 0.1.9(2021-03-09) +- 修复 微信小程序某些情况下无法选择的问题 +## 0.1.8(2021-02-05) +- 优化 部分样式在 nvue 上的兼容表现 +## 0.1.7(2021-02-05) +- 调整为 uni_modules 目录规范 diff --git a/uni_modules/uni-data-picker/components/uni-data-picker/keypress.js b/uni_modules/uni-data-picker/components/uni-data-picker/keypress.js new file mode 100644 index 0000000..6ef26a2 --- /dev/null +++ b/uni_modules/uni-data-picker/components/uni-data-picker/keypress.js @@ -0,0 +1,45 @@ +// #ifdef H5 +export default { + name: 'Keypress', + props: { + disable: { + type: Boolean, + default: false + } + }, + mounted () { + const keyNames = { + esc: ['Esc', 'Escape'], + tab: 'Tab', + enter: 'Enter', + space: [' ', 'Spacebar'], + up: ['Up', 'ArrowUp'], + left: ['Left', 'ArrowLeft'], + right: ['Right', 'ArrowRight'], + down: ['Down', 'ArrowDown'], + delete: ['Backspace', 'Delete', 'Del'] + } + const listener = ($event) => { + if (this.disable) { + return + } + const keyName = Object.keys(keyNames).find(key => { + const keyName = $event.key + const value = keyNames[key] + return value === keyName || (Array.isArray(value) && value.includes(keyName)) + }) + if (keyName) { + // 避免和其他按键事件冲突 + setTimeout(() => { + this.$emit(keyName, {}) + }, 0) + } + } + document.addEventListener('keyup', listener) + this.$once('hook:beforeDestroy', () => { + document.removeEventListener('keyup', listener) + }) + }, + render: () => {} +} +// #endif diff --git a/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.uvue b/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.uvue new file mode 100644 index 0000000..dcbfd5f --- /dev/null +++ b/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.uvue @@ -0,0 +1,381 @@ +<template> + <view class="uni-data-tree"> + <view class="uni-data-tree-input" @click="handleInput"> + <slot :data="selectedPaths" :error="error"> + <view class="input-value" :class="{'input-value-border': border}"> + <text v-if="error!=null" class="error-text">{{error!.errMsg}}</text> + <scroll-view v-if="selectedPaths.length" class="selected-path" scroll-x="true"> + <view class="selected-list"> + <template v-for="(item, index) in selectedPaths"> + <text class="text-color">{{item[mappingTextName]}}</text> + <text v-if="index<selectedPaths.length-1" class="input-split-line">{{split}}</text> + </template> + </view> + </scroll-view> + <text v-else-if="error==null&&!loading" class="placeholder">{{placeholder}}</text> + <view v-if="!readonly" class="arrow-area"> + <view class="input-arrow"></view> + </view> + </view> + </slot> + <view v-if="loading && !isOpened" class="selected-loading"> + <slot name="picker-loading" :loading="loading"></slot> + </view> + </view> + <view class="uni-data-tree-cover" v-if="isOpened" @click="handleClose"></view> + <view class="uni-data-tree-dialog" v-if="isOpened"> + <view class="uni-popper__arrow"></view> + <view class="dialog-caption"> + <view class="dialog-title-view"> + <text class="dialog-title">{{popupTitle}}</text> + </view> + <view class="dialog-close" @click="handleClose"> + <view class="dialog-close-plus" data-id="close"></view> + <view class="dialog-close-plus dialog-close-rotate" data-id="close"></view> + </view> + </view> + <view ref="pickerView" class="uni-data-pickerview"> + <view v-if="error!=null" class="error"> + <text class="error-text">{{error!.errMsg}}</text> + </view> + <scroll-view v-if="!isCloudDataList" :scroll-x="true"> + <view class="selected-node-list"> + <template v-for="(item, index) in selectedNodes"> + <text class="selected-node-item" :class="{'selected-node-item-active':index==selectedIndex}" + @click="onTabSelect(index)"> + {{item[mappingTextName]}} + </text> + </template> + </view> + </scroll-view> + <list-view class="list-view" :scroll-y="true"> + <list-item class="list-item" v-for="(item, _) in currentDataList" @click="onNodeClick(item)"> + <text class="item-text" :class="{'item-text-disabled': item['disable']}">{{item[mappingTextName]}}</text> + <text class="check" v-if="item[mappingValueName] == selectedNodes[selectedIndex][mappingValueName]"></text> + </list-item> + </list-view> + <view class="loading-cover" v-if="loading"> + <slot name="pickerview-loading" :loading="loading"></slot> + </view> + </view> + </view> + </view> +</template> + +<script> + import { dataPicker } from "../uni-data-pickerview/uni-data-picker.uts" + + /** + * DataPicker 级联选择 + * @description 支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。 + * @tutorial https://ext.dcloud.net.cn/plugin?id=3796 + * @property {String} popup-title 弹出窗口标题 + * @property {Array} localdata 本地数据,参考 + * @property {Boolean} border = [true|false] 是否有边框 + * @property {Boolean} readonly = [true|false] 是否仅读 + * @property {Boolean} preload = [true|false] 是否预加载数据 + * @value true 开启预加载数据,点击弹出窗口后显示已加载数据 + * @value false 关闭预加载数据,点击弹出窗口后开始加载数据 + * @property {Boolean} step-searh = [true|false] 是否分布查询 + * @value true 启用分布查询,仅查询当前选中节点 + * @value false 关闭分布查询,一次查询出所有数据 + * @property {String|DBFieldString} self-field 分布查询当前字段名称 + * @property {String|DBFieldString} parent-field 分布查询父字段名称 + * @property {String|DBCollectionString} collection 表名 + * @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割 + * @property {String} orderby 排序字段及正序倒叙设置 + * @property {String|JQLString} where 查询条件 + * @event {Function} popupshow 弹出的选择窗口打开时触发此事件 + * @event {Function} popuphide 弹出的选择窗口关闭时触发此事件 + */ + export default { + name: 'UniDataPicker', + emits: ['popupopened', 'popupclosed', 'nodeclick', 'change', 'input', 'update:modelValue', 'inputclick'], + mixins: [dataPicker], + props: { + popupTitle: { + type: String, + default: '请选择' + }, + placeholder: { + type: String, + default: '请选择' + }, + heightMobile: { + type: String, + default: '' + }, + readonly: { + type: Boolean, + default: false + }, + clearIcon: { + type: Boolean, + default: true + }, + border: { + type: Boolean, + default: true + }, + split: { + type: String, + default: '/' + }, + ellipsis: { + type: Boolean, + default: true + } + }, + data() { + return { + isOpened: false + } + }, + computed: { + isShowClearIcon() : boolean { + if (this.readonly) { + return false + } + + if (this.clearIcon && this.selectedPaths.length > 0) { + return true + } + + return false + } + }, + created() { + this.load() + }, + methods: { + clear() { + }, + load() { + if (this.isLocalData) { + this.loadLocalData() + } else if (this.isCloudDataList || this.isCloudDataTree) { + this.loadCloudDataPath() + } + }, + show() { + this.isOpened = true + this.$emit('popupopened') + if (!this.hasCloudTreeData) { + this.loadData() + } + }, + hide() { + this.isOpened = false + this.$emit('popupclosed') + }, + handleInput() { + if (this.readonly) { + this.$emit('inputclick') + } else { + this.show() + } + }, + handleClose() { + this.hide() + }, + onFinish() { + this.selectedPaths = this.getChangeNodes() + this.$emit('update:modelValue', this.selectedPaths) + this.$emit('change', this.selectedPaths) + this.hide() + } + } + } +</script> + +<style> + @import url("../uni-data-pickerview/uni-data-pickerview.css"); + + .uni-data-tree { + position: relative; + } + + .uni-data-tree-input { + position: relative; + } + + .selected-loading { + display: flex; + justify-content: center; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + } + + .error-text { + flex: 1; + font-size: 12px; + color: #DD524D; + } + + .input-value { + flex-direction: row; + align-items: center; + flex-wrap: nowrap; + padding: 5px 5px; + padding-right: 5px; + overflow: hidden; + min-height: 28px; + } + + .input-value-border { + border: 1px solid #e5e5e5; + border-radius: 5px; + } + + .selected-path { + flex: 1; + flex-direction: row; + overflow: hidden; + } + + .load-more { + width: 40px; + } + + .selected-list { + flex-direction: row; + flex-wrap: nowrap; + } + + .selected-item { + flex-direction: row; + flex-wrap: nowrap; + } + + .text-color { + font-size: 14px; + color: #333; + } + + .placeholder { + color: grey; + font-size: 14px; + } + + .input-split-line { + opacity: .5; + margin-left: 1px; + margin-right: 1px; + } + + .arrow-area { + position: relative; + padding: 0 12px; + margin-left: auto; + justify-content: center; + transform: rotate(-45deg); + transform-origin: center; + } + + .input-arrow { + width: 8px; + height: 8px; + border-left: 2px solid #999; + border-bottom: 2px solid #999; + } + + .uni-data-tree-cover { + position: fixed; + left: 0; + top: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, .4); + flex-direction: column; + z-index: 100; + } + + .uni-data-tree-dialog { + position: fixed; + left: 0; + top: 20%; + right: 0; + bottom: 0; + background-color: #FFFFFF; + border-top-left-radius: 10px; + border-top-right-radius: 10px; + flex-direction: column; + z-index: 102; + overflow: hidden; + } + + .dialog-caption { + position: relative; + flex-direction: row; + } + + .dialog-title-view { + flex: 1; + } + + .dialog-title { + align-self: center; + padding: 0 10px; + line-height: 44px; + } + + .dialog-close { + position: absolute; + top: 0; + right: 0; + bottom: 0; + flex-direction: row; + align-items: center; + padding: 0 15px; + } + + .dialog-close-plus { + width: 16px; + height: 2px; + background-color: #666; + border-radius: 2px; + transform: rotate(45deg); + } + + .dialog-close-rotate { + position: absolute; + transform: rotate(-45deg); + } + + .uni-data-pickerview { + flex: 1; + } + + .icon-clear { + display: flex; + align-items: center; + } + + /* #ifdef H5 */ + @media all and (min-width: 768px) { + .uni-data-tree-cover { + background-color: transparent; + } + + .uni-data-tree-dialog { + position: absolute; + top: 55px; + height: auto; + min-height: 400px; + max-height: 50vh; + background-color: #fff; + border: 1px solid #EBEEF5; + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); + border-radius: 4px; + overflow: unset; + } + + .dialog-caption { + display: none; + } + } + /* #endif */ +</style> diff --git a/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue b/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue new file mode 100644 index 0000000..179a4e0 --- /dev/null +++ b/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue @@ -0,0 +1,551 @@ +<template> + <view class="uni-data-tree"> + <view class="uni-data-tree-input" @click="handleInput"> + <slot :options="options" :data="inputSelected" :error="errorMessage"> + <view class="input-value" :class="{'input-value-border': border}"> + <text v-if="errorMessage" class="selected-area error-text">{{errorMessage}}</text> + <view v-else-if="loading && !isOpened" class="selected-area"> + <uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more> + </view> + <scroll-view v-else-if="inputSelected.length" class="selected-area" scroll-x="true"> + <view class="selected-list"> + <view class="selected-item" v-for="(item,index) in inputSelected" :key="index"> + <text class="text-color">{{item.text}}</text><text v-if="index<inputSelected.length-1" + class="input-split-line">{{split}}</text> + </view> + </view> + </scroll-view> + <text v-else class="selected-area placeholder">{{placeholder}}</text> + <view v-if="clearIcon && !readonly && inputSelected.length" class="icon-clear" @click.stop="clear"> + <uni-icons type="clear" color="#c0c4cc" size="24"></uni-icons> + </view> + <view class="arrow-area" v-if="(!clearIcon || !inputSelected.length) && !readonly "> + <view class="input-arrow"></view> + </view> + </view> + </slot> + </view> + <view class="uni-data-tree-cover" v-if="isOpened" @click="handleClose"></view> + <view class="uni-data-tree-dialog" v-if="isOpened"> + <view class="uni-popper__arrow"></view> + <view class="dialog-caption"> + <view class="title-area"> + <text class="dialog-title">{{popupTitle}}</text> + </view> + <view class="dialog-close" @click="handleClose"> + <view class="dialog-close-plus" data-id="close"></view> + <view class="dialog-close-plus dialog-close-rotate" data-id="close"></view> + </view> + </view> + <data-picker-view class="picker-view" ref="pickerView" v-model="dataValue" :localdata="localdata" + :preload="preload" :collection="collection" :field="field" :orderby="orderby" :where="where" + :step-searh="stepSearh" :self-field="selfField" :parent-field="parentField" :managed-mode="true" :map="map" + :ellipsis="ellipsis" @change="onchange" @datachange="ondatachange" @nodeclick="onnodeclick"> + </data-picker-view> + </view> + </view> +</template> + +<script> + import dataPicker from "../uni-data-pickerview/uni-data-picker.js" + import DataPickerView from "../uni-data-pickerview/uni-data-pickerview.vue" + + /** + * DataPicker 级联选择 + * @description 支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。 + * @tutorial https://ext.dcloud.net.cn/plugin?id=3796 + * @property {String} popup-title 弹出窗口标题 + * @property {Array} localdata 本地数据,参考 + * @property {Boolean} border = [true|false] 是否有边框 + * @property {Boolean} readonly = [true|false] 是否仅读 + * @property {Boolean} preload = [true|false] 是否预加载数据 + * @value true 开启预加载数据,点击弹出窗口后显示已加载数据 + * @value false 关闭预加载数据,点击弹出窗口后开始加载数据 + * @property {Boolean} step-searh = [true|false] 是否分布查询 + * @value true 启用分布查询,仅查询当前选中节点 + * @value false 关闭分布查询,一次查询出所有数据 + * @property {String|DBFieldString} self-field 分布查询当前字段名称 + * @property {String|DBFieldString} parent-field 分布查询父字段名称 + * @property {String|DBCollectionString} collection 表名 + * @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割 + * @property {String} orderby 排序字段及正序倒叙设置 + * @property {String|JQLString} where 查询条件 + * @event {Function} popupshow 弹出的选择窗口打开时触发此事件 + * @event {Function} popuphide 弹出的选择窗口关闭时触发此事件 + */ + export default { + name: 'UniDataPicker', + emits: ['popupopened', 'popupclosed', 'nodeclick', 'input', 'change', 'update:modelValue','inputclick'], + mixins: [dataPicker], + components: { + DataPickerView + }, + props: { + options: { + type: [Object, Array], + default () { + return {} + } + }, + popupTitle: { + type: String, + default: '请选择' + }, + placeholder: { + type: String, + default: '请选择' + }, + heightMobile: { + type: String, + default: '' + }, + readonly: { + type: Boolean, + default: false + }, + clearIcon: { + type: Boolean, + default: true + }, + border: { + type: Boolean, + default: true + }, + split: { + type: String, + default: '/' + }, + ellipsis: { + type: Boolean, + default: true + } + }, + data() { + return { + isOpened: false, + inputSelected: [] + } + }, + created() { + this.$nextTick(() => { + this.load(); + }) + }, + watch: { + localdata: { + handler() { + this.load() + }, + deep: true + }, + }, + methods: { + clear() { + this._dispatchEvent([]); + }, + onPropsChange() { + this._treeData = []; + this.selectedIndex = 0; + + this.load(); + }, + load() { + if (this.readonly) { + this._processReadonly(this.localdata, this.dataValue); + return; + } + + // 回显本地数据 + if (this.isLocalData) { + this.loadData(); + this.inputSelected = this.selected.slice(0); + } else if (this.isCloudDataList || this.isCloudDataTree) { // 回显 Cloud 数据 + this.loading = true; + this.getCloudDataValue().then((res) => { + this.loading = false; + this.inputSelected = res; + }).catch((err) => { + this.loading = false; + this.errorMessage = err; + }) + } + }, + show() { + this.isOpened = true + setTimeout(() => { + this.$refs.pickerView.updateData({ + treeData: this._treeData, + selected: this.selected, + selectedIndex: this.selectedIndex + }) + }, 200) + this.$emit('popupopened') + }, + hide() { + this.isOpened = false + this.$emit('popupclosed') + }, + handleInput() { + if (this.readonly) { + this.$emit('inputclick') + return + } + this.show() + }, + handleClose(e) { + this.hide() + }, + onnodeclick(e) { + this.$emit('nodeclick', e) + }, + ondatachange(e) { + this._treeData = this.$refs.pickerView._treeData + }, + onchange(e) { + this.hide() + this.$nextTick(() => { + this.inputSelected = e; + }) + this._dispatchEvent(e) + }, + _processReadonly(dataList, value) { + var isTree = dataList.findIndex((item) => { + return item.children + }) + if (isTree > -1) { + let inputValue + if (Array.isArray(value)) { + inputValue = value[value.length - 1] + if (typeof inputValue === 'object' && inputValue.value) { + inputValue = inputValue.value + } + } else { + inputValue = value + } + this.inputSelected = this._findNodePath(inputValue, this.localdata) + return + } + + if (!this.hasValue) { + this.inputSelected = [] + return + } + + let result = [] + for (let i = 0; i < value.length; i++) { + var val = value[i] + var item = dataList.find((v) => { + return v.value == val + }) + if (item) { + result.push(item) + } + } + if (result.length) { + this.inputSelected = result + } + }, + _filterForArray(data, valueArray) { + var result = [] + for (let i = 0; i < valueArray.length; i++) { + var value = valueArray[i] + var found = data.find((item) => { + return item.value == value + }) + if (found) { + result.push(found) + } + } + return result + }, + _dispatchEvent(selected) { + let item = {} + if (selected.length) { + var value = new Array(selected.length) + for (var i = 0; i < selected.length; i++) { + value[i] = selected[i].value + } + item = selected[selected.length - 1] + } else { + item.value = '' + } + if (this.formItem) { + this.formItem.setValue(item.value) + } + + this.$emit('input', item.value) + this.$emit('update:modelValue', item.value) + this.$emit('change', { + detail: { + value: selected + } + }) + } + } + } +</script> + +<style> + .uni-data-tree { + flex: 1; + position: relative; + font-size: 14px; + } + + .error-text { + color: #DD524D; + } + + .input-value { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + align-items: center; + flex-wrap: nowrap; + font-size: 14px; + /* line-height: 35px; */ + padding: 0 10px; + padding-right: 5px; + overflow: hidden; + height: 35px; + /* #ifndef APP-NVUE */ + box-sizing: border-box; + /* #endif */ + } + + .input-value-border { + border: 1px solid #e5e5e5; + border-radius: 5px; + } + + .selected-area { + flex: 1; + overflow: hidden; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + } + + .load-more { + /* #ifndef APP-NVUE */ + margin-right: auto; + /* #endif */ + /* #ifdef APP-NVUE */ + width: 40px; + /* #endif */ + } + + .selected-list { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + flex-wrap: nowrap; + /* padding: 0 5px; */ + } + + .selected-item { + flex-direction: row; + /* padding: 0 1px; */ + /* #ifndef APP-NVUE */ + white-space: nowrap; + /* #endif */ + } + + .text-color { + color: #333; + } + + .placeholder { + color: grey; + font-size: 12px; + } + + .input-split-line { + opacity: .5; + } + + .arrow-area { + position: relative; + width: 20px; + /* #ifndef APP-NVUE */ + margin-bottom: 5px; + margin-left: auto; + display: flex; + /* #endif */ + justify-content: center; + transform: rotate(-45deg); + transform-origin: center; + } + + .input-arrow { + width: 7px; + height: 7px; + border-left: 1px solid #999; + border-bottom: 1px solid #999; + } + + .uni-data-tree-cover { + position: fixed; + left: 0; + top: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, .4); + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + z-index: 100; + } + + .uni-data-tree-dialog { + position: fixed; + left: 0; + /* #ifndef APP-NVUE */ + top: 20%; + /* #endif */ + /* #ifdef APP-NVUE */ + top: 200px; + /* #endif */ + right: 0; + bottom: 0; + background-color: #FFFFFF; + border-top-left-radius: 10px; + border-top-right-radius: 10px; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + z-index: 102; + overflow: hidden; + /* #ifdef APP-NVUE */ + width: 750rpx; + /* #endif */ + } + + .dialog-caption { + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + /* border-bottom: 1px solid #f0f0f0; */ + } + + .title-area { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + align-items: center; + /* #ifndef APP-NVUE */ + margin: auto; + /* #endif */ + padding: 0 10px; + } + + .dialog-title { + /* font-weight: bold; */ + line-height: 44px; + } + + .dialog-close { + position: absolute; + top: 0; + right: 0; + bottom: 0; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + align-items: center; + padding: 0 15px; + } + + .dialog-close-plus { + width: 16px; + height: 2px; + background-color: #666; + border-radius: 2px; + transform: rotate(45deg); + } + + .dialog-close-rotate { + position: absolute; + transform: rotate(-45deg); + } + + .picker-view { + flex: 1; + overflow: hidden; + } + + .icon-clear { + display: flex; + align-items: center; + } + + /* #ifdef H5 */ + @media all and (min-width: 768px) { + .uni-data-tree-cover { + background-color: transparent; + } + + .uni-data-tree-dialog { + position: absolute; + top: 55px; + height: auto; + min-height: 400px; + max-height: 50vh; + background-color: #fff; + border: 1px solid #EBEEF5; + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); + border-radius: 4px; + overflow: unset; + } + + .dialog-caption { + display: none; + } + + .icon-clear { + /* margin-right: 5px; */ + } + } + + /* #endif */ + + /* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */ + /* #ifndef APP-NVUE */ + .uni-popper__arrow, + .uni-popper__arrow::after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + border-width: 6px; + } + + .uni-popper__arrow { + filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); + top: -6px; + left: 10%; + margin-right: 3px; + border-top-width: 0; + border-bottom-color: #EBEEF5; + } + + .uni-popper__arrow::after { + content: " "; + top: 1px; + margin-left: -6px; + border-top-width: 0; + border-bottom-color: #fff; + } + + /* #endif */ +</style> diff --git a/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js new file mode 100644 index 0000000..cfae22a --- /dev/null +++ b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js @@ -0,0 +1,622 @@ +export default { + props: { + localdata: { + type: [Array, Object], + default () { + return [] + } + }, + spaceInfo: { + type: Object, + default () { + return {} + } + }, + collection: { + type: String, + default: '' + }, + action: { + type: String, + default: '' + }, + field: { + type: String, + default: '' + }, + orderby: { + type: String, + default: '' + }, + where: { + type: [String, Object], + default: '' + }, + pageData: { + type: String, + default: 'add' + }, + pageCurrent: { + type: Number, + default: 1 + }, + pageSize: { + type: Number, + default: 500 + }, + getcount: { + type: [Boolean, String], + default: false + }, + getone: { + type: [Boolean, String], + default: false + }, + gettree: { + type: [Boolean, String], + default: false + }, + manual: { + type: Boolean, + default: false + }, + value: { + type: [Array, String, Number], + default () { + return [] + } + }, + modelValue: { + type: [Array, String, Number], + default () { + return [] + } + }, + preload: { + type: Boolean, + default: false + }, + stepSearh: { + type: Boolean, + default: true + }, + selfField: { + type: String, + default: '' + }, + parentField: { + type: String, + default: '' + }, + multiple: { + type: Boolean, + default: false + }, + map: { + type: Object, + default () { + return { + text: "text", + value: "value" + } + } + } + }, + data() { + return { + loading: false, + errorMessage: '', + loadMore: { + contentdown: '', + contentrefresh: '', + contentnomore: '' + }, + dataList: [], + selected: [], + selectedIndex: 0, + page: { + current: this.pageCurrent, + size: this.pageSize, + count: 0 + } + } + }, + computed: { + isLocalData() { + return !this.collection.length; + }, + isCloudData() { + return this.collection.length > 0; + }, + isCloudDataList() { + return (this.isCloudData && (!this.parentField && !this.selfField)); + }, + isCloudDataTree() { + return (this.isCloudData && this.parentField && this.selfField); + }, + dataValue() { + let isModelValue = Array.isArray(this.modelValue) ? (this.modelValue.length > 0) : (this.modelValue !== null || + this.modelValue !== undefined); + return isModelValue ? this.modelValue : this.value; + }, + hasValue() { + if (typeof this.dataValue === 'number') { + return true + } + return (this.dataValue != null) && (this.dataValue.length > 0) + } + }, + created() { + this.$watch(() => { + var al = []; + ['pageCurrent', + 'pageSize', + 'spaceInfo', + 'value', + 'modelValue', + 'localdata', + 'collection', + 'action', + 'field', + 'orderby', + 'where', + 'getont', + 'getcount', + 'gettree' + ].forEach(key => { + al.push(this[key]) + }); + return al + }, (newValue, oldValue) => { + let needReset = false + for (let i = 2; i < newValue.length; i++) { + if (newValue[i] != oldValue[i]) { + needReset = true + break + } + } + if (newValue[0] != oldValue[0]) { + this.page.current = this.pageCurrent + } + this.page.size = this.pageSize + + this.onPropsChange() + }) + this._treeData = [] + }, + methods: { + onPropsChange() { + this._treeData = []; + }, + + // 填充 pickview 数据 + async loadData() { + if (this.isLocalData) { + this.loadLocalData(); + } else if (this.isCloudDataList) { + this.loadCloudDataList(); + } else if (this.isCloudDataTree) { + this.loadCloudDataTree(); + } + }, + + // 加载本地数据 + async loadLocalData() { + this._treeData = []; + this._extractTree(this.localdata, this._treeData); + + let inputValue = this.dataValue; + if (inputValue === undefined) { + return; + } + + if (Array.isArray(inputValue)) { + inputValue = inputValue[inputValue.length - 1]; + if (typeof inputValue === 'object' && inputValue[this.map.value]) { + inputValue = inputValue[this.map.value]; + } + } + + this.selected = this._findNodePath(inputValue, this.localdata); + }, + + // 加载 Cloud 数据 (单列) + async loadCloudDataList() { + if (this.loading) { + return; + } + this.loading = true; + + try { + let response = await this.getCommand(); + let responseData = response.result.data; + + this._treeData = responseData; + + this._updateBindData(); + this._updateSelected(); + + this.onDataChange(); + } catch (e) { + this.errorMessage = e; + } finally { + this.loading = false; + } + }, + + // 加载 Cloud 数据 (树形) + async loadCloudDataTree() { + if (this.loading) { + return; + } + this.loading = true; + + try { + let commandOptions = { + field: this._cloudDataPostField(), + where: this._cloudDataTreeWhere() + }; + if (this.gettree) { + commandOptions.startwith = `${this.selfField}=='${this.dataValue}'`; + } + + let response = await this.getCommand(commandOptions); + let responseData = response.result.data; + + this._treeData = responseData; + this._updateBindData(); + this._updateSelected(); + + this.onDataChange(); + } catch (e) { + this.errorMessage = e; + } finally { + this.loading = false; + } + }, + + // 加载 Cloud 数据 (节点) + async loadCloudDataNode(callback) { + if (this.loading) { + return; + } + this.loading = true; + + try { + let commandOptions = { + field: this._cloudDataPostField(), + where: this._cloudDataNodeWhere() + }; + + let response = await this.getCommand(commandOptions); + let responseData = response.result.data; + + callback(responseData); + } catch (e) { + this.errorMessage = e; + } finally { + this.loading = false; + } + }, + + // 回显 Cloud 数据 + getCloudDataValue() { + if (this.isCloudDataList) { + return this.getCloudDataListValue(); + } + + if (this.isCloudDataTree) { + return this.getCloudDataTreeValue(); + } + }, + + // 回显 Cloud 数据 (单列) + getCloudDataListValue() { + // 根据 field's as value标识匹配 where 条件 + let where = []; + let whereField = this._getForeignKeyByField(); + if (whereField) { + where.push(`${whereField} == '${this.dataValue}'`) + } + + where = where.join(' || '); + + if (this.where) { + where = `(${this.where}) && (${where})` + } + + return this.getCommand({ + field: this._cloudDataPostField(), + where + }).then((res) => { + this.selected = res.result.data; + return res.result.data; + }); + }, + + // 回显 Cloud 数据 (树形) + getCloudDataTreeValue() { + return this.getCommand({ + field: this._cloudDataPostField(), + getTreePath: { + startWith: `${this.selfField}=='${this.dataValue}'` + } + }).then((res) => { + let treePath = []; + this._extractTreePath(res.result.data, treePath); + this.selected = treePath; + return treePath; + }); + }, + + getCommand(options = {}) { + /* eslint-disable no-undef */ + let db = uniCloud.database(this.spaceInfo) + + const action = options.action || this.action + if (action) { + db = db.action(action) + } + + const collection = options.collection || this.collection + db = db.collection(collection) + + const where = options.where || this.where + if (!(!where || !Object.keys(where).length)) { + db = db.where(where) + } + + const field = options.field || this.field + if (field) { + db = db.field(field) + } + + const orderby = options.orderby || this.orderby + if (orderby) { + db = db.orderBy(orderby) + } + + const current = options.pageCurrent !== undefined ? options.pageCurrent : this.page.current + const size = options.pageSize !== undefined ? options.pageSize : this.page.size + const getCount = options.getcount !== undefined ? options.getcount : this.getcount + const getTree = options.gettree !== undefined ? options.gettree : this.gettree + + const getOptions = { + getCount, + getTree + } + if (options.getTreePath) { + getOptions.getTreePath = options.getTreePath + } + + db = db.skip(size * (current - 1)).limit(size).get(getOptions) + + return db + }, + + _cloudDataPostField() { + let fields = [this.field]; + if (this.parentField) { + fields.push(`${this.parentField} as parent_value`); + } + return fields.join(','); + }, + + _cloudDataTreeWhere() { + let result = [] + let selected = this.selected + let parentField = this.parentField + if (parentField) { + result.push(`${parentField} == null || ${parentField} == ""`) + } + if (selected.length) { + for (var i = 0; i < selected.length - 1; i++) { + result.push(`${parentField} == '${selected[i].value}'`) + } + } + + let where = [] + if (this.where) { + where.push(`(${this.where})`) + } + + if (result.length) { + where.push(`(${result.join(' || ')})`) + } + + return where.join(' && ') + }, + + _cloudDataNodeWhere() { + let where = [] + let selected = this.selected; + if (selected.length) { + where.push(`${this.parentField} == '${selected[selected.length - 1].value}'`); + } + + where = where.join(' || '); + + if (this.where) { + return `(${this.where}) && (${where})` + } + + return where + }, + + _getWhereByForeignKey() { + let result = [] + let whereField = this._getForeignKeyByField(); + if (whereField) { + result.push(`${whereField} == '${this.dataValue}'`) + } + + if (this.where) { + return `(${this.where}) && (${result.join(' || ')})` + } + + return result.join(' || ') + }, + + _getForeignKeyByField() { + let fields = this.field.split(','); + let whereField = null; + for (let i = 0; i < fields.length; i++) { + const items = fields[i].split('as'); + if (items.length < 2) { + continue; + } + if (items[1].trim() === 'value') { + whereField = items[0].trim(); + break; + } + } + return whereField; + }, + + _updateBindData(node) { + const { + dataList, + hasNodes + } = this._filterData(this._treeData, this.selected) + + let isleaf = this._stepSearh === false && !hasNodes + + if (node) { + node.isleaf = isleaf + } + + this.dataList = dataList + this.selectedIndex = dataList.length - 1 + + if (!isleaf && this.selected.length < dataList.length) { + this.selected.push({ + value: null, + text: "请选择" + }) + } + + return { + isleaf, + hasNodes + } + }, + + _updateSelected() { + let dl = this.dataList + let sl = this.selected + let textField = this.map.text + let valueField = this.map.value + for (let i = 0; i < sl.length; i++) { + let value = sl[i].value + let dl2 = dl[i] + for (let j = 0; j < dl2.length; j++) { + let item2 = dl2[j] + if (item2[valueField] === value) { + sl[i].text = item2[textField] + break + } + } + } + }, + + _filterData(data, paths) { + let dataList = [] + let hasNodes = true + + dataList.push(data.filter((item) => { + return (item.parent_value === null || item.parent_value === undefined || item.parent_value === '') + })) + for (let i = 0; i < paths.length; i++) { + let value = paths[i].value + let nodes = data.filter((item) => { + return item.parent_value === value + }) + + if (nodes.length) { + dataList.push(nodes) + } else { + hasNodes = false + } + } + + return { + dataList, + hasNodes + } + }, + + _extractTree(nodes, result, parent_value) { + let list = result || [] + let valueField = this.map.value + for (let i = 0; i < nodes.length; i++) { + let node = nodes[i] + + let child = {} + for (let key in node) { + if (key !== 'children') { + child[key] = node[key] + } + } + if (parent_value !== null && parent_value !== undefined && parent_value !== '') { + child.parent_value = parent_value + } + result.push(child) + + let children = node.children + if (children) { + this._extractTree(children, result, node[valueField]) + } + } + }, + + _extractTreePath(nodes, result) { + let list = result || [] + for (let i = 0; i < nodes.length; i++) { + let node = nodes[i] + + let child = {} + for (let key in node) { + if (key !== 'children') { + child[key] = node[key] + } + } + result.push(child) + + let children = node.children + if (children) { + this._extractTreePath(children, result) + } + } + }, + + _findNodePath(key, nodes, path = []) { + let textField = this.map.text + let valueField = this.map.value + for (let i = 0; i < nodes.length; i++) { + let node = nodes[i] + let children = node.children + let text = node[textField] + let value = node[valueField] + + path.push({ + value, + text + }) + + if (value === key) { + return path + } + + if (children) { + const p = this._findNodePath(key, children, path) + if (p.length) { + return p + } + } + + path.pop() + } + return [] + } + } +} diff --git a/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.uts b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.uts new file mode 100644 index 0000000..857408d --- /dev/null +++ b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.uts @@ -0,0 +1,692 @@ +export type PaginationType = { + current : number, + size : number, + count : number +} + +export type LoadMoreType = { + contentdown : string, + contentrefresh : string, + contentnomore : string +} + +export type SelectedItemType = { + name : string, + value : string, +} + +export type GetCommandOptions = { + collection ?: UTSJSONObject, + field ?: string, + orderby ?: string, + where ?: any, + pageData ?: string, + pageCurrent ?: number, + pageSize ?: number, + getCount ?: boolean, + getTree ?: any, + getTreePath ?: UTSJSONObject, + startwith ?: string, + limitlevel ?: number, + groupby ?: string, + groupField ?: string, + distinct ?: boolean, + pageIndistinct ?: boolean, + foreignKey ?: string, + loadtime ?: string, + manual ?: boolean +} + +const DefaultSelectedNode = { + text: '请选择', + value: '' +} + +export const dataPicker = defineMixin({ + props: { + localdata: { + type: Array as PropType<Array<UTSJSONObject>>, + default: [] as Array<UTSJSONObject> + }, + collection: { + type: Object, + default: '' + }, + field: { + type: String, + default: '' + }, + orderby: { + type: String, + default: '' + }, + where: { + type: Object, + default: '' + }, + pageData: { + type: String, + default: 'add' + }, + pageCurrent: { + type: Number, + default: 1 + }, + pageSize: { + type: Number, + default: 20 + }, + getcount: { + type: Boolean, + default: false + }, + gettree: { + type: Object, + default: '' + }, + gettreepath: { + type: Object, + default: '' + }, + startwith: { + type: String, + default: '' + }, + limitlevel: { + type: Number, + default: 10 + }, + groupby: { + type: String, + default: '' + }, + groupField: { + type: String, + default: '' + }, + distinct: { + type: Boolean, + default: false + }, + pageIndistinct: { + type: Boolean, + default: false + }, + foreignKey: { + type: String, + default: '' + }, + loadtime: { + type: String, + default: 'auto' + }, + manual: { + type: Boolean, + default: false + }, + preload: { + type: Boolean, + default: false + }, + stepSearh: { + type: Boolean, + default: true + }, + selfField: { + type: String, + default: '' + }, + parentField: { + type: String, + default: '' + }, + multiple: { + type: Boolean, + default: false + }, + value: { + type: Object, + default: '' + }, + modelValue: { + type: Object, + default: '' + }, + defaultProps: { + type: Object as PropType<UTSJSONObject>, + } + }, + data() { + return { + loading: false, + error: null as UniCloudError | null, + treeData: [] as Array<UTSJSONObject>, + selectedIndex: 0, + selectedNodes: [] as Array<UTSJSONObject>, + selectedPages: [] as Array<UTSJSONObject>[], + selectedValue: '', + selectedPaths: [] as Array<UTSJSONObject>, + pagination: { + current: 1, + size: 20, + count: 0 + } as PaginationType + } + }, + computed: { + mappingTextName() : string { + // TODO + return (this.defaultProps != null) ? this.defaultProps!.getString('text', 'text') : 'text' + }, + mappingValueName() : string { + // TODO + return (this.defaultProps != null) ? this.defaultProps!.getString('value', 'value') : 'value' + }, + currentDataList() : Array<UTSJSONObject> { + if (this.selectedIndex > this.selectedPages.length - 1) { + return [] as Array<UTSJSONObject> + } + return this.selectedPages[this.selectedIndex] + }, + isLocalData() : boolean { + return this.localdata.length > 0 + }, + isCloudData() : boolean { + return this._checkIsNotNull(this.collection) + }, + isCloudDataList() : boolean { + return (this.isCloudData && (this.parentField.length == 0 && this.selfField.length == 0)) + }, + isCloudDataTree() : boolean { + return (this.isCloudData && this.parentField.length > 0 && this.selfField.length > 0) + }, + dataValue() : any { + return this.hasModelValue ? this.modelValue : this.value + }, + hasCloudTreeData() : boolean { + return this.treeData.length > 0 + }, + hasModelValue() : boolean { + if (typeof this.modelValue == 'string') { + const valueString = this.modelValue as string + return (valueString.length > 0) + } else if (Array.isArray(this.modelValue)) { + const valueArray = this.modelValue as Array<string> + return (valueArray.length > 0) + } + return false + }, + hasCloudDataValue() : boolean { + if (typeof this.dataValue == 'string') { + const valueString = this.dataValue as string + return (valueString.length > 0) + } + return false + } + }, + created() { + this.pagination.current = this.pageCurrent + this.pagination.size = this.pageSize + + this.$watch( + () : any => [ + this.pageCurrent, + this.pageSize, + this.localdata, + this.value, + this.collection, + this.field, + this.getcount, + this.orderby, + this.where, + this.groupby, + this.groupField, + this.distinct + ], + (newValue : Array<any>, oldValue : Array<any>) => { + this.pagination.size = this.pageSize + if (newValue[0] !== oldValue[0]) { + this.pagination.current = this.pageCurrent + } + + this.onPropsChange() + } + ) + }, + methods: { + onPropsChange() { + this.selectedIndex = 0 + this.selectedNodes.length = 0 + this.selectedPages.length = 0 + this.selectedPaths.length = 0 + + // 加载数据 + this.$nextTick(() => { + this.loadData() + }) + }, + + onTabSelect(index : number) { + this.selectedIndex = index + }, + + onNodeClick(nodeData : UTSJSONObject) { + if (nodeData.getBoolean('disable', false)) { + return + } + + const isLeaf = this._checkIsLeafNode(nodeData) + + this._trimSelectedNodes(nodeData) + + this.$emit('nodeclick', nodeData) + + if (this.isLocalData) { + if (isLeaf || !this._checkHasChildren(nodeData)) { + this.onFinish() + } + } else if (this.isCloudDataList) { + this.onFinish() + } else if (this.isCloudDataTree) { + if (isLeaf) { + this.onFinish() + } else if (!this._checkHasChildren(nodeData)) { + // 尝试请求一次,如果没有返回数据标记为叶子节点 + this.loadCloudDataNode(nodeData) + } + } + }, + + getChangeNodes(): Array<UTSJSONObject> { + const nodes: Array<UTSJSONObject> = [] + this.selectedNodes.forEach((node : UTSJSONObject) => { + const newNode: UTSJSONObject = {} + newNode[this.mappingTextName] = node.getString(this.mappingTextName) + newNode[this.mappingValueName] = node.getString(this.mappingValueName) + nodes.push(newNode) + }) + return nodes + }, + + onFinish() { }, + + // 加载数据(自动判定环境) + loadData() { + if (this.isLocalData) { + this.loadLocalData() + } else if (this.isCloudDataList) { + this.loadCloudDataList() + } else if (this.isCloudDataTree) { + this.loadCloudDataTree() + } + }, + + // 加载本地数据 + loadLocalData() { + this.treeData = this.localdata + if (Array.isArray(this.dataValue)) { + const value = this.dataValue as Array<UTSJSONObject> + this.selectedPaths = value.slice(0) + this._pushSelectedTreeNodes(value, this.localdata) + } else { + this._pushSelectedNodes(this.localdata) + } + }, + + // 加载 Cloud 数据 (单列) + loadCloudDataList() { + this._loadCloudData(null, (data : Array<UTSJSONObject>) => { + this.treeData = data + this._pushSelectedNodes(data) + }) + }, + + // 加载 Cloud 数据 (树形) + loadCloudDataTree() { + let commandOptions = { + field: this._cloudDataPostField(), + where: this._cloudDataTreeWhere(), + getTree: true + } as GetCommandOptions + if (this._checkIsNotNull(this.gettree)) { + commandOptions.startwith = `${this.selfField}=='${this.dataValue as string}'` + } + this._loadCloudData(commandOptions, (data : Array<UTSJSONObject>) => { + this.treeData = data + if (this.selectedPaths.length > 0) { + this._pushSelectedTreeNodes(this.selectedPaths, data) + } else { + this._pushSelectedNodes(data) + } + }) + }, + + // 加载 Cloud 数据 (节点) + loadCloudDataNode(nodeData : UTSJSONObject) { + const commandOptions = { + field: this._cloudDataPostField(), + where: this._cloudDataNodeWhere() + } as GetCommandOptions + this._loadCloudData(commandOptions, (data : Array<UTSJSONObject>) => { + nodeData['children'] = data + if (data.length == 0) { + nodeData['isleaf'] = true + this.onFinish() + } else { + this._pushSelectedNodes(data) + } + }) + }, + + // 回显 Cloud Tree Path + loadCloudDataPath() { + if (!this.hasCloudDataValue) { + return + } + + const command : GetCommandOptions = {} + + // 单列 + if (this.isCloudDataList) { + // 根据 field's as value标识匹配 where 条件 + let where : Array<string> = []; + let whereField = this._getForeignKeyByField(); + if (whereField.length > 0) { + where.push(`${whereField} == '${this.dataValue as string}'`) + } + + let whereString = where.join(' || ') + if (this._checkIsNotNull(this.where)) { + whereString = `(${this.where}) && (${whereString})` + } + + command.field = this._cloudDataPostField() + command.where = whereString + } + + // 树形 + if (this.isCloudDataTree) { + command.field = this._cloudDataPostField() + command.getTreePath = { + startWith: `${this.selfField}=='${this.dataValue as string}'` + } + } + + this._loadCloudData(command, (data : Array<UTSJSONObject>) => { + this._extractTreePath(data, this.selectedPaths) + }) + }, + + _loadCloudData(options ?: GetCommandOptions, callback ?: ((data : Array<UTSJSONObject>) => void)) { + if (this.loading) { + return + } + this.loading = true + + this.error = null + + this._getCommand(options).then((response : UniCloudDBGetResult) => { + callback?.(response.data) + }).catch((err : any | null) => { + this.error = err as UniCloudError + }).finally(() => { + this.loading = false + }) + }, + + _cloudDataPostField() : string { + let fields = [this.field]; + if (this.parentField.length > 0) { + fields.push(`${this.parentField} as parent_value`) + } + return fields.join(',') + }, + + _cloudDataTreeWhere() : string { + let result : Array<string> = [] + let selectedNodes = this.selectedNodes.length > 0 ? this.selectedNodes : this.selectedPaths + let parentField = this.parentField + if (parentField.length > 0) { + result.push(`${parentField} == null || ${parentField} == ""`) + } + if (selectedNodes.length > 0) { + for (var i = 0; i < selectedNodes.length - 1; i++) { + const parentFieldValue = selectedNodes[i].getString('value', '') + result.push(`${parentField} == '${parentFieldValue}'`) + } + } + + let where : Array<string> = [] + if (this._checkIsNotNull(this.where)) { + where.push(`(${this.where as string})`) + } + + if (result.length > 0) { + where.push(`(${result.join(' || ')})`) + } + + return where.join(' && ') + }, + + _cloudDataNodeWhere() : string { + const where : Array<string> = [] + if (this.selectedNodes.length > 0) { + const value = this.selectedNodes[this.selectedNodes.length - 1].getString('value', '') + where.push(`${this.parentField} == '${value}'`) + } + + let whereString = where.join(' || ') + if (this._checkIsNotNull(this.where)) { + return `(${this.where as string}) && (${whereString})` + } + + return whereString + }, + + _getWhereByForeignKey() : string { + let result : Array<string> = [] + let whereField = this._getForeignKeyByField(); + if (whereField.length > 0) { + result.push(`${whereField} == '${this.dataValue as string}'`) + } + + if (this._checkIsNotNull(this.where)) { + return `(${this.where}) && (${result.join(' || ')})` + } + + return result.join(' || ') + }, + + _getForeignKeyByField() : string { + const fields = this.field.split(',') + let whereField = '' + for (let i = 0; i < fields.length; i++) { + const items = fields[i].split('as') + if (items.length < 2) { + continue + } + if (items[1].trim() === 'value') { + whereField = items[0].trim() + break + } + } + return whereField + }, + + _getCommand(options ?: GetCommandOptions) : Promise<UniCloudDBGetResult> { + let db = uniCloud.databaseForJQL() + + let collection = Array.isArray(this.collection) ? db.collection(...(this.collection as Array<any>)) : db.collection(this.collection) + + let filter : UniCloudDBFilter | null = null + if (this.foreignKey.length > 0) { + filter = collection.foreignKey(this.foreignKey) + } + + const where : any = options?.where ?? this.where + if (typeof where == 'string') { + const whereString = where as string + if (whereString.length > 0) { + filter = (filter != null) ? filter.where(where) : collection.where(where) + } + } else { + filter = (filter != null) ? filter.where(where) : collection.where(where) + } + + let query : UniCloudDBQuery | null = null + if (this.field.length > 0) { + query = (filter != null) ? filter.field(this.field) : collection.field(this.field) + } + if (this.groupby.length > 0) { + if (query != null) { + query = query.groupBy(this.groupby) + } else if (filter != null) { + query = filter.groupBy(this.groupby) + } + } + if (this.groupField.length > 0) { + if (query != null) { + query = query.groupField(this.groupField) + } else if (filter != null) { + query = filter.groupField(this.groupField) + } + } + if (this.distinct == true) { + if (query != null) { + query = query.distinct(this.field) + } else if (filter != null) { + query = filter.distinct(this.field) + } + } + if (this.orderby.length > 0) { + if (query != null) { + query = query.orderBy(this.orderby) + } else if (filter != null) { + query = filter.orderBy(this.orderby) + } + } + + const size = this.pagination.size + const current = this.pagination.current + if (query != null) { + query = query.skip(size * (current - 1)).limit(size) + } else if (filter != null) { + query = filter.skip(size * (current - 1)).limit(size) + } else { + query = collection.skip(size * (current - 1)).limit(size) + } + + const getOptions = {} + const treeOptions = { + limitLevel: this.limitlevel, + startWith: this.startwith + } + if (this.getcount == true) { + getOptions['getCount'] = this.getcount + } + + const getTree : any = options?.getTree ?? this.gettree + if (typeof getTree == 'string') { + const getTreeString = getTree as string + if (getTreeString.length > 0) { + getOptions['getTree'] = treeOptions + } + } else if (typeof getTree == 'object') { + getOptions['getTree'] = treeOptions + } else { + getOptions['getTree'] = getTree + } + + const getTreePath = options?.getTreePath ?? this.gettreepath + if (typeof getTreePath == 'string') { + const getTreePathString = getTreePath as string + if (getTreePathString.length > 0) { + getOptions['getTreePath'] = getTreePath + } + } else { + getOptions['getTreePath'] = getTreePath + } + + return query.get(getOptions) + }, + + _checkIsNotNull(value : any) : boolean { + if (typeof value == 'string') { + const valueString = value as string + return (valueString.length > 0) + } else if (value instanceof UTSJSONObject) { + return true + } + return false + }, + + _checkIsLeafNode(nodeData : UTSJSONObject) : boolean { + if (this.selectedIndex >= this.limitlevel) { + return true + } + + if (nodeData.getBoolean('isleaf', false)) { + return true + } + + return false + }, + + _checkHasChildren(nodeData : UTSJSONObject) : boolean { + const children = nodeData.getArray('children') ?? ([] as Array<any>) + return children.length > 0 + }, + + _pushSelectedNodes(nodes : Array<UTSJSONObject>) { + this.selectedNodes.push(DefaultSelectedNode) + this.selectedPages.push(nodes) + this.selectedIndex = this.selectedPages.length - 1 + }, + + _trimSelectedNodes(nodeData : UTSJSONObject) { + this.selectedNodes.splice(this.selectedIndex) + this.selectedNodes.push(nodeData) + + if (this.selectedPages.length > 0) { + this.selectedPages.splice(this.selectedIndex + 1) + } + + const children = nodeData.getArray<UTSJSONObject>('children') ?? ([] as Array<UTSJSONObject>) + if (children.length > 0) { + this.selectedNodes.push(DefaultSelectedNode) + this.selectedPages.push(children) + } + + this.selectedIndex = this.selectedPages.length - 1 + }, + + _pushSelectedTreeNodes(paths : Array<UTSJSONObject>, nodes : Array<UTSJSONObject>) { + let children : Array<UTSJSONObject> = nodes + paths.forEach((node : UTSJSONObject) => { + const findNode = children.find((item : UTSJSONObject) : boolean => { + return (item.getString(this.mappingValueName) == node.getString(this.mappingValueName)) + }) + if (findNode != null) { + this.selectedPages.push(children) + this.selectedNodes.push(node) + children = findNode.getArray<UTSJSONObject>('children') ?? ([] as Array<UTSJSONObject>) + } + }) + this.selectedIndex = this.selectedPages.length - 1 + }, + + _extractTreePath(nodes : Array<UTSJSONObject>, result : Array<UTSJSONObject>) { + if (nodes.length == 0) { + return + } + + const node = nodes[0] + result.push(node) + + const children = node.getArray<UTSJSONObject>('children') + if (Array.isArray(children) && children!.length > 0) { + this._extractTreePath(children, result) + } + } + } +}) diff --git a/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.css b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.css new file mode 100644 index 0000000..39fe1c3 --- /dev/null +++ b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.css @@ -0,0 +1,76 @@ +.uni-data-pickerview { + position: relative; + flex-direction: column; + overflow: hidden; +} + +.loading-cover { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + align-items: center; + justify-content: center; + background-color: rgba(150, 150, 150, .1); +} + +.error { + background-color: #fff; + padding: 15px; +} + +.error-text { + color: #DD524D; +} + +.selected-node-list { + flex-direction: row; + flex-wrap: nowrap; +} + +.selected-node-item { + margin-left: 10px; + margin-right: 10px; + padding: 8px 10px 8px 10px; + border-bottom: 2px solid transparent; +} + +.selected-node-item-active { + color: #007aff; + border-bottom-color: #007aff; +} + +.list-view { + flex: 1; +} + +.list-item { + flex-direction: row; + justify-content: space-between; + padding: 12px 15px; + border-bottom: 1px solid #f0f0f0; +} + +.item-text { + color: #333333; +} + +.item-text-disabled { + opacity: .5; +} + +.item-text-overflow { + overflow: hidden; +} + +.check { + margin-right: 5px; + border: 2px solid #007aff; + border-left: 0; + border-top: 0; + height: 12px; + width: 6px; + transform-origin: center; + transform: rotate(45deg); +} diff --git a/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.uvue b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.uvue new file mode 100644 index 0000000..f4780f3 --- /dev/null +++ b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.uvue @@ -0,0 +1,69 @@ +<template> + <view class="uni-data-pickerview"> + <view v-if="error!=null" class="error"> + <text class="error-text">{{error!.errMsg}}</text> + </view> + <scroll-view v-if="!isCloudDataList" :scroll-x="true"> + <view class="selected-node-list"> + <template v-for="(item, index) in selectedNodes"> + <text class="selected-node-item" :class="{'selected-node-item-active':index==selectedIndex}" + @click="onTabSelect(index)"> + {{item[mappingTextName]}} + </text> + </template> + </view> + </scroll-view> + <list-view class="list-view" :scroll-y="true"> + <list-item class="list-item" v-for="(item, _) in currentDataList" @click="onNodeClick(item)"> + <text class="item-text" :class="{'item-text-disabled': item['disable']}">{{item[mappingTextName]}}</text> + <text class="check" v-if="item[mappingValueName] == selectedNodes[selectedIndex][mappingValueName]"></text> + </list-item> + </list-view> + <view class="loading-cover" v-if="loading"> + <slot name="pickerview-loading" :loading="loading"></slot> + </view> + </view> +</template> + +<script> + import { dataPicker } from "./uni-data-picker.uts" + + /** + * DataPickerview + * @description uni-data-pickerview + * @tutorial https://ext.dcloud.net.cn/plugin?id=3796 + * @property {Array} localdata 本地数据,参考 + * @property {Boolean} step-searh = [true|false] 是否分布查询 + * @value true 启用分布查询,仅查询当前选中节点 + * @value false 关闭分布查询,一次查询出所有数据 + * @property {String|DBFieldString} self-field 分布查询当前字段名称 + * @property {String|DBFieldString} parent-field 分布查询父字段名称 + * @property {String|DBCollectionString} collection 表名 + * @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割 + * @property {String} orderby 排序字段及正序倒叙设置 + * @property {String|JQLString} where 查询条件 + */ + export default { + name: 'UniDataPickerView', + emits: ['nodeclick', 'change', 'update:modelValue'], + mixins: [dataPicker], + props: { + ellipsis: { + type: Boolean, + default: true + } + }, + created() { + this.loadData() + }, + methods: { + onFinish() { + this.$emit('change', this.getChangeNodes()) + } + } + } +</script> + +<style> + @import url("uni-data-pickerview.css"); +</style> diff --git a/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue new file mode 100644 index 0000000..6ebced9 --- /dev/null +++ b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue @@ -0,0 +1,323 @@ +<template> + <view class="uni-data-pickerview"> + <scroll-view v-if="!isCloudDataList" class="selected-area" scroll-x="true"> + <view class="selected-list"> + <view + class="selected-item" + v-for="(item,index) in selected" + :key="index" + :class="{ + 'selected-item-active':index == selectedIndex + }" + @click="handleSelect(index)" + > + <text>{{item.text || ''}}</text> + </view> + </view> + </scroll-view> + <view class="tab-c"> + <scroll-view class="list" :scroll-y="true"> + <view class="item" :class="{'is-disabled': !!item.disable}" v-for="(item, j) in dataList[selectedIndex]" :key="j" + @click="handleNodeClick(item, selectedIndex, j)"> + <text class="item-text">{{item[map.text]}}</text> + <view class="check" v-if="selected.length > selectedIndex && item[map.value] == selected[selectedIndex].value"></view> + </view> + </scroll-view> + + <view class="loading-cover" v-if="loading"> + <uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more> + </view> + <view class="error-message" v-if="errorMessage"> + <text class="error-text">{{errorMessage}}</text> + </view> + </view> + </view> +</template> + +<script> + import dataPicker from "./uni-data-picker.js" + + /** + * DataPickerview + * @description uni-data-pickerview + * @tutorial https://ext.dcloud.net.cn/plugin?id=3796 + * @property {Array} localdata 本地数据,参考 + * @property {Boolean} step-searh = [true|false] 是否分布查询 + * @value true 启用分布查询,仅查询当前选中节点 + * @value false 关闭分布查询,一次查询出所有数据 + * @property {String|DBFieldString} self-field 分布查询当前字段名称 + * @property {String|DBFieldString} parent-field 分布查询父字段名称 + * @property {String|DBCollectionString} collection 表名 + * @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割 + * @property {String} orderby 排序字段及正序倒叙设置 + * @property {String|JQLString} where 查询条件 + */ + export default { + name: 'UniDataPickerView', + emits: ['nodeclick', 'change', 'datachange', 'update:modelValue'], + mixins: [dataPicker], + props: { + managedMode: { + type: Boolean, + default: false + }, + ellipsis: { + type: Boolean, + default: true + } + }, + created() { + if (!this.managedMode) { + this.$nextTick(() => { + this.loadData(); + }) + } + }, + methods: { + onPropsChange() { + this._treeData = []; + this.selectedIndex = 0; + this.$nextTick(() => { + this.loadData(); + }) + }, + handleSelect(index) { + this.selectedIndex = index; + }, + handleNodeClick(item, i, j) { + if (item.disable) { + return; + } + + const node = this.dataList[i][j]; + const text = node[this.map.text]; + const value = node[this.map.value]; + + if (i < this.selected.length - 1) { + this.selected.splice(i, this.selected.length - i) + this.selected.push({ + text, + value + }) + } else if (i === this.selected.length - 1) { + this.selected.splice(i, 1, { + text, + value + }) + } + + if (node.isleaf) { + this.onSelectedChange(node, node.isleaf) + return + } + + const { + isleaf, + hasNodes + } = this._updateBindData() + + // 本地数据 + if (this.isLocalData) { + this.onSelectedChange(node, (!hasNodes || isleaf)) + } else if (this.isCloudDataList) { // Cloud 数据 (单列) + this.onSelectedChange(node, true) + } else if (this.isCloudDataTree) { // Cloud 数据 (树形) + if (isleaf) { + this.onSelectedChange(node, node.isleaf) + } else if (!hasNodes) { // 请求一次服务器以确定是否为叶子节点 + this.loadCloudDataNode((data) => { + if (!data.length) { + node.isleaf = true + } else { + this._treeData.push(...data) + this._updateBindData(node) + } + this.onSelectedChange(node, node.isleaf) + }) + } + } + }, + updateData(data) { + this._treeData = data.treeData + this.selected = data.selected + if (!this._treeData.length) { + this.loadData() + } else { + //this.selected = data.selected + this._updateBindData() + } + }, + onDataChange() { + this.$emit('datachange'); + }, + onSelectedChange(node, isleaf) { + if (isleaf) { + this._dispatchEvent() + } + + if (node) { + this.$emit('nodeclick', node) + } + }, + _dispatchEvent() { + this.$emit('change', this.selected.slice(0)) + } + } + } +</script> + +<style lang="scss"> + $uni-primary: #007aff !default; + + .uni-data-pickerview { + flex: 1; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + overflow: hidden; + height: 100%; + } + + .error-text { + color: #DD524D; + } + + .loading-cover { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + background-color: rgba(255, 255, 255, .5); + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + align-items: center; + z-index: 1001; + } + + .load-more { + /* #ifndef APP-NVUE */ + margin: auto; + /* #endif */ + } + + .error-message { + background-color: #fff; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + padding: 15px; + opacity: .9; + z-index: 102; + } + + /* #ifdef APP-NVUE */ + .selected-area { + width: 750rpx; + } + /* #endif */ + + .selected-list { + /* #ifndef APP-NVUE */ + display: flex; + flex-wrap: nowrap; + /* #endif */ + flex-direction: row; + padding: 0 5px; + border-bottom: 1px solid #f8f8f8; + } + + .selected-item { + margin-left: 10px; + margin-right: 10px; + padding: 12px 0; + text-align: center; + /* #ifndef APP-NVUE */ + white-space: nowrap; + /* #endif */ + } + + .selected-item-text-overflow { + width: 168px; + /* fix nvue */ + overflow: hidden; + /* #ifndef APP-NVUE */ + width: 6em; + white-space: nowrap; + text-overflow: ellipsis; + -o-text-overflow: ellipsis; + /* #endif */ + } + + .selected-item-active { + border-bottom: 2px solid $uni-primary; + } + + .selected-item-text { + color: $uni-primary; + } + + .tab-c { + position: relative; + flex: 1; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + overflow: hidden; + } + + .list { + flex: 1; + } + + .item { + padding: 12px 15px; + /* border-bottom: 1px solid #f0f0f0; */ + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + justify-content: space-between; + } + + .is-disabled { + opacity: .5; + } + + .item-text { + /* flex: 1; */ + color: #333333; + } + + .item-text-overflow { + width: 280px; + /* fix nvue */ + overflow: hidden; + /* #ifndef APP-NVUE */ + width: 20em; + white-space: nowrap; + text-overflow: ellipsis; + -o-text-overflow: ellipsis; + /* #endif */ + } + + .check { + margin-right: 5px; + border: 2px solid $uni-primary; + border-left: 0; + border-top: 0; + height: 12px; + width: 6px; + transform-origin: center; + /* #ifndef APP-NVUE */ + transition: all 0.3s; + /* #endif */ + transform: rotate(45deg); + } +</style> diff --git a/uni_modules/uni-data-picker/package.json b/uni_modules/uni-data-picker/package.json new file mode 100644 index 0000000..efc60c2 --- /dev/null +++ b/uni_modules/uni-data-picker/package.json @@ -0,0 +1,91 @@ +{ + "id": "uni-data-picker", + "displayName": "uni-data-picker 数据驱动的picker选择器", + "version": "2.0.1", + "description": "单列、多列级联选择器,常用于省市区城市选择、公司部门选择、多级分类等场景", + "keywords": [ + "uni-ui", + "uniui", + "picker", + "级联", + "省市区", + "" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-load-more", + "uni-icons", + "uni-scss" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-data-picker/readme.md b/uni_modules/uni-data-picker/readme.md new file mode 100644 index 0000000..19dd0e8 --- /dev/null +++ b/uni_modules/uni-data-picker/readme.md @@ -0,0 +1,22 @@ +## DataPicker 级联选择 +> **组件名:uni-data-picker** +> 代码块: `uDataPicker` +> 关联组件:`uni-data-pickerview`、`uni-load-more`。 + + +`<uni-data-picker>` 是一个选择类[datacom组件](https://uniapp.dcloud.net.cn/component/datacom)。 + +支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。 + +候选数据支持一次性加载完毕,也支持懒加载,比如示例图中,选择了“北京”后,动态加载北京的区县数据。 + +`<uni-data-picker>` 组件尤其适用于地址选择、分类选择等选择类。 + +`<uni-data-picker>` 支持本地数据、云端静态数据(json),uniCloud云数据库数据。 + +`<uni-data-picker>` 可以通过JQL直连uniCloud云数据库,配套[DB Schema](https://uniapp.dcloud.net.cn/uniCloud/schema),可在schema2code中自动生成前端页面,还支持服务器端校验。 + +在uniCloud数据表中新建表“uni-id-address”和“opendb-city-china”,这2个表的schema自带foreignKey关联。在“uni-id-address”表的表结构页面使用schema2code生成前端页面,会自动生成地址管理的维护页面,自动从“opendb-city-china”表包含的中国所有省市区信息里选择地址。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-picker) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-data-select/changelog.md b/uni_modules/uni-data-select/changelog.md new file mode 100644 index 0000000..016e3d2 --- /dev/null +++ b/uni_modules/uni-data-select/changelog.md @@ -0,0 +1,39 @@ +## 1.0.8(2024-03-28) +- 修复 在vue2下:style动态绑定导致编译失败的bug +## 1.0.7(2024-01-20) +- 修复 长文本回显超过容器的bug,超过容器部分显示省略号 +## 1.0.6(2023-04-12) +- 修复 微信小程序点击时会改变背景颜色的 bug +## 1.0.5(2023-02-03) +- 修复 禁用时会显示清空按钮 +## 1.0.4(2023-02-02) +- 优化 查询条件短期内多次变更只查询最后一次变更后的结果 +- 调整 内部缓存键名调整为 uni-data-select-lastSelectedValue +## 1.0.3(2023-01-16) +- 修复 不关联服务空间报错的问题 +## 1.0.2(2023-01-14) +- 新增 属性 `format` 可用于格式化显示选项内容 +## 1.0.1(2022-12-06) +- 修复 当where变化时,数据不会自动更新的问题 +## 0.1.9(2022-09-05) +- 修复 微信小程序下拉框出现后选择会点击到蒙板后面的输入框 +## 0.1.8(2022-08-29) +- 修复 点击的位置不准确 +## 0.1.7(2022-08-12) +- 新增 支持 disabled 属性 +## 0.1.6(2022-07-06) +- 修复 pc端宽度异常的bug +## 0.1.5 +- 修复 pc端宽度异常的bug +## 0.1.4(2022-07-05) +- 优化 显示样式 +## 0.1.3(2022-06-02) +- 修复 localdata 赋值不生效的 bug +- 新增 支持 uni.scss 修改颜色 +- 新增 支持选项禁用(数据选项设置 disabled: true 即禁用) +## 0.1.2(2022-05-08) +- 修复 当 value 为 0 时选择不生效的 bug +## 0.1.1(2022-05-07) +- 新增 记住上次的选项(仅 collection 存在时有效) +## 0.1.0(2022-04-22) +- 初始化 diff --git a/uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue b/uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue new file mode 100644 index 0000000..edab65a --- /dev/null +++ b/uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue @@ -0,0 +1,562 @@ +<template> + <view class="uni-stat__select"> + <span v-if="label" class="uni-label-text hide-on-phone">{{label + ':'}}</span> + <view class="uni-stat-box" :class="{'uni-stat__actived': current}"> + <view class="uni-select" :class="{'uni-select--disabled':disabled}"> + <view class="uni-select__input-box" @click="toggleSelector"> + <view v-if="current" class="uni-select__input-text">{{textShow}}</view> + <view v-else class="uni-select__input-text uni-select__input-placeholder">{{typePlaceholder}}</view> + <view v-if="current && clear && !disabled" @click.stop="clearVal"> + <uni-icons type="clear" color="#c0c4cc" size="24" /> + </view> + <view v-else> + <uni-icons :type="showSelector? 'top' : 'bottom'" size="14" color="#999" /> + </view> + </view> + <view class="uni-select--mask" v-if="showSelector" @click="toggleSelector" /> + <view class="uni-select__selector" :style="getOffsetByPlacement" v-if="showSelector"> + <view :class="placement=='bottom'?'uni-popper__arrow_bottom':'uni-popper__arrow_top'"></view> + <scroll-view scroll-y="true" class="uni-select__selector-scroll"> + <view class="uni-select__selector-empty" v-if="mixinDatacomResData.length === 0"> + <text>{{emptyTips}}</text> + </view> + <view v-else class="uni-select__selector-item" v-for="(item,index) in mixinDatacomResData" :key="index" + @click="change(item)"> + <text :class="{'uni-select__selector__disabled': item.disable}">{{formatItemName(item)}}</text> + </view> + </scroll-view> + </view> + </view> + </view> + </view> +</template> + +<script> + /** + * DataChecklist 数据选择器 + * @description 通过数据渲染的下拉框组件 + * @tutorial https://uniapp.dcloud.io/component/uniui/uni-data-select + * @property {String} value 默认值 + * @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}] + * @property {Boolean} clear 是否可以清空已选项 + * @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效 + * @property {String} label 左侧标题 + * @property {String} placeholder 输入框的提示文字 + * @property {Boolean} disabled 是否禁用 + * @property {String} placement 弹出位置 + * @value top 顶部弹出 + * @value bottom 底部弹出(default) + * @event {Function} change 选中发生变化触发 + */ + + export default { + name: "uni-data-select", + mixins: [uniCloud.mixinDatacom || {}], + props: { + localdata: { + type: Array, + default () { + return [] + } + }, + value: { + type: [String, Number], + default: '' + }, + modelValue: { + type: [String, Number], + default: '' + }, + label: { + type: String, + default: '' + }, + placeholder: { + type: String, + default: '请选择' + }, + emptyTips: { + type: String, + default: '无选项' + }, + clear: { + type: Boolean, + default: true + }, + defItem: { + type: Number, + default: 0 + }, + disabled: { + type: Boolean, + default: false + }, + // 格式化输出 用法 field="_id as value, version as text, uni_platform as label" format="{label} - {text}" + format: { + type: String, + default: '' + }, + placement: { + type: String, + default: 'bottom' + } + }, + data() { + return { + showSelector: false, + current: '', + mixinDatacomResData: [], + apps: [], + channels: [], + cacheKey: "uni-data-select-lastSelectedValue", + }; + }, + created() { + this.debounceGet = this.debounce(() => { + this.query(); + }, 300); + if (this.collection && !this.localdata.length) { + this.debounceGet(); + } + }, + computed: { + typePlaceholder() { + const text = { + 'opendb-stat-app-versions': '版本', + 'opendb-app-channels': '渠道', + 'opendb-app-list': '应用' + } + const common = this.placeholder + const placeholder = text[this.collection] + return placeholder ? + common + placeholder : + common + }, + valueCom() { + // #ifdef VUE3 + return this.modelValue; + // #endif + // #ifndef VUE3 + return this.value; + // #endif + }, + textShow() { + // 长文本显示 + let text = this.current; + if (text.length > 10) { + return text.slice(0, 25) + '...'; + } + return text; + }, + getOffsetByPlacement() { + switch (this.placement) { + case 'top': + return "bottom:calc(100% + 12px);"; + case 'bottom': + return "top:calc(100% + 12px);"; + } + } + }, + + watch: { + localdata: { + immediate: true, + handler(val, old) { + if (Array.isArray(val) && old !== val) { + this.mixinDatacomResData = val + } + } + }, + valueCom(val, old) { + this.initDefVal() + }, + mixinDatacomResData: { + immediate: true, + handler(val) { + if (val.length) { + this.initDefVal() + } + } + }, + + }, + methods: { + debounce(fn, time = 100) { + let timer = null + return function(...args) { + if (timer) clearTimeout(timer) + timer = setTimeout(() => { + fn.apply(this, args) + }, time) + } + }, + // 执行数据库查询 + query() { + this.mixinDatacomEasyGet(); + }, + // 监听查询条件变更事件 + onMixinDatacomPropsChange() { + if (this.collection) { + this.debounceGet(); + } + }, + initDefVal() { + let defValue = '' + if ((this.valueCom || this.valueCom === 0) && !this.isDisabled(this.valueCom)) { + defValue = this.valueCom + } else { + let strogeValue + if (this.collection) { + strogeValue = this.getCache() + } + if (strogeValue || strogeValue === 0) { + defValue = strogeValue + } else { + let defItem = '' + if (this.defItem > 0 && this.defItem <= this.mixinDatacomResData.length) { + defItem = this.mixinDatacomResData[this.defItem - 1].value + } + defValue = defItem + } + if (defValue || defValue === 0) { + this.emit(defValue) + } + } + const def = this.mixinDatacomResData.find(item => item.value === defValue) + this.current = def ? this.formatItemName(def) : '' + }, + + /** + * @param {[String, Number]} value + * 判断用户给的 value 是否同时为禁用状态 + */ + isDisabled(value) { + let isDisabled = false; + + this.mixinDatacomResData.forEach(item => { + if (item.value === value) { + isDisabled = item.disable + } + }) + + return isDisabled; + }, + + clearVal() { + this.emit('') + if (this.collection) { + this.removeCache() + } + }, + change(item) { + if (!item.disable) { + this.showSelector = false + this.current = this.formatItemName(item) + this.emit(item.value) + } + }, + emit(val) { + this.$emit('input', val) + this.$emit('update:modelValue', val) + this.$emit('change', val) + if (this.collection) { + this.setCache(val); + } + }, + toggleSelector() { + if (this.disabled) { + return + } + + this.showSelector = !this.showSelector + }, + formatItemName(item) { + let { + text, + value, + channel_code + } = item + channel_code = channel_code ? `(${channel_code})` : '' + + if (this.format) { + // 格式化输出 + let str = ""; + str = this.format; + for (let key in item) { + str = str.replace(new RegExp(`{${key}}`, "g"), item[key]); + } + return str; + } else { + return this.collection.indexOf('app-list') > 0 ? + `${text}(${value})` : + ( + text ? + text : + `未命名${channel_code}` + ) + } + }, + // 获取当前加载的数据 + getLoadData() { + return this.mixinDatacomResData; + }, + // 获取当前缓存key + getCurrentCacheKey() { + return this.collection; + }, + // 获取缓存 + getCache(name = this.getCurrentCacheKey()) { + let cacheData = uni.getStorageSync(this.cacheKey) || {}; + return cacheData[name]; + }, + // 设置缓存 + setCache(value, name = this.getCurrentCacheKey()) { + let cacheData = uni.getStorageSync(this.cacheKey) || {}; + cacheData[name] = value; + uni.setStorageSync(this.cacheKey, cacheData); + }, + // 删除缓存 + removeCache(name = this.getCurrentCacheKey()) { + let cacheData = uni.getStorageSync(this.cacheKey) || {}; + delete cacheData[name]; + uni.setStorageSync(this.cacheKey, cacheData); + }, + } + } +</script> + +<style lang="scss"> + $uni-base-color: #6a6a6a !default; + $uni-main-color: #333 !default; + $uni-secondary-color: #909399 !default; + $uni-border-3: #e5e5e5; + + /* #ifndef APP-NVUE */ + @media screen and (max-width: 500px) { + .hide-on-phone { + display: none; + } + } + + /* #endif */ + .uni-stat__select { + display: flex; + align-items: center; + // padding: 15px; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + width: 100%; + flex: 1; + box-sizing: border-box; + } + + .uni-stat-box { + width: 100%; + flex: 1; + } + + .uni-stat__actived { + width: 100%; + flex: 1; + // outline: 1px solid #2979ff; + } + + .uni-label-text { + font-size: 14px; + font-weight: bold; + color: $uni-base-color; + margin: auto 0; + margin-right: 5px; + } + + .uni-select { + font-size: 14px; + border: 1px solid $uni-border-3; + box-sizing: border-box; + border-radius: 4px; + padding: 0 5px; + padding-left: 10px; + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + user-select: none; + /* #endif */ + flex-direction: row; + align-items: center; + border-bottom: solid 1px $uni-border-3; + width: 100%; + flex: 1; + height: 35px; + + &--disabled { + background-color: #f5f7fa; + cursor: not-allowed; + } + } + + .uni-select__label { + font-size: 16px; + // line-height: 22px; + height: 35px; + padding-right: 10px; + color: $uni-secondary-color; + } + + .uni-select__input-box { + height: 35px; + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex: 1; + flex-direction: row; + align-items: center; + } + + .uni-select__input { + flex: 1; + font-size: 14px; + height: 22px; + line-height: 22px; + } + + .uni-select__input-plac { + font-size: 14px; + color: $uni-secondary-color; + } + + .uni-select__selector { + /* #ifndef APP-NVUE */ + box-sizing: border-box; + /* #endif */ + position: absolute; + left: 0; + width: 100%; + background-color: #FFFFFF; + border: 1px solid #EBEEF5; + border-radius: 6px; + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); + z-index: 3; + padding: 4px 0; + } + + .uni-select__selector-scroll { + /* #ifndef APP-NVUE */ + max-height: 200px; + box-sizing: border-box; + /* #endif */ + } + + /* #ifdef H5 */ + @media (min-width: 768px) { + .uni-select__selector-scroll { + max-height: 600px; + } + } + + /* #endif */ + + .uni-select__selector-empty, + .uni-select__selector-item { + /* #ifndef APP-NVUE */ + display: flex; + cursor: pointer; + /* #endif */ + line-height: 35px; + font-size: 14px; + text-align: center; + /* border-bottom: solid 1px $uni-border-3; */ + padding: 0px 10px; + } + + .uni-select__selector-item:hover { + background-color: #f9f9f9; + } + + .uni-select__selector-empty:last-child, + .uni-select__selector-item:last-child { + /* #ifndef APP-NVUE */ + border-bottom: none; + /* #endif */ + } + + .uni-select__selector__disabled { + opacity: 0.4; + cursor: default; + } + + /* picker 弹出层通用的指示小三角 */ + .uni-popper__arrow_bottom, + .uni-popper__arrow_bottom::after, + .uni-popper__arrow_top, + .uni-popper__arrow_top::after, + { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + border-width: 6px; + } + + .uni-popper__arrow_bottom { + filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); + top: -6px; + left: 10%; + margin-right: 3px; + border-top-width: 0; + border-bottom-color: #EBEEF5; + } + + .uni-popper__arrow_bottom::after { + content: " "; + top: 1px; + margin-left: -6px; + border-top-width: 0; + border-bottom-color: #fff; + } + + .uni-popper__arrow_top { + filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); + bottom: -6px; + left: 10%; + margin-right: 3px; + border-bottom-width: 0; + border-top-color: #EBEEF5; + } + + .uni-popper__arrow_top::after { + content: " "; + bottom: 1px; + margin-left: -6px; + border-bottom-width: 0; + border-top-color: #fff; + } + + + .uni-select__input-text { + // width: 280px; + width: 100%; + color: $uni-main-color; + white-space: nowrap; + text-overflow: ellipsis; + -o-text-overflow: ellipsis; + overflow: hidden; + } + + .uni-select__input-placeholder { + color: $uni-base-color; + font-size: 12px; + } + + .uni-select--mask { + position: fixed; + top: 0; + bottom: 0; + right: 0; + left: 0; + z-index: 2; + } +</style> diff --git a/uni_modules/uni-data-select/package.json b/uni_modules/uni-data-select/package.json new file mode 100644 index 0000000..5864594 --- /dev/null +++ b/uni_modules/uni-data-select/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-data-select", + "displayName": "uni-data-select 下拉框选择器", + "version": "1.0.8", + "description": "通过数据驱动的下拉框选择器", + "keywords": [ + "uni-ui", + "select", + "uni-data-select", + "下拉框", + "下拉选" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "^3.1.1" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-load-more"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "u", + "app-nvue": "n" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-data-select/readme.md b/uni_modules/uni-data-select/readme.md new file mode 100644 index 0000000..eb58de3 --- /dev/null +++ b/uni_modules/uni-data-select/readme.md @@ -0,0 +1,8 @@ +## DataSelect 下拉框选择器 +> **组件名:uni-data-select** +> 代码块: `uDataSelect` + +当选项过多时,使用下拉菜单展示并选择内容 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-select) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 diff --git a/uni_modules/uni-dateformat/changelog.md b/uni_modules/uni-dateformat/changelog.md new file mode 100644 index 0000000..d551d7b --- /dev/null +++ b/uni_modules/uni-dateformat/changelog.md @@ -0,0 +1,10 @@ +## 1.0.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-dateformat](https://uniapp.dcloud.io/component/uniui/uni-dateformat) +## 0.0.5(2021-07-08) +- 调整 默认时间不再是当前时间,而是显示'-'字符 +## 0.0.4(2021-05-12) +- 新增 组件示例地址 +## 0.0.3(2021-02-04) +- 调整为uni_modules目录规范 +- 修复 iOS 平台日期格式化出错的问题 diff --git a/uni_modules/uni-dateformat/components/uni-dateformat/date-format.js b/uni_modules/uni-dateformat/components/uni-dateformat/date-format.js new file mode 100644 index 0000000..e00d559 --- /dev/null +++ b/uni_modules/uni-dateformat/components/uni-dateformat/date-format.js @@ -0,0 +1,200 @@ +// yyyy-MM-dd hh:mm:ss.SSS 所有支持的类型 +function pad(str, length = 2) { + str += '' + while (str.length < length) { + str = '0' + str + } + return str.slice(-length) +} + +const parser = { + yyyy: (dateObj) => { + return pad(dateObj.year, 4) + }, + yy: (dateObj) => { + return pad(dateObj.year) + }, + MM: (dateObj) => { + return pad(dateObj.month) + }, + M: (dateObj) => { + return dateObj.month + }, + dd: (dateObj) => { + return pad(dateObj.day) + }, + d: (dateObj) => { + return dateObj.day + }, + hh: (dateObj) => { + return pad(dateObj.hour) + }, + h: (dateObj) => { + return dateObj.hour + }, + mm: (dateObj) => { + return pad(dateObj.minute) + }, + m: (dateObj) => { + return dateObj.minute + }, + ss: (dateObj) => { + return pad(dateObj.second) + }, + s: (dateObj) => { + return dateObj.second + }, + SSS: (dateObj) => { + return pad(dateObj.millisecond, 3) + }, + S: (dateObj) => { + return dateObj.millisecond + }, +} + +// 这都n年了iOS依然不认识2020-12-12,需要转换为2020/12/12 +function getDate(time) { + if (time instanceof Date) { + return time + } + switch (typeof time) { + case 'string': + { + // 2020-12-12T12:12:12.000Z、2020-12-12T12:12:12.000 + if (time.indexOf('T') > -1) { + return new Date(time) + } + return new Date(time.replace(/-/g, '/')) + } + default: + return new Date(time) + } +} + +export function formatDate(date, format = 'yyyy/MM/dd hh:mm:ss') { + if (!date && date !== 0) { + return '' + } + date = getDate(date) + const dateObj = { + year: date.getFullYear(), + month: date.getMonth() + 1, + day: date.getDate(), + hour: date.getHours(), + minute: date.getMinutes(), + second: date.getSeconds(), + millisecond: date.getMilliseconds() + } + const tokenRegExp = /yyyy|yy|MM|M|dd|d|hh|h|mm|m|ss|s|SSS|SS|S/ + let flag = true + let result = format + while (flag) { + flag = false + result = result.replace(tokenRegExp, function(matched) { + flag = true + return parser[matched](dateObj) + }) + } + return result +} + +export function friendlyDate(time, { + locale = 'zh', + threshold = [60000, 3600000], + format = 'yyyy/MM/dd hh:mm:ss' +}) { + if (time === '-') { + return time + } + if (!time && time !== 0) { + return '' + } + const localeText = { + zh: { + year: '年', + month: '月', + day: '天', + hour: '小时', + minute: '分钟', + second: '秒', + ago: '前', + later: '后', + justNow: '刚刚', + soon: '马上', + template: '{num}{unit}{suffix}' + }, + en: { + year: 'year', + month: 'month', + day: 'day', + hour: 'hour', + minute: 'minute', + second: 'second', + ago: 'ago', + later: 'later', + justNow: 'just now', + soon: 'soon', + template: '{num} {unit} {suffix}' + } + } + const text = localeText[locale] || localeText.zh + let date = getDate(time) + let ms = date.getTime() - Date.now() + let absMs = Math.abs(ms) + if (absMs < threshold[0]) { + return ms < 0 ? text.justNow : text.soon + } + if (absMs >= threshold[1]) { + return formatDate(date, format) + } + let num + let unit + let suffix = text.later + if (ms < 0) { + suffix = text.ago + ms = -ms + } + const seconds = Math.floor((ms) / 1000) + const minutes = Math.floor(seconds / 60) + const hours = Math.floor(minutes / 60) + const days = Math.floor(hours / 24) + const months = Math.floor(days / 30) + const years = Math.floor(months / 12) + switch (true) { + case years > 0: + num = years + unit = text.year + break + case months > 0: + num = months + unit = text.month + break + case days > 0: + num = days + unit = text.day + break + case hours > 0: + num = hours + unit = text.hour + break + case minutes > 0: + num = minutes + unit = text.minute + break + default: + num = seconds + unit = text.second + break + } + + if (locale === 'en') { + if (num === 1) { + num = 'a' + } else { + unit += 's' + } + } + + return text.template.replace(/{\s*num\s*}/g, num + '').replace(/{\s*unit\s*}/g, unit).replace(/{\s*suffix\s*}/g, + suffix) +} diff --git a/uni_modules/uni-dateformat/components/uni-dateformat/uni-dateformat.vue b/uni_modules/uni-dateformat/components/uni-dateformat/uni-dateformat.vue new file mode 100644 index 0000000..c5ed030 --- /dev/null +++ b/uni_modules/uni-dateformat/components/uni-dateformat/uni-dateformat.vue @@ -0,0 +1,88 @@ +<template> + <text>{{dateShow}}</text> +</template> + +<script> + import {friendlyDate} from './date-format.js' + /** + * Dateformat 日期格式化 + * @description 日期格式化组件 + * @tutorial https://ext.dcloud.net.cn/plugin?id=3279 + * @property {Object|String|Number} date 日期对象/日期字符串/时间戳 + * @property {String} locale 格式化使用的语言 + * @value zh 中文 + * @value en 英文 + * @property {Array} threshold 应用不同类型格式化的阈值 + * @property {String} format 输出日期字符串时的格式 + */ + export default { + name: 'uniDateformat', + props: { + date: { + type: [Object, String, Number], + default () { + return '-' + } + }, + locale: { + type: String, + default: 'zh', + }, + threshold: { + type: Array, + default () { + return [0, 0] + } + }, + format: { + type: String, + default: 'yyyy/MM/dd hh:mm:ss' + }, + // refreshRate使用不当可能导致性能问题,谨慎使用 + refreshRate: { + type: [Number, String], + default: 0 + } + }, + data() { + return { + refreshMark: 0 + } + }, + computed: { + dateShow() { + this.refreshMark + return friendlyDate(this.date, { + locale: this.locale, + threshold: this.threshold, + format: this.format + }) + } + }, + watch: { + refreshRate: { + handler() { + this.setAutoRefresh() + }, + immediate: true + } + }, + methods: { + refresh() { + this.refreshMark++ + }, + setAutoRefresh() { + clearInterval(this.refreshInterval) + if (this.refreshRate) { + this.refreshInterval = setInterval(() => { + this.refresh() + }, parseInt(this.refreshRate)) + } + } + } + } +</script> + +<style> + +</style> diff --git a/uni_modules/uni-dateformat/package.json b/uni_modules/uni-dateformat/package.json new file mode 100644 index 0000000..786a670 --- /dev/null +++ b/uni_modules/uni-dateformat/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-dateformat", + "displayName": "uni-dateformat 日期格式化", + "version": "1.0.0", + "description": "日期格式化组件,可以将日期格式化为1分钟前、刚刚等形式", + "keywords": [ + "uni-ui", + "uniui", + "日期格式化", + "时间格式化", + "格式化时间", + "" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "y", + "联盟": "y" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-dateformat/readme.md b/uni_modules/uni-dateformat/readme.md new file mode 100644 index 0000000..37ddb6e --- /dev/null +++ b/uni_modules/uni-dateformat/readme.md @@ -0,0 +1,11 @@ + + +### DateFormat 日期格式化 +> **组件名:uni-dateformat** +> 代码块: `uDateformat` + + +日期格式化组件。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-dateformat) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-datetime-picker/changelog.md b/uni_modules/uni-datetime-picker/changelog.md new file mode 100644 index 0000000..102ddb1 --- /dev/null +++ b/uni_modules/uni-datetime-picker/changelog.md @@ -0,0 +1,168 @@ +## 2.2.38(2024-10-15) +- 修复 微信小程序中的getSystemInfo警告 +## 2.2.37(2024-10-12) +- 修复 微信小程序中的getSystemInfo警告 +## 2.2.36(2024-10-12) +- 修复 微信小程序中的getSystemInfo警告 +## 2.2.35(2024-09-21) +- 修复 没有选中日期时点击确定直接报错的Bug [详情](https://ask.dcloud.net.cn/question/198168) +## 2.2.34(2024-04-24) +- 新增 日期点击事件,在点击日期时会触发该事件。 +## 2.2.33(2024-04-15) +- 修复 抖音小程序事件传递失效bug +## 2.2.32(2024-02-20) +- 修复 日历的close事件触发异常的bug [详情](https://github.com/dcloudio/uni-ui/issues/844) +## 2.2.31(2024-02-20) +- 修复 h5平台 右边日历的月份默认+1的bug [详情](https://github.com/dcloudio/uni-ui/issues/841) +## 2.2.30(2024-01-31) +- 修复 隐藏“秒”时,在IOS15及以下版本时出现 结束时间在开始时间之前 的bug [详情](https://github.com/dcloudio/uni-ui/issues/788) +## 2.2.29(2024-01-20) +- 新增 show事件,弹窗弹出时触发该事件 [详情](https://github.com/dcloudio/uni-app/issues/4694) +## 2.2.28(2024-01-18) +- 去除 noChange事件,当进行日期范围选择时,若只选了一天,则开始结束日期都为同一天 [详情](https://github.com/dcloudio/uni-ui/issues/815) +## 2.2.27(2024-01-10) +- 优化 增加noChange事件,当进行日期范围选择时,若有空值,则触发该事件 [详情](https://github.com/dcloudio/uni-ui/issues/815) +## 2.2.26(2024-01-08) +- 修复 字节小程序时间选择范围器失效问题 [详情](https://github.com/dcloudio/uni-ui/issues/834) +## 2.2.25(2023-10-18) +- 修复 PC端初次修改时间,开始时间未更新的Bug [详情](https://github.com/dcloudio/uni-ui/issues/737) +## 2.2.24(2023-06-02) +- 修复 部分情况修改时间,开始、结束时间显示异常的Bug [详情](https://ask.dcloud.net.cn/question/171146) +- 优化 当前月可以选择上月、下月的日期的Bug +## 2.2.23(2023-05-02) +- 修复 部分情况修改时间,开始时间未更新的Bug [详情](https://github.com/dcloudio/uni-ui/issues/737) +- 修复 部分平台及设备第一次点击无法显示弹框的Bug +- 修复 ios 日期格式未补零显示及使用异常的Bug [详情](https://ask.dcloud.net.cn/question/162979) +## 2.2.22(2023-03-30) +- 修复 日历 picker 修改年月后,自动选中当月1日的Bug [详情](https://ask.dcloud.net.cn/question/165937) +- 修复 小程序端 低版本 ios NaN的Bug [详情](https://ask.dcloud.net.cn/question/162979) +## 2.2.21(2023-02-20) +- 修复 firefox 浏览器显示区域点击无法拉起日历弹框的Bug [详情](https://ask.dcloud.net.cn/question/163362) +## 2.2.20(2023-02-17) +- 优化 值为空依然选中当天问题 +- 优化 提供 default-value 属性支持配置选择器打开时默认显示的时间 +- 优化 非范围选择未选择日期时间,点击确认按钮选中当前日期时间 +- 优化 字节小程序日期时间范围选择,底部日期换行的Bug +## 2.2.19(2023-02-09) +- 修复 2.2.18 引起范围选择配置 end 选择无效的Bug [详情](https://github.com/dcloudio/uni-ui/issues/686) +## 2.2.18(2023-02-08) +- 修复 移动端范围选择change事件触发异常的Bug [详情](https://github.com/dcloudio/uni-ui/issues/684) +- 优化 PC端输入日期格式错误时返回当前日期时间 +- 优化 PC端输入日期时间超出 start、end 限制的Bug +- 优化 移动端日期时间范围用法时间展示不完整问题 +## 2.2.17(2023-02-04) +- 修复 小程序端绑定 Date 类型报错的Bug [详情](https://github.com/dcloudio/uni-ui/issues/679) +- 修复 vue3 time-picker 无法显示绑定时分秒的Bug +## 2.2.16(2023-02-02) +- 修复 字节小程序报错的Bug +## 2.2.15(2023-02-02) +- 修复 某些情况切换月份错误的Bug +## 2.2.14(2023-01-30) +- 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/162033) +## 2.2.13(2023-01-10) +- 修复 多次加载组件造成内存占用的Bug +## 2.2.12(2022-12-01) +- 修复 vue3 下 i18n 国际化初始值不正确的Bug +## 2.2.11(2022-09-19) +- 修复 支付宝小程序样式错乱的Bug [详情](https://github.com/dcloudio/uni-app/issues/3861) +## 2.2.10(2022-09-19) +- 修复 反向选择日期范围,日期显示异常的Bug [详情](https://ask.dcloud.net.cn/question/153401?item_id=212892&rf=false) +## 2.2.9(2022-09-16) +- 可以使用 uni-scss 控制主题色 +## 2.2.8(2022-09-08) +- 修复 close事件无效的Bug +## 2.2.7(2022-09-05) +- 修复 移动端 maskClick 无效的Bug [详情](https://ask.dcloud.net.cn/question/140824) +## 2.2.6(2022-06-30) +- 优化 组件样式,调整了组件图标大小、高度、颜色等,与uni-ui风格保持一致 +## 2.2.5(2022-06-24) +- 修复 日历顶部年月及底部确认未国际化的Bug +## 2.2.4(2022-03-31) +- 修复 Vue3 下动态赋值,单选类型未响应的Bug +## 2.2.3(2022-03-28) +- 修复 Vue3 下动态赋值未响应的Bug +## 2.2.2(2021-12-10) +- 修复 clear-icon 属性在小程序平台不生效的Bug +## 2.2.1(2021-12-10) +- 修复 日期范围选在小程序平台,必须多点击一次才能取消选中状态的Bug +## 2.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源 [详情](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移 [https://uniapp.dcloud.io/component/uniui/uni-datetime-picker](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker) +## 2.1.5(2021-11-09) +- 新增 提供组件设计资源,组件样式调整 +## 2.1.4(2021-09-10) +- 修复 hide-second 在移动端的Bug +- 修复 单选赋默认值时,赋值日期未高亮的Bug +- 修复 赋默认值时,移动端未正确显示时间的Bug +## 2.1.3(2021-09-09) +- 新增 hide-second 属性,支持只使用时分,隐藏秒 +## 2.1.2(2021-09-03) +- 优化 取消选中时(范围选)直接开始下一次选择, 避免多点一次 +- 优化 移动端支持清除按钮,同时支持通过 ref 调用组件的 clear 方法 +- 优化 调整字号大小,美化日历界面 +- 修复 因国际化导致的 placeholder 失效的Bug +## 2.1.1(2021-08-24) +- 新增 支持国际化 +- 优化 范围选择器在 pc 端过宽的问题 +## 2.1.0(2021-08-09) +- 新增 适配 vue3 +## 2.0.19(2021-08-09) +- 新增 支持作为 uni-forms 子组件相关功能 +- 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的Bug +## 2.0.18(2021-08-05) +- 修复 type 属性动态赋值无效的Bug +- 修复 ‘确认’按钮被 tabbar 遮盖 bug +- 修复 组件未赋值时范围选左、右日历相同的Bug +## 2.0.17(2021-08-04) +- 修复 范围选未正确显示当前值的Bug +- 修复 h5 平台(移动端)报错 'cale' of undefined 的Bug +## 2.0.16(2021-07-21) +- 新增 return-type 属性支持返回 date 日期对象 +## 2.0.15(2021-07-14) +- 修复 单选日期类型,初始赋值后不在当前日历的Bug +- 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效) +- 优化 移动端移除显示框的清空按钮,无实际用途 +## 2.0.14(2021-07-14) +- 修复 组件赋值为空,界面未更新的Bug +- 修复 start 和 end 不能动态赋值的Bug +- 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的Bug +## 2.0.13(2021-07-08) +- 修复 范围选择不能动态赋值的Bug +## 2.0.12(2021-07-08) +- 修复 范围选择的初始时间在一个月内时,造成无法选择的bug +## 2.0.11(2021-07-08) +- 优化 弹出层在超出视窗边缘定位不准确的问题 +## 2.0.10(2021-07-08) +- 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的Bug +- 优化 弹出层在超出视窗边缘被遮盖的问题 +## 2.0.9(2021-07-07) +- 新增 maskClick 事件 +- 修复 特殊情况日历 rpx 布局错误的Bug,rpx -> px +- 修复 范围选择时清空返回值不合理的bug,['', ''] -> [] +## 2.0.8(2021-07-07) +- 新增 日期时间显示框支持插槽 +## 2.0.7(2021-07-01) +- 优化 添加 uni-icons 依赖 +## 2.0.6(2021-05-22) +- 修复 图标在小程序上不显示的Bug +- 优化 重命名引用组件,避免潜在组件命名冲突 +## 2.0.5(2021-05-20) +- 优化 代码目录扁平化 +## 2.0.4(2021-05-12) +- 新增 组件示例地址 +## 2.0.3(2021-05-10) +- 修复 ios 下不识别 '-' 日期格式的Bug +- 优化 pc 下弹出层添加边框和阴影 +## 2.0.2(2021-05-08) +- 修复 在 admin 中获取弹出层定位错误的bug +## 2.0.1(2021-05-08) +- 修复 type 属性向下兼容,默认值从 date 变更为 datetime +## 2.0.0(2021-04-30) +- 支持日历形式的日期+时间的范围选择 + > 注意:此版本不向后兼容,不再支持单独时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker) +## 1.0.6(2021-03-18) +- 新增 hide-second 属性,时间支持仅选择时、分 +- 修复 选择跟显示的日期不一样的Bug +- 修复 chang事件触发2次的Bug +- 修复 分、秒 end 范围错误的Bug +- 优化 更好的 nvue 适配 diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue new file mode 100644 index 0000000..dba9887 --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue @@ -0,0 +1,177 @@ +<template> + <view class="uni-calendar-item__weeks-box" :class="{ + 'uni-calendar-item--disable':weeks.disable, + 'uni-calendar-item--before-checked-x':weeks.beforeMultiple, + 'uni-calendar-item--multiple': weeks.multiple, + 'uni-calendar-item--after-checked-x':weeks.afterMultiple, + }" @click="choiceDate(weeks)" @mouseenter="handleMousemove(weeks)"> + <view class="uni-calendar-item__weeks-box-item" :class="{ + 'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && (calendar.userChecked || !checkHover), + 'uni-calendar-item--checked-range-text': checkHover, + 'uni-calendar-item--before-checked':weeks.beforeMultiple, + 'uni-calendar-item--multiple': weeks.multiple, + 'uni-calendar-item--after-checked':weeks.afterMultiple, + 'uni-calendar-item--disable':weeks.disable, + }"> + <text v-if="selected && weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text> + <text class="uni-calendar-item__weeks-box-text uni-calendar-item__weeks-box-text-disable uni-calendar-item--checked-text">{{weeks.date}}</text> + </view> + <view :class="{'uni-calendar-item--today': weeks.isToday}"></view> + </view> +</template> + +<script> + export default { + props: { + weeks: { + type: Object, + default () { + return {} + } + }, + calendar: { + type: Object, + default: () => { + return {} + } + }, + selected: { + type: Array, + default: () => { + return [] + } + }, + checkHover: { + type: Boolean, + default: false + } + }, + methods: { + choiceDate(weeks) { + this.$emit('change', weeks) + }, + handleMousemove(weeks) { + this.$emit('handleMouse', weeks) + } + } + } +</script> + +<style lang="scss" > + $uni-primary: #007aff !default; + + .uni-calendar-item__weeks-box { + flex: 1; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + justify-content: center; + align-items: center; + margin: 1px 0; + position: relative; + } + + .uni-calendar-item__weeks-box-text { + font-size: 14px; + // font-family: Lato-Bold, Lato; + font-weight: bold; + color: darken($color: $uni-primary, $amount: 40%); + } + + .uni-calendar-item__weeks-box-item { + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + justify-content: center; + align-items: center; + width: 40px; + height: 40px; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + + .uni-calendar-item__weeks-box-circle { + position: absolute; + top: 5px; + right: 5px; + width: 8px; + height: 8px; + border-radius: 8px; + background-color: #dd524d; + + } + + .uni-calendar-item__weeks-box .uni-calendar-item--disable { + cursor: default; + } + + .uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable { + color: #D1D1D1; + } + + .uni-calendar-item--today { + position: absolute; + top: 10px; + right: 17%; + background-color: #dd524d; + width:6px; + height: 6px; + border-radius: 50%; + } + + .uni-calendar-item--extra { + color: #dd524d; + opacity: 0.8; + } + + .uni-calendar-item__weeks-box .uni-calendar-item--checked { + background-color: $uni-primary; + border-radius: 50%; + box-sizing: border-box; + border: 3px solid #fff; + } + + .uni-calendar-item--checked .uni-calendar-item--checked-text { + color: #fff; + } + + .uni-calendar-item--multiple .uni-calendar-item--checked-range-text { + color: #333; + } + + .uni-calendar-item--multiple { + background-color: #F6F7FC; + // color: #fff; + } + + .uni-calendar-item--multiple .uni-calendar-item--before-checked, + .uni-calendar-item--multiple .uni-calendar-item--after-checked { + background-color: $uni-primary; + border-radius: 50%; + box-sizing: border-box; + border: 3px solid #F6F7FC; + } + + .uni-calendar-item--before-checked .uni-calendar-item--checked-text, + .uni-calendar-item--after-checked .uni-calendar-item--checked-text { + color: #fff; + } + + .uni-calendar-item--before-checked-x { + border-top-left-radius: 50px; + border-bottom-left-radius: 50px; + box-sizing: border-box; + background-color: #F6F7FC; + } + + .uni-calendar-item--after-checked-x { + border-top-right-radius: 50px; + border-bottom-right-radius: 50px; + background-color: #F6F7FC; + } +</style> diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue new file mode 100644 index 0000000..0f9e121 --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue @@ -0,0 +1,947 @@ +<template> + <view class="uni-calendar" @mouseleave="leaveCale"> + + <view v-if="!insert && show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" + @click="maskClick"></view> + + <view v-if="insert || show" class="uni-calendar__content" + :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow, 'uni-calendar__content-mobile': aniMaskShow}"> + <view class="uni-calendar__header" :class="{'uni-calendar__header-mobile' :!insert}"> + + <view class="uni-calendar__header-btn-box" @click.stop="changeMonth('pre')"> + <view class="uni-calendar__header-btn uni-calendar--left"></view> + </view> + + <picker mode="date" :value="date" fields="month" @change="bindDateChange"> + <text + class="uni-calendar__header-text">{{ (nowDate.year||'') + yearText + ( nowDate.month||'') + monthText}}</text> + </picker> + + <view class="uni-calendar__header-btn-box" @click.stop="changeMonth('next')"> + <view class="uni-calendar__header-btn uni-calendar--right"></view> + </view> + + <view v-if="!insert" class="dialog-close" @click="maskClick"> + <view class="dialog-close-plus" data-id="close"></view> + <view class="dialog-close-plus dialog-close-rotate" data-id="close"></view> + </view> + </view> + <view class="uni-calendar__box"> + + <view v-if="showMonth" class="uni-calendar__box-bg"> + <text class="uni-calendar__box-bg-text">{{nowDate.month}}</text> + </view> + + <view class="uni-calendar__weeks" style="padding-bottom: 7px;"> + <view class="uni-calendar__weeks-day"> + <text class="uni-calendar__weeks-day-text">{{SUNText}}</text> + </view> + <view class="uni-calendar__weeks-day"> + <text class="uni-calendar__weeks-day-text">{{MONText}}</text> + </view> + <view class="uni-calendar__weeks-day"> + <text class="uni-calendar__weeks-day-text">{{TUEText}}</text> + </view> + <view class="uni-calendar__weeks-day"> + <text class="uni-calendar__weeks-day-text">{{WEDText}}</text> + </view> + <view class="uni-calendar__weeks-day"> + <text class="uni-calendar__weeks-day-text">{{THUText}}</text> + </view> + <view class="uni-calendar__weeks-day"> + <text class="uni-calendar__weeks-day-text">{{FRIText}}</text> + </view> + <view class="uni-calendar__weeks-day"> + <text class="uni-calendar__weeks-day-text">{{SATText}}</text> + </view> + </view> + + <view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex"> + <view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex"> + <calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected" + :checkHover="range" @change="choiceDate" @handleMouse="handleMouse"> + </calendar-item> + </view> + </view> + </view> + + <view v-if="!insert && !range && hasTime" class="uni-date-changed uni-calendar--fixed-top" + style="padding: 0 80px;"> + <view class="uni-date-changed--time-date">{{tempSingleDate ? tempSingleDate : selectDateText}}</view> + <time-picker type="time" :start="timepickerStartTime" :end="timepickerEndTime" v-model="time" + :disabled="!tempSingleDate" :border="false" :hide-second="hideSecond" class="time-picker-style"> + </time-picker> + </view> + + <view v-if="!insert && range && hasTime" class="uni-date-changed uni-calendar--fixed-top"> + <view class="uni-date-changed--time-start"> + <view class="uni-date-changed--time-date">{{tempRange.before ? tempRange.before : startDateText}} + </view> + <time-picker type="time" :start="timepickerStartTime" v-model="timeRange.startTime" :border="false" + :hide-second="hideSecond" :disabled="!tempRange.before" class="time-picker-style"> + </time-picker> + </view> + <view style="line-height: 50px;"> + <uni-icons type="arrowthinright" color="#999"></uni-icons> + </view> + <view class="uni-date-changed--time-end"> + <view class="uni-date-changed--time-date">{{tempRange.after ? tempRange.after : endDateText}}</view> + <time-picker type="time" :end="timepickerEndTime" v-model="timeRange.endTime" :border="false" + :hide-second="hideSecond" :disabled="!tempRange.after" class="time-picker-style"> + </time-picker> + </view> + </view> + + <view v-if="!insert" class="uni-date-changed uni-date-btn--ok"> + <view class="uni-datetime-picker--btn" @click="confirm">{{confirmText}}</view> + </view> + </view> + </view> +</template> + +<script> + import { + Calendar, + getDate, + getTime + } from './util.js'; + import calendarItem from './calendar-item.vue' + import timePicker from './time-picker.vue' + + import { + initVueI18n + } from '@dcloudio/uni-i18n' + import i18nMessages from './i18n/index.js' + const { + t + } = initVueI18n(i18nMessages) + + /** + * Calendar 日历 + * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等 + * @tutorial https://ext.dcloud.net.cn/plugin?id=56 + * @property {String} date 自定义当前时间,默认为今天 + * @property {String} startDate 日期选择范围-开始日期 + * @property {String} endDate 日期选择范围-结束日期 + * @property {Boolean} 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 {Boolean} showMonth 是否选择月份为背景 + * @property {[String} defaultValue 选择器打开时默认显示的时间 + * @event {Function} change 日期改变,`insert :ture` 时生效 + * @event {Function} confirm 确认选择`insert :false` 时生效 + * @event {Function} monthSwitch 切换月份时触发 + * @example <uni-calendar :insert="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" /> + */ + export default { + components: { + calendarItem, + timePicker + }, + + options: { + // #ifdef MP-TOUTIAO + virtualHost: false, + // #endif + // #ifndef MP-TOUTIAO + virtualHost: true + // #endif + }, + props: { + date: { + type: String, + default: '' + }, + defTime: { + type: [String, Object], + default: '' + }, + selectableTimes: { + type: [Object], + default () { + return {} + } + }, + selected: { + type: Array, + default () { + return [] + } + }, + startDate: { + type: String, + default: '' + }, + endDate: { + type: String, + default: '' + }, + startPlaceholder: { + type: String, + default: '' + }, + endPlaceholder: { + type: String, + default: '' + }, + range: { + type: Boolean, + default: false + }, + hasTime: { + type: Boolean, + default: false + }, + insert: { + type: Boolean, + default: true + }, + showMonth: { + type: Boolean, + default: true + }, + clearDate: { + type: Boolean, + default: true + }, + checkHover: { + type: Boolean, + default: true + }, + hideSecond: { + type: [Boolean], + default: false + }, + pleStatus: { + type: Object, + default () { + return { + before: '', + after: '', + data: [], + fulldate: '' + } + } + }, + defaultValue: { + type: [String, Object, Array], + default: '' + } + }, + data() { + return { + show: false, + weeks: [], + calendar: {}, + nowDate: {}, + aniMaskShow: false, + firstEnter: true, + time: '', + timeRange: { + startTime: '', + endTime: '' + }, + tempSingleDate: '', + tempRange: { + before: '', + after: '' + } + } + }, + watch: { + date: { + immediate: true, + handler(newVal) { + if (!this.range) { + this.tempSingleDate = newVal + setTimeout(() => { + this.init(newVal) + }, 100) + } + } + }, + defTime: { + immediate: true, + handler(newVal) { + if (!this.range) { + this.time = newVal + } else { + this.timeRange.startTime = newVal.start + this.timeRange.endTime = newVal.end + } + } + }, + startDate(val) { + // 字节小程序 watch 早于 created + if (!this.cale) { + return + } + this.cale.setStartDate(val) + this.cale.setDate(this.nowDate.fullDate) + this.weeks = this.cale.weeks + }, + endDate(val) { + // 字节小程序 watch 早于 created + if (!this.cale) { + return + } + this.cale.setEndDate(val) + this.cale.setDate(this.nowDate.fullDate) + this.weeks = this.cale.weeks + }, + selected(newVal) { + // 字节小程序 watch 早于 created + if (!this.cale) { + return + } + this.cale.setSelectInfo(this.nowDate.fullDate, newVal) + this.weeks = this.cale.weeks + }, + pleStatus: { + immediate: true, + handler(newVal) { + const { + before, + after, + fulldate, + which + } = newVal + this.tempRange.before = before + this.tempRange.after = after + setTimeout(() => { + if (fulldate) { + this.cale.setHoverMultiple(fulldate) + if (before && after) { + this.cale.lastHover = true + if (this.rangeWithinMonth(after, before)) return + this.setDate(before) + } else { + this.cale.setMultiple(fulldate) + this.setDate(this.nowDate.fullDate) + this.calendar.fullDate = '' + this.cale.lastHover = false + } + } else { + // 字节小程序 watch 早于 created + if (!this.cale) { + return + } + + this.cale.setDefaultMultiple(before, after) + if (which === 'left' && before) { + this.setDate(before) + this.weeks = this.cale.weeks + } else if (after) { + this.setDate(after) + this.weeks = this.cale.weeks + } + this.cale.lastHover = true + } + }, 16) + } + } + }, + computed: { + timepickerStartTime() { + const activeDate = this.range ? this.tempRange.before : this.calendar.fullDate + return activeDate === this.startDate ? this.selectableTimes.start : '' + }, + timepickerEndTime() { + const activeDate = this.range ? this.tempRange.after : this.calendar.fullDate + return activeDate === this.endDate ? this.selectableTimes.end : '' + }, + /** + * for i18n + */ + selectDateText() { + return t("uni-datetime-picker.selectDate") + }, + startDateText() { + return this.startPlaceholder || t("uni-datetime-picker.startDate") + }, + endDateText() { + return this.endPlaceholder || t("uni-datetime-picker.endDate") + }, + okText() { + return t("uni-datetime-picker.ok") + }, + yearText() { + return t("uni-datetime-picker.year") + }, + monthText() { + return t("uni-datetime-picker.month") + }, + MONText() { + return t("uni-calender.MON") + }, + TUEText() { + return t("uni-calender.TUE") + }, + WEDText() { + return t("uni-calender.WED") + }, + THUText() { + return t("uni-calender.THU") + }, + FRIText() { + return t("uni-calender.FRI") + }, + SATText() { + return t("uni-calender.SAT") + }, + SUNText() { + return t("uni-calender.SUN") + }, + confirmText() { + return t("uni-calender.confirm") + }, + }, + created() { + // 获取日历方法实例 + this.cale = new Calendar({ + selected: this.selected, + startDate: this.startDate, + endDate: this.endDate, + range: this.range, + }) + // 选中某一天 + this.init(this.date) + }, + methods: { + leaveCale() { + this.firstEnter = true + }, + handleMouse(weeks) { + if (weeks.disable) return + if (this.cale.lastHover) return + let { + before, + after + } = this.cale.multipleStatus + if (!before) return + this.calendar = weeks + // 设置范围选 + this.cale.setHoverMultiple(this.calendar.fullDate) + this.weeks = this.cale.weeks + // hover时,进入一个日历,更新另一个 + if (this.firstEnter) { + this.$emit('firstEnterCale', this.cale.multipleStatus) + this.firstEnter = false + } + }, + rangeWithinMonth(A, B) { + const [yearA, monthA] = A.split('-') + const [yearB, monthB] = B.split('-') + return yearA === yearB && monthA === monthB + }, + // 蒙版点击事件 + maskClick() { + this.close() + this.$emit('maskClose') + }, + + clearCalender() { + if (this.range) { + this.timeRange.startTime = '' + this.timeRange.endTime = '' + this.tempRange.before = '' + this.tempRange.after = '' + this.cale.multipleStatus.before = '' + this.cale.multipleStatus.after = '' + this.cale.multipleStatus.data = [] + this.cale.lastHover = false + } else { + this.time = '' + this.tempSingleDate = '' + } + this.calendar.fullDate = '' + this.setDate(new Date()) + }, + + bindDateChange(e) { + const value = e.detail.value + '-1' + this.setDate(value) + }, + /** + * 初始化日期显示 + * @param {Object} date + */ + init(date) { + // 字节小程序 watch 早于 created + if (!this.cale) { + return + } + this.cale.setDate(date || new Date()) + this.weeks = this.cale.weeks + this.nowDate = this.cale.getInfo(date) + this.calendar = { + ...this.nowDate + } + if (!date) { + // 优化date为空默认不选中今天 + this.calendar.fullDate = '' + if (this.defaultValue && !this.range) { + // 暂时只支持移动端非范围选择 + const defaultDate = new Date(this.defaultValue) + const fullDate = getDate(defaultDate) + const year = defaultDate.getFullYear() + const month = defaultDate.getMonth() + 1 + const date = defaultDate.getDate() + const day = defaultDate.getDay() + this.calendar = { + fullDate, + year, + month, + date, + day + }, + this.tempSingleDate = fullDate + this.time = getTime(defaultDate, this.hideSecond) + } + } + }, + /** + * 打开日历弹窗 + */ + open() { + // 弹窗模式并且清理数据 + if (this.clearDate && !this.insert) { + this.cale.cleanMultipleStatus() + this.init(this.date) + } + this.show = true + this.$nextTick(() => { + setTimeout(() => { + this.aniMaskShow = true + }, 50) + }) + }, + /** + * 关闭日历弹窗 + */ + close() { + this.aniMaskShow = false + this.$nextTick(() => { + setTimeout(() => { + this.show = false + this.$emit('close') + }, 300) + }) + }, + /** + * 确认按钮 + */ + confirm() { + this.setEmit('confirm') + this.close() + }, + /** + * 变化触发 + */ + change(isSingleChange) { + if (!this.insert && !isSingleChange) return + this.setEmit('change') + }, + /** + * 选择月份触发 + */ + monthSwitch() { + let { + year, + month + } = this.nowDate + this.$emit('monthSwitch', { + year, + month: Number(month) + }) + }, + /** + * 派发事件 + * @param {Object} name + */ + setEmit(name) { + if (!this.range) { + if (!this.calendar.fullDate) { + this.calendar = this.cale.getInfo(new Date()) + this.tempSingleDate = this.calendar.fullDate + } + if (this.hasTime && !this.time) { + this.time = getTime(new Date(), this.hideSecond) + } + } + let { + year, + month, + date, + fullDate, + extraInfo + } = this.calendar + this.$emit(name, { + range: this.cale.multipleStatus, + year, + month, + date, + time: this.time, + timeRange: this.timeRange, + fulldate: fullDate, + extraInfo: extraInfo || {} + }) + }, + /** + * 选择天触发 + * @param {Object} weeks + */ + choiceDate(weeks) { + if (weeks.disable) return + this.calendar = weeks + this.calendar.userChecked = true + // 设置多选 + this.cale.setMultiple(this.calendar.fullDate, true) + this.weeks = this.cale.weeks + this.tempSingleDate = this.calendar.fullDate + const beforeDate = new Date(this.cale.multipleStatus.before).getTime() + const afterDate = new Date(this.cale.multipleStatus.after).getTime() + if (beforeDate > afterDate && afterDate) { + this.tempRange.before = this.cale.multipleStatus.after + this.tempRange.after = this.cale.multipleStatus.before + } else { + this.tempRange.before = this.cale.multipleStatus.before + this.tempRange.after = this.cale.multipleStatus.after + } + this.change(true) + }, + changeMonth(type) { + let newDate + if (type === 'pre') { + newDate = this.cale.getPreMonthObj(this.nowDate.fullDate).fullDate + } else if (type === 'next') { + newDate = this.cale.getNextMonthObj(this.nowDate.fullDate).fullDate + } + + this.setDate(newDate) + this.monthSwitch() + }, + /** + * 设置日期 + * @param {Object} date + */ + setDate(date) { + this.cale.setDate(date) + this.weeks = this.cale.weeks + this.nowDate = this.cale.getInfo(date) + } + } + } +</script> + +<style lang="scss"> + $uni-primary: #007aff !default; + + .uni-calendar { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + } + + .uni-calendar__mask { + position: fixed; + bottom: 0; + top: 0; + left: 0; + right: 0; + background-color: rgba(0, 0, 0, 0.4); + transition-property: opacity; + transition-duration: 0.3s; + opacity: 0; + /* #ifndef APP-NVUE */ + z-index: 99; + /* #endif */ + } + + .uni-calendar--mask-show { + opacity: 1 + } + + .uni-calendar--fixed { + position: fixed; + bottom: calc(var(--window-bottom)); + left: 0; + right: 0; + transition-property: transform; + transition-duration: 0.3s; + transform: translateY(460px); + /* #ifndef APP-NVUE */ + z-index: 99; + /* #endif */ + } + + .uni-calendar--ani-show { + transform: translateY(0); + } + + .uni-calendar__content { + background-color: #fff; + } + + .uni-calendar__content-mobile { + border-top-left-radius: 10px; + border-top-right-radius: 10px; + box-shadow: 0px 0px 5px 3px rgba(0, 0, 0, 0.1); + } + + .uni-calendar__header { + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + justify-content: center; + align-items: center; + height: 50px; + } + + .uni-calendar__header-mobile { + padding: 10px; + padding-bottom: 0; + } + + .uni-calendar--fixed-top { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + justify-content: space-between; + border-top-color: rgba(0, 0, 0, 0.4); + border-top-style: solid; + border-top-width: 1px; + } + + .uni-calendar--fixed-width { + width: 50px; + } + + .uni-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: #fff; + background-color: #f1f1f1; + } + + .uni-calendar__header-text { + text-align: center; + width: 100px; + font-size: 15px; + color: #666; + } + + .uni-calendar__button-text { + text-align: center; + width: 100px; + font-size: 14px; + color: $uni-primary; + /* #ifndef APP-NVUE */ + letter-spacing: 3px; + /* #endif */ + } + + .uni-calendar__header-btn-box { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + align-items: center; + justify-content: center; + width: 50px; + height: 50px; + } + + .uni-calendar__header-btn { + width: 9px; + height: 9px; + border-left-color: #808080; + border-left-style: solid; + border-left-width: 1px; + border-top-color: #555555; + border-top-style: solid; + border-top-width: 1px; + } + + .uni-calendar--left { + transform: rotate(-45deg); + } + + .uni-calendar--right { + transform: rotate(135deg); + } + + + .uni-calendar__weeks { + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + } + + .uni-calendar__weeks-item { + flex: 1; + } + + .uni-calendar__weeks-day { + flex: 1; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + justify-content: center; + align-items: center; + height: 40px; + border-bottom-color: #F5F5F5; + border-bottom-style: solid; + border-bottom-width: 1px; + } + + .uni-calendar__weeks-day-text { + font-size: 12px; + color: #B2B2B2; + } + + .uni-calendar__box { + position: relative; + // padding: 0 10px; + padding-bottom: 7px; + } + + .uni-calendar__box-bg { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + justify-content: center; + align-items: center; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + } + + .uni-calendar__box-bg-text { + font-size: 200px; + font-weight: bold; + color: #999; + opacity: 0.1; + text-align: center; + /* #ifndef APP-NVUE */ + line-height: 1; + /* #endif */ + } + + .uni-date-changed { + padding: 0 10px; + // line-height: 50px; + text-align: center; + color: #333; + border-top-color: #DCDCDC; + ; + border-top-style: solid; + border-top-width: 1px; + flex: 1; + } + + .uni-date-btn--ok { + padding: 20px 15px; + } + + .uni-date-changed--time-start { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + align-items: center; + } + + .uni-date-changed--time-end { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + align-items: center; + } + + .uni-date-changed--time-date { + color: #999; + line-height: 50px; + /* #ifdef MP-TOUTIAO */ + font-size: 16px; + /* #endif */ + margin-right: 5px; + // opacity: 0.6; + } + + .time-picker-style { + // width: 62px; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + justify-content: center; + align-items: center + } + + .mr-10 { + margin-right: 10px; + } + + .dialog-close { + position: absolute; + top: 0; + right: 0; + bottom: 0; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + align-items: center; + padding: 0 25px; + margin-top: 10px; + } + + .dialog-close-plus { + width: 16px; + height: 2px; + background-color: #737987; + border-radius: 2px; + transform: rotate(45deg); + } + + .dialog-close-rotate { + position: absolute; + transform: rotate(-45deg); + } + + .uni-datetime-picker--btn { + border-radius: 100px; + height: 40px; + line-height: 40px; + background-color: $uni-primary; + color: #fff; + font-size: 16px; + letter-spacing: 2px; + } + + /* #ifndef APP-NVUE */ + .uni-datetime-picker--btn:active { + opacity: 0.7; + } + + /* #endif */ +</style> diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json new file mode 100644 index 0000000..024f22f --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json @@ -0,0 +1,22 @@ +{ + "uni-datetime-picker.selectDate": "select date", + "uni-datetime-picker.selectTime": "select time", + "uni-datetime-picker.selectDateTime": "select date and time", + "uni-datetime-picker.startDate": "start date", + "uni-datetime-picker.endDate": "end date", + "uni-datetime-picker.startTime": "start time", + "uni-datetime-picker.endTime": "end time", + "uni-datetime-picker.ok": "ok", + "uni-datetime-picker.clear": "clear", + "uni-datetime-picker.cancel": "cancel", + "uni-datetime-picker.year": "-", + "uni-datetime-picker.month": "", + "uni-calender.MON": "MON", + "uni-calender.TUE": "TUE", + "uni-calender.WED": "WED", + "uni-calender.THU": "THU", + "uni-calender.FRI": "FRI", + "uni-calender.SAT": "SAT", + "uni-calender.SUN": "SUN", + "uni-calender.confirm": "confirm" +} diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js new file mode 100644 index 0000000..de7509c --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json new file mode 100644 index 0000000..d2df5e7 --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json @@ -0,0 +1,22 @@ +{ + "uni-datetime-picker.selectDate": "选择日期", + "uni-datetime-picker.selectTime": "选择时间", + "uni-datetime-picker.selectDateTime": "选择日期时间", + "uni-datetime-picker.startDate": "开始日期", + "uni-datetime-picker.endDate": "结束日期", + "uni-datetime-picker.startTime": "开始时间", + "uni-datetime-picker.endTime": "结束时间", + "uni-datetime-picker.ok": "确定", + "uni-datetime-picker.clear": "清除", + "uni-datetime-picker.cancel": "取消", + "uni-datetime-picker.year": "年", + "uni-datetime-picker.month": "月", + "uni-calender.SUN": "日", + "uni-calender.MON": "一", + "uni-calender.TUE": "二", + "uni-calender.WED": "三", + "uni-calender.THU": "四", + "uni-calender.FRI": "五", + "uni-calender.SAT": "六", + "uni-calender.confirm": "确认" +} \ No newline at end of file diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json new file mode 100644 index 0000000..d23fa3c --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json @@ -0,0 +1,22 @@ +{ + "uni-datetime-picker.selectDate": "選擇日期", + "uni-datetime-picker.selectTime": "選擇時間", + "uni-datetime-picker.selectDateTime": "選擇日期時間", + "uni-datetime-picker.startDate": "開始日期", + "uni-datetime-picker.endDate": "結束日期", + "uni-datetime-picker.startTime": "開始时间", + "uni-datetime-picker.endTime": "結束时间", + "uni-datetime-picker.ok": "確定", + "uni-datetime-picker.clear": "清除", + "uni-datetime-picker.cancel": "取消", + "uni-datetime-picker.year": "年", + "uni-datetime-picker.month": "月", + "uni-calender.SUN": "日", + "uni-calender.MON": "一", + "uni-calender.TUE": "二", + "uni-calender.WED": "三", + "uni-calender.THU": "四", + "uni-calender.FRI": "五", + "uni-calender.SAT": "六", + "uni-calender.confirm": "確認" +} \ No newline at end of file diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue new file mode 100644 index 0000000..1817692 --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue @@ -0,0 +1,940 @@ +<template> + <view class="uni-datetime-picker"> + <view @click="initTimePicker"> + <slot> + <view class="uni-datetime-picker-timebox-pointer" + :class="{'uni-datetime-picker-disabled': disabled, 'uni-datetime-picker-timebox': border}"> + <text class="uni-datetime-picker-text">{{time}}</text> + <view v-if="!time" class="uni-datetime-picker-time"> + <text class="uni-datetime-picker-text">{{selectTimeText}}</text> + </view> + </view> + </slot> + </view> + <view v-if="visible" id="mask" class="uni-datetime-picker-mask" @click="tiggerTimePicker"></view> + <view v-if="visible" class="uni-datetime-picker-popup" :class="[dateShow && timeShow ? '' : 'fix-nvue-height']" + :style="fixNvueBug"> + <view class="uni-title"> + <text class="uni-datetime-picker-text">{{selectTimeText}}</text> + </view> + <view v-if="dateShow" class="uni-datetime-picker__container-box"> + <picker-view class="uni-datetime-picker-view" :indicator-style="indicatorStyle" :value="ymd" + @change="bindDateChange"> + <picker-view-column> + <view class="uni-datetime-picker-item" v-for="(item,index) in years" :key="index"> + <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> + </view> + </picker-view-column> + <picker-view-column> + <view class="uni-datetime-picker-item" v-for="(item,index) in months" :key="index"> + <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> + </view> + </picker-view-column> + <picker-view-column> + <view class="uni-datetime-picker-item" v-for="(item,index) in days" :key="index"> + <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> + </view> + </picker-view-column> + </picker-view> + <!-- 兼容 nvue 不支持伪类 --> + <text class="uni-datetime-picker-sign sign-left">-</text> + <text class="uni-datetime-picker-sign sign-right">-</text> + </view> + <view v-if="timeShow" class="uni-datetime-picker__container-box"> + <picker-view class="uni-datetime-picker-view" :class="[hideSecond ? 'time-hide-second' : '']" + :indicator-style="indicatorStyle" :value="hms" @change="bindTimeChange"> + <picker-view-column> + <view class="uni-datetime-picker-item" v-for="(item,index) in hours" :key="index"> + <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> + </view> + </picker-view-column> + <picker-view-column> + <view class="uni-datetime-picker-item" v-for="(item,index) in minutes" :key="index"> + <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> + </view> + </picker-view-column> + <picker-view-column v-if="!hideSecond"> + <view class="uni-datetime-picker-item" v-for="(item,index) in seconds" :key="index"> + <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> + </view> + </picker-view-column> + </picker-view> + <!-- 兼容 nvue 不支持伪类 --> + <text class="uni-datetime-picker-sign" :class="[hideSecond ? 'sign-center' : 'sign-left']">:</text> + <text v-if="!hideSecond" class="uni-datetime-picker-sign sign-right">:</text> + </view> + <view class="uni-datetime-picker-btn"> + <view @click="clearTime"> + <text class="uni-datetime-picker-btn-text">{{clearText}}</text> + </view> + <view class="uni-datetime-picker-btn-group"> + <view class="uni-datetime-picker-cancel" @click="tiggerTimePicker"> + <text class="uni-datetime-picker-btn-text">{{cancelText}}</text> + </view> + <view @click="setTime"> + <text class="uni-datetime-picker-btn-text">{{okText}}</text> + </view> + </view> + </view> + </view> + </view> +</template> + +<script> + import { + initVueI18n + } from '@dcloudio/uni-i18n' + import i18nMessages from './i18n/index.js' + const { + t + } = initVueI18n(i18nMessages) + import { + fixIosDateFormat + } from './util' + + /** + * DatetimePicker 时间选择器 + * @description 可以同时选择日期和时间的选择器 + * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx + * @property {String} type = [datetime | date | time] 显示模式 + * @property {Boolean} multiple = [true|false] 是否多选 + * @property {String|Number} value 默认值 + * @property {String|Number} start 起始日期或时间 + * @property {String|Number} end 起始日期或时间 + * @property {String} return-type = [timestamp | string] + * @event {Function} change 选中发生变化触发 + */ + + export default { + name: 'UniDatetimePicker', + data() { + return { + indicatorStyle: `height: 50px;`, + visible: false, + fixNvueBug: {}, + dateShow: true, + timeShow: true, + title: '日期和时间', + // 输入框当前时间 + time: '', + // 当前的年月日时分秒 + year: 1920, + month: 0, + day: 0, + hour: 0, + minute: 0, + second: 0, + // 起始时间 + startYear: 1920, + startMonth: 1, + startDay: 1, + startHour: 0, + startMinute: 0, + startSecond: 0, + // 结束时间 + endYear: 2120, + endMonth: 12, + endDay: 31, + endHour: 23, + endMinute: 59, + endSecond: 59, + } + }, + options: { + // #ifdef MP-TOUTIAO + virtualHost: false, + // #endif + // #ifndef MP-TOUTIAO + virtualHost: true + // #endif + }, + props: { + type: { + type: String, + default: 'datetime' + }, + value: { + type: [String, Number], + default: '' + }, + modelValue: { + type: [String, Number], + default: '' + }, + start: { + type: [Number, String], + default: '' + }, + end: { + type: [Number, String], + default: '' + }, + returnType: { + type: String, + default: 'string' + }, + disabled: { + type: [Boolean, String], + default: false + }, + border: { + type: [Boolean, String], + default: true + }, + hideSecond: { + type: [Boolean, String], + default: false + } + }, + watch: { + // #ifndef VUE3 + value: { + handler(newVal) { + if (newVal) { + this.parseValue(fixIosDateFormat(newVal)) + this.initTime(false) + } else { + this.time = '' + this.parseValue(Date.now()) + } + }, + immediate: true + }, + // #endif + // #ifdef VUE3 + modelValue: { + handler(newVal) { + if (newVal) { + this.parseValue(fixIosDateFormat(newVal)) + this.initTime(false) + } else { + this.time = '' + this.parseValue(Date.now()) + } + }, + immediate: true + }, + // #endif + type: { + handler(newValue) { + if (newValue === 'date') { + this.dateShow = true + this.timeShow = false + this.title = '日期' + } else if (newValue === 'time') { + this.dateShow = false + this.timeShow = true + this.title = '时间' + } else { + this.dateShow = true + this.timeShow = true + this.title = '日期和时间' + } + }, + immediate: true + }, + start: { + handler(newVal) { + this.parseDatetimeRange(fixIosDateFormat(newVal), 'start') + }, + immediate: true + }, + end: { + handler(newVal) { + this.parseDatetimeRange(fixIosDateFormat(newVal), 'end') + }, + immediate: true + }, + + // 月、日、时、分、秒可选范围变化后,检查当前值是否在范围内,不在则当前值重置为可选范围第一项 + months(newVal) { + this.checkValue('month', this.month, newVal) + }, + days(newVal) { + this.checkValue('day', this.day, newVal) + }, + hours(newVal) { + this.checkValue('hour', this.hour, newVal) + }, + minutes(newVal) { + this.checkValue('minute', this.minute, newVal) + }, + seconds(newVal) { + this.checkValue('second', this.second, newVal) + } + }, + computed: { + // 当前年、月、日、时、分、秒选择范围 + years() { + return this.getCurrentRange('year') + }, + + months() { + return this.getCurrentRange('month') + }, + + days() { + return this.getCurrentRange('day') + }, + + hours() { + return this.getCurrentRange('hour') + }, + + minutes() { + return this.getCurrentRange('minute') + }, + + seconds() { + return this.getCurrentRange('second') + }, + + // picker 当前值数组 + ymd() { + return [this.year - this.minYear, this.month - this.minMonth, this.day - this.minDay] + }, + hms() { + return [this.hour - this.minHour, this.minute - this.minMinute, this.second - this.minSecond] + }, + + // 当前 date 是 start + currentDateIsStart() { + return this.year === this.startYear && this.month === this.startMonth && this.day === this.startDay + }, + + // 当前 date 是 end + currentDateIsEnd() { + return this.year === this.endYear && this.month === this.endMonth && this.day === this.endDay + }, + + // 当前年、月、日、时、分、秒的最小值和最大值 + minYear() { + return this.startYear + }, + maxYear() { + return this.endYear + }, + minMonth() { + if (this.year === this.startYear) { + return this.startMonth + } else { + return 1 + } + }, + maxMonth() { + if (this.year === this.endYear) { + return this.endMonth + } else { + return 12 + } + }, + minDay() { + if (this.year === this.startYear && this.month === this.startMonth) { + return this.startDay + } else { + return 1 + } + }, + maxDay() { + if (this.year === this.endYear && this.month === this.endMonth) { + return this.endDay + } else { + return this.daysInMonth(this.year, this.month) + } + }, + minHour() { + if (this.type === 'datetime') { + if (this.currentDateIsStart) { + return this.startHour + } else { + return 0 + } + } + if (this.type === 'time') { + return this.startHour + } + }, + maxHour() { + if (this.type === 'datetime') { + if (this.currentDateIsEnd) { + return this.endHour + } else { + return 23 + } + } + if (this.type === 'time') { + return this.endHour + } + }, + minMinute() { + if (this.type === 'datetime') { + if (this.currentDateIsStart && this.hour === this.startHour) { + return this.startMinute + } else { + return 0 + } + } + if (this.type === 'time') { + if (this.hour === this.startHour) { + return this.startMinute + } else { + return 0 + } + } + }, + maxMinute() { + if (this.type === 'datetime') { + if (this.currentDateIsEnd && this.hour === this.endHour) { + return this.endMinute + } else { + return 59 + } + } + if (this.type === 'time') { + if (this.hour === this.endHour) { + return this.endMinute + } else { + return 59 + } + } + }, + minSecond() { + if (this.type === 'datetime') { + if (this.currentDateIsStart && this.hour === this.startHour && this.minute === this.startMinute) { + return this.startSecond + } else { + return 0 + } + } + if (this.type === 'time') { + if (this.hour === this.startHour && this.minute === this.startMinute) { + return this.startSecond + } else { + return 0 + } + } + }, + maxSecond() { + if (this.type === 'datetime') { + if (this.currentDateIsEnd && this.hour === this.endHour && this.minute === this.endMinute) { + return this.endSecond + } else { + return 59 + } + } + if (this.type === 'time') { + if (this.hour === this.endHour && this.minute === this.endMinute) { + return this.endSecond + } else { + return 59 + } + } + }, + + /** + * for i18n + */ + selectTimeText() { + return t("uni-datetime-picker.selectTime") + }, + okText() { + return t("uni-datetime-picker.ok") + }, + clearText() { + return t("uni-datetime-picker.clear") + }, + cancelText() { + return t("uni-datetime-picker.cancel") + } + }, + + mounted() { + // #ifdef APP-NVUE + const res = uni.getSystemInfoSync(); + this.fixNvueBug = { + top: res.windowHeight / 2, + left: res.windowWidth / 2 + } + // #endif + }, + + methods: { + /** + * @param {Object} item + * 小于 10 在前面加个 0 + */ + + lessThanTen(item) { + return item < 10 ? '0' + item : item + }, + + /** + * 解析时分秒字符串,例如:00:00:00 + * @param {String} timeString + */ + parseTimeType(timeString) { + if (timeString) { + let timeArr = timeString.split(':') + this.hour = Number(timeArr[0]) + this.minute = Number(timeArr[1]) + this.second = Number(timeArr[2]) + } + }, + + /** + * 解析选择器初始值,类型可以是字符串、时间戳,例如:2000-10-02、'08:30:00'、 1610695109000 + * @param {String | Number} datetime + */ + initPickerValue(datetime) { + let defaultValue = null + if (datetime) { + defaultValue = this.compareValueWithStartAndEnd(datetime, this.start, this.end) + } else { + defaultValue = Date.now() + defaultValue = this.compareValueWithStartAndEnd(defaultValue, this.start, this.end) + } + this.parseValue(defaultValue) + }, + + /** + * 初始值规则: + * - 用户设置初始值 value + * - 设置了起始时间 start、终止时间 end,并 start < value < end,初始值为 value, 否则初始值为 start + * - 只设置了起始时间 start,并 start < value,初始值为 value,否则初始值为 start + * - 只设置了终止时间 end,并 value < end,初始值为 value,否则初始值为 end + * - 无起始终止时间,则初始值为 value + * - 无初始值 value,则初始值为当前本地时间 Date.now() + * @param {Object} value + * @param {Object} dateBase + */ + compareValueWithStartAndEnd(value, start, end) { + let winner = null + value = this.superTimeStamp(value) + start = this.superTimeStamp(start) + end = this.superTimeStamp(end) + + if (start && end) { + if (value < start) { + winner = new Date(start) + } else if (value > end) { + winner = new Date(end) + } else { + winner = new Date(value) + } + } else if (start && !end) { + winner = start <= value ? new Date(value) : new Date(start) + } else if (!start && end) { + winner = value <= end ? new Date(value) : new Date(end) + } else { + winner = new Date(value) + } + + return winner + }, + + /** + * 转换为可比较的时间戳,接受日期、时分秒、时间戳 + * @param {Object} value + */ + superTimeStamp(value) { + let dateBase = '' + if (this.type === 'time' && value && typeof value === 'string') { + const now = new Date() + const year = now.getFullYear() + const month = now.getMonth() + 1 + const day = now.getDate() + dateBase = year + '/' + month + '/' + day + ' ' + } + if (Number(value)) { + value = parseInt(value) + dateBase = 0 + } + return this.createTimeStamp(dateBase + value) + }, + + /** + * 解析默认值 value,字符串、时间戳 + * @param {Object} defaultTime + */ + parseValue(value) { + if (!value) { + return + } + if (this.type === 'time' && typeof value === "string") { + this.parseTimeType(value) + } else { + let defaultDate = null + defaultDate = new Date(value) + if (this.type !== 'time') { + this.year = defaultDate.getFullYear() + this.month = defaultDate.getMonth() + 1 + this.day = defaultDate.getDate() + } + if (this.type !== 'date') { + this.hour = defaultDate.getHours() + this.minute = defaultDate.getMinutes() + this.second = defaultDate.getSeconds() + } + } + if (this.hideSecond) { + this.second = 0 + } + }, + + /** + * 解析可选择时间范围 start、end,年月日字符串、时间戳 + * @param {Object} defaultTime + */ + parseDatetimeRange(point, pointType) { + // 时间为空,则重置为初始值 + if (!point) { + if (pointType === 'start') { + this.startYear = 1920 + this.startMonth = 1 + this.startDay = 1 + this.startHour = 0 + this.startMinute = 0 + this.startSecond = 0 + } + if (pointType === 'end') { + this.endYear = 2120 + this.endMonth = 12 + this.endDay = 31 + this.endHour = 23 + this.endMinute = 59 + this.endSecond = 59 + } + return + } + if (this.type === 'time') { + const pointArr = point.split(':') + this[pointType + 'Hour'] = Number(pointArr[0]) + this[pointType + 'Minute'] = Number(pointArr[1]) + this[pointType + 'Second'] = Number(pointArr[2]) + } else { + if (!point) { + pointType === 'start' ? this.startYear = this.year - 60 : this.endYear = this.year + 60 + return + } + if (Number(point)) { + point = parseInt(point) + } + // datetime 的 end 没有时分秒, 则不限制 + const hasTime = /[0-9]:[0-9]/ + if (this.type === 'datetime' && pointType === 'end' && typeof point === 'string' && !hasTime.test( + point)) { + point = point + ' 23:59:59' + } + const pointDate = new Date(point) + this[pointType + 'Year'] = pointDate.getFullYear() + this[pointType + 'Month'] = pointDate.getMonth() + 1 + this[pointType + 'Day'] = pointDate.getDate() + if (this.type === 'datetime') { + this[pointType + 'Hour'] = pointDate.getHours() + this[pointType + 'Minute'] = pointDate.getMinutes() + this[pointType + 'Second'] = pointDate.getSeconds() + } + } + }, + + // 获取 年、月、日、时、分、秒 当前可选范围 + getCurrentRange(value) { + const range = [] + for (let i = this['min' + this.capitalize(value)]; i <= this['max' + this.capitalize(value)]; i++) { + range.push(i) + } + return range + }, + + // 字符串首字母大写 + capitalize(str) { + return str.charAt(0).toUpperCase() + str.slice(1) + }, + + // 检查当前值是否在范围内,不在则当前值重置为可选范围第一项 + checkValue(name, value, values) { + if (values.indexOf(value) === -1) { + this[name] = values[0] + } + }, + + // 每个月的实际天数 + daysInMonth(year, month) { // Use 1 for January, 2 for February, etc. + return new Date(year, month, 0).getDate(); + }, + + /** + * 生成时间戳 + * @param {Object} time + */ + createTimeStamp(time) { + if (!time) return + if (typeof time === "number") { + return time + } else { + time = time.replace(/-/g, '/') + if (this.type === 'date') { + time = time + ' ' + '00:00:00' + } + return Date.parse(time) + } + }, + + /** + * 生成日期或时间的字符串 + */ + createDomSting() { + const yymmdd = this.year + + '-' + + this.lessThanTen(this.month) + + '-' + + this.lessThanTen(this.day) + + let hhmmss = this.lessThanTen(this.hour) + + ':' + + this.lessThanTen(this.minute) + + if (!this.hideSecond) { + hhmmss = hhmmss + ':' + this.lessThanTen(this.second) + } + + if (this.type === 'date') { + return yymmdd + } else if (this.type === 'time') { + return hhmmss + } else { + return yymmdd + ' ' + hhmmss + } + }, + + /** + * 初始化返回值,并抛出 change 事件 + */ + initTime(emit = true) { + this.time = this.createDomSting() + if (!emit) return + if (this.returnType === 'timestamp' && this.type !== 'time') { + this.$emit('change', this.createTimeStamp(this.time)) + this.$emit('input', this.createTimeStamp(this.time)) + this.$emit('update:modelValue', this.createTimeStamp(this.time)) + } else { + this.$emit('change', this.time) + this.$emit('input', this.time) + this.$emit('update:modelValue', this.time) + } + }, + + /** + * 用户选择日期或时间更新 data + * @param {Object} e + */ + bindDateChange(e) { + const val = e.detail.value + this.year = this.years[val[0]] + this.month = this.months[val[1]] + this.day = this.days[val[2]] + }, + bindTimeChange(e) { + const val = e.detail.value + this.hour = this.hours[val[0]] + this.minute = this.minutes[val[1]] + this.second = this.seconds[val[2]] + }, + + /** + * 初始化弹出层 + */ + initTimePicker() { + if (this.disabled) return + const value = fixIosDateFormat(this.time) + this.initPickerValue(value) + this.visible = !this.visible + }, + + /** + * 触发或关闭弹框 + */ + tiggerTimePicker(e) { + this.visible = !this.visible + }, + + /** + * 用户点击“清空”按钮,清空当前值 + */ + clearTime() { + this.time = '' + this.$emit('change', this.time) + this.$emit('input', this.time) + this.$emit('update:modelValue', this.time) + this.tiggerTimePicker() + }, + + /** + * 用户点击“确定”按钮 + */ + setTime() { + this.initTime() + this.tiggerTimePicker() + } + } + } +</script> + +<style lang="scss"> + $uni-primary: #007aff !default; + + .uni-datetime-picker { + /* #ifndef APP-NVUE */ + /* width: 100%; */ + /* #endif */ + } + + .uni-datetime-picker-view { + height: 130px; + width: 270px; + /* #ifndef APP-NVUE */ + cursor: pointer; + /* #endif */ + } + + .uni-datetime-picker-item { + height: 50px; + line-height: 50px; + text-align: center; + font-size: 14px; + } + + .uni-datetime-picker-btn { + margin-top: 60px; + /* #ifndef APP-NVUE */ + display: flex; + cursor: pointer; + /* #endif */ + flex-direction: row; + justify-content: space-between; + } + + .uni-datetime-picker-btn-text { + font-size: 14px; + color: $uni-primary; + } + + .uni-datetime-picker-btn-group { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + } + + .uni-datetime-picker-cancel { + margin-right: 30px; + } + + .uni-datetime-picker-mask { + position: fixed; + bottom: 0px; + top: 0px; + left: 0px; + right: 0px; + background-color: rgba(0, 0, 0, 0.4); + transition-duration: 0.3s; + z-index: 998; + } + + .uni-datetime-picker-popup { + border-radius: 8px; + padding: 30px; + width: 270px; + /* #ifdef APP-NVUE */ + height: 500px; + /* #endif */ + /* #ifdef APP-NVUE */ + width: 330px; + /* #endif */ + background-color: #fff; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + transition-duration: 0.3s; + z-index: 999; + } + + .fix-nvue-height { + /* #ifdef APP-NVUE */ + height: 330px; + /* #endif */ + } + + .uni-datetime-picker-time { + color: grey; + } + + .uni-datetime-picker-column { + height: 50px; + } + + .uni-datetime-picker-timebox { + + border: 1px solid #E5E5E5; + border-radius: 5px; + padding: 7px 10px; + /* #ifndef APP-NVUE */ + box-sizing: border-box; + cursor: pointer; + /* #endif */ + } + + .uni-datetime-picker-timebox-pointer { + /* #ifndef APP-NVUE */ + cursor: pointer; + /* #endif */ + } + + + .uni-datetime-picker-disabled { + opacity: 0.4; + /* #ifdef H5 */ + cursor: not-allowed !important; + /* #endif */ + } + + .uni-datetime-picker-text { + font-size: 14px; + line-height: 50px + } + + .uni-datetime-picker-sign { + position: absolute; + top: 53px; + /* 减掉 10px 的元素高度,兼容nvue */ + color: #999; + /* #ifdef APP-NVUE */ + font-size: 16px; + /* #endif */ + } + + .sign-left { + left: 86px; + } + + .sign-right { + right: 86px; + } + + .sign-center { + left: 135px; + } + + .uni-datetime-picker__container-box { + position: relative; + display: flex; + align-items: center; + justify-content: center; + margin-top: 40px; + } + + .time-hide-second { + width: 180px; + } +</style> diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue new file mode 100644 index 0000000..6843e80 --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue @@ -0,0 +1,1064 @@ +<template> + <view class="uni-date"> + <view class="uni-date-editor" @click="show"> + <slot> + <view class="uni-date-editor--x" + :class="{'uni-date-editor--x__disabled': disabled,'uni-date-x--border': border}"> + <view v-if="!isRange" class="uni-date-x uni-date-single"> + <uni-icons class="icon-calendar" type="calendar" color="#c0c4cc" size="22"></uni-icons> + <view class="uni-date__x-input">{{ displayValue || singlePlaceholderText }}</view> + </view> + + <view v-else class="uni-date-x uni-date-range"> + <uni-icons class="icon-calendar" type="calendar" color="#c0c4cc" size="22"></uni-icons> + <view class="uni-date__x-input text-center">{{ displayRangeValue.startDate || startPlaceholderText }}</view> + + <view class="range-separator">{{rangeSeparator}}</view> + + <view class="uni-date__x-input text-center">{{ displayRangeValue.endDate || endPlaceholderText }}</view> + </view> + + <view v-if="showClearIcon" class="uni-date__icon-clear" @click.stop="clear"> + <uni-icons type="clear" color="#c0c4cc" size="22"></uni-icons> + </view> + </view> + </slot> + </view> + + <view v-show="pickerVisible" class="uni-date-mask--pc" @click="close"></view> + + <view v-if="!isPhone" v-show="pickerVisible" ref="datePicker" class="uni-date-picker__container"> + <view v-if="!isRange" class="uni-date-single--x" :style="pickerPositionStyle"> + <view class="uni-popper__arrow"></view> + + <view v-if="hasTime" class="uni-date-changed popup-x-header"> + <input class="uni-date__input text-center" type="text" v-model="inputDate" :placeholder="selectDateText" /> + + <time-picker type="time" v-model="pickerTime" :border="false" :disabled="!inputDate" + :start="timepickerStartTime" :end="timepickerEndTime" :hideSecond="hideSecond" style="width: 100%;"> + <input class="uni-date__input text-center" type="text" v-model="pickerTime" :placeholder="selectTimeText" + :disabled="!inputDate" /> + </time-picker> + </view> + + <Calendar ref="pcSingle" :showMonth="false" :start-date="calendarRange.startDate" + :end-date="calendarRange.endDate" :date="calendarDate" @change="singleChange" :default-value="defaultValue" + style="padding: 0 8px;" /> + + <view v-if="hasTime" class="popup-x-footer"> + <text class="confirm-text" @click="confirmSingleChange">{{okText}}</text> + </view> + </view> + + <view v-else class="uni-date-range--x" :style="pickerPositionStyle"> + <view class="uni-popper__arrow"></view> + <view v-if="hasTime" class="popup-x-header uni-date-changed"> + <view class="popup-x-header--datetime"> + <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.startDate" + :placeholder="startDateText" /> + + <time-picker type="time" v-model="tempRange.startTime" :start="timepickerStartTime" :border="false" + :disabled="!tempRange.startDate" :hideSecond="hideSecond"> + <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.startTime" + :placeholder="startTimeText" :disabled="!tempRange.startDate" /> + </time-picker> + </view> + + <uni-icons type="arrowthinright" color="#999" style="line-height: 40px;"></uni-icons> + + <view class="popup-x-header--datetime"> + <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endDate" + :placeholder="endDateText" /> + + <time-picker type="time" v-model="tempRange.endTime" :end="timepickerEndTime" :border="false" + :disabled="!tempRange.endDate" :hideSecond="hideSecond"> + <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endTime" + :placeholder="endTimeText" :disabled="!tempRange.endDate" /> + </time-picker> + </view> + </view> + + <view class="popup-x-body"> + <Calendar ref="left" :showMonth="false" :start-date="calendarRange.startDate" + :end-date="calendarRange.endDate" :range="true" :pleStatus="endMultipleStatus" @change="leftChange" + @firstEnterCale="updateRightCale" style="padding: 0 8px;"/> + <Calendar ref="right" :showMonth="false" :start-date="calendarRange.startDate" + :end-date="calendarRange.endDate" :range="true" @change="rightChange" :pleStatus="startMultipleStatus" + @firstEnterCale="updateLeftCale" style="padding: 0 8px;border-left: 1px solid #F1F1F1;" /> + </view> + + <view v-if="hasTime" class="popup-x-footer"> + <text @click="clear">{{clearText}}</text> + <text class="confirm-text" @click="confirmRangeChange">{{okText}}</text> + </view> + </view> + </view> + + <Calendar v-if="isPhone" ref="mobile" :clearDate="false" :date="calendarDate" :defTime="mobileCalendarTime" + :start-date="calendarRange.startDate" :end-date="calendarRange.endDate" :selectableTimes="mobSelectableTime" + :startPlaceholder="startPlaceholder" :endPlaceholder="endPlaceholder" :default-value="defaultValue" + :pleStatus="endMultipleStatus" :showMonth="false" :range="isRange" :hasTime="hasTime" :insert="false" + :hideSecond="hideSecond" @confirm="mobileChange" @maskClose="close" @change="calendarClick"/> + </view> +</template> +<script> + /** + * DatetimePicker 时间选择器 + * @description 同时支持 PC 和移动端使用日历选择日期和日期范围 + * @tutorial https://ext.dcloud.net.cn/plugin?id=3962 + * @property {String} type 选择器类型 + * @property {String|Number|Array|Date} value 绑定值 + * @property {String} placeholder 单选择时的占位内容 + * @property {String} start 起始时间 + * @property {String} end 终止时间 + * @property {String} start-placeholder 范围选择时开始日期的占位内容 + * @property {String} end-placeholder 范围选择时结束日期的占位内容 + * @property {String} range-separator 选择范围时的分隔符 + * @property {Boolean} border = [true|false] 是否有边框 + * @property {Boolean} disabled = [true|false] 是否禁用 + * @property {Boolean} clearIcon = [true|false] 是否显示清除按钮(仅PC端适用) + * @property {[String} defaultValue 选择器打开时默认显示的时间 + * @event {Function} change 确定日期时触发的事件 + * @event {Function} maskClick 点击遮罩层触发的事件 + * @event {Function} show 打开弹出层 + * @event {Function} close 关闭弹出层 + * @event {Function} clear 清除上次选中的状态和值 + **/ + import Calendar from './calendar.vue' + import TimePicker from './time-picker.vue' + import { + initVueI18n + } from '@dcloudio/uni-i18n' + import i18nMessages from './i18n/index.js' + import { + getDateTime, + getDate, + getTime, + getDefaultSecond, + dateCompare, + checkDate, + fixIosDateFormat + } from './util' + + export default { + name: 'UniDatetimePicker', + + options: { + // #ifdef MP-TOUTIAO + virtualHost: false, + // #endif + // #ifndef MP-TOUTIAO + virtualHost: true + // #endif + }, + components: { + Calendar, + TimePicker + }, + data() { + return { + isRange: false, + hasTime: false, + displayValue: '', + inputDate: '', + calendarDate: '', + pickerTime: '', + calendarRange: { + startDate: '', + startTime: '', + endDate: '', + endTime: '' + }, + displayRangeValue: { + startDate: '', + endDate: '', + }, + tempRange: { + startDate: '', + startTime: '', + endDate: '', + endTime: '' + }, + // 左右日历同步数据 + startMultipleStatus: { + before: '', + after: '', + data: [], + fulldate: '' + }, + endMultipleStatus: { + before: '', + after: '', + data: [], + fulldate: '' + }, + pickerVisible: false, + pickerPositionStyle: null, + isEmitValue: false, + isPhone: false, + isFirstShow: true, + i18nT: () => {} + } + }, + props: { + type: { + type: String, + default: 'datetime' + }, + value: { + type: [String, Number, Array, Date], + default: '' + }, + modelValue: { + type: [String, Number, Array, Date], + default: '' + }, + start: { + type: [Number, String], + default: '' + }, + end: { + type: [Number, String], + default: '' + }, + returnType: { + type: String, + default: 'string' + }, + placeholder: { + type: String, + default: '' + }, + startPlaceholder: { + type: String, + default: '' + }, + endPlaceholder: { + type: String, + default: '' + }, + rangeSeparator: { + type: String, + default: '-' + }, + border: { + type: [Boolean], + default: true + }, + disabled: { + type: [Boolean], + default: false + }, + clearIcon: { + type: [Boolean], + default: true + }, + hideSecond: { + type: [Boolean], + default: false + }, + defaultValue: { + type: [String, Object, Array], + default: '' + } + }, + watch: { + type: { + immediate: true, + handler(newVal) { + this.hasTime = newVal.indexOf('time') !== -1 + this.isRange = newVal.indexOf('range') !== -1 + } + }, + // #ifndef VUE3 + value: { + immediate: true, + handler(newVal) { + if (this.isEmitValue) { + this.isEmitValue = false + return + } + this.initPicker(newVal) + } + }, + // #endif + // #ifdef VUE3 + modelValue: { + immediate: true, + handler(newVal) { + if (this.isEmitValue) { + this.isEmitValue = false + return + } + this.initPicker(newVal) + } + }, + // #endif + start: { + immediate: true, + handler(newVal) { + if (!newVal) return + this.calendarRange.startDate = getDate(newVal) + if (this.hasTime) { + this.calendarRange.startTime = getTime(newVal) + } + } + }, + end: { + immediate: true, + handler(newVal) { + if (!newVal) return + this.calendarRange.endDate = getDate(newVal) + if (this.hasTime) { + this.calendarRange.endTime = getTime(newVal, this.hideSecond) + } + } + }, + }, + computed: { + timepickerStartTime() { + const activeDate = this.isRange ? this.tempRange.startDate : this.inputDate + return activeDate === this.calendarRange.startDate ? this.calendarRange.startTime : '' + }, + timepickerEndTime() { + const activeDate = this.isRange ? this.tempRange.endDate : this.inputDate + return activeDate === this.calendarRange.endDate ? this.calendarRange.endTime : '' + }, + mobileCalendarTime() { + const timeRange = { + start: this.tempRange.startTime, + end: this.tempRange.endTime + } + return this.isRange ? timeRange : this.pickerTime + }, + mobSelectableTime() { + return { + start: this.calendarRange.startTime, + end: this.calendarRange.endTime + } + }, + datePopupWidth() { + // todo + return this.isRange ? 653 : 301 + }, + + /** + * for i18n + */ + singlePlaceholderText() { + return this.placeholder || (this.type === 'date' ? this.selectDateText : this.selectDateTimeText) + }, + startPlaceholderText() { + return this.startPlaceholder || this.startDateText + }, + endPlaceholderText() { + return this.endPlaceholder || this.endDateText + }, + selectDateText() { + return this.i18nT("uni-datetime-picker.selectDate") + }, + selectDateTimeText() { + return this.i18nT("uni-datetime-picker.selectDateTime") + }, + selectTimeText() { + return this.i18nT("uni-datetime-picker.selectTime") + }, + startDateText() { + return this.startPlaceholder || this.i18nT("uni-datetime-picker.startDate") + }, + startTimeText() { + return this.i18nT("uni-datetime-picker.startTime") + }, + endDateText() { + return this.endPlaceholder || this.i18nT("uni-datetime-picker.endDate") + }, + endTimeText() { + return this.i18nT("uni-datetime-picker.endTime") + }, + okText() { + return this.i18nT("uni-datetime-picker.ok") + }, + clearText() { + return this.i18nT("uni-datetime-picker.clear") + }, + showClearIcon() { + return this.clearIcon && !this.disabled && (this.displayValue || (this.displayRangeValue.startDate && this + .displayRangeValue.endDate)) + } + }, + created() { + this.initI18nT() + this.platform() + }, + methods: { + initI18nT() { + const vueI18n = initVueI18n(i18nMessages) + this.i18nT = vueI18n.t + }, + initPicker(newVal) { + if ((!newVal && !this.defaultValue) || Array.isArray(newVal) && !newVal.length) { + this.$nextTick(() => { + this.clear(false) + }) + return + } + + if (!Array.isArray(newVal) && !this.isRange) { + if (newVal) { + this.displayValue = this.inputDate = this.calendarDate = getDate(newVal) + if (this.hasTime) { + this.pickerTime = getTime(newVal, this.hideSecond) + this.displayValue = `${this.displayValue} ${this.pickerTime}` + } + } else if (this.defaultValue) { + this.inputDate = this.calendarDate = getDate(this.defaultValue) + if (this.hasTime) { + this.pickerTime = getTime(this.defaultValue, this.hideSecond) + } + } + } else { + const [before, after] = newVal + if (!before && !after) return + const beforeDate = getDate(before) + const beforeTime = getTime(before, this.hideSecond) + + const afterDate = getDate(after) + const afterTime = getTime(after, this.hideSecond) + const startDate = beforeDate + const endDate = afterDate + this.displayRangeValue.startDate = this.tempRange.startDate = startDate + this.displayRangeValue.endDate = this.tempRange.endDate = endDate + + if (this.hasTime) { + this.displayRangeValue.startDate = `${beforeDate} ${beforeTime}` + this.displayRangeValue.endDate = `${afterDate} ${afterTime}` + this.tempRange.startTime = beforeTime + this.tempRange.endTime = afterTime + } + const defaultRange = { + before: beforeDate, + after: afterDate + } + this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, defaultRange, { + which: 'right' + }) + this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, defaultRange, { + which: 'left' + }) + } + }, + updateLeftCale(e) { + const left = this.$refs.left + // 设置范围选 + left.cale.setHoverMultiple(e.after) + left.setDate(this.$refs.left.nowDate.fullDate) + }, + updateRightCale(e) { + const right = this.$refs.right + // 设置范围选 + right.cale.setHoverMultiple(e.after) + right.setDate(this.$refs.right.nowDate.fullDate) + }, + platform() { + if (typeof navigator !== "undefined") { + this.isPhone = navigator.userAgent.toLowerCase().indexOf('mobile') !== -1 + return + } + // #ifdef MP-WEIXIN + const { + windowWidth + } = uni.getWindowInfo() + // #endif + // #ifndef MP-WEIXIN + const { + windowWidth + } = uni.getSystemInfoSync() + // #endif + this.isPhone = windowWidth <= 500 + this.windowWidth = windowWidth + }, + show() { + this.$emit("show") + if (this.disabled) { + return + } + this.platform() + if (this.isPhone) { + setTimeout(() => { + this.$refs.mobile.open() + }, 0); + return + } + this.pickerPositionStyle = { + top: '10px' + } + const dateEditor = uni.createSelectorQuery().in(this).select(".uni-date-editor") + dateEditor.boundingClientRect(rect => { + if (this.windowWidth - rect.left < this.datePopupWidth) { + this.pickerPositionStyle.right = 0 + } + }).exec() + setTimeout(() => { + this.pickerVisible = !this.pickerVisible + if (!this.isPhone && this.isRange && this.isFirstShow) { + this.isFirstShow = false + const { + startDate, + endDate + } = this.calendarRange + if (startDate && endDate) { + if (this.diffDate(startDate, endDate) < 30) { + this.$refs.right.changeMonth('pre') + } + } else { + // this.$refs.right.changeMonth('next') + if (this.isPhone) { + this.$refs.right.cale.lastHover = false; + } + } + } + + }, 50) + }, + close() { + setTimeout(() => { + this.pickerVisible = false + this.$emit('maskClick', this.value) + this.$refs.mobile && this.$refs.mobile.close() + }, 20) + }, + setEmit(value) { + if (this.returnType === "timestamp" || this.returnType === "date") { + if (!Array.isArray(value)) { + if (!this.hasTime) { + value = value + ' ' + '00:00:00' + } + value = this.createTimestamp(value) + if (this.returnType === "date") { + value = new Date(value) + } + } else { + if (!this.hasTime) { + value[0] = value[0] + ' ' + '00:00:00' + value[1] = value[1] + ' ' + '00:00:00' + } + value[0] = this.createTimestamp(value[0]) + value[1] = this.createTimestamp(value[1]) + if (this.returnType === "date") { + value[0] = new Date(value[0]) + value[1] = new Date(value[1]) + } + } + } + + this.$emit('update:modelValue', value) + this.$emit('input', value) + this.$emit('change', value) + this.isEmitValue = true + }, + createTimestamp(date) { + date = fixIosDateFormat(date) + return Date.parse(new Date(date)) + }, + singleChange(e) { + this.calendarDate = this.inputDate = e.fulldate + if (this.hasTime) return + this.confirmSingleChange() + }, + confirmSingleChange() { + if (!checkDate(this.inputDate)) { + const now = new Date() + this.calendarDate = this.inputDate = getDate(now) + this.pickerTime = getTime(now, this.hideSecond) + } + + let startLaterInputDate = false + let startDate, startTime + if (this.start) { + let startString = this.start + if (typeof this.start === 'number') { + startString = getDateTime(this.start, this.hideSecond) + } + [startDate, startTime] = startString.split(' ') + if (this.start && !dateCompare(startDate, this.inputDate)) { + startLaterInputDate = true + this.inputDate = startDate + } + } + + let endEarlierInputDate = false + let endDate, endTime + if (this.end) { + let endString = this.end + if (typeof this.end === 'number') { + endString = getDateTime(this.end, this.hideSecond) + } + [endDate, endTime] = endString.split(' ') + if (this.end && !dateCompare(this.inputDate, endDate)) { + endEarlierInputDate = true + this.inputDate = endDate + } + } + if (this.hasTime) { + if (startLaterInputDate) { + this.pickerTime = startTime || getDefaultSecond(this.hideSecond) + } + if (endEarlierInputDate) { + this.pickerTime = endTime || getDefaultSecond(this.hideSecond) + } + if (!this.pickerTime) { + this.pickerTime = getTime(Date.now(), this.hideSecond) + } + this.displayValue = `${this.inputDate} ${this.pickerTime}` + } else { + this.displayValue = this.inputDate + } + this.setEmit(this.displayValue) + this.pickerVisible = false + }, + leftChange(e) { + const { + before, + after + } = e.range + this.rangeChange(before, after) + const obj = { + before: e.range.before, + after: e.range.after, + data: e.range.data, + fulldate: e.fulldate + } + this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, obj) + this.$emit('calendarClick', e) + }, + rightChange(e) { + const { + before, + after + } = e.range + this.rangeChange(before, after) + const obj = { + before: e.range.before, + after: e.range.after, + data: e.range.data, + fulldate: e.fulldate + } + this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, obj) + this.$emit('calendarClick', e) + }, + mobileChange(e) { + if (this.isRange) { + const { + before, + after + } = e.range + if (!before) { + return; + } + + this.handleStartAndEnd(before, after, true) + if (this.hasTime) { + const { + startTime, + endTime + } = e.timeRange + this.tempRange.startTime = startTime + this.tempRange.endTime = endTime + } + this.confirmRangeChange() + } else { + if (this.hasTime) { + this.displayValue = e.fulldate + ' ' + e.time + } else { + this.displayValue = e.fulldate + } + this.setEmit(this.displayValue) + } + this.$refs.mobile.close() + }, + rangeChange(before, after) { + if (!(before && after)) return + this.handleStartAndEnd(before, after, true) + if (this.hasTime) return + this.confirmRangeChange() + }, + confirmRangeChange() { + if (!this.tempRange.startDate || !this.tempRange.endDate) { + this.pickerVisible = false + return + } + if (!checkDate(this.tempRange.startDate)) { + this.tempRange.startDate = getDate(Date.now()) + } + if (!checkDate(this.tempRange.endDate)) { + this.tempRange.endDate = getDate(Date.now()) + } + + let start, end + + let startDateLaterRangeStartDate = false + let startDateLaterRangeEndDate = false + let startDate, startTime + if (this.start) { + let startString = this.start + if (typeof this.start === 'number') { + startString = getDateTime(this.start, this.hideSecond) + } + [startDate, startTime] = startString.split(' ') + if (this.start && !dateCompare(this.start, `${this.tempRange.startDate} ${this.tempRange.startTime}`)) { + startDateLaterRangeStartDate = true + this.tempRange.startDate = startDate + } + if (this.start && !dateCompare(this.start, `${this.tempRange.endDate} ${this.tempRange.endTime}`)) { + startDateLaterRangeEndDate = true + this.tempRange.endDate = startDate + } + } + let endDateEarlierRangeStartDate = false + let endDateEarlierRangeEndDate = false + let endDate, endTime + if (this.end) { + let endString = this.end + if (typeof this.end === 'number') { + endString = getDateTime(this.end, this.hideSecond) + } + [endDate, endTime] = endString.split(' ') + + if (this.end && !dateCompare(`${this.tempRange.startDate} ${this.tempRange.startTime}`, this.end)) { + endDateEarlierRangeStartDate = true + this.tempRange.startDate = endDate + } + if (this.end && !dateCompare(`${this.tempRange.endDate} ${this.tempRange.endTime}`, this.end)) { + endDateEarlierRangeEndDate = true + this.tempRange.endDate = endDate + } + } + if (!this.hasTime) { + start = this.displayRangeValue.startDate = this.tempRange.startDate + end = this.displayRangeValue.endDate = this.tempRange.endDate + } else { + if (startDateLaterRangeStartDate) { + this.tempRange.startTime = startTime || getDefaultSecond(this.hideSecond) + } else if (endDateEarlierRangeStartDate) { + this.tempRange.startTime = endTime || getDefaultSecond(this.hideSecond) + } + if (!this.tempRange.startTime) { + this.tempRange.startTime = getTime(Date.now(), this.hideSecond) + } + + if (startDateLaterRangeEndDate) { + this.tempRange.endTime = startTime || getDefaultSecond(this.hideSecond) + } else if (endDateEarlierRangeEndDate) { + this.tempRange.endTime = endTime || getDefaultSecond(this.hideSecond) + } + if (!this.tempRange.endTime) { + this.tempRange.endTime = getTime(Date.now(), this.hideSecond) + } + start = this.displayRangeValue.startDate = `${this.tempRange.startDate} ${this.tempRange.startTime}` + end = this.displayRangeValue.endDate = `${this.tempRange.endDate} ${this.tempRange.endTime}` + } + if (!dateCompare(start, end)) { + [start, end] = [end, start] + } + this.displayRangeValue.startDate = start + this.displayRangeValue.endDate = end + const displayRange = [start, end] + this.setEmit(displayRange) + this.pickerVisible = false + }, + handleStartAndEnd(before, after, temp = false) { + if (!before) return + if (!after) after = before; + const type = temp ? 'tempRange' : 'range' + const isStartEarlierEnd = dateCompare(before, after) + this[type].startDate = isStartEarlierEnd ? before : after + this[type].endDate = isStartEarlierEnd ? after : before + }, + /** + * 比较时间大小 + */ + dateCompare(startDate, endDate) { + // 计算截止时间 + startDate = new Date(startDate.replace('-', '/').replace('-', '/')) + // 计算详细项的截止时间 + endDate = new Date(endDate.replace('-', '/').replace('-', '/')) + return startDate <= endDate + }, + + /** + * 比较时间差 + */ + diffDate(startDate, endDate) { + // 计算截止时间 + startDate = new Date(startDate.replace('-', '/').replace('-', '/')) + // 计算详细项的截止时间 + endDate = new Date(endDate.replace('-', '/').replace('-', '/')) + const diff = (endDate - startDate) / (24 * 60 * 60 * 1000) + return Math.abs(diff) + }, + + clear(needEmit = true) { + if (!this.isRange) { + this.displayValue = '' + this.inputDate = '' + this.pickerTime = '' + if (this.isPhone) { + this.$refs.mobile && this.$refs.mobile.clearCalender() + } else { + this.$refs.pcSingle && this.$refs.pcSingle.clearCalender() + } + if (needEmit) { + this.$emit('change', '') + this.$emit('input', '') + this.$emit('update:modelValue', '') + } + } else { + this.displayRangeValue.startDate = '' + this.displayRangeValue.endDate = '' + this.tempRange.startDate = '' + this.tempRange.startTime = '' + this.tempRange.endDate = '' + this.tempRange.endTime = '' + if (this.isPhone) { + this.$refs.mobile && this.$refs.mobile.clearCalender() + } else { + this.$refs.left && this.$refs.left.clearCalender() + this.$refs.right && this.$refs.right.clearCalender() + this.$refs.right && this.$refs.right.changeMonth('next') + } + if (needEmit) { + this.$emit('change', []) + this.$emit('input', []) + this.$emit('update:modelValue', []) + } + } + }, + + calendarClick(e) { + this.$emit('calendarClick', e) + } + } + } +</script> + +<style lang="scss"> + $uni-primary: #007aff !default; + + .uni-date { + width: 100%; + flex: 1; + } + + .uni-date-x { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + border-radius: 4px; + background-color: #fff; + color: #666; + font-size: 14px; + flex: 1; + + .icon-calendar { + padding-left: 3px; + } + + .range-separator { + height: 35px; + /* #ifndef MP */ + padding: 0 2px; + /* #endif */ + line-height: 35px; + } + } + + .uni-date-x--border { + box-sizing: border-box; + border-radius: 4px; + border: 1px solid #e5e5e5; + } + + .uni-date-editor--x { + display: flex; + align-items: center; + position: relative; + } + + .uni-date-editor--x .uni-date__icon-clear { + padding-right: 3px; + display: flex; + align-items: center; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + .uni-date__x-input { + width: auto; + height: 35px; + /* #ifndef MP */ + padding-left: 5px; + /* #endif */ + position: relative; + flex: 1; + line-height: 35px; + font-size: 14px; + overflow: hidden; + } + + .text-center { + text-align: center; + } + + .uni-date__input { + height: 40px; + width: 100%; + line-height: 40px; + font-size: 14px; + } + + .uni-date-range__input { + text-align: center; + max-width: 142px; + } + + .uni-date-picker__container { + position: relative; + } + + .uni-date-mask--pc { + position: fixed; + bottom: 0px; + top: 0px; + left: 0px; + right: 0px; + background-color: rgba(0, 0, 0, 0); + transition-duration: 0.3s; + z-index: 996; + } + + .uni-date-single--x { + background-color: #fff; + position: absolute; + top: 0; + z-index: 999; + border: 1px solid #EBEEF5; + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); + border-radius: 4px; + } + + .uni-date-range--x { + background-color: #fff; + position: absolute; + top: 0; + z-index: 999; + border: 1px solid #EBEEF5; + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); + border-radius: 4px; + } + + .uni-date-editor--x__disabled { + opacity: 0.4; + cursor: default; + } + + .uni-date-editor--logo { + width: 16px; + height: 16px; + vertical-align: middle; + } + + /* 添加时间 */ + .popup-x-header { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + } + + .popup-x-header--datetime { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + flex: 1; + } + + .popup-x-body { + display: flex; + } + + .popup-x-footer { + padding: 0 15px; + border-top-color: #F1F1F1; + border-top-style: solid; + border-top-width: 1px; + line-height: 40px; + text-align: right; + color: #666; + } + + .popup-x-footer text:hover { + color: $uni-primary; + cursor: pointer; + opacity: 0.8; + } + + .popup-x-footer .confirm-text { + margin-left: 20px; + color: $uni-primary; + } + + .uni-date-changed { + text-align: center; + color: #333; + border-bottom-color: #F1F1F1; + border-bottom-style: solid; + border-bottom-width: 1px; + } + + .uni-date-changed--time text { + height: 50px; + line-height: 50px; + } + + .uni-date-changed .uni-date-changed--time { + flex: 1; + } + + .uni-date-changed--time-date { + color: #333; + opacity: 0.6; + } + + .mr-50 { + margin-right: 50px; + } + + /* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */ + .uni-popper__arrow, + .uni-popper__arrow::after { + position: absolute; + display: block; + width: 0; + height: 0; + border: 6px solid transparent; + border-top-width: 0; + } + + .uni-popper__arrow { + filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); + top: -6px; + left: 10%; + margin-right: 3px; + border-bottom-color: #EBEEF5; + } + + .uni-popper__arrow::after { + content: " "; + top: 1px; + margin-left: -6px; + border-bottom-color: #fff; + } +</style> diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js new file mode 100644 index 0000000..6e9f47d --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js @@ -0,0 +1,421 @@ +class Calendar { + constructor({ + selected, + startDate, + endDate, + range, + } = {}) { + // 当前日期 + this.date = this.getDateObj(new Date()) // 当前初入日期 + // 打点信息 + this.selected = selected || []; + // 起始时间 + this.startDate = startDate + // 终止时间 + this.endDate = endDate + // 是否范围选择 + this.range = range + // 多选状态 + this.cleanMultipleStatus() + // 每周日期 + this.weeks = {} + this.lastHover = false + } + /** + * 设置日期 + * @param {Object} date + */ + setDate(date) { + const selectDate = this.getDateObj(date) + this.getWeeks(selectDate.fullDate) + } + + /** + * 清理多选状态 + */ + cleanMultipleStatus() { + this.multipleStatus = { + before: '', + after: '', + data: [] + } + } + + setStartDate(startDate) { + this.startDate = startDate + } + + setEndDate(endDate) { + this.endDate = endDate + } + + getPreMonthObj(date) { + date = fixIosDateFormat(date) + date = new Date(date) + + const oldMonth = date.getMonth() + date.setMonth(oldMonth - 1) + const newMonth = date.getMonth() + if (oldMonth !== 0 && newMonth - oldMonth === 0) { + date.setMonth(newMonth - 1) + } + return this.getDateObj(date) + } + getNextMonthObj(date) { + date = fixIosDateFormat(date) + date = new Date(date) + + const oldMonth = date.getMonth() + date.setMonth(oldMonth + 1) + const newMonth = date.getMonth() + if (newMonth - oldMonth > 1) { + date.setMonth(newMonth - 1) + } + return this.getDateObj(date) + } + + /** + * 获取指定格式Date对象 + */ + getDateObj(date) { + date = fixIosDateFormat(date) + date = new Date(date) + + return { + fullDate: getDate(date), + year: date.getFullYear(), + month: addZero(date.getMonth() + 1), + date: addZero(date.getDate()), + day: date.getDay() + } + } + + /** + * 获取上一个月日期集合 + */ + getPreMonthDays(amount, dateObj) { + const result = [] + for (let i = amount - 1; i >= 0; i--) { + const month = dateObj.month - 1 + result.push({ + date: new Date(dateObj.year, month, -i).getDate(), + month, + disable: true + }) + } + return result + } + /** + * 获取本月日期集合 + */ + getCurrentMonthDays(amount, dateObj) { + const result = [] + const fullDate = this.date.fullDate + for (let i = 1; i <= amount; i++) { + const currentDate = `${dateObj.year}-${dateObj.month}-${addZero(i)}` + const isToday = fullDate === currentDate + // 获取打点信息 + const info = this.selected && this.selected.find((item) => { + if (this.dateEqual(currentDate, item.date)) { + return item + } + }) + + // 日期禁用 + let disableBefore = true + let disableAfter = true + if (this.startDate) { + disableBefore = dateCompare(this.startDate, currentDate) + } + + if (this.endDate) { + disableAfter = dateCompare(currentDate, this.endDate) + } + + let multiples = this.multipleStatus.data + let multiplesStatus = -1 + if (this.range && multiples) { + multiplesStatus = multiples.findIndex((item) => { + return this.dateEqual(item, currentDate) + }) + } + const checked = multiplesStatus !== -1 + + result.push({ + fullDate: currentDate, + year: dateObj.year, + date: i, + multiple: this.range ? checked : false, + beforeMultiple: this.isLogicBefore(currentDate, this.multipleStatus.before, this.multipleStatus.after), + afterMultiple: this.isLogicAfter(currentDate, this.multipleStatus.before, this.multipleStatus.after), + month: dateObj.month, + disable: (this.startDate && !dateCompare(this.startDate, currentDate)) || (this.endDate && !dateCompare( + currentDate, this.endDate)), + isToday, + userChecked: false, + extraInfo: info + }) + } + return result + } + /** + * 获取下一个月日期集合 + */ + _getNextMonthDays(amount, dateObj) { + const result = [] + const month = dateObj.month + 1 + for (let i = 1; i <= amount; i++) { + result.push({ + date: i, + month, + disable: true + }) + } + return result + } + + /** + * 获取当前日期详情 + * @param {Object} date + */ + getInfo(date) { + if (!date) { + date = new Date() + } + const res = this.calendar.find(item => item.fullDate === this.getDateObj(date).fullDate) + return res ? res : this.getDateObj(date) + } + + /** + * 比较时间是否相等 + */ + dateEqual(before, after) { + before = new Date(fixIosDateFormat(before)) + after = new Date(fixIosDateFormat(after)) + return before.valueOf() === after.valueOf() + } + + /** + * 比较真实起始日期 + */ + + isLogicBefore(currentDate, before, after) { + let logicBefore = before + if (before && after) { + logicBefore = dateCompare(before, after) ? before : after + } + return this.dateEqual(logicBefore, currentDate) + } + + isLogicAfter(currentDate, before, after) { + let logicAfter = after + if (before && after) { + logicAfter = dateCompare(before, after) ? after : before + } + return this.dateEqual(logicAfter, currentDate) + } + + /** + * 获取日期范围内所有日期 + * @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.getDateObj(new Date(parseInt(k))).fullDate) + } + return arr + } + + /** + * 获取多选状态 + */ + setMultiple(fullDate) { + if (!this.range) return + + let { + before, + after + } = this.multipleStatus + if (before && after) { + if (!this.lastHover) { + this.lastHover = true + return + } + this.multipleStatus.before = fullDate + this.multipleStatus.after = '' + this.multipleStatus.data = [] + this.multipleStatus.fulldate = '' + this.lastHover = false + } else { + if (!before) { + this.multipleStatus.before = fullDate + this.multipleStatus.after = undefined; + this.lastHover = false + } else { + this.multipleStatus.after = fullDate + if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) { + this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus + .after); + } else { + this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus + .before); + } + this.lastHover = true + } + } + this.getWeeks(fullDate) + } + + /** + * 鼠标 hover 更新多选状态 + */ + setHoverMultiple(fullDate) { + //抖音小程序点击会触发hover事件,需要避免一下 + // #ifndef MP-TOUTIAO + if (!this.range || this.lastHover) return + const { + before + } = this.multipleStatus + + if (!before) { + this.multipleStatus.before = fullDate + } else { + this.multipleStatus.after = fullDate + if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) { + this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after); + } else { + this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before); + } + } + this.getWeeks(fullDate) + // #endif + + } + + /** + * 更新默认值多选状态 + */ + setDefaultMultiple(before, after) { + this.multipleStatus.before = before + this.multipleStatus.after = after + if (before && after) { + if (dateCompare(before, after)) { + this.multipleStatus.data = this.geDateAll(before, after); + this.getWeeks(after) + } else { + this.multipleStatus.data = this.geDateAll(after, before); + this.getWeeks(before) + } + } + } + + /** + * 获取每周数据 + * @param {Object} dateData + */ + getWeeks(dateData) { + const { + year, + month, + } = this.getDateObj(dateData) + + const preMonthDayAmount = new Date(year, month - 1, 1).getDay() + const preMonthDays = this.getPreMonthDays(preMonthDayAmount, this.getDateObj(dateData)) + + const currentMonthDayAmount = new Date(year, month, 0).getDate() + const currentMonthDays = this.getCurrentMonthDays(currentMonthDayAmount, this.getDateObj(dateData)) + + const nextMonthDayAmount = 42 - preMonthDayAmount - currentMonthDayAmount + const nextMonthDays = this._getNextMonthDays(nextMonthDayAmount, this.getDateObj(dateData)) + + const calendarDays = [...preMonthDays, ...currentMonthDays, ...nextMonthDays] + + const weeks = new Array(6) + for (let i = 0; i < calendarDays.length; i++) { + const index = Math.floor(i / 7) + if (!weeks[index]) { + weeks[index] = new Array(7) + } + weeks[index][i % 7] = calendarDays[i] + } + + this.calendar = calendarDays + this.weeks = weeks + } +} + +function getDateTime(date, hideSecond) { + return `${getDate(date)} ${getTime(date, hideSecond)}` +} + +function getDate(date) { + date = fixIosDateFormat(date) + date = new Date(date) + const year = date.getFullYear() + const month = date.getMonth() + 1 + const day = date.getDate() + return `${year}-${addZero(month)}-${addZero(day)}` +} + +function getTime(date, hideSecond) { + date = fixIosDateFormat(date) + date = new Date(date) + const hour = date.getHours() + const minute = date.getMinutes() + const second = date.getSeconds() + return hideSecond ? `${addZero(hour)}:${addZero(minute)}` : `${addZero(hour)}:${addZero(minute)}:${addZero(second)}` +} + +function addZero(num) { + if (num < 10) { + num = `0${num}` + } + return num +} + +function getDefaultSecond(hideSecond) { + return hideSecond ? '00:00' : '00:00:00' +} + +function dateCompare(startDate, endDate) { + startDate = new Date(fixIosDateFormat(startDate)) + endDate = new Date(fixIosDateFormat(endDate)) + return startDate <= endDate +} + +function checkDate(date) { + const dateReg = /((19|20)\d{2})(-|\/)\d{1,2}(-|\/)\d{1,2}/g + return date.match(dateReg) +} +//ios低版本15及以下,无法匹配 没有 ’秒‘ 时的情况,所以需要在末尾 秒 加上 问号 +const dateTimeReg = /^\d{4}-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])( [0-5]?[0-9]:[0-5]?[0-9](:[0-5]?[0-9])?)?$/; + +function fixIosDateFormat(value) { + if (typeof value === 'string' && dateTimeReg.test(value)) { + value = value.replace(/-/g, '/') + } + return value +} + +export { + Calendar, + getDateTime, + getDate, + getTime, + addZero, + getDefaultSecond, + dateCompare, + checkDate, + fixIosDateFormat +} diff --git a/uni_modules/uni-datetime-picker/package.json b/uni_modules/uni-datetime-picker/package.json new file mode 100644 index 0000000..a886f1b --- /dev/null +++ b/uni_modules/uni-datetime-picker/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-datetime-picker", + "displayName": "uni-datetime-picker 日期选择器", + "version": "2.2.38", + "description": "uni-datetime-picker 日期时间选择器,支持日历,支持范围选择", + "keywords": [ + "uni-datetime-picker", + "uni-ui", + "uniui", + "日期时间选择器", + "日期时间" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "n" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-datetime-picker/readme.md b/uni_modules/uni-datetime-picker/readme.md new file mode 100644 index 0000000..162fbef --- /dev/null +++ b/uni_modules/uni-datetime-picker/readme.md @@ -0,0 +1,21 @@ + + +> `重要通知:组件升级更新 2.0.0 后,支持日期+时间范围选择,组件 ui 将使用日历选择日期,ui 变化较大,同时支持 PC 和 移动端。此版本不向后兼容,不再支持单独的时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)。若仍需使用旧版本,可在插件市场下载*非uni_modules版本*,旧版本将不再维护` + +## DatetimePicker 时间选择器 + +> **组件名:uni-datetime-picker** +> 代码块: `uDatetimePicker` + + +该组件的优势是,支持**时间戳**输入和输出(起始时间、终止时间也支持时间戳),可**同时选择**日期和时间。 + +若只是需要单独选择日期和时间,不需要时间戳输入和输出,可使用原生的 picker 组件。 + +**_点击 picker 默认值规则:_** + +- 若设置初始值 value, 会显示在 picker 显示框中 +- 若无初始值 value,则初始值 value 为当前本地时间 Date.now(), 但不会显示在 picker 显示框中 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-drawer/changelog.md b/uni_modules/uni-drawer/changelog.md new file mode 100644 index 0000000..6d2488c --- /dev/null +++ b/uni_modules/uni-drawer/changelog.md @@ -0,0 +1,13 @@ +## 1.2.1(2021-11-22) +- 修复 vue3中个别scss变量无法找到的问题 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-drawer](https://uniapp.dcloud.io/component/uniui/uni-drawer) +## 1.1.1(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-05-12) +- 新增 组件示例地址 +## 1.0.6(2021-02-04) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-drawer/components/uni-drawer/keypress.js b/uni_modules/uni-drawer/components/uni-drawer/keypress.js new file mode 100644 index 0000000..62dda46 --- /dev/null +++ b/uni_modules/uni-drawer/components/uni-drawer/keypress.js @@ -0,0 +1,45 @@ +// #ifdef H5 +export default { + name: 'Keypress', + props: { + disable: { + type: Boolean, + default: false + } + }, + mounted () { + const keyNames = { + esc: ['Esc', 'Escape'], + tab: 'Tab', + enter: 'Enter', + space: [' ', 'Spacebar'], + up: ['Up', 'ArrowUp'], + left: ['Left', 'ArrowLeft'], + right: ['Right', 'ArrowRight'], + down: ['Down', 'ArrowDown'], + delete: ['Backspace', 'Delete', 'Del'] + } + const listener = ($event) => { + if (this.disable) { + return + } + const keyName = Object.keys(keyNames).find(key => { + const keyName = $event.key + const value = keyNames[key] + return value === keyName || (Array.isArray(value) && value.includes(keyName)) + }) + if (keyName) { + // 避免和其他按键事件冲突 + setTimeout(() => { + this.$emit(keyName, {}) + }, 0) + } + } + document.addEventListener('keyup', listener) + // this.$once('hook:beforeDestroy', () => { + // document.removeEventListener('keyup', listener) + // }) + }, + render: () => {} +} +// #endif diff --git a/uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue b/uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue new file mode 100644 index 0000000..2471521 --- /dev/null +++ b/uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue @@ -0,0 +1,183 @@ +<template> + <view v-if="visibleSync" :class="{ 'uni-drawer--visible': showDrawer }" class="uni-drawer" @touchmove.stop.prevent="clear"> + <view class="uni-drawer__mask" :class="{ 'uni-drawer__mask--visible': showDrawer && mask }" @tap="close('mask')" /> + <view class="uni-drawer__content" :class="{'uni-drawer--right': rightMode,'uni-drawer--left': !rightMode, 'uni-drawer__content--visible': showDrawer}" :style="{width:drawerWidth+'px'}"> + <slot /> + </view> + <!-- #ifdef H5 --> + <keypress @esc="close('mask')" /> + <!-- #endif --> + </view> +</template> + +<script> + // #ifdef H5 + import keypress from './keypress.js' + // #endif + /** + * Drawer 抽屉 + * @description 抽屉侧滑菜单 + * @tutorial https://ext.dcloud.net.cn/plugin?id=26 + * @property {Boolean} mask = [true | false] 是否显示遮罩 + * @property {Boolean} maskClick = [true | false] 点击遮罩是否关闭 + * @property {Boolean} mode = [left | right] Drawer 滑出位置 + * @value left 从左侧滑出 + * @value right 从右侧侧滑出 + * @property {Number} width 抽屉的宽度 ,仅 vue 页面生效 + * @event {Function} close 组件关闭时触发事件 + */ + export default { + name: 'UniDrawer', + components: { + // #ifdef H5 + keypress + // #endif + }, + emits:['change'], + props: { + /** + * 显示模式(左、右),只在初始化生效 + */ + mode: { + type: String, + default: '' + }, + /** + * 蒙层显示状态 + */ + mask: { + type: Boolean, + default: true + }, + /** + * 遮罩是否可点击关闭 + */ + maskClick:{ + type: Boolean, + default: true + }, + /** + * 抽屉宽度 + */ + width: { + type: Number, + default: 220 + } + }, + data() { + return { + visibleSync: false, + showDrawer: false, + rightMode: false, + watchTimer: null, + drawerWidth: 220 + } + }, + created() { + // #ifndef APP-NVUE + this.drawerWidth = this.width + // #endif + this.rightMode = this.mode === 'right' + }, + methods: { + clear(){}, + close(type) { + // fixed by mehaotian 抽屉尚未完全关闭或遮罩禁止点击时不触发以下逻辑 + if((type === 'mask' && !this.maskClick) || !this.visibleSync) return + this._change('showDrawer', 'visibleSync', false) + }, + open() { + // fixed by mehaotian 处理重复点击打开的事件 + if(this.visibleSync) return + this._change('visibleSync', 'showDrawer', true) + }, + _change(param1, param2, status) { + this[param1] = status + if (this.watchTimer) { + clearTimeout(this.watchTimer) + } + this.watchTimer = setTimeout(() => { + this[param2] = status + this.$emit('change',status) + }, status ? 50 : 300) + } + } + } +</script> + +<style lang="scss" scoped> + $uni-mask: rgba($color: #000000, $alpha: 0.4) ; + // 抽屉宽度 + $drawer-width: 220px; + + .uni-drawer { + /* #ifndef APP-NVUE */ + display: block; + /* #endif */ + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: hidden; + z-index: 999; + } + + .uni-drawer__content { + /* #ifndef APP-NVUE */ + display: block; + /* #endif */ + position: absolute; + top: 0; + width: $drawer-width; + bottom: 0; + background-color: $uni-bg-color; + transition: transform 0.3s ease; + } + + .uni-drawer--left { + left: 0; + /* #ifdef APP-NVUE */ + transform: translateX(-$drawer-width); + /* #endif */ + /* #ifndef APP-NVUE */ + transform: translateX(-100%); + /* #endif */ + } + + .uni-drawer--right { + right: 0; + /* #ifdef APP-NVUE */ + transform: translateX($drawer-width); + /* #endif */ + /* #ifndef APP-NVUE */ + transform: translateX(100%); + /* #endif */ + } + + .uni-drawer__content--visible { + transform: translateX(0px); + } + + + .uni-drawer__mask { + /* #ifndef APP-NVUE */ + display: block; + /* #endif */ + opacity: 0; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + background-color: $uni-mask; + transition: opacity 0.3s; + } + + .uni-drawer__mask--visible { + /* #ifndef APP-NVUE */ + display: block; + /* #endif */ + opacity: 1; + } +</style> diff --git a/uni_modules/uni-drawer/package.json b/uni_modules/uni-drawer/package.json new file mode 100644 index 0000000..dd056e4 --- /dev/null +++ b/uni_modules/uni-drawer/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-drawer", + "displayName": "uni-drawer 抽屉", + "version": "1.2.1", + "description": "抽屉式导航,用于展示侧滑菜单,侧滑导航。", + "keywords": [ + "uni-ui", + "uniui", + "drawer", + "抽屉", + "侧滑导航" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-drawer/readme.md b/uni_modules/uni-drawer/readme.md new file mode 100644 index 0000000..dcf6e6b --- /dev/null +++ b/uni_modules/uni-drawer/readme.md @@ -0,0 +1,10 @@ + + +## Drawer 抽屉 +> **组件名:uni-drawer** +> 代码块: `uDrawer` + +抽屉侧滑菜单。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-drawer) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-easyinput/changelog.md b/uni_modules/uni-easyinput/changelog.md new file mode 100644 index 0000000..84c72eb --- /dev/null +++ b/uni_modules/uni-easyinput/changelog.md @@ -0,0 +1,115 @@ +## 1.1.19(2024-07-18) +- 修复 初始值传入 null 导致input报错的bug +## 1.1.18(2024-04-11) +- 修复 easyinput组件双向绑定问题 +## 1.1.17(2024-03-28) +- 修复 在头条小程序下丢失事件绑定的问题 +## 1.1.16(2024-03-20) +- 修复 在密码输入情况下 清除和小眼睛覆盖bug 在edge浏览器下显示双眼睛bug +## 1.1.15(2024-02-21) +- 新增 左侧插槽:left +## 1.1.14(2024-02-19) +- 修复 onBlur的emit传值错误 +## 1.1.12(2024-01-29) +- 补充 adjust-position文档属性补充 +## 1.1.11(2024-01-29) +- 补充 adjust-position属性传递值:(Boolean)当键盘弹起时,是否自动上推页面 +## 1.1.10(2024-01-22) +- 去除 移除无用的log输出 +## 1.1.9(2023-04-11) +- 修复 vue3 下 keyboardheightchange 事件报错的bug +## 1.1.8(2023-03-29) +- 优化 trim 属性默认值 +## 1.1.7(2023-03-29) +- 新增 cursor-spacing 属性 +## 1.1.6(2023-01-28) +- 新增 keyboardheightchange 事件,可监听键盘高度变化 +## 1.1.5(2022-11-29) +- 优化 主题样式 +## 1.1.4(2022-10-27) +- 修复 props 中背景颜色无默认值的bug +## 1.1.0(2022-06-30) + +- 新增 在 uni-forms 1.4.0 中使用可以在 blur 时校验内容 +- 新增 clear 事件,点击右侧叉号图标触发 +- 新增 change 事件 ,仅在输入框失去焦点或用户按下回车时触发 +- 优化 组件样式,组件获取焦点时高亮显示,图标颜色调整等 + +## 1.0.5(2022-06-07) + +- 优化 clearable 显示策略 + +## 1.0.4(2022-06-07) + +- 优化 clearable 显示策略 + +## 1.0.3(2022-05-20) + +- 修复 关闭图标某些情况下无法取消的 bug + +## 1.0.2(2022-04-12) + +- 修复 默认值不生效的 bug + +## 1.0.1(2022-04-02) + +- 修复 value 不能为 0 的 bug + +## 1.0.0(2021-11-19) + +- 优化 组件 UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-easyinput](https://uniapp.dcloud.io/component/uniui/uni-easyinput) + +## 0.1.4(2021-08-20) + +- 修复 在 uni-forms 的动态表单中默认值校验不通过的 bug + +## 0.1.3(2021-08-11) + +- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题 + +## 0.1.2(2021-07-30) + +- 优化 vue3 下事件警告的问题 + +## 0.1.1 + +- 优化 errorMessage 属性支持 Boolean 类型 + +## 0.1.0(2021-07-13) + +- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) + +## 0.0.16(2021-06-29) + +- 修复 confirmType 属性(仅 type="text" 生效)导致多行文本框无法换行的 bug + +## 0.0.15(2021-06-21) + +- 修复 passwordIcon 属性拼写错误的 bug + +## 0.0.14(2021-06-18) + +- 新增 passwordIcon 属性,当 type=password 时是否显示小眼睛图标 +- 修复 confirmType 属性不生效的问题 + +## 0.0.13(2021-06-04) + +- 修复 disabled 状态可清出内容的 bug + +## 0.0.12(2021-05-12) + +- 新增 组件示例地址 + +## 0.0.11(2021-05-07) + +- 修复 input-border 属性不生效的问题 + +## 0.0.10(2021-04-30) + +- 修复 ios 遮挡文字、显示一半的问题 + +## 0.0.9(2021-02-05) + +- 调整为 uni_modules 目录规范 +- 优化 兼容 nvue 页面 diff --git a/uni_modules/uni-easyinput/components/uni-easyinput/common.js b/uni_modules/uni-easyinput/components/uni-easyinput/common.js new file mode 100644 index 0000000..fde8d3c --- /dev/null +++ b/uni_modules/uni-easyinput/components/uni-easyinput/common.js @@ -0,0 +1,54 @@ +/** + * @desc 函数防抖 + * @param func 目标函数 + * @param wait 延迟执行毫秒数 + * @param immediate true - 立即执行, false - 延迟执行 + */ +export const debounce = function(func, wait = 1000, immediate = true) { + let timer; + return function() { + let context = this, + args = arguments; + if (timer) clearTimeout(timer); + if (immediate) { + let callNow = !timer; + timer = setTimeout(() => { + timer = null; + }, wait); + if (callNow) func.apply(context, args); + } else { + timer = setTimeout(() => { + func.apply(context, args); + }, wait) + } + } +} +/** + * @desc 函数节流 + * @param func 函数 + * @param wait 延迟执行毫秒数 + * @param type 1 使用表时间戳,在时间段开始的时候触发 2 使用表定时器,在时间段结束的时候触发 + */ +export const throttle = (func, wait = 1000, type = 1) => { + let previous = 0; + let timeout; + return function() { + let context = this; + let args = arguments; + if (type === 1) { + let now = Date.now(); + + if (now - previous > wait) { + func.apply(context, args); + previous = now; + } + } else if (type === 2) { + if (!timeout) { + timeout = setTimeout(() => { + timeout = null; + func.apply(context, args) + }, wait) + } + } + } +} diff --git a/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue b/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue new file mode 100644 index 0000000..93506d6 --- /dev/null +++ b/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue @@ -0,0 +1,676 @@ +<template> + <view class="uni-easyinput" :class="{ 'uni-easyinput-error': msg }" :style="boxStyle"> + <view class="uni-easyinput__content" :class="inputContentClass" :style="inputContentStyle"> + <uni-icons v-if="prefixIcon" class="content-clear-icon" :type="prefixIcon" color="#c0c4cc" @click="onClickIcon('prefix')" size="22"></uni-icons> + <slot name="left"> + </slot> + <!-- #ifdef MP-ALIPAY --> + <textarea :enableNative="enableNative" v-if="type === 'textarea'" class="uni-easyinput__content-textarea" :class="{ 'input-padding': inputBorder }" :name="name" :value="val" :placeholder="placeholder" :placeholderStyle="placeholderStyle" :disabled="disabled" placeholder-class="uni-easyinput__placeholder-class" :maxlength="inputMaxlength" :focus="focused" :autoHeight="autoHeight" :cursor-spacing="cursorSpacing" :adjust-position="adjustPosition" @input="onInput" @blur="_Blur" @focus="_Focus" @confirm="onConfirm" @keyboardheightchange="onkeyboardheightchange"></textarea> + <input :enableNative="enableNative" v-else :type="type === 'password' ? 'text' : type" class="uni-easyinput__content-input" :style="inputStyle" :name="name" :value="val" :password="!showPassword && type === 'password'" :placeholder="placeholder" :placeholderStyle="placeholderStyle" placeholder-class="uni-easyinput__placeholder-class" :disabled="disabled" :maxlength="inputMaxlength" :focus="focused" :confirmType="confirmType" :cursor-spacing="cursorSpacing" :adjust-position="adjustPosition" @focus="_Focus" @blur="_Blur" @input="onInput" @confirm="onConfirm" @keyboardheightchange="onkeyboardheightchange" /> + <!-- #endif --> + <!-- #ifndef MP-ALIPAY --> + <textarea v-if="type === 'textarea'" class="uni-easyinput__content-textarea" :class="{ 'input-padding': inputBorder }" :name="name" :value="val" :placeholder="placeholder" :placeholderStyle="placeholderStyle" :disabled="disabled" placeholder-class="uni-easyinput__placeholder-class" :maxlength="inputMaxlength" :focus="focused" :autoHeight="autoHeight" :cursor-spacing="cursorSpacing" :adjust-position="adjustPosition" @input="onInput" @blur="_Blur" @focus="_Focus" @confirm="onConfirm" @keyboardheightchange="onkeyboardheightchange"></textarea> + <input v-else :type="type === 'password' ? 'text' : type" class="uni-easyinput__content-input" :style="inputStyle" :name="name" :value="val" :password="!showPassword && type === 'password'" :placeholder="placeholder" :placeholderStyle="placeholderStyle" placeholder-class="uni-easyinput__placeholder-class" :disabled="disabled" :maxlength="inputMaxlength" :focus="focused" :confirmType="confirmType" :cursor-spacing="cursorSpacing" :adjust-position="adjustPosition" @focus="_Focus" @blur="_Blur" @input="onInput" @confirm="onConfirm" @keyboardheightchange="onkeyboardheightchange" /> + <!-- #endif --> + + <template v-if="type === 'password' && passwordIcon"> + <!-- 开启密码时显示小眼睛 --> + <uni-icons v-if="isVal" class="content-clear-icon" :class="{ 'is-textarea-icon': type === 'textarea' }" :type="showPassword ? 'eye-slash-filled' : 'eye-filled'" :size="22" :color="focusShow ? primaryColor : '#c0c4cc'" @click="onEyes"></uni-icons> + </template> + <template v-if="suffixIcon"> + <uni-icons v-if="suffixIcon" class="content-clear-icon" :type="suffixIcon" color="#c0c4cc" @click="onClickIcon('suffix')" size="22"></uni-icons> + </template> + <template v-else> + <uni-icons v-if="clearable && isVal && !disabled && type !== 'textarea'" class="content-clear-icon" :class="{ 'is-textarea-icon': type === 'textarea' }" type="clear" :size="clearSize" :color="msg ? '#dd524d' : focusShow ? primaryColor : '#c0c4cc'" @click="onClear"></uni-icons> + </template> + <slot name="right"></slot> + </view> + </view> +</template> + +<script> + /** + * Easyinput 输入框 + * @description 此组件可以实现表单的输入与校验,包括 "text" 和 "textarea" 类型。 + * @tutorial https://ext.dcloud.net.cn/plugin?id=3455 + * @property {String} value 输入内容 + * @property {String } type 输入框的类型(默认text) password/text/textarea/.. + * @value text 文本输入键盘 + * @value textarea 多行文本输入键盘 + * @value password 密码输入键盘 + * @value number 数字输入键盘,注意iOS上app-vue弹出的数字键盘并非9宫格方式 + * @value idcard 身份证输入键盘,信、支付宝、百度、QQ小程序 + * @value digit 带小数点的数字键盘 ,App的nvue页面、微信、支付宝、百度、头条、QQ小程序支持 + * @property {Boolean} clearable 是否显示右侧清空内容的图标控件,点击可清空输入框内容(默认true) + * @property {Boolean} autoHeight 是否自动增高输入区域,type为textarea时有效(默认true) + * @property {String } placeholder 输入框的提示文字 + * @property {String } placeholderStyle placeholder的样式(内联样式,字符串),如"color: #ddd" + * @property {Boolean} focus 是否自动获得焦点(默认false) + * @property {Boolean} disabled 是否禁用(默认false) + * @property {Number } maxlength 最大输入长度,设置为 -1 的时候不限制最大长度(默认140) + * @property {String } confirmType 设置键盘右下角按钮的文字,仅在type="text"时生效(默认done) + * @property {Number } clearSize 清除图标的大小,单位px(默认15) + * @property {String} prefixIcon 输入框头部图标 + * @property {String} suffixIcon 输入框尾部图标 + * @property {String} primaryColor 设置主题色(默认#2979ff) + * @property {Boolean} trim 是否自动去除两端的空格 + * @property {Boolean} cursorSpacing 指定光标与键盘的距离,单位 px + * @property {Boolean} ajust-position 当键盘弹起时,是否上推内容,默认值:true + * @value both 去除两端空格 + * @value left 去除左侧空格 + * @value right 去除右侧空格 + * @value start 去除左侧空格 + * @value end 去除右侧空格 + * @value all 去除全部空格 + * @value none 不去除空格 + * @property {Boolean} inputBorder 是否显示input输入框的边框(默认true) + * @property {Boolean} passwordIcon type=password时是否显示小眼睛图标 + * @property {Object} styles 自定义颜色 + * @event {Function} input 输入框内容发生变化时触发 + * @event {Function} focus 输入框获得焦点时触发 + * @event {Function} blur 输入框失去焦点时触发 + * @event {Function} confirm 点击完成按钮时触发 + * @event {Function} iconClick 点击图标时触发 + * @example <uni-easyinput v-model="mobile"></uni-easyinput> + */ + function obj2strClass(obj) { + let classess = ''; + for (let key in obj) { + const val = obj[key]; + if (val) { + classess += `${key} `; + } + } + return classess; + } + + function obj2strStyle(obj) { + let style = ''; + for (let key in obj) { + const val = obj[key]; + style += `${key}:${val};`; + } + return style; + } + export default { + name: 'uni-easyinput', + emits: [ + 'click', + 'iconClick', + 'update:modelValue', + 'input', + 'focus', + 'blur', + 'confirm', + 'clear', + 'eyes', + 'change', + 'keyboardheightchange' + ], + model: { + prop: 'modelValue', + event: 'update:modelValue' + }, + options: { + // #ifdef MP-TOUTIAO + virtualHost: false, + // #endif + // #ifndef MP-TOUTIAO + virtualHost: true + // #endif + }, + inject: { + form: { + from: 'uniForm', + default: null + }, + formItem: { + from: 'uniFormItem', + default: null + } + }, + props: { + name: String, + value: [Number, String], + modelValue: [Number, String], + type: { + type: String, + default: 'text' + }, + clearable: { + type: Boolean, + default: true + }, + autoHeight: { + type: Boolean, + default: false + }, + placeholder: { + type: String, + default: ' ' + }, + placeholderStyle: String, + focus: { + type: Boolean, + default: false + }, + disabled: { + type: Boolean, + default: false + }, + maxlength: { + type: [Number, String], + default: 140 + }, + confirmType: { + type: String, + default: 'done' + }, + clearSize: { + type: [Number, String], + default: 24 + }, + inputBorder: { + type: Boolean, + default: true + }, + prefixIcon: { + type: String, + default: '' + }, + suffixIcon: { + type: String, + default: '' + }, + trim: { + type: [Boolean, String], + default: false + }, + cursorSpacing: { + type: Number, + default: 0 + }, + passwordIcon: { + type: Boolean, + default: true + }, + adjustPosition: { + type: Boolean, + default: true + }, + primaryColor: { + type: String, + default: '#2979ff' + }, + styles: { + type: Object, + default () { + return { + color: '#333', + backgroundColor: '#fff', + disableColor: '#F7F6F6', + borderColor: '#e5e5e5' + }; + } + }, + errorMessage: { + type: [String, Boolean], + default: '' + }, + // #ifdef MP-ALIPAY + enableNative: { + type: Boolean, + default: false + } + // #endif + }, + data() { + return { + focused: false, + val: '', + showMsg: '', + border: false, + isFirstBorder: false, + showClearIcon: false, + showPassword: false, + focusShow: false, + localMsg: '', + isEnter: false // 用于判断当前是否是使用回车操作 + }; + }, + computed: { + // 输入框内是否有值 + isVal() { + const val = this.val; + // fixed by mehaotian 处理值为0的情况,字符串0不在处理范围 + if (val || val === 0) { + return true; + } + return false; + }, + + msg() { + // console.log('computed', this.form, this.formItem); + // if (this.form) { + // return this.errorMessage || this.formItem.errMsg; + // } + // TODO 处理头条 formItem 中 errMsg 不更新的问题 + return this.localMsg || this.errorMessage; + }, + // 因为uniapp的input组件的maxlength组件必须要数值,这里转为数值,用户可以传入字符串数值 + inputMaxlength() { + return Number(this.maxlength); + }, + + // 处理外层样式的style + boxStyle() { + return `color:${ + this.inputBorder && this.msg ? '#e43d33' : this.styles.color + };`; + }, + // input 内容的类和样式处理 + inputContentClass() { + return obj2strClass({ + 'is-input-border': this.inputBorder, + 'is-input-error-border': this.inputBorder && this.msg, + 'is-textarea': this.type === 'textarea', + 'is-disabled': this.disabled, + 'is-focused': this.focusShow + }); + }, + inputContentStyle() { + const focusColor = this.focusShow ? + this.primaryColor : + this.styles.borderColor; + const borderColor = + this.inputBorder && this.msg ? '#dd524d' : focusColor; + return obj2strStyle({ + 'border-color': borderColor || '#e5e5e5', + 'background-color': this.disabled ? + this.styles.disableColor : this.styles.backgroundColor + }); + }, + // input右侧样式 + inputStyle() { + const paddingRight = + this.type === 'password' || this.clearable || this.prefixIcon ? + '' : + '10px'; + return obj2strStyle({ + 'padding-right': paddingRight, + 'padding-left': this.prefixIcon ? '' : '10px' + }); + } + }, + watch: { + value(newVal) { + // fix by mehaotian 解决 值为null的情况下,input报错的bug + if (newVal === null) { + this.val = ''; + return + } + this.val = newVal; + }, + modelValue(newVal) { + if (newVal === null) { + this.val = ''; + return + } + this.val = newVal; + }, + focus(newVal) { + this.$nextTick(() => { + this.focused = this.focus; + this.focusShow = this.focus; + }); + } + }, + created() { + this.init(); + // TODO 处理头条vue3 computed 不监听 inject 更改的问题(formItem.errMsg) + if (this.form && this.formItem) { + this.$watch('formItem.errMsg', newVal => { + this.localMsg = newVal; + }); + } + }, + mounted() { + this.$nextTick(() => { + this.focused = this.focus; + this.focusShow = this.focus; + }); + }, + methods: { + /** + * 初始化变量值 + */ + init() { + if (this.value || this.value === 0) { + this.val = this.value; + } else if ( + this.modelValue || + this.modelValue === 0 || + this.modelValue === '' + ) { + this.val = this.modelValue; + } else { + // fix by ht 如果初始值为null,则input报错,待框架修复 + this.val = ''; + } + }, + + /** + * 点击图标时触发 + * @param {Object} type + */ + onClickIcon(type) { + this.$emit('iconClick', type); + }, + + /** + * 显示隐藏内容,密码框时生效 + */ + onEyes() { + this.showPassword = !this.showPassword; + this.$emit('eyes', this.showPassword); + }, + + /** + * 输入时触发 + * @param {Object} event + */ + onInput(event) { + let value = event.detail.value; + // 判断是否去除空格 + if (this.trim) { + if (typeof this.trim === 'boolean' && this.trim) { + value = this.trimStr(value); + } + if (typeof this.trim === 'string') { + value = this.trimStr(value, this.trim); + } + } + if (this.errMsg) this.errMsg = ''; + this.val = value; + // TODO 兼容 vue2 + this.$emit('input', value); + // TODO 兼容 vue3 + this.$emit('update:modelValue', value); + }, + + /** + * 外部调用方法 + * 获取焦点时触发 + * @param {Object} event + */ + onFocus() { + this.$nextTick(() => { + this.focused = true; + }); + this.$emit('focus', null); + }, + + _Focus(event) { + this.focusShow = true; + this.$emit('focus', event); + }, + + /** + * 外部调用方法 + * 失去焦点时触发 + * @param {Object} event + */ + onBlur() { + this.focused = false; + this.$emit('blur', null); + }, + _Blur(event) { + let value = event.detail.value; + this.focusShow = false; + this.$emit('blur', event); + // 根据类型返回值,在event中获取的值理论上讲都是string + if (this.isEnter === false) { + this.$emit('change', this.val); + } + // 失去焦点时参与表单校验 + if (this.form && this.formItem) { + const { validateTrigger } = this.form; + if (validateTrigger === 'blur') { + this.formItem.onFieldChange(); + } + } + }, + + /** + * 按下键盘的发送键 + * @param {Object} e + */ + onConfirm(e) { + this.$emit('confirm', this.val); + this.isEnter = true; + this.$emit('change', this.val); + this.$nextTick(() => { + this.isEnter = false; + }); + }, + + /** + * 清理内容 + * @param {Object} event + */ + onClear(event) { + this.val = ''; + // TODO 兼容 vue2 + this.$emit('input', ''); + // TODO 兼容 vue2 + // TODO 兼容 vue3 + this.$emit('update:modelValue', ''); + // 点击叉号触发 + this.$emit('clear'); + }, + + /** + * 键盘高度发生变化的时候触发此事件 + * 兼容性:微信小程序2.7.0+、App 3.1.0+ + * @param {Object} event + */ + onkeyboardheightchange(event) { + this.$emit('keyboardheightchange', event); + }, + + /** + * 去除空格 + */ + trimStr(str, pos = 'both') { + if (pos === 'both') { + return str.trim(); + } else if (pos === 'left') { + return str.trimLeft(); + } else if (pos === 'right') { + return str.trimRight(); + } else if (pos === 'start') { + return str.trimStart(); + } else if (pos === 'end') { + return str.trimEnd(); + } else if (pos === 'all') { + return str.replace(/\s+/g, ''); + } else if (pos === 'none') { + return str; + } + return str; + } + } + }; +</script> + +<style lang="scss"> + $uni-error: #e43d33; + $uni-border-1: #dcdfe6 !default; + + .uni-easyinput { + /* #ifndef APP-NVUE */ + width: 100%; + /* #endif */ + flex: 1; + position: relative; + text-align: left; + color: #333; + font-size: 14px; + } + + .uni-easyinput__content { + flex: 1; + /* #ifndef APP-NVUE */ + width: 100%; + display: flex; + box-sizing: border-box; + // min-height: 36px; + /* #endif */ + flex-direction: row; + align-items: center; + // 处理border动画刚开始显示黑色的问题 + border-color: #fff; + transition-property: border-color; + transition-duration: 0.3s; + } + + .uni-easyinput__content-input { + /* #ifndef APP-NVUE */ + width: auto; + /* #endif */ + position: relative; + overflow: hidden; + flex: 1; + line-height: 1; + font-size: 14px; + height: 35px; + // min-height: 36px; + + /*ifdef H5*/ + & ::-ms-reveal { + display: none; + } + + & ::-ms-clear { + display: none; + } + + & ::-o-clear { + display: none; + } + + /*endif*/ + } + + .uni-easyinput__placeholder-class { + color: #999; + font-size: 12px; + // font-weight: 200; + } + + .is-textarea { + align-items: flex-start; + } + + .is-textarea-icon { + margin-top: 5px; + } + + .uni-easyinput__content-textarea { + position: relative; + overflow: hidden; + flex: 1; + line-height: 1.5; + font-size: 14px; + margin: 6px; + margin-left: 0; + height: 80px; + min-height: 80px; + /* #ifndef APP-NVUE */ + min-height: 80px; + width: auto; + /* #endif */ + } + + .input-padding { + padding-left: 10px; + } + + .content-clear-icon { + padding: 0 5px; + } + + .label-icon { + margin-right: 5px; + margin-top: -1px; + } + + // 显示边框 + .is-input-border { + /* #ifndef APP-NVUE */ + display: flex; + box-sizing: border-box; + /* #endif */ + flex-direction: row; + align-items: center; + border: 1px solid $uni-border-1; + border-radius: 4px; + /* #ifdef MP-ALIPAY */ + overflow: hidden; + /* #endif */ + } + + .uni-error-message { + position: absolute; + bottom: -17px; + left: 0; + line-height: 12px; + color: $uni-error; + font-size: 12px; + text-align: left; + } + + .uni-error-msg--boeder { + position: relative; + bottom: 0; + line-height: 22px; + } + + .is-input-error-border { + border-color: $uni-error; + + .uni-easyinput__placeholder-class { + color: mix(#fff, $uni-error, 50%); + } + } + + .uni-easyinput--border { + margin-bottom: 0; + padding: 10px 15px; + // padding-bottom: 0; + border-top: 1px #eee solid; + } + + .uni-easyinput-error { + padding-bottom: 0; + } + + .is-first-border { + /* #ifndef APP-NVUE */ + border: none; + /* #endif */ + /* #ifdef APP-NVUE */ + border-width: 0; + /* #endif */ + } + + .is-disabled { + background-color: #f7f6f6; + color: #d5d5d5; + + .uni-easyinput__placeholder-class { + color: #d5d5d5; + font-size: 12px; + } + } +</style> \ No newline at end of file diff --git a/uni_modules/uni-easyinput/package.json b/uni_modules/uni-easyinput/package.json new file mode 100644 index 0000000..2939256 --- /dev/null +++ b/uni_modules/uni-easyinput/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-easyinput", + "displayName": "uni-easyinput 增强输入框", + "version": "1.1.19", + "description": "Easyinput 组件是对原生input组件的增强", + "keywords": [ + "uni-ui", + "uniui", + "input", + "uni-easyinput", + "输入框" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-easyinput/readme.md b/uni_modules/uni-easyinput/readme.md new file mode 100644 index 0000000..f1faf8f --- /dev/null +++ b/uni_modules/uni-easyinput/readme.md @@ -0,0 +1,11 @@ + + +### Easyinput 增强输入框 +> **组件名:uni-easyinput** +> 代码块: `uEasyinput` + + +easyinput 组件是对原生input组件的增强 ,是专门为配合表单组件[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)而设计的,easyinput 内置了边框,图标等,同时包含 input 所有功能 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-easyinput) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-fab/changelog.md b/uni_modules/uni-fab/changelog.md new file mode 100644 index 0000000..8a22807 --- /dev/null +++ b/uni_modules/uni-fab/changelog.md @@ -0,0 +1,25 @@ +## 1.2.6(2024-10-12) +- 修复 微信小程序中的getSystemInfo警告 +## 1.2.5(2023-03-29) +- 新增 pattern.icon 属性,可自定义图标 +## 1.2.4(2022-09-07) +小程序端由于 style 使用了对象导致报错,[详情](https://ask.dcloud.net.cn/question/152790?item_id=211778&rf=false) +## 1.2.3(2022-09-05) +- 修复 nvue 环境下,具有 tabBar 时,fab 组件下部位置无法正常获取 --window-bottom 的bug,详见:[https://ask.dcloud.net.cn/question/110638?notification_id=826310](https://ask.dcloud.net.cn/question/110638?notification_id=826310) +## 1.2.2(2021-12-29) +- 更新 组件依赖 +## 1.2.1(2021-11-19) +- 修复 阴影颜色不正确的bug +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-fab](https://uniapp.dcloud.io/component/uniui/uni-fab) +## 1.1.1(2021-11-09) +- 新增 提供组件设计资源,组件样式调整 +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-05-12) +- 新增 组件示例地址 +## 1.0.6(2021-02-05) +- 调整为uni_modules目录规范 +- 优化 按钮背景色调整 +- 优化 兼容pc端 diff --git a/uni_modules/uni-fab/components/uni-fab/uni-fab.vue b/uni_modules/uni-fab/components/uni-fab/uni-fab.vue new file mode 100644 index 0000000..492b5d1 --- /dev/null +++ b/uni_modules/uni-fab/components/uni-fab/uni-fab.vue @@ -0,0 +1,491 @@ +<template> + <view class="uni-cursor-point"> + <view v-if="popMenu && (leftBottom||rightBottom||leftTop||rightTop) && content.length > 0" :class="{ + 'uni-fab--leftBottom': leftBottom, + 'uni-fab--rightBottom': rightBottom, + 'uni-fab--leftTop': leftTop, + 'uni-fab--rightTop': rightTop + }" class="uni-fab" + :style="nvueBottom" + > + <view :class="{ + 'uni-fab__content--left': horizontal === 'left', + 'uni-fab__content--right': horizontal === 'right', + 'uni-fab__content--flexDirection': direction === 'vertical', + 'uni-fab__content--flexDirectionStart': flexDirectionStart, + 'uni-fab__content--flexDirectionEnd': flexDirectionEnd, + 'uni-fab__content--other-platform': !isAndroidNvue + }" :style="{ width: boxWidth, height: boxHeight, backgroundColor: styles.backgroundColor }" + class="uni-fab__content" elevation="5"> + <view v-if="flexDirectionStart || horizontalLeft" class="uni-fab__item uni-fab__item--first" /> + <view v-for="(item, index) in content" :key="index" :class="{ 'uni-fab__item--active': isShow }" + class="uni-fab__item" @click="_onItemClick(index, item)"> + <image :src="item.active ? item.selectedIconPath : item.iconPath" class="uni-fab__item-image" + mode="aspectFit" /> + <text class="uni-fab__item-text" + :style="{ color: item.active ? styles.selectedColor : styles.color }">{{ item.text }}</text> + </view> + <view v-if="flexDirectionEnd || horizontalRight" class="uni-fab__item uni-fab__item--first" /> + </view> + </view> + <view :class="{ + 'uni-fab__circle--leftBottom': leftBottom, + 'uni-fab__circle--rightBottom': rightBottom, + 'uni-fab__circle--leftTop': leftTop, + 'uni-fab__circle--rightTop': rightTop, + 'uni-fab__content--other-platform': !isAndroidNvue + }" class="uni-fab__circle uni-fab__plus" :style="{ 'background-color': styles.buttonColor, 'bottom': nvueBottom }" @click="_onClick"> + <uni-icons class="fab-circle-icon" :type="styles.icon" :color="styles.iconColor" size="32" + :class="{'uni-fab__plus--active': isShow && content.length > 0}"></uni-icons> + <!-- <view class="fab-circle-v" :class="{'uni-fab__plus--active': isShow && content.length > 0}"></view> + <view class="fab-circle-h" :class="{'uni-fab__plus--active': isShow && content.length > 0}"></view> --> + </view> + </view> +</template> + +<script> + let platform = 'other' + // #ifdef APP-NVUE + platform = uni.getSystemInfoSync().platform + // #endif + + /** + * Fab 悬浮按钮 + * @description 点击可展开一个图形按钮菜单 + * @tutorial https://ext.dcloud.net.cn/plugin?id=144 + * @property {Object} pattern 可选样式配置项 + * @property {Object} horizontal = [left | right] 水平对齐方式 + * @value left 左对齐 + * @value right 右对齐 + * @property {Object} vertical = [bottom | top] 垂直对齐方式 + * @value bottom 下对齐 + * @value top 上对齐 + * @property {Object} direction = [horizontal | vertical] 展开菜单显示方式 + * @value horizontal 水平显示 + * @value vertical 垂直显示 + * @property {Array} content 展开菜单内容配置项 + * @property {Boolean} popMenu 是否使用弹出菜单 + * @event {Function} trigger 展开菜单点击事件,返回点击信息 + * @event {Function} fabClick 悬浮按钮点击事件 + */ + export default { + name: 'UniFab', + emits: ['fabClick', 'trigger'], + props: { + pattern: { + type: Object, + default () { + return {} + } + }, + horizontal: { + type: String, + default: 'left' + }, + vertical: { + type: String, + default: 'bottom' + }, + direction: { + type: String, + default: 'horizontal' + }, + content: { + type: Array, + default () { + return [] + } + }, + show: { + type: Boolean, + default: false + }, + popMenu: { + type: Boolean, + default: true + } + }, + data() { + return { + fabShow: false, + isShow: false, + isAndroidNvue: platform === 'android', + styles: { + color: '#3c3e49', + selectedColor: '#007AFF', + backgroundColor: '#fff', + buttonColor: '#007AFF', + iconColor: '#fff', + icon: 'plusempty' + } + } + }, + computed: { + contentWidth(e) { + return (this.content.length + 1) * 55 + 15 + 'px' + }, + contentWidthMin() { + return '55px' + }, + // 动态计算宽度 + boxWidth() { + return this.getPosition(3, 'horizontal') + }, + // 动态计算高度 + boxHeight() { + return this.getPosition(3, 'vertical') + }, + // 计算左下位置 + leftBottom() { + return this.getPosition(0, 'left', 'bottom') + }, + // 计算右下位置 + rightBottom() { + return this.getPosition(0, 'right', 'bottom') + }, + // 计算左上位置 + leftTop() { + return this.getPosition(0, 'left', 'top') + }, + rightTop() { + return this.getPosition(0, 'right', 'top') + }, + flexDirectionStart() { + return this.getPosition(1, 'vertical', 'top') + }, + flexDirectionEnd() { + return this.getPosition(1, 'vertical', 'bottom') + }, + horizontalLeft() { + return this.getPosition(2, 'horizontal', 'left') + }, + horizontalRight() { + return this.getPosition(2, 'horizontal', 'right') + }, + // 计算 nvue bottom + nvueBottom() { + // #ifdef APP-NVUE + const safeBottom = uni.getSystemInfoSync().windowBottom; + return 30 + safeBottom + // #endif + // #ifndef APP-NVUE + return 30 + // #endif + } + }, + watch: { + pattern: { + handler(val, oldVal) { + this.styles = Object.assign({}, this.styles, val) + }, + deep: true + } + }, + created() { + this.isShow = this.show + if (this.top === 0) { + this.fabShow = true + } + // 初始化样式 + this.styles = Object.assign({}, this.styles, this.pattern) + }, + methods: { + _onClick() { + this.$emit('fabClick') + if (!this.popMenu) { + return + } + this.isShow = !this.isShow + }, + open() { + this.isShow = true + }, + close() { + this.isShow = false + }, + /** + * 按钮点击事件 + */ + _onItemClick(index, item) { + if (!this.isShow) { + return + } + this.$emit('trigger', { + index, + item + }) + }, + /** + * 获取 位置信息 + */ + getPosition(types, paramA, paramB) { + if (types === 0) { + return this.horizontal === paramA && this.vertical === paramB + } else if (types === 1) { + return this.direction === paramA && this.vertical === paramB + } else if (types === 2) { + return this.direction === paramA && this.horizontal === paramB + } else { + return this.isShow && this.direction === paramA ? this.contentWidth : this.contentWidthMin + } + } + } + } +</script> + +<style lang="scss" > + $uni-shadow-base:0 1px 5px 2px rgba($color: #000000, $alpha: 0.3) !default; + + .uni-fab { + position: fixed; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + justify-content: center; + align-items: center; + z-index: 10; + border-radius: 45px; + box-shadow: $uni-shadow-base; + } + + .uni-cursor-point { + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + .uni-fab--active { + opacity: 1; + } + + .uni-fab--leftBottom { + left: 15px; + bottom: 30px; + /* #ifdef H5 */ + left: calc(15px + var(--window-left)); + bottom: calc(30px + var(--window-bottom)); + /* #endif */ + // padding: 10px; + } + + .uni-fab--leftTop { + left: 15px; + top: 30px; + /* #ifdef H5 */ + left: calc(15px + var(--window-left)); + top: calc(30px + var(--window-top)); + /* #endif */ + // padding: 10px; + } + + .uni-fab--rightBottom { + right: 15px; + bottom: 30px; + /* #ifdef H5 */ + right: calc(15px + var(--window-right)); + bottom: calc(30px + var(--window-bottom)); + /* #endif */ + // padding: 10px; + } + + .uni-fab--rightTop { + right: 15px; + top: 30px; + /* #ifdef H5 */ + right: calc(15px + var(--window-right)); + top: calc(30px + var(--window-top)); + /* #endif */ + // padding: 10px; + } + + .uni-fab__circle { + position: fixed; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + justify-content: center; + align-items: center; + width: 55px; + height: 55px; + background-color: #3c3e49; + border-radius: 45px; + z-index: 11; + // box-shadow: $uni-shadow-base; + } + + .uni-fab__circle--leftBottom { + left: 15px; + bottom: 30px; + /* #ifdef H5 */ + left: calc(15px + var(--window-left)); + bottom: calc(30px + var(--window-bottom)); + /* #endif */ + } + + .uni-fab__circle--leftTop { + left: 15px; + top: 30px; + /* #ifdef H5 */ + left: calc(15px + var(--window-left)); + top: calc(30px + var(--window-top)); + /* #endif */ + } + + .uni-fab__circle--rightBottom { + right: 15px; + bottom: 30px; + /* #ifdef H5 */ + right: calc(15px + var(--window-right)); + bottom: calc(30px + var(--window-bottom)); + /* #endif */ + } + + .uni-fab__circle--rightTop { + right: 15px; + top: 30px; + /* #ifdef H5 */ + right: calc(15px + var(--window-right)); + top: calc(30px + var(--window-top)); + /* #endif */ + } + + .uni-fab__circle--left { + left: 0; + } + + .uni-fab__circle--right { + right: 0; + } + + .uni-fab__circle--top { + top: 0; + } + + .uni-fab__circle--bottom { + bottom: 0; + } + + .uni-fab__plus { + font-weight: bold; + } + + // .fab-circle-v { + // position: absolute; + // width: 2px; + // height: 24px; + // left: 0; + // top: 0; + // right: 0; + // bottom: 0; + // /* #ifndef APP-NVUE */ + // margin: auto; + // /* #endif */ + // background-color: white; + // transform: rotate(0deg); + // transition: transform 0.3s; + // } + + // .fab-circle-h { + // position: absolute; + // width: 24px; + // height: 2px; + // left: 0; + // top: 0; + // right: 0; + // bottom: 0; + // /* #ifndef APP-NVUE */ + // margin: auto; + // /* #endif */ + // background-color: white; + // transform: rotate(0deg); + // transition: transform 0.3s; + // } + + .fab-circle-icon { + transform: rotate(0deg); + transition: transform 0.3s; + font-weight: 200; + } + + .uni-fab__plus--active { + transform: rotate(135deg); + } + + .uni-fab__content { + /* #ifndef APP-NVUE */ + box-sizing: border-box; + display: flex; + /* #endif */ + flex-direction: row; + border-radius: 55px; + overflow: hidden; + transition-property: width, height; + transition-duration: 0.2s; + width: 55px; + border-color: #DDDDDD; + border-width: 1rpx; + border-style: solid; + } + + .uni-fab__content--other-platform { + border-width: 0px; + box-shadow: $uni-shadow-base; + } + + .uni-fab__content--left { + justify-content: flex-start; + } + + .uni-fab__content--right { + justify-content: flex-end; + } + + .uni-fab__content--flexDirection { + flex-direction: column; + justify-content: flex-end; + } + + .uni-fab__content--flexDirectionStart { + flex-direction: column; + justify-content: flex-start; + } + + .uni-fab__content--flexDirectionEnd { + flex-direction: column; + justify-content: flex-end; + } + + .uni-fab__item { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + justify-content: center; + align-items: center; + width: 55px; + height: 55px; + opacity: 0; + transition: opacity 0.2s; + } + + .uni-fab__item--active { + opacity: 1; + } + + .uni-fab__item-image { + width: 20px; + height: 20px; + margin-bottom: 4px; + } + + .uni-fab__item-text { + color: #FFFFFF; + font-size: 12px; + line-height: 12px; + margin-top: 2px; + } + + .uni-fab__item--first { + width: 55px; + } +</style> diff --git a/uni_modules/uni-fab/package.json b/uni_modules/uni-fab/package.json new file mode 100644 index 0000000..1b8543c --- /dev/null +++ b/uni_modules/uni-fab/package.json @@ -0,0 +1,85 @@ +{ + "id": "uni-fab", + "displayName": "uni-fab 悬浮按钮", + "version": "1.2.6", + "description": "悬浮按钮 fab button ,点击可展开一个图标按钮菜单。", + "keywords": [ + "uni-ui", + "uniui", + "按钮", + "悬浮按钮", + "fab" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss","uni-icons"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-fab/readme.md b/uni_modules/uni-fab/readme.md new file mode 100644 index 0000000..9a444e8 --- /dev/null +++ b/uni_modules/uni-fab/readme.md @@ -0,0 +1,9 @@ +## Fab 悬浮按钮 +> **组件名:uni-fab** +> 代码块: `uFab` + + +点击可展开一个图形按钮菜单 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-fab) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-fav/changelog.md b/uni_modules/uni-fav/changelog.md new file mode 100644 index 0000000..d8a08d4 --- /dev/null +++ b/uni_modules/uni-fav/changelog.md @@ -0,0 +1,19 @@ +## 1.2.1(2022-05-30) +- 新增 stat 属性 ,是否开启uni统计功能 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-fav](https://uniapp.dcloud.io/component/uniui/uni-fav) +## 1.1.1(2021-08-24) +- 新增 支持国际化 +## 1.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.6(2021-05-12) +- 新增 组件示例地址 +## 1.0.5(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.4(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 1.0.3(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 1.0.2(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-fav/components/uni-fav/i18n/en.json b/uni_modules/uni-fav/components/uni-fav/i18n/en.json new file mode 100644 index 0000000..9a0759e --- /dev/null +++ b/uni_modules/uni-fav/components/uni-fav/i18n/en.json @@ -0,0 +1,4 @@ +{ + "uni-fav.collect": "collect", + "uni-fav.collected": "collected" +} diff --git a/uni_modules/uni-fav/components/uni-fav/i18n/index.js b/uni_modules/uni-fav/components/uni-fav/i18n/index.js new file mode 100644 index 0000000..de7509c --- /dev/null +++ b/uni_modules/uni-fav/components/uni-fav/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json b/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json new file mode 100644 index 0000000..67c89bf --- /dev/null +++ b/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json @@ -0,0 +1,4 @@ +{ + "uni-fav.collect": "收藏", + "uni-fav.collected": "已收藏" +} diff --git a/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json b/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json new file mode 100644 index 0000000..67c89bf --- /dev/null +++ b/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json @@ -0,0 +1,4 @@ +{ + "uni-fav.collect": "收藏", + "uni-fav.collected": "已收藏" +} diff --git a/uni_modules/uni-fav/components/uni-fav/uni-fav.vue b/uni_modules/uni-fav/components/uni-fav/uni-fav.vue new file mode 100644 index 0000000..d2c58df --- /dev/null +++ b/uni_modules/uni-fav/components/uni-fav/uni-fav.vue @@ -0,0 +1,161 @@ +<template> + <view :class="[circle === true || circle === 'true' ? 'uni-fav--circle' : '']" :style="[{ backgroundColor: checked ? bgColorChecked : bgColor }]" + @click="onClick" class="uni-fav"> + <!-- #ifdef MP-ALIPAY --> + <view class="uni-fav-star" v-if="!checked && (star === true || star === 'true')"> + <uni-icons :color="fgColor" :style="{color: checked ? fgColorChecked : fgColor}" size="14" type="star-filled" /> + </view> + <!-- #endif --> + <!-- #ifndef MP-ALIPAY --> + <uni-icons :color="fgColor" :style="{color: checked ? fgColorChecked : fgColor}" class="uni-fav-star" size="14" type="star-filled" + v-if="!checked && (star === true || star === 'true')" /> + <!-- #endif --> + <text :style="{color: checked ? fgColorChecked : fgColor}" class="uni-fav-text">{{ checked ? contentFav : contentDefault }}</text> + </view> +</template> + +<script> + + /** + * Fav 收藏按钮 + * @description 用于收藏功能,可点击切换选中、不选中的状态 + * @tutorial https://ext.dcloud.net.cn/plugin?id=864 + * @property {Boolean} star = [true|false] 按钮是否带星星 + * @property {String} bgColor 未收藏时的背景色 + * @property {String} bgColorChecked 已收藏时的背景色 + * @property {String} fgColor 未收藏时的文字颜色 + * @property {String} fgColorChecked 已收藏时的文字颜色 + * @property {Boolean} circle = [true|false] 是否为圆角 + * @property {Boolean} checked = [true|false] 是否为已收藏 + * @property {Object} contentText = [true|false] 收藏按钮文字 + * @property {Boolean} stat 是否开启统计功能 + * @event {Function} click 点击 fav按钮触发事件 + * @example <uni-fav :checked="true"/> + */ + + import { + initVueI18n + } from '@dcloudio/uni-i18n' + import messages from './i18n/index.js' + const { t } = initVueI18n(messages) + + export default { + name: "UniFav", + // TODO 兼容 vue3,需要注册事件 + emits: ['click'], + props: { + star: { + type: [Boolean, String], + default: true + }, + bgColor: { + type: String, + default: "#eeeeee" + }, + fgColor: { + type: String, + default: "#666666" + }, + bgColorChecked: { + type: String, + default: "#007aff" + }, + fgColorChecked: { + type: String, + default: "#FFFFFF" + }, + circle: { + type: [Boolean, String], + default: false + }, + checked: { + type: Boolean, + default: false + }, + contentText: { + type: Object, + default () { + return { + contentDefault: "", + contentFav: "" + }; + } + }, + stat:{ + type: Boolean, + default: false + } + }, + computed: { + contentDefault() { + return this.contentText.contentDefault || t("uni-fav.collect") + }, + contentFav() { + return this.contentText.contentFav || t("uni-fav.collected") + }, + }, + watch: { + checked() { + if (uni.report && this.stat) { + if (this.checked) { + uni.report("收藏", "收藏"); + } else { + uni.report("取消收藏", "取消收藏"); + } + } + } + }, + methods: { + onClick() { + this.$emit("click"); + } + } + }; +</script> + +<style lang="scss" > + $fav-height: 25px; + + .uni-fav { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + align-items: center; + justify-content: center; + width: 60px; + height: $fav-height; + line-height: $fav-height; + text-align: center; + border-radius: 3px; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + .uni-fav--circle { + border-radius: 30px; + } + + .uni-fav-star { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + height: $fav-height; + line-height: 24px; + margin-right: 3px; + align-items: center; + justify-content: center; + } + + .uni-fav-text { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + height: $fav-height; + line-height: $fav-height; + align-items: center; + justify-content: center; + font-size: 12px; + } +</style> diff --git a/uni_modules/uni-fav/package.json b/uni_modules/uni-fav/package.json new file mode 100644 index 0000000..cc14697 --- /dev/null +++ b/uni_modules/uni-fav/package.json @@ -0,0 +1,89 @@ +{ + "id": "uni-fav", + "displayName": "uni-fav 收藏按钮", + "version": "1.2.1", + "description": " Fav 收藏组件,可自定义颜色、大小。", + "keywords": [ + "fav", + "uni-ui", + "uniui", + "收藏" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-fav/readme.md b/uni_modules/uni-fav/readme.md new file mode 100644 index 0000000..4de125d --- /dev/null +++ b/uni_modules/uni-fav/readme.md @@ -0,0 +1,10 @@ + + +## Fav 收藏按钮 +> **组件名:uni-fav** +> 代码块: `uFav` + +用于收藏功能,可点击切换选中、不选中的状态。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-fav) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-file-picker/changelog.md b/uni_modules/uni-file-picker/changelog.md new file mode 100644 index 0000000..b81e7f9 --- /dev/null +++ b/uni_modules/uni-file-picker/changelog.md @@ -0,0 +1,81 @@ +## 1.0.11(2024-07-19) +- 修复 vue3 使用value报错的bug +## 1.0.10(2024-07-09) +- 优化 vue3兼容性 +## 1.0.9(2024-07-09) +- 修复 value 属性不兼容vue3的bug +## 1.0.8(2024-03-20) +- 补充 删除文件时返回文件下标 +## 1.0.7(2024-02-21) +- 新增 微信小程序选择视频时改用chooseMedia,并返回视频缩略图 +## 1.0.6(2024-01-06) +- 新增 微信小程序不再调用chooseImage,而是调用chooseMedia +## 1.0.5(2024-01-03) +- 新增 上传文件至云存储携带本地文件名称 +## 1.0.4(2023-03-29) +- 修复 手动上传删除一个文件后不能再上传的bug +## 1.0.3(2022-12-19) +- 新增 sourceType 属性, 可以自定义图片和视频选择的来源 +## 1.0.2(2022-07-04) +- 修复 在uni-forms下样式不生效的bug +## 1.0.1(2021-11-23) +- 修复 参数为对象的情况下,url在某些情况显示错误的bug +## 1.0.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-file-picker](https://uniapp.dcloud.io/component/uniui/uni-file-picker) +## 0.2.16(2021-11-08) +- 修复 传入空对象 ,显示错误的Bug +## 0.2.15(2021-08-30) +- 修复 return-type="object" 时且存在v-model时,无法删除文件的Bug +## 0.2.14(2021-08-23) +- 新增 参数中返回 fileID 字段 +## 0.2.13(2021-08-23) +- 修复 腾讯云传入fileID 不能回显的bug +- 修复 选择图片后,不能放大的问题 +## 0.2.12(2021-08-17) +- 修复 由于 0.2.11 版本引起的不能回显图片的Bug +## 0.2.11(2021-08-16) +- 新增 clearFiles(index) 方法,可以手动删除指定文件 +- 修复 v-model 值设为 null 报错的Bug +## 0.2.10(2021-08-13) +- 修复 return-type="object" 时,无法删除文件的Bug +## 0.2.9(2021-08-03) +- 修复 auto-upload 属性失效的Bug +## 0.2.8(2021-07-31) +- 修复 fileExtname属性不指定值报错的Bug +## 0.2.7(2021-07-31) +- 修复 在某种场景下图片不回显的Bug +## 0.2.6(2021-07-30) +- 修复 return-type为object下,返回值不正确的Bug +## 0.2.5(2021-07-30) +- 修复(重要) H5 平台下如果和uni-forms组件一同使用导致页面卡死的问题 +## 0.2.3(2021-07-28) +- 优化 调整示例代码 +## 0.2.2(2021-07-27) +- 修复 vue3 下赋值错误的Bug +- 优化 h5平台下上传文件导致页面卡死的问题 +## 0.2.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 0.1.1(2021-07-02) +- 修复 sourceType 缺少默认值导致 ios 无法选择文件 +## 0.1.0(2021-06-30) +- 优化 解耦与uniCloud的强绑定关系 ,如不绑定服务空间,默认autoUpload为false且不可更改 +## 0.0.11(2021-06-30) +- 修复 由 0.0.10 版本引发的 returnType 属性失效的问题 +## 0.0.10(2021-06-29) +- 优化 文件上传后进度条消失时机 +## 0.0.9(2021-06-29) +- 修复 在uni-forms 中,删除文件 ,获取的值不对的Bug +## 0.0.8(2021-06-15) +- 修复 删除文件时无法触发 v-model 的Bug +## 0.0.7(2021-05-12) +- 新增 组件示例地址 +## 0.0.6(2021-04-09) +- 修复 选择的文件非 file-extname 字段指定的扩展名报错的Bug +## 0.0.5(2021-04-09) +- 优化 更新组件示例 +## 0.0.4(2021-04-09) +- 优化 file-extname 字段支持字符串写法,多个扩展名需要用逗号分隔 +## 0.0.3(2021-02-05) +- 调整为uni_modules目录规范 +- 修复 微信小程序不指定 fileExtname 属性选择失败的Bug diff --git a/uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js b/uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js new file mode 100644 index 0000000..9c6bcdf --- /dev/null +++ b/uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js @@ -0,0 +1,287 @@ +'use strict'; + +const ERR_MSG_OK = 'chooseAndUploadFile:ok'; +const ERR_MSG_FAIL = 'chooseAndUploadFile:fail'; + +function chooseImage(opts) { + const { + count, + sizeType = ['original', 'compressed'], + sourceType, + extension + } = opts + return new Promise((resolve, reject) => { + // 微信由于旧接口不再维护,针对微信小程序平台改用chooseMedia接口 + // #ifdef MP-WEIXIN + uni.chooseMedia({ + count, + sizeType, + sourceType, + mediaType: ['image'], + extension, + success(res) { + res.tempFiles.forEach(item => { + item.path = item.tempFilePath; + }) + resolve(normalizeChooseAndUploadFileRes(res, 'image')); + }, + fail(res) { + reject({ + errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL), + }); + }, + }) + // #endif + // #ifndef MP-WEIXIN + uni.chooseImage({ + count, + sizeType, + sourceType, + extension, + success(res) { + resolve(normalizeChooseAndUploadFileRes(res, 'image')); + }, + fail(res) { + reject({ + errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL), + }); + }, + }); + // #endif + + }); +} + +function chooseVideo(opts) { + const { + count, + camera, + compressed, + maxDuration, + sourceType, + extension + } = opts; + return new Promise((resolve, reject) => { + // 微信由于旧接口不再维护,针对微信小程序平台改用chooseMedia接口 + // #ifdef MP-WEIXIN + uni.chooseMedia({ + count, + compressed, + maxDuration, + sourceType, + extension, + mediaType: ['video'], + success(res) { + const { + tempFiles, + } = res; + resolve(normalizeChooseAndUploadFileRes({ + errMsg: 'chooseVideo:ok', + tempFiles: tempFiles.map(item => { + return { + name: item.name || '', + path: item.tempFilePath, + thumbTempFilePath: item.thumbTempFilePath, + size:item.size, + type: (res.tempFile && res.tempFile.type) || '', + width:item.width, + height:item.height, + duration:item.duration, + fileType: 'video', + cloudPath: '', + } + }), + }, 'video')); + }, + fail(res) { + reject({ + errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL), + }); + }, + }) + // #endif + // #ifndef MP-WEIXIN + uni.chooseVideo({ + camera, + compressed, + maxDuration, + sourceType, + extension, + success(res) { + const { + tempFilePath, + duration, + size, + height, + width + } = res; + resolve(normalizeChooseAndUploadFileRes({ + errMsg: 'chooseVideo:ok', + tempFilePaths: [tempFilePath], + tempFiles: [{ + name: (res.tempFile && res.tempFile.name) || '', + path: tempFilePath, + size, + type: (res.tempFile && res.tempFile.type) || '', + width, + height, + duration, + fileType: 'video', + cloudPath: '', + }, ], + }, 'video')); + }, + fail(res) { + reject({ + errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL), + }); + }, + }); + // #endif + }); +} + +function chooseAll(opts) { + const { + count, + extension + } = opts; + return new Promise((resolve, reject) => { + let chooseFile = uni.chooseFile; + if (typeof wx !== 'undefined' && + typeof wx.chooseMessageFile === 'function') { + chooseFile = wx.chooseMessageFile; + } + if (typeof chooseFile !== 'function') { + return reject({ + errMsg: ERR_MSG_FAIL + ' 请指定 type 类型,该平台仅支持选择 image 或 video。', + }); + } + chooseFile({ + type: 'all', + count, + extension, + success(res) { + resolve(normalizeChooseAndUploadFileRes(res)); + }, + fail(res) { + reject({ + errMsg: res.errMsg.replace('chooseFile:fail', ERR_MSG_FAIL), + }); + }, + }); + }); +} + +function normalizeChooseAndUploadFileRes(res, fileType) { + res.tempFiles.forEach((item, index) => { + if (!item.name) { + item.name = item.path.substring(item.path.lastIndexOf('/') + 1); + } + if (fileType) { + item.fileType = fileType; + } + item.cloudPath = + Date.now() + '_' + index + item.name.substring(item.name.lastIndexOf('.')); + }); + if (!res.tempFilePaths) { + res.tempFilePaths = res.tempFiles.map((file) => file.path); + } + return res; +} + +function uploadCloudFiles(files, max = 5, onUploadProgress) { + files = JSON.parse(JSON.stringify(files)) + const len = files.length + let count = 0 + let self = this + return new Promise(resolve => { + while (count < max) { + next() + } + + function next() { + let cur = count++ + if (cur >= len) { + !files.find(item => !item.url && !item.errMsg) && resolve(files) + return + } + const fileItem = files[cur] + const index = self.files.findIndex(v => v.uuid === fileItem.uuid) + fileItem.url = '' + delete fileItem.errMsg + + uniCloud + .uploadFile({ + filePath: fileItem.path, + cloudPath: fileItem.cloudPath, + fileType: fileItem.fileType, + onUploadProgress: res => { + res.index = index + onUploadProgress && onUploadProgress(res) + } + }) + .then(res => { + fileItem.url = res.fileID + fileItem.index = index + if (cur < len) { + next() + } + }) + .catch(res => { + fileItem.errMsg = res.errMsg || res.message + fileItem.index = index + if (cur < len) { + next() + } + }) + } + }) +} + + + + + +function uploadFiles(choosePromise, { + onChooseFile, + onUploadProgress +}) { + return choosePromise + .then((res) => { + if (onChooseFile) { + const customChooseRes = onChooseFile(res); + if (typeof customChooseRes !== 'undefined') { + return Promise.resolve(customChooseRes).then((chooseRes) => typeof chooseRes === 'undefined' ? + res : chooseRes); + } + } + return res; + }) + .then((res) => { + if (res === false) { + return { + errMsg: ERR_MSG_OK, + tempFilePaths: [], + tempFiles: [], + }; + } + return res + }) +} + +function chooseAndUploadFile(opts = { + type: 'all' +}) { + if (opts.type === 'image') { + return uploadFiles(chooseImage(opts), opts); + } else if (opts.type === 'video') { + return uploadFiles(chooseVideo(opts), opts); + } + return uploadFiles(chooseAll(opts), opts); +} + +export { + chooseAndUploadFile, + uploadCloudFiles +}; diff --git a/uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue b/uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue new file mode 100644 index 0000000..785c7eb --- /dev/null +++ b/uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue @@ -0,0 +1,668 @@ +<template> + <view class="uni-file-picker"> + <view v-if="title" class="uni-file-picker__header"> + <text class="file-title">{{ title }}</text> + <text class="file-count">{{ filesList.length }}/{{ limitLength }}</text> + </view> + <upload-image v-if="fileMediatype === 'image' && showType === 'grid'" :readonly="readonly" + :image-styles="imageStyles" :files-list="filesList" :limit="limitLength" :disablePreview="disablePreview" + :delIcon="delIcon" @uploadFiles="uploadFiles" @choose="choose" @delFile="delFile"> + <slot> + <view class="is-add"> + <view class="icon-add"></view> + <view class="icon-add rotate"></view> + </view> + </slot> + </upload-image> + <upload-file v-if="fileMediatype !== 'image' || showType !== 'grid'" :readonly="readonly" + :list-styles="listStyles" :files-list="filesList" :showType="showType" :delIcon="delIcon" + @uploadFiles="uploadFiles" @choose="choose" @delFile="delFile"> + <slot><button type="primary" size="mini">选择文件</button></slot> + </upload-file> + </view> +</template> + +<script> + import { + chooseAndUploadFile, + uploadCloudFiles + } from './choose-and-upload-file.js' + import { + get_file_ext, + get_extname, + get_files_and_is_max, + get_file_info, + get_file_data + } from './utils.js' + import uploadImage from './upload-image.vue' + import uploadFile from './upload-file.vue' + let fileInput = null + /** + * FilePicker 文件选择上传 + * @description 文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间 + * @tutorial https://ext.dcloud.net.cn/plugin?id=4079 + * @property {Object|Array} value 组件数据,通常用来回显 ,类型由return-type属性决定 + * @property {Boolean} disabled = [true|false] 组件禁用 + * @value true 禁用 + * @value false 取消禁用 + * @property {Boolean} readonly = [true|false] 组件只读,不可选择,不显示进度,不显示删除按钮 + * @value true 只读 + * @value false 取消只读 + * @property {String} return-type = [array|object] 限制 value 格式,当为 object 时 ,组件只能单选,且会覆盖 + * @value array 规定 value 属性的类型为数组 + * @value object 规定 value 属性的类型为对象 + * @property {Boolean} disable-preview = [true|false] 禁用图片预览,仅 mode:grid 时生效 + * @value true 禁用图片预览 + * @value false 取消禁用图片预览 + * @property {Boolean} del-icon = [true|false] 是否显示删除按钮 + * @value true 显示删除按钮 + * @value false 不显示删除按钮 + * @property {Boolean} auto-upload = [true|false] 是否自动上传,值为true则只触发@select,可自行上传 + * @value true 自动上传 + * @value false 取消自动上传 + * @property {Number|String} limit 最大选择个数 ,h5 会自动忽略多选的部分 + * @property {String} title 组件标题,右侧显示上传计数 + * @property {String} mode = [list|grid] 选择文件后的文件列表样式 + * @value list 列表显示 + * @value grid 宫格显示 + * @property {String} file-mediatype = [image|video|all] 选择文件类型 + * @value image 只选择图片 + * @value video 只选择视频 + * @value all 选择所有文件 + * @property {Array} file-extname 选择文件后缀,根据 file-mediatype 属性而不同 + * @property {Object} list-style mode:list 时的样式 + * @property {Object} image-styles 选择文件后缀,根据 file-mediatype 属性而不同 + * @event {Function} select 选择文件后触发 + * @event {Function} progress 文件上传时触发 + * @event {Function} success 上传成功触发 + * @event {Function} fail 上传失败触发 + * @event {Function} delete 文件从列表移除时触发 + */ + export default { + name: 'uniFilePicker', + components: { + uploadImage, + uploadFile + }, + options: { + virtualHost: true + }, + emits: ['select', 'success', 'fail', 'progress', 'delete', 'update:modelValue', 'input'], + props: { + modelValue: { + type: [Array, Object], + default () { + return [] + } + }, + value: { + type: [Array, Object], + default () { + return [] + } + }, + disabled: { + type: Boolean, + default: false + }, + disablePreview: { + type: Boolean, + default: false + }, + delIcon: { + type: Boolean, + default: true + }, + // 自动上传 + autoUpload: { + type: Boolean, + default: true + }, + // 最大选择个数 ,h5只能限制单选或是多选 + limit: { + type: [Number, String], + default: 9 + }, + // 列表样式 grid | list | list-card + mode: { + type: String, + default: 'grid' + }, + // 选择文件类型 image/video/all + fileMediatype: { + type: String, + default: 'image' + }, + // 文件类型筛选 + fileExtname: { + type: [Array, String], + default () { + return [] + } + }, + title: { + type: String, + default: '' + }, + listStyles: { + type: Object, + default () { + return { + // 是否显示边框 + border: true, + // 是否显示分隔线 + dividline: true, + // 线条样式 + borderStyle: {} + } + } + }, + imageStyles: { + type: Object, + default () { + return { + width: 'auto', + height: 'auto' + } + } + }, + readonly: { + type: Boolean, + default: false + }, + returnType: { + type: String, + default: 'array' + }, + sizeType: { + type: Array, + default () { + return ['original', 'compressed'] + } + }, + sourceType: { + type: Array, + default () { + return ['album', 'camera'] + } + }, + provider: { + type: String, + default: '' // 默认上传到 unicloud 内置存储 extStorage 扩展存储 + } + }, + data() { + return { + files: [], + localValue: [] + } + }, + watch: { + value: { + handler(newVal, oldVal) { + this.setValue(newVal, oldVal) + }, + immediate: true + }, + modelValue: { + handler(newVal, oldVal) { + this.setValue(newVal, oldVal) + }, + immediate: true + }, + }, + computed: { + filesList() { + let files = [] + this.files.forEach(v => { + files.push(v) + }) + return files + }, + showType() { + if (this.fileMediatype === 'image') { + return this.mode + } + return 'list' + }, + limitLength() { + if (this.returnType === 'object') { + return 1 + } + if (!this.limit) { + return 1 + } + if (this.limit >= 9) { + return 9 + } + return this.limit + } + }, + created() { + // TODO 兼容不开通服务空间的情况 + if (!(uniCloud.config && uniCloud.config.provider)) { + this.noSpace = true + uniCloud.chooseAndUploadFile = chooseAndUploadFile + } + this.form = this.getForm('uniForms') + this.formItem = this.getForm('uniFormsItem') + if (this.form && this.formItem) { + if (this.formItem.name) { + this.rename = this.formItem.name + this.form.inputChildrens.push(this) + } + } + }, + methods: { + /** + * 公开用户使用,清空文件 + * @param {Object} index + */ + clearFiles(index) { + if (index !== 0 && !index) { + this.files = [] + this.$nextTick(() => { + this.setEmit() + }) + } else { + this.files.splice(index, 1) + } + this.$nextTick(() => { + this.setEmit() + }) + }, + /** + * 公开用户使用,继续上传 + */ + upload() { + let files = [] + this.files.forEach((v, index) => { + if (v.status === 'ready' || v.status === 'error') { + files.push(Object.assign({}, v)) + } + }) + return this.uploadFiles(files) + }, + async setValue(newVal, oldVal) { + const newData = async (v) => { + const reg = /cloud:\/\/([\w.]+\/?)\S*/ + let url = '' + if(v.fileID){ + url = v.fileID + }else{ + url = v.url + } + if (reg.test(url)) { + v.fileID = url + v.url = await this.getTempFileURL(url) + } + if(v.url) v.path = v.url + return v + } + if (this.returnType === 'object') { + if (newVal) { + await newData(newVal) + } else { + newVal = {} + } + } else { + if (!newVal) newVal = [] + for(let i =0 ;i < newVal.length ;i++){ + let v = newVal[i] + await newData(v) + } + } + this.localValue = newVal + if (this.form && this.formItem &&!this.is_reset) { + this.is_reset = false + this.formItem.setValue(this.localValue) + } + let filesData = Object.keys(newVal).length > 0 ? newVal : []; + this.files = [].concat(filesData) + }, + + /** + * 选择文件 + */ + choose() { + if (this.disabled) return + if (this.files.length >= Number(this.limitLength) && this.showType !== 'grid' && this.returnType === + 'array') { + uni.showToast({ + title: `您最多选择 ${this.limitLength} 个文件`, + icon: 'none' + }) + return + } + this.chooseFiles() + }, + + /** + * 选择文件并上传 + */ + chooseFiles() { + const _extname = get_extname(this.fileExtname) + // 获取后缀 + uniCloud + .chooseAndUploadFile({ + type: this.fileMediatype, + compressed: false, + sizeType: this.sizeType, + sourceType: this.sourceType, + // TODO 如果为空,video 有问题 + extension: _extname.length > 0 ? _extname : undefined, + count: this.limitLength - this.files.length, //默认9 + onChooseFile: this.chooseFileCallback, + onUploadProgress: progressEvent => { + this.setProgress(progressEvent, progressEvent.index) + } + }) + .then(result => { + this.setSuccessAndError(result.tempFiles) + }) + .catch(err => { + console.log('选择失败', err) + }) + }, + + /** + * 选择文件回调 + * @param {Object} res + */ + async chooseFileCallback(res) { + const _extname = get_extname(this.fileExtname) + const is_one = (Number(this.limitLength) === 1 && + this.disablePreview && + !this.disabled) || + this.returnType === 'object' + // 如果这有一个文件 ,需要清空本地缓存数据 + if (is_one) { + this.files = [] + } + + let { + filePaths, + files + } = get_files_and_is_max(res, _extname) + if (!(_extname && _extname.length > 0)) { + filePaths = res.tempFilePaths + files = res.tempFiles + } + + let currentData = [] + for (let i = 0; i < files.length; i++) { + if (this.limitLength - this.files.length <= 0) break + files[i].uuid = Date.now() + let filedata = await get_file_data(files[i], this.fileMediatype) + filedata.progress = 0 + filedata.status = 'ready' + this.files.push(filedata) + currentData.push({ + ...filedata, + file: files[i] + }) + } + this.$emit('select', { + tempFiles: currentData, + tempFilePaths: filePaths + }) + res.tempFiles = files + // 停止自动上传 + if (!this.autoUpload || this.noSpace) { + res.tempFiles = [] + } + res.tempFiles.forEach((fileItem, index) => { + this.provider && (fileItem.provider = this.provider); + const fileNameSplit = fileItem.name.split('.') + const ext = fileNameSplit.pop() + const fileName = fileNameSplit.join('.').replace(/[\s\/\?<>\\:\*\|":]/g, '_') + fileItem.cloudPath = fileName + '_' + Date.now() + '_' + index + '.' + ext + }) + }, + + /** + * 批传 + * @param {Object} e + */ + uploadFiles(files) { + files = [].concat(files) + return uploadCloudFiles.call(this, files, 5, res => { + this.setProgress(res, res.index, true) + }) + .then(result => { + this.setSuccessAndError(result) + return result; + }) + .catch(err => { + console.log(err) + }) + }, + + /** + * 成功或失败 + */ + async setSuccessAndError(res, fn) { + let successData = [] + let errorData = [] + let tempFilePath = [] + let errorTempFilePath = [] + for (let i = 0; i < res.length; i++) { + const item = res[i] + const index = item.uuid ? this.files.findIndex(p => p.uuid === item.uuid) : item.index + + if (index === -1 || !this.files) break + if (item.errMsg === 'request:fail') { + this.files[index].url = item.path + this.files[index].status = 'error' + this.files[index].errMsg = item.errMsg + // this.files[index].progress = -1 + errorData.push(this.files[index]) + errorTempFilePath.push(this.files[index].url) + } else { + this.files[index].errMsg = '' + this.files[index].fileID = item.url + const reg = /cloud:\/\/([\w.]+\/?)\S*/ + if (reg.test(item.url)) { + this.files[index].url = await this.getTempFileURL(item.url) + }else{ + this.files[index].url = item.url + } + + this.files[index].status = 'success' + this.files[index].progress += 1 + successData.push(this.files[index]) + tempFilePath.push(this.files[index].fileID) + } + } + + if (successData.length > 0) { + this.setEmit() + // 状态改变返回 + this.$emit('success', { + tempFiles: this.backObject(successData), + tempFilePaths: tempFilePath + }) + } + + if (errorData.length > 0) { + this.$emit('fail', { + tempFiles: this.backObject(errorData), + tempFilePaths: errorTempFilePath + }) + } + }, + + /** + * 获取进度 + * @param {Object} progressEvent + * @param {Object} index + * @param {Object} type + */ + setProgress(progressEvent, index, type) { + const fileLenth = this.files.length + const percentNum = (index / fileLenth) * 100 + const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total) + let idx = index + if (!type) { + idx = this.files.findIndex(p => p.uuid === progressEvent.tempFile.uuid) + } + if (idx === -1 || !this.files[idx]) return + // fix by mehaotian 100 就会消失,-1 是为了让进度条消失 + this.files[idx].progress = percentCompleted - 1 + // 上传中 + this.$emit('progress', { + index: idx, + progress: parseInt(percentCompleted), + tempFile: this.files[idx] + }) + }, + + /** + * 删除文件 + * @param {Object} index + */ + delFile(index) { + this.$emit('delete', { + index, + tempFile: this.files[index], + tempFilePath: this.files[index].url + }) + this.files.splice(index, 1) + this.$nextTick(() => { + this.setEmit() + }) + }, + + /** + * 获取文件名和后缀 + * @param {Object} name + */ + getFileExt(name) { + const last_len = name.lastIndexOf('.') + const len = name.length + return { + name: name.substring(0, last_len), + ext: name.substring(last_len + 1, len) + } + }, + + /** + * 处理返回事件 + */ + setEmit() { + let data = [] + if (this.returnType === 'object') { + data = this.backObject(this.files)[0] + this.localValue = data?data:null + } else { + data = this.backObject(this.files) + if (!this.localValue) { + this.localValue = [] + } + this.localValue = [...data] + } + // #ifdef VUE3 + this.$emit('update:modelValue', this.localValue) + // #endif + // #ifndef VUE3 + this.$emit('input', this.localValue) + // #endif + }, + + /** + * 处理返回参数 + * @param {Object} files + */ + backObject(files) { + let newFilesData = [] + files.forEach(v => { + newFilesData.push({ + extname: v.extname, + fileType: v.fileType, + image: v.image, + name: v.name, + path: v.path, + size: v.size, + fileID:v.fileID, + url: v.url, + // 修改删除一个文件后不能再上传的bug, #694 + uuid: v.uuid, + status: v.status, + cloudPath: v.cloudPath + }) + }) + return newFilesData + }, + async getTempFileURL(fileList) { + fileList = { + fileList: [].concat(fileList) + } + const urls = await uniCloud.getTempFileURL(fileList) + return urls.fileList[0].tempFileURL || '' + }, + /** + * 获取父元素实例 + */ + getForm(name = 'uniForms') { + 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; + } + } + } +</script> + +<style> + .uni-file-picker { + /* #ifndef APP-NVUE */ + box-sizing: border-box; + overflow: hidden; + width: 100%; + /* #endif */ + flex: 1; + } + + .uni-file-picker__header { + padding-top: 5px; + padding-bottom: 10px; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + justify-content: space-between; + } + + .file-title { + font-size: 14px; + color: #333; + } + + .file-count { + font-size: 14px; + color: #999; + } + + .is-add { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + align-items: center; + justify-content: center; + } + + .icon-add { + width: 50px; + height: 5px; + background-color: #f1f1f1; + border-radius: 2px; + } + + .rotate { + position: absolute; + transform: rotate(90deg); + } +</style> diff --git a/uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue b/uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue new file mode 100644 index 0000000..625d92e --- /dev/null +++ b/uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue @@ -0,0 +1,325 @@ +<template> + <view class="uni-file-picker__files"> + <view v-if="!readonly" class="files-button" @click="choose"> + <slot></slot> + </view> + <!-- :class="{'is-text-box':showType === 'list'}" --> + <view v-if="list.length > 0" class="uni-file-picker__lists is-text-box" :style="borderStyle"> + <!-- ,'is-list-card':showType === 'list-card' --> + + <view class="uni-file-picker__lists-box" v-for="(item ,index) in list" :key="index" :class="{ + 'files-border':index !== 0 && styles.dividline}" + :style="index !== 0 && styles.dividline &&borderLineStyle"> + <view class="uni-file-picker__item"> + <!-- :class="{'is-text-image':showType === 'list'}" --> + <!-- <view class="files__image is-text-image"> + <image class="header-image" :src="item.logo" mode="aspectFit"></image> + </view> --> + <view class="files__name">{{item.name}}</view> + <view v-if="delIcon&&!readonly" class="icon-del-box icon-files" @click="delFile(index)"> + <view class="icon-del icon-files"></view> + <view class="icon-del rotate"></view> + </view> + </view> + <view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress"> + <progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4" + :backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" /> + </view> + <view v-if="item.status === 'error'" class="file-picker__mask" @click.stop="uploadFiles(item,index)"> + 点击重试 + </view> + </view> + + </view> + </view> +</template> + +<script> + export default { + name: "uploadFile", + emits:['uploadFiles','choose','delFile'], + props: { + filesList: { + type: Array, + default () { + return [] + } + }, + delIcon: { + type: Boolean, + default: true + }, + limit: { + type: [Number, String], + default: 9 + }, + showType: { + type: String, + default: '' + }, + listStyles: { + type: Object, + default () { + return { + // 是否显示边框 + border: true, + // 是否显示分隔线 + dividline: true, + // 线条样式 + borderStyle: {} + } + } + }, + readonly:{ + type:Boolean, + default:false + } + }, + computed: { + list() { + let files = [] + this.filesList.forEach(v => { + files.push(v) + }) + return files + }, + styles() { + let styles = { + border: true, + dividline: true, + 'border-style': {} + } + return Object.assign(styles, this.listStyles) + }, + borderStyle() { + let { + borderStyle, + border + } = this.styles + let obj = {} + if (!border) { + obj.border = 'none' + } else { + let width = (borderStyle && borderStyle.width) || 1 + width = this.value2px(width) + let radius = (borderStyle && borderStyle.radius) || 5 + radius = this.value2px(radius) + obj = { + 'border-width': width, + 'border-style': (borderStyle && borderStyle.style) || 'solid', + 'border-color': (borderStyle && borderStyle.color) || '#eee', + 'border-radius': radius + } + } + let classles = '' + for (let i in obj) { + classles += `${i}:${obj[i]};` + } + return classles + }, + borderLineStyle() { + let obj = {} + let { + borderStyle + } = this.styles + if (borderStyle && borderStyle.color) { + obj['border-color'] = borderStyle.color + } + if (borderStyle && borderStyle.width) { + let width = borderStyle && borderStyle.width || 1 + let style = borderStyle && borderStyle.style || 0 + if (typeof width === 'number') { + width += 'px' + } else { + width = width.indexOf('px') ? width : width + 'px' + } + obj['border-width'] = width + + if (typeof style === 'number') { + style += 'px' + } else { + style = style.indexOf('px') ? style : style + 'px' + } + obj['border-top-style'] = style + } + let classles = '' + for (let i in obj) { + classles += `${i}:${obj[i]};` + } + return classles + } + }, + + methods: { + uploadFiles(item, index) { + this.$emit("uploadFiles", { + item, + index + }) + }, + choose() { + this.$emit("choose") + }, + delFile(index) { + this.$emit('delFile', index) + }, + value2px(value) { + if (typeof value === 'number') { + value += 'px' + } else { + value = value.indexOf('px') !== -1 ? value : value + 'px' + } + return value + } + } + } +</script> + +<style lang="scss"> + .uni-file-picker__files { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + justify-content: flex-start; + } + + .files-button { + // border: 1px red solid; + } + + .uni-file-picker__lists { + position: relative; + margin-top: 5px; + overflow: hidden; + } + + .file-picker__mask { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + justify-content: center; + align-items: center; + position: absolute; + right: 0; + top: 0; + bottom: 0; + left: 0; + color: #fff; + font-size: 14px; + background-color: rgba(0, 0, 0, 0.4); + } + + .uni-file-picker__lists-box { + position: relative; + } + + .uni-file-picker__item { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + align-items: center; + padding: 8px 10px; + padding-right: 5px; + padding-left: 10px; + } + + .files-border { + border-top: 1px #eee solid; + } + + .files__name { + flex: 1; + font-size: 14px; + color: #666; + margin-right: 25px; + /* #ifndef APP-NVUE */ + word-break: break-all; + word-wrap: break-word; + /* #endif */ + } + + .icon-files { + /* #ifndef APP-NVUE */ + position: static; + background-color: initial; + /* #endif */ + } + + // .icon-files .icon-del { + // background-color: #333; + // width: 12px; + // height: 1px; + // } + + + .is-list-card { + border: 1px #eee solid; + margin-bottom: 5px; + border-radius: 5px; + box-shadow: 0 0 2px 0px rgba(0, 0, 0, 0.1); + padding: 5px; + } + + .files__image { + width: 40px; + height: 40px; + margin-right: 10px; + } + + .header-image { + width: 100%; + height: 100%; + } + + .is-text-box { + border: 1px #eee solid; + border-radius: 5px; + } + + .is-text-image { + width: 25px; + height: 25px; + margin-left: 5px; + } + + .rotate { + position: absolute; + transform: rotate(90deg); + } + + .icon-del-box { + /* #ifndef APP-NVUE */ + display: flex; + margin: auto 0; + /* #endif */ + align-items: center; + justify-content: center; + position: absolute; + top: 0px; + bottom: 0; + right: 5px; + height: 26px; + width: 26px; + // border-radius: 50%; + // background-color: rgba(0, 0, 0, 0.5); + z-index: 2; + transform: rotate(-45deg); + } + + .icon-del { + width: 15px; + height: 1px; + background-color: #333; + // border-radius: 1px; + } + + /* #ifdef H5 */ + @media all and (min-width: 768px) { + .uni-file-picker__files { + max-width: 375px; + } + } + + /* #endif */ +</style> diff --git a/uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue b/uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue new file mode 100644 index 0000000..2a29bc2 --- /dev/null +++ b/uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue @@ -0,0 +1,292 @@ +<template> + <view class="uni-file-picker__container"> + <view class="file-picker__box" v-for="(item,index) in filesList" :key="index" :style="boxStyle"> + <view class="file-picker__box-content" :style="borderStyle"> + <image class="file-image" :src="item.url" mode="aspectFill" @click.stop="prviewImage(item,index)"></image> + <view v-if="delIcon && !readonly" class="icon-del-box" @click.stop="delFile(index)"> + <view class="icon-del"></view> + <view class="icon-del rotate"></view> + </view> + <view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress"> + <progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4" + :backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" /> + </view> + <view v-if="item.errMsg" class="file-picker__mask" @click.stop="uploadFiles(item,index)"> + 点击重试 + </view> + </view> + </view> + <view v-if="filesList.length < limit && !readonly" class="file-picker__box" :style="boxStyle"> + <view class="file-picker__box-content is-add" :style="borderStyle" @click="choose"> + <slot> + <view class="icon-add"></view> + <view class="icon-add rotate"></view> + </slot> + </view> + </view> + </view> +</template> + +<script> + export default { + name: "uploadImage", + emits:['uploadFiles','choose','delFile'], + props: { + filesList: { + type: Array, + default () { + return [] + } + }, + disabled:{ + type: Boolean, + default: false + }, + disablePreview: { + type: Boolean, + default: false + }, + limit: { + type: [Number, String], + default: 9 + }, + imageStyles: { + type: Object, + default () { + return { + width: 'auto', + height: 'auto', + border: {} + } + } + }, + delIcon: { + type: Boolean, + default: true + }, + readonly:{ + type:Boolean, + default:false + } + }, + computed: { + styles() { + let styles = { + width: 'auto', + height: 'auto', + border: {} + } + return Object.assign(styles, this.imageStyles) + }, + boxStyle() { + const { + width = 'auto', + height = 'auto' + } = this.styles + let obj = {} + if (height === 'auto') { + if (width !== 'auto') { + obj.height = this.value2px(width) + obj['padding-top'] = 0 + } else { + obj.height = 0 + } + } else { + obj.height = this.value2px(height) + obj['padding-top'] = 0 + } + + if (width === 'auto') { + if (height !== 'auto') { + obj.width = this.value2px(height) + } else { + obj.width = '33.3%' + } + } else { + obj.width = this.value2px(width) + } + + let classles = '' + for(let i in obj){ + classles+= `${i}:${obj[i]};` + } + return classles + }, + borderStyle() { + let { + border + } = this.styles + let obj = {} + const widthDefaultValue = 1 + const radiusDefaultValue = 3 + if (typeof border === 'boolean') { + obj.border = border ? '1px #eee solid' : 'none' + } else { + let width = (border && border.width) || widthDefaultValue + width = this.value2px(width) + let radius = (border && border.radius) || radiusDefaultValue + radius = this.value2px(radius) + obj = { + 'border-width': width, + 'border-style': (border && border.style) || 'solid', + 'border-color': (border && border.color) || '#eee', + 'border-radius': radius + } + } + let classles = '' + for(let i in obj){ + classles+= `${i}:${obj[i]};` + } + return classles + } + }, + methods: { + uploadFiles(item, index) { + this.$emit("uploadFiles", item) + }, + choose() { + this.$emit("choose") + }, + delFile(index) { + this.$emit('delFile', index) + }, + prviewImage(img, index) { + let urls = [] + if(Number(this.limit) === 1&&this.disablePreview&&!this.disabled){ + this.$emit("choose") + } + if(this.disablePreview) return + this.filesList.forEach(i => { + urls.push(i.url) + }) + + uni.previewImage({ + urls: urls, + current: index + }); + }, + value2px(value) { + if (typeof value === 'number') { + value += 'px' + } else { + if (value.indexOf('%') === -1) { + value = value.indexOf('px') !== -1 ? value : value + 'px' + } + } + return value + } + } + } +</script> + +<style lang="scss"> + .uni-file-picker__container { + /* #ifndef APP-NVUE */ + display: flex; + box-sizing: border-box; + /* #endif */ + flex-wrap: wrap; + margin: -5px; + } + + .file-picker__box { + position: relative; + // flex: 0 0 33.3%; + width: 33.3%; + height: 0; + padding-top: 33.33%; + /* #ifndef APP-NVUE */ + box-sizing: border-box; + /* #endif */ + } + + .file-picker__box-content { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: 5px; + border: 1px #eee solid; + border-radius: 5px; + overflow: hidden; + } + + .file-picker__progress { + position: absolute; + bottom: 0; + left: 0; + right: 0; + /* border: 1px red solid; */ + z-index: 2; + } + + .file-picker__progress-item { + width: 100%; + } + + .file-picker__mask { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + justify-content: center; + align-items: center; + position: absolute; + right: 0; + top: 0; + bottom: 0; + left: 0; + color: #fff; + font-size: 12px; + background-color: rgba(0, 0, 0, 0.4); + } + + .file-image { + width: 100%; + height: 100%; + } + + .is-add { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + align-items: center; + justify-content: center; + } + + .icon-add { + width: 50px; + height: 5px; + background-color: #f1f1f1; + border-radius: 2px; + } + + .rotate { + position: absolute; + transform: rotate(90deg); + } + + .icon-del-box { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + align-items: center; + justify-content: center; + position: absolute; + top: 3px; + right: 3px; + height: 26px; + width: 26px; + border-radius: 50%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 2; + transform: rotate(-45deg); + } + + .icon-del { + width: 15px; + height: 2px; + background-color: #fff; + border-radius: 2px; + } +</style> diff --git a/uni_modules/uni-file-picker/components/uni-file-picker/utils.js b/uni_modules/uni-file-picker/components/uni-file-picker/utils.js new file mode 100644 index 0000000..1bc9259 --- /dev/null +++ b/uni_modules/uni-file-picker/components/uni-file-picker/utils.js @@ -0,0 +1,110 @@ +/** + * 获取文件名和后缀 + * @param {String} name + */ +export const get_file_ext = (name) => { + const last_len = name.lastIndexOf('.') + const len = name.length + return { + name: name.substring(0, last_len), + ext: name.substring(last_len + 1, len) + } +} + +/** + * 获取扩展名 + * @param {Array} fileExtname + */ +export const get_extname = (fileExtname) => { + if (!Array.isArray(fileExtname)) { + let extname = fileExtname.replace(/(\[|\])/g, '') + return extname.split(',') + } else { + return fileExtname + } + return [] +} + +/** + * 获取文件和检测是否可选 + */ +export const get_files_and_is_max = (res, _extname) => { + let filePaths = [] + let files = [] + if(!_extname || _extname.length === 0){ + return { + filePaths, + files + } + } + res.tempFiles.forEach(v => { + let fileFullName = get_file_ext(v.name) + const extname = fileFullName.ext.toLowerCase() + if (_extname.indexOf(extname) !== -1) { + files.push(v) + filePaths.push(v.path) + } + }) + if (files.length !== res.tempFiles.length) { + uni.showToast({ + title: `当前选择了${res.tempFiles.length}个文件 ,${res.tempFiles.length - files.length} 个文件格式不正确`, + icon: 'none', + duration: 5000 + }) + } + + return { + filePaths, + files + } +} + + +/** + * 获取图片信息 + * @param {Object} filepath + */ +export const get_file_info = (filepath) => { + return new Promise((resolve, reject) => { + uni.getImageInfo({ + src: filepath, + success(res) { + resolve(res) + }, + fail(err) { + reject(err) + } + }) + }) +} +/** + * 获取封装数据 + */ +export const get_file_data = async (files, type = 'image') => { + // 最终需要上传数据库的数据 + let fileFullName = get_file_ext(files.name) + const extname = fileFullName.ext.toLowerCase() + let filedata = { + name: files.name, + uuid: files.uuid, + extname: extname || '', + cloudPath: files.cloudPath, + fileType: files.fileType, + thumbTempFilePath: files.thumbTempFilePath, + url: files.path || files.path, + size: files.size, //单位是字节 + image: {}, + path: files.path, + video: {} + } + if (type === 'image') { + const imageinfo = await get_file_info(files.path) + delete filedata.video + filedata.image.width = imageinfo.width + filedata.image.height = imageinfo.height + filedata.image.location = imageinfo.path + } else { + delete filedata.image + } + return filedata +} diff --git a/uni_modules/uni-file-picker/package.json b/uni_modules/uni-file-picker/package.json new file mode 100644 index 0000000..34bb18f --- /dev/null +++ b/uni_modules/uni-file-picker/package.json @@ -0,0 +1,84 @@ +{ + "id": "uni-file-picker", + "displayName": "uni-file-picker 文件选择上传", + "version": "1.0.11", + "description": "文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间", + "keywords": [ + "uni-ui", + "uniui", + "图片上传", + "文件上传" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "n" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-file-picker/readme.md b/uni_modules/uni-file-picker/readme.md new file mode 100644 index 0000000..c8399a5 --- /dev/null +++ b/uni_modules/uni-file-picker/readme.md @@ -0,0 +1,11 @@ + +## FilePicker 文件选择上传 + +> **组件名:uni-file-picker** +> 代码块: `uFilePicker` + + +文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-file-picker) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-forms/changelog.md b/uni_modules/uni-forms/changelog.md new file mode 100644 index 0000000..0b58ab8 --- /dev/null +++ b/uni_modules/uni-forms/changelog.md @@ -0,0 +1,100 @@ +## 1.4.13(2024-10-08) +- 修复 校验规则在抖音开发者工具上不生效的bug,详见:[https://ask.dcloud.net.cn/question/191933](https://ask.dcloud.net.cn/question/191933) +## 1.4.12 (2024-9-21) +- 修复 form上次修改的问题 +## 1.4.11 (2024-9-14) +- 修复 binddata的兼容性问题 +## 1.4.10(2023-11-03) +- 优化 labelWidth 描述错误 +## 1.4.9(2023-02-10) +- 修复 required 参数无法动态绑定 +## 1.4.8(2022-08-23) +- 优化 根据 rules 自动添加 required 的问题 +## 1.4.7(2022-08-22) +- 修复 item 未设置 require 属性,rules 设置 require 后,星号也显示的 bug,详见:[https://ask.dcloud.net.cn/question/151540](https://ask.dcloud.net.cn/question/151540) +## 1.4.6(2022-07-13) +- 修复 model 需要校验的值没有声明对应字段时,导致第一次不触发校验的bug +## 1.4.5(2022-07-05) +- 新增 更多表单示例 +- 优化 子表单组件过期提示的问题 +- 优化 子表单组件uni-datetime-picker、uni-data-select、uni-data-picker的显示样式 +## 1.4.4(2022-07-04) +- 更新 删除组件日志 +## 1.4.3(2022-07-04) +- 修复 由 1.4.0 引发的 label 插槽不生效的bug +## 1.4.2(2022-07-04) +- 修复 子组件找不到 setValue 报错的bug +## 1.4.1(2022-07-04) +- 修复 uni-data-picker 在 uni-forms-item 中报错的bug +- 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug +## 1.4.0(2022-06-30) +- 【重要】组件逻辑重构,部分用法用旧版本不兼容,请注意兼容问题 +- 【重要】组件使用 Provide/Inject 方式注入依赖,提供了自定义表单组件调用 uni-forms 校验表单的能力 +- 新增 model 属性,等同于原 value/modelValue 属性,旧属性即将废弃 +- 新增 validateTrigger 属性的 blur 值,仅 uni-easyinput 生效 +- 新增 onFieldChange 方法,可以对子表单进行校验,可替代binddata方法 +- 新增 子表单的 setRules 方法,配合自定义校验函数使用 +- 新增 uni-forms-item 的 setRules 方法,配置动态表单使用可动态更新校验规则 +- 优化 动态表单校验方式,废弃拼接name的方式 +## 1.3.3(2022-06-22) +- 修复 表单校验顺序无序问题 +## 1.3.2(2021-12-09) +- +## 1.3.1(2021-11-19) +- 修复 label 插槽不生效的bug +## 1.3.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-forms](https://uniapp.dcloud.io/component/uniui/uni-forms) +## 1.2.7(2021-08-13) +- 修复 没有添加校验规则的字段依然报错的Bug +## 1.2.6(2021-08-11) +- 修复 重置表单错误信息无法清除的问题 +## 1.2.5(2021-08-11) +- 优化 组件文档 +## 1.2.4(2021-08-11) +- 修复 表单验证只生效一次的问题 +## 1.2.3(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.2.2(2021-07-26) +- 修复 vue2 下条件编译导致destroyed生命周期失效的Bug +- 修复 1.2.1 引起的示例在小程序平台报错的Bug +## 1.2.1(2021-07-22) +- 修复 动态校验表单,默认值为空的情况下校验失效的Bug +- 修复 不指定name属性时,运行报错的Bug +- 优化 label默认宽度从65调整至70,使required为true且四字时不换行 +- 优化 组件示例,新增动态校验示例代码 +- 优化 组件文档,使用方式更清晰 +## 1.2.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.2(2021-06-25) +- 修复 pattern 属性在微信小程序平台无效的问题 +## 1.1.1(2021-06-22) +- 修复 validate-trigger属性为submit且err-show-type属性为toast时不能弹出的Bug +## 1.1.0(2021-06-22) +- 修复 只写setRules方法而导致校验不生效的Bug +- 修复 由上个办法引发的错误提示文字错位的Bug +## 1.0.48(2021-06-21) +- 修复 不设置 label 属性 ,无法设置label插槽的问题 +## 1.0.47(2021-06-21) +- 修复 不设置label属性,label-width属性不生效的bug +- 修复 setRules 方法与rules属性冲突的问题 +## 1.0.46(2021-06-04) +- 修复 动态删减数据导致报错的问题 +## 1.0.45(2021-06-04) +- 新增 modelValue 属性 ,value 即将废弃 +## 1.0.44(2021-06-02) +- 新增 uni-forms-item 可以设置单独的 rules +- 新增 validate 事件增加 keepitem 参数,可以选择那些字段不过滤 +- 优化 submit 事件重命名为 validate +## 1.0.43(2021-05-12) +- 新增 组件示例地址 +## 1.0.42(2021-04-30) +- 修复 自定义检验器失效的问题 +## 1.0.41(2021-03-05) +- 更新 校验器 +- 修复 表单规则设置类型为 number 的情况下,值为0校验失败的Bug +## 1.0.40(2021-03-04) +- 修复 动态显示uni-forms-item的情况下,submit 方法获取值错误的Bug +## 1.0.39(2021-02-05) +- 调整为uni_modules目录规范 +- 修复 校验器传入 int 等类型 ,返回String类型的Bug diff --git a/uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue b/uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue new file mode 100644 index 0000000..c924882 --- /dev/null +++ b/uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue @@ -0,0 +1,632 @@ +<template> + <view class="uni-forms-item" + :class="['is-direction-' + localLabelPos ,border?'uni-forms-item--border':'' ,border && isFirstBorder?'is-first-border':'']"> + <slot name="label"> + <view class="uni-forms-item__label" :class="{'no-label':!label && !required}" + :style="{width:localLabelWidth,justifyContent: localLabelAlign}"> + <text v-if="required" class="is-required">*</text> + <text>{{label}}</text> + </view> + </slot> + <!-- #ifndef APP-NVUE --> + <view class="uni-forms-item__content"> + <slot></slot> + <view class="uni-forms-item__error" :class="{'msg--active':msg}"> + <text>{{msg}}</text> + </view> + </view> + <!-- #endif --> + <!-- #ifdef APP-NVUE --> + <view class="uni-forms-item__nuve-content"> + <view class="uni-forms-item__content"> + <slot></slot> + </view> + <view class="uni-forms-item__error" :class="{'msg--active':msg}"> + <text class="error-text">{{msg}}</text> + </view> + </view> + <!-- #endif --> + </view> +</template> + +<script> + /** + * uni-fomrs-item 表单子组件 + * @description uni-fomrs-item 表单子组件,提供了基础布局已经校验能力 + * @tutorial https://ext.dcloud.net.cn/plugin?id=2773 + * @property {Boolean} required 是否必填,左边显示红色"*"号 + * @property {String } label 输入框左边的文字提示 + * @property {Number } labelWidth label的宽度,单位px(默认70) + * @property {String } labelAlign = [left|center|right] label的文字对齐方式(默认left) + * @value left label 左侧显示 + * @value center label 居中 + * @value right label 右侧对齐 + * @property {String } errorMessage 显示的错误提示内容,如果为空字符串或者false,则不显示错误信息 + * @property {String } name 表单域的属性名,在使用校验规则时必填 + * @property {String } leftIcon 【1.4.0废弃】label左边的图标,限 uni-ui 的图标名称 + * @property {String } iconColor 【1.4.0废弃】左边通过icon配置的图标的颜色(默认#606266) + * @property {String} validateTrigger = [bind|submit|blur] 【1.4.0废弃】校验触发器方式 默认 submit + * @value bind 发生变化时触发 + * @value submit 提交时触发 + * @value blur 失去焦点触发 + * @property {String } labelPosition = [top|left] 【1.4.0废弃】label的文字的位置(默认left) + * @value top 顶部显示 label + * @value left 左侧显示 label + */ + + export default { + name: 'uniFormsItem', + options: { + // #ifdef MP-TOUTIAO + virtualHost: false, + // #endif + // #ifndef MP-TOUTIAO + virtualHost: true + // #endif + }, + provide() { + return { + uniFormItem: this + } + }, + inject: { + form: { + from: 'uniForm', + default: null + }, + }, + props: { + // 表单校验规则 + rules: { + type: Array, + default () { + return null; + } + }, + // 表单域的属性名,在使用校验规则时必填 + name: { + type: [String, Array], + default: '' + }, + required: { + type: Boolean, + default: false + }, + label: { + type: String, + default: '' + }, + // label的宽度 + labelWidth: { + type: [String, Number], + default: '' + }, + // label 居中方式,默认 left 取值 left/center/right + labelAlign: { + type: String, + default: '' + }, + // 强制显示错误信息 + errorMessage: { + type: [String, Boolean], + default: '' + }, + // 1.4.0 弃用,统一使用 form 的校验时机 + // validateTrigger: { + // type: String, + // default: '' + // }, + // 1.4.0 弃用,统一使用 form 的label 位置 + // labelPosition: { + // type: String, + // default: '' + // }, + // 1.4.0 以下属性已经废弃,请使用 #label 插槽代替 + leftIcon: String, + iconColor: { + type: String, + default: '#606266' + }, + }, + data() { + return { + errMsg: '', + userRules: null, + localLabelAlign: 'left', + localLabelWidth: '70px', + localLabelPos: 'left', + border: false, + isFirstBorder: false, + }; + }, + computed: { + // 处理错误信息 + msg() { + return this.errorMessage || this.errMsg; + } + }, + watch: { + // 规则发生变化通知子组件更新 + 'form.formRules'(val) { + // TODO 处理头条vue3 watch不生效的问题 + // #ifndef MP-TOUTIAO + this.init() + // #endif + }, + 'form.labelWidth'(val) { + // 宽度 + this.localLabelWidth = this._labelWidthUnit(val) + + }, + 'form.labelPosition'(val) { + // 标签位置 + this.localLabelPos = this._labelPosition() + }, + 'form.labelAlign'(val) { + + } + }, + created() { + this.init(true) + if (this.name && this.form) { + // TODO 处理头条vue3 watch不生效的问题 + // #ifdef MP-TOUTIAO + this.$watch('form.formRules', () => { + this.init() + }) + // #endif + + // 监听变化 + this.$watch( + () => { + const val = this.form._getDataValue(this.name, this.form.localData) + return val + }, + (value, oldVal) => { + const isEqual = this.form._isEqual(value, oldVal) + // 简单判断前后值的变化,只有发生变化才会发生校验 + // TODO 如果 oldVal = undefined ,那么大概率是源数据里没有值导致 ,这个情况不哦校验 ,可能不严谨 ,需要在做观察 + // fix by mehaotian 暂时取消 && oldVal !== undefined ,如果formData 中不存在,可能会不校验 + if (!isEqual) { + const val = this.itemSetValue(value) + this.onFieldChange(val, false) + } + }, { + immediate: false + } + ); + } + + }, + // #ifndef VUE3 + destroyed() { + if (this.__isUnmounted) return + this.unInit() + }, + // #endif + // #ifdef VUE3 + unmounted() { + this.__isUnmounted = true + this.unInit() + }, + // #endif + methods: { + /** + * 外部调用方法 + * 设置规则 ,主要用于小程序自定义检验规则 + * @param {Array} rules 规则源数据 + */ + setRules(rules = null) { + this.userRules = rules + this.init(false) + }, + // 兼容老版本表单组件 + setValue() { + // console.log('setValue 方法已经弃用,请使用最新版本的 uni-forms 表单组件以及其他关联组件。'); + }, + /** + * 外部调用方法 + * 校验数据 + * @param {any} value 需要校验的数据 + * @param {boolean} 是否立即校验 + * @return {Array|null} 校验内容 + */ + async onFieldChange(value, formtrigger = true) { + const { + formData, + localData, + errShowType, + validateCheck, + validateTrigger, + _isRequiredField, + _realName + } = this.form + const name = _realName(this.name) + if (!value) { + value = this.form.formData[name] + } + // fixd by mehaotian 不在校验前清空信息,解决闪屏的问题 + // this.errMsg = ''; + + // fix by mehaotian 解决没有检验规则的情况下,抛出错误的问题 + const ruleLen = this.itemRules.rules && this.itemRules.rules.length + if (!this.validator || !ruleLen || ruleLen === 0) return; + + // 检验时机 + // let trigger = this.isTrigger(this.itemRules.validateTrigger, this.validateTrigger, validateTrigger); + const isRequiredField = _isRequiredField(this.itemRules.rules || []); + let result = null; + // 只有等于 bind 时 ,才能开启时实校验 + if (validateTrigger === 'bind' || formtrigger) { + // 校验当前表单项 + result = await this.validator.validateUpdate({ + [name]: value + }, + formData + ); + + // 判断是否必填,非必填,不填不校验,填写才校验 ,暂时只处理 undefined 和空的情况 + if (!isRequiredField && (value === undefined || value === '')) { + result = null; + } + + // 判断错误信息显示类型 + if (result && result.errorMessage) { + if (errShowType === 'undertext') { + // 获取错误信息 + this.errMsg = !result ? '' : result.errorMessage; + } + if (errShowType === 'toast') { + uni.showToast({ + title: result.errorMessage || '校验错误', + icon: 'none' + }); + } + if (errShowType === 'modal') { + uni.showModal({ + title: '提示', + content: result.errorMessage || '校验错误' + }); + } + } else { + this.errMsg = '' + } + // 通知 form 组件更新事件 + validateCheck(result ? result : null) + } else { + this.errMsg = '' + } + return result ? result : null; + }, + /** + * 初始组件数据 + */ + init(type = false) { + const { + validator, + formRules, + childrens, + formData, + localData, + _realName, + labelWidth, + _getDataValue, + _setDataValue + } = this.form || {} + // 对齐方式 + this.localLabelAlign = this._justifyContent() + // 宽度 + this.localLabelWidth = this._labelWidthUnit(labelWidth) + // 标签位置 + this.localLabelPos = this._labelPosition() + // 将需要校验的子组件加入form 队列 + this.form && type && childrens.push(this) + + if (!validator || !formRules) return + // 判断第一个 item + if (!this.form.isFirstBorder) { + this.form.isFirstBorder = true; + this.isFirstBorder = true; + } + + // 判断 group 里的第一个 item + if (this.group) { + if (!this.group.isFirstBorder) { + this.group.isFirstBorder = true; + this.isFirstBorder = true; + } + } + this.border = this.form.border; + // 获取子域的真实名称 + const name = _realName(this.name) + const itemRule = this.userRules || this.rules + if (typeof formRules === 'object' && itemRule) { + // 子规则替换父规则 + formRules[name] = { + rules: itemRule + } + validator.updateSchema(formRules); + } + // 注册校验规则 + const itemRules = formRules[name] || {} + this.itemRules = itemRules + // 注册校验函数 + this.validator = validator + // 默认值赋予 + this.itemSetValue(_getDataValue(this.name, localData)) + }, + unInit() { + if (this.form) { + const { + childrens, + formData, + _realName + } = this.form + childrens.forEach((item, index) => { + if (item === this) { + this.form.childrens.splice(index, 1) + delete formData[_realName(item.name)] + } + }) + } + }, + // 设置item 的值 + itemSetValue(value) { + const name = this.form._realName(this.name) + const rules = this.itemRules.rules || [] + const val = this.form._getValue(name, value, rules) + this.form._setDataValue(name, this.form.formData, val) + return val + }, + + /** + * 移除该表单项的校验结果 + */ + clearValidate() { + this.errMsg = ''; + }, + + // 是否显示星号 + _isRequired() { + // TODO 不根据规则显示 星号,考虑后续兼容 + // if (this.form) { + // if (this.form._isRequiredField(this.itemRules.rules || []) && this.required) { + // return true + // } + // return false + // } + return this.required + }, + + // 处理对齐方式 + _justifyContent() { + if (this.form) { + const { + labelAlign + } = this.form + let labelAli = this.labelAlign ? this.labelAlign : labelAlign; + if (labelAli === 'left') return 'flex-start'; + if (labelAli === 'center') return 'center'; + if (labelAli === 'right') return 'flex-end'; + } + return 'flex-start'; + }, + // 处理 label宽度单位 ,继承父元素的值 + _labelWidthUnit(labelWidth) { + + // if (this.form) { + // const { + // labelWidth + // } = this.form + return this.num2px(this.labelWidth ? this.labelWidth : (labelWidth || (this.label ? 70 : 'auto'))) + // } + // return '70px' + }, + // 处理 label 位置 + _labelPosition() { + if (this.form) return this.form.labelPosition || 'left' + return 'left' + + }, + + /** + * 触发时机 + * @param {Object} rule 当前规则内时机 + * @param {Object} itemRlue 当前组件时机 + * @param {Object} parentRule 父组件时机 + */ + isTrigger(rule, itemRlue, parentRule) { + // bind submit + if (rule === 'submit' || !rule) { + if (rule === undefined) { + if (itemRlue !== 'bind') { + if (!itemRlue) { + return parentRule === '' ? 'bind' : 'submit'; + } + return 'submit'; + } + return 'bind'; + } + return 'submit'; + } + return 'bind'; + }, + num2px(num) { + if (typeof num === 'number') { + return `${num}px` + } + return num + } + } + }; +</script> + +<style lang="scss"> + .uni-forms-item { + position: relative; + display: flex; + /* #ifdef APP-NVUE */ + // 在 nvue 中,使用 margin-bottom error 信息会被隐藏 + padding-bottom: 22px; + /* #endif */ + /* #ifndef APP-NVUE */ + margin-bottom: 22px; + /* #endif */ + flex-direction: row; + + &__label { + display: flex; + flex-direction: row; + align-items: center; + text-align: left; + font-size: 14px; + color: #606266; + height: 36px; + padding: 0 12px 0 0; + /* #ifndef APP-NVUE */ + vertical-align: middle; + flex-shrink: 0; + /* #endif */ + + /* #ifndef APP-NVUE */ + box-sizing: border-box; + + /* #endif */ + &.no-label { + padding: 0; + } + } + + &__content { + /* #ifndef MP-TOUTIAO */ + // display: flex; + // align-items: center; + /* #endif */ + position: relative; + font-size: 14px; + flex: 1; + /* #ifndef APP-NVUE */ + box-sizing: border-box; + /* #endif */ + flex-direction: row; + + /* #ifndef APP || H5 || MP-WEIXIN || APP-NVUE */ + // TODO 因为小程序平台会多一层标签节点 ,所以需要在多余节点继承当前样式 + &>uni-easyinput, + &>uni-data-picker { + width: 100%; + } + + /* #endif */ + + } + + & .uni-forms-item__nuve-content { + display: flex; + flex-direction: column; + flex: 1; + } + + &__error { + color: #f56c6c; + font-size: 12px; + line-height: 1; + padding-top: 4px; + position: absolute; + /* #ifndef APP-NVUE */ + top: 100%; + left: 0; + transition: transform 0.3s; + transform: translateY(-100%); + /* #endif */ + /* #ifdef APP-NVUE */ + bottom: 5px; + /* #endif */ + + opacity: 0; + + .error-text { + // 只有 nvue 下这个样式才生效 + color: #f56c6c; + font-size: 12px; + } + + &.msg--active { + opacity: 1; + transform: translateY(0%); + } + } + + // 位置修饰样式 + &.is-direction-left { + flex-direction: row; + } + + &.is-direction-top { + flex-direction: column; + + .uni-forms-item__label { + padding: 0 0 8px; + line-height: 1.5715; + text-align: left; + /* #ifndef APP-NVUE */ + white-space: initial; + /* #endif */ + } + } + + .is-required { + // color: $uni-color-error; + color: #dd524d; + font-weight: bold; + } + } + + + .uni-forms-item--border { + margin-bottom: 0; + padding: 10px 0; + // padding-bottom: 0; + border-top: 1px #eee solid; + + /* #ifndef APP-NVUE */ + .uni-forms-item__content { + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + + .uni-forms-item__error { + position: relative; + top: 5px; + left: 0; + padding-top: 0; + } + } + + /* #endif */ + + /* #ifdef APP-NVUE */ + display: flex; + flex-direction: column; + + .uni-forms-item__error { + position: relative; + top: 0px; + left: 0; + padding-top: 0; + margin-top: 5px; + } + + /* #endif */ + + } + + .is-first-border { + /* #ifndef APP-NVUE */ + border: none; + /* #endif */ + /* #ifdef APP-NVUE */ + border-width: 0; + /* #endif */ + } +</style> diff --git a/uni_modules/uni-forms/components/uni-forms/uni-forms.vue b/uni_modules/uni-forms/components/uni-forms/uni-forms.vue new file mode 100644 index 0000000..d061313 --- /dev/null +++ b/uni_modules/uni-forms/components/uni-forms/uni-forms.vue @@ -0,0 +1,404 @@ +<template> + <view class="uni-forms"> + <form> + <slot></slot> + </form> + </view> +</template> + +<script> + import Validator from './validate.js'; + import { + deepCopy, + getValue, + isRequiredField, + setDataValue, + getDataValue, + realName, + isRealName, + rawData, + isEqual + } from './utils.js' + + // #ifndef VUE3 + // 后续会慢慢废弃这个方法 + import Vue from 'vue'; + Vue.prototype.binddata = function(name, value, formName) { + if (formName) { + this.$refs[formName].setValue(name, value); + } else { + let formVm; + for (let i in this.$refs) { + const vm = this.$refs[i]; + if (vm && vm.$options && vm.$options.name === 'uniForms') { + formVm = vm; + break; + } + } + if (!formVm) return console.error('当前 uni-froms 组件缺少 ref 属性'); + formVm.setValue(name, value); + } + }; + // #endif + /** + * Forms 表单 + * @description 由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据 + * @tutorial https://ext.dcloud.net.cn/plugin?id=2773 + * @property {Object} rules 表单校验规则 + * @property {String} validateTrigger = [bind|submit|blur] 校验触发器方式 默认 submit + * @value bind 发生变化时触发 + * @value submit 提交时触发 + * @value blur 失去焦点时触发 + * @property {String} labelPosition = [top|left] label 位置 默认 left + * @value top 顶部显示 label + * @value left 左侧显示 label + * @property {String} labelWidth label 宽度,默认 70px + * @property {String} labelAlign = [left|center|right] label 居中方式 默认 left + * @value left label 左侧显示 + * @value center label 居中 + * @value right label 右侧对齐 + * @property {String} errShowType = [undertext|toast|modal] 校验错误信息提示方式 + * @value undertext 错误信息在底部显示 + * @value toast 错误信息toast显示 + * @value modal 错误信息modal显示 + * @event {Function} submit 提交时触发 + * @event {Function} validate 校验结果发生变化触发 + */ + export default { + name: 'uniForms', + emits: ['validate', 'submit'], + options: { + // #ifdef MP-TOUTIAO + virtualHost: false, + // #endif + // #ifndef MP-TOUTIAO + virtualHost: true + // #endif + }, + props: { + // 即将弃用 + value: { + type: Object, + default () { + return null; + } + }, + // vue3 替换 value 属性 + modelValue: { + type: Object, + default () { + return null; + } + }, + // 1.4.0 开始将不支持 v-model ,且废弃 value 和 modelValue + model: { + type: Object, + default () { + return null; + } + }, + // 表单校验规则 + rules: { + type: Object, + default () { + return {}; + } + }, + //校验错误信息提示方式 默认 undertext 取值 [undertext|toast|modal] + errShowType: { + type: String, + default: 'undertext' + }, + // 校验触发器方式 默认 bind 取值 [bind|submit] + validateTrigger: { + type: String, + default: 'submit' + }, + // label 位置,默认 left 取值 top/left + labelPosition: { + type: String, + default: 'left' + }, + // label 宽度 + labelWidth: { + type: [String, Number], + default: '' + }, + // label 居中方式,默认 left 取值 left/center/right + labelAlign: { + type: String, + default: 'left' + }, + border: { + type: Boolean, + default: false + } + }, + provide() { + return { + uniForm: this + } + }, + data() { + return { + // 表单本地值的记录,不应该与传如的值进行关联 + formData: {}, + formRules: {} + }; + }, + computed: { + // 计算数据源变化的 + localData() { + const localVal = this.model || this.modelValue || this.value + if (localVal) { + return deepCopy(localVal) + } + return {} + } + }, + watch: { + // 监听数据变化 ,暂时不使用,需要单独赋值 + // localData: {}, + // 监听规则变化 + rules: { + handler: function(val, oldVal) { + this.setRules(val) + }, + deep: true, + immediate: true + } + }, + created() { + // #ifdef VUE3 + let getbinddata = getApp().$vm.$.appContext.config.globalProperties.binddata + if (!getbinddata) { + getApp().$vm.$.appContext.config.globalProperties.binddata = function(name, value, formName) { + if (formName) { + this.$refs[formName].setValue(name, value); + } else { + let formVm; + for (let i in this.$refs) { + const vm = this.$refs[i]; + if (vm && vm.$options && vm.$options.name === 'uniForms') { + formVm = vm; + break; + } + } + if (!formVm) return console.error('当前 uni-froms 组件缺少 ref 属性'); + if(formVm.model)formVm.model[name] = value + if(formVm.modelValue)formVm.modelValue[name] = value + if(formVm.value)formVm.value[name] = value + } + } + } + // #endif + + // 子组件实例数组 + this.childrens = [] + // TODO 兼容旧版 uni-data-picker ,新版本中无效,只是避免报错 + this.inputChildrens = [] + this.setRules(this.rules) + }, + methods: { + /** + * 外部调用方法 + * 设置规则 ,主要用于小程序自定义检验规则 + * @param {Array} rules 规则源数据 + */ + setRules(rules) { + // TODO 有可能子组件合并规则的时机比这个要早,所以需要合并对象 ,而不是直接赋值,可能会被覆盖 + this.formRules = Object.assign({}, this.formRules, rules) + // 初始化校验函数 + this.validator = new Validator(rules); + }, + + /** + * 外部调用方法 + * 设置数据,用于设置表单数据,公开给用户使用 , 不支持在动态表单中使用 + * @param {Object} key + * @param {Object} value + */ + setValue(key, value) { + let example = this.childrens.find(child => child.name === key); + if (!example) return null; + this.formData[key] = getValue(key, value, (this.formRules[key] && this.formRules[key].rules) || []) + return example.onFieldChange(this.formData[key]); + }, + + /** + * 外部调用方法 + * 手动提交校验表单 + * 对整个表单进行校验的方法,参数为一个回调函数。 + * @param {Array} keepitem 保留不参与校验的字段 + * @param {type} callback 方法回调 + */ + validate(keepitem, callback) { + return this.checkAll(this.formData, keepitem, callback); + }, + + /** + * 外部调用方法 + * 部分表单校验 + * @param {Array|String} props 需要校验的字段 + * @param {Function} 回调函数 + */ + validateField(props = [], callback) { + props = [].concat(props); + let invalidFields = {}; + this.childrens.forEach(item => { + const name = realName(item.name) + if (props.indexOf(name) !== -1) { + invalidFields = Object.assign({}, invalidFields, { + [name]: this.formData[name] + }); + } + }); + return this.checkAll(invalidFields, [], callback); + }, + + /** + * 外部调用方法 + * 移除表单项的校验结果。传入待移除的表单项的 prop 属性或者 prop 组成的数组,如不传则移除整个表单的校验结果 + * @param {Array|String} props 需要移除校验的字段 ,不填为所有 + */ + clearValidate(props = []) { + props = [].concat(props); + this.childrens.forEach(item => { + if (props.length === 0) { + item.errMsg = ''; + } else { + const name = realName(item.name) + if (props.indexOf(name) !== -1) { + item.errMsg = ''; + } + } + }); + }, + + /** + * 外部调用方法 ,即将废弃 + * 手动提交校验表单 + * 对整个表单进行校验的方法,参数为一个回调函数。 + * @param {Array} keepitem 保留不参与校验的字段 + * @param {type} callback 方法回调 + */ + submit(keepitem, callback, type) { + for (let i in this.dataValue) { + const itemData = this.childrens.find(v => v.name === i); + if (itemData) { + if (this.formData[i] === undefined) { + this.formData[i] = this._getValue(i, this.dataValue[i]); + } + } + } + + if (!type) { + console.warn('submit 方法即将废弃,请使用validate方法代替!'); + } + + return this.checkAll(this.formData, keepitem, callback, 'submit'); + }, + + // 校验所有 + async checkAll(invalidFields, keepitem, callback, type) { + // 不存在校验规则 ,则停止校验流程 + if (!this.validator) return + let childrens = [] + // 处理参与校验的item实例 + for (let i in invalidFields) { + const item = this.childrens.find(v => realName(v.name) === i) + if (item) { + childrens.push(item) + } + } + + // 如果validate第一个参数是funciont ,那就走回调 + if (!callback && typeof keepitem === 'function') { + callback = keepitem; + } + + let promise; + // 如果不存在回调,那么使用 Promise 方式返回 + if (!callback && typeof callback !== 'function' && Promise) { + promise = new Promise((resolve, reject) => { + callback = function(valid, invalidFields) { + !valid ? resolve(invalidFields) : reject(valid); + }; + }); + } + + let results = []; + // 避免引用错乱 ,建议拷贝对象处理 + let tempFormData = JSON.parse(JSON.stringify(invalidFields)) + // 所有子组件参与校验,使用 for 可以使用 awiat + for (let i in childrens) { + const child = childrens[i] + let name = realName(child.name); + const result = await child.onFieldChange(tempFormData[name]); + if (result) { + results.push(result); + // toast ,modal 只需要执行第一次就可以 + if (this.errShowType === 'toast' || this.errShowType === 'modal') break; + } + } + + + if (Array.isArray(results)) { + if (results.length === 0) results = null; + } + if (Array.isArray(keepitem)) { + keepitem.forEach(v => { + let vName = realName(v); + let value = getDataValue(v, this.localData) + if (value !== undefined) { + tempFormData[vName] = value + } + }); + } + + // TODO submit 即将废弃 + if (type === 'submit') { + this.$emit('submit', { + detail: { + value: tempFormData, + errors: results + } + }); + } else { + this.$emit('validate', results); + } + + // const resetFormData = rawData(tempFormData, this.localData, this.name) + let resetFormData = {} + resetFormData = rawData(tempFormData, this.name) + callback && typeof callback === 'function' && callback(results, resetFormData); + + if (promise && callback) { + return promise; + } else { + return null; + } + + }, + + /** + * 返回validate事件 + * @param {Object} result + */ + validateCheck(result) { + this.$emit('validate', result); + }, + _getValue: getValue, + _isRequiredField: isRequiredField, + _setDataValue: setDataValue, + _getDataValue: getDataValue, + _realName: realName, + _isRealName: isRealName, + _isEqual: isEqual + } + }; +</script> + +<style lang="scss"> + .uni-forms {} +</style> diff --git a/uni_modules/uni-forms/components/uni-forms/utils.js b/uni_modules/uni-forms/components/uni-forms/utils.js new file mode 100644 index 0000000..6da2421 --- /dev/null +++ b/uni_modules/uni-forms/components/uni-forms/utils.js @@ -0,0 +1,293 @@ +/** + * 简单处理对象拷贝 + * @param {Obejct} 被拷贝对象 + * @@return {Object} 拷贝对象 + */ +export const deepCopy = (val) => { + return JSON.parse(JSON.stringify(val)) +} +/** + * 过滤数字类型 + * @param {String} format 数字类型 + * @@return {Boolean} 返回是否为数字类型 + */ +export const typeFilter = (format) => { + return format === 'int' || format === 'double' || format === 'number' || format === 'timestamp'; +} + +/** + * 把 value 转换成指定的类型,用于处理初始值,原因是初始值需要入库不能为 undefined + * @param {String} key 字段名 + * @param {any} value 字段值 + * @param {Object} rules 表单校验规则 + */ +export const getValue = (key, value, rules) => { + const isRuleNumType = rules.find(val => val.format && typeFilter(val.format)); + const isRuleBoolType = rules.find(val => (val.format && val.format === 'boolean') || val.format === 'bool'); + // 输入类型为 number + if (!!isRuleNumType) { + if (!value && value !== 0) { + value = null + } else { + value = isNumber(Number(value)) ? Number(value) : value + } + } + + // 输入类型为 boolean + if (!!isRuleBoolType) { + value = isBoolean(value) ? value : false + } + + return value; +} + +/** + * 获取表单数据 + * @param {String|Array} name 真实名称,需要使用 realName 获取 + * @param {Object} data 原始数据 + * @param {any} value 需要设置的值 + */ +export const setDataValue = (field, formdata, value) => { + formdata[field] = value + return value || '' +} + +/** + * 获取表单数据 + * @param {String|Array} field 真实名称,需要使用 realName 获取 + * @param {Object} data 原始数据 + */ +export const getDataValue = (field, data) => { + return objGet(data, field) +} + +/** + * 获取表单类型 + * @param {String|Array} field 真实名称,需要使用 realName 获取 + */ +export const getDataValueType = (field, data) => { + const value = getDataValue(field, data) + return { + type: type(value), + value + } +} + +/** + * 获取表单可用的真实name + * @param {String|Array} name 表单name + * @@return {String} 表单可用的真实name + */ +export const realName = (name, data = {}) => { + const base_name = _basePath(name) + if (typeof base_name === 'object' && Array.isArray(base_name) && base_name.length > 1) { + const realname = base_name.reduce((a, b) => a += `#${b}`, '_formdata_') + return realname + } + return base_name[0] || name +} + +/** + * 判断是否表单可用的真实name + * @param {String|Array} name 表单name + * @@return {String} 表单可用的真实name + */ +export const isRealName = (name) => { + const reg = /^_formdata_#*/ + return reg.test(name) +} + +/** + * 获取表单数据的原始格式 + * @@return {Object|Array} object 需要解析的数据 + */ +export const rawData = (object = {}, name) => { + let newData = JSON.parse(JSON.stringify(object)) + let formData = {} + for(let i in newData){ + let path = name2arr(i) + objSet(formData,path,newData[i]) + } + return formData +} + +/** + * 真实name还原为 array + * @param {*} name + */ +export const name2arr = (name) => { + let field = name.replace('_formdata_#', '') + field = field.split('#').map(v => (isNumber(v) ? Number(v) : v)) + return field +} + +/** + * 对象中设置值 + * @param {Object|Array} object 源数据 + * @param {String| Array} path 'a.b.c' 或 ['a',0,'b','c'] + * @param {String} value 需要设置的值 + */ +export const objSet = (object, path, value) => { + if (typeof object !== 'object') return object; + _basePath(path).reduce((o, k, i, _) => { + if (i === _.length - 1) { + // 若遍历结束直接赋值 + o[k] = value + return null + } else if (k in o) { + // 若存在对应路径,则返回找到的对象,进行下一次遍历 + return o[k] + } else { + // 若不存在对应路径,则创建对应对象,若下一路径是数字,新对象赋值为空数组,否则赋值为空对象 + o[k] = /^[0-9]{1,}$/.test(_[i + 1]) ? [] : {} + return o[k] + } + }, object) + // 返回object + return object; +} + +// 处理 path, path有三种形式:'a[0].b.c'、'a.0.b.c' 和 ['a','0','b','c'],需要统一处理成数组,便于后续使用 +function _basePath(path) { + // 若是数组,则直接返回 + if (Array.isArray(path)) return path + // 若有 '[',']',则替换成将 '[' 替换成 '.',去掉 ']' + return path.replace(/\[/g, '.').replace(/\]/g, '').split('.') +} + +/** + * 从对象中获取值 + * @param {Object|Array} object 源数据 + * @param {String| Array} path 'a.b.c' 或 ['a',0,'b','c'] + * @param {String} defaultVal 如果无法从调用链中获取值的默认值 + */ +export const objGet = (object, path, defaultVal = 'undefined') => { + // 先将path处理成统一格式 + let newPath = _basePath(path) + // 递归处理,返回最后结果 + let val = newPath.reduce((o, k) => { + return (o || {})[k] + }, object); + return !val || val !== undefined ? val : defaultVal +} + + +/** + * 是否为 number 类型 + * @param {any} num 需要判断的值 + * @return {Boolean} 是否为 number + */ +export const isNumber = (num) => { + return !isNaN(Number(num)) +} + +/** + * 是否为 boolean 类型 + * @param {any} bool 需要判断的值 + * @return {Boolean} 是否为 boolean + */ +export const isBoolean = (bool) => { + return (typeof bool === 'boolean') +} +/** + * 是否有必填字段 + * @param {Object} rules 规则 + * @return {Boolean} 是否有必填字段 + */ +export const isRequiredField = (rules) => { + let isNoField = false; + for (let i = 0; i < rules.length; i++) { + const ruleData = rules[i]; + if (ruleData.required) { + isNoField = true; + break; + } + } + return isNoField; +} + + +/** + * 获取数据类型 + * @param {Any} obj 需要获取数据类型的值 + */ +export const type = (obj) => { + var class2type = {}; + + // 生成class2type映射 + "Boolean Number String Function Array Date RegExp Object Error".split(" ").map(function(item, index) { + class2type["[object " + item + "]"] = item.toLowerCase(); + }) + if (obj == null) { + return obj + ""; + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[Object.prototype.toString.call(obj)] || "object" : + typeof obj; +} + +/** + * 判断两个值是否相等 + * @param {any} a 值 + * @param {any} b 值 + * @return {Boolean} 是否相等 + */ +export const isEqual = (a, b) => { + //如果a和b本来就全等 + if (a === b) { + //判断是否为0和-0 + return a !== 0 || 1 / a === 1 / b; + } + //判断是否为null和undefined + if (a == null || b == null) { + return a === b; + } + //接下来判断a和b的数据类型 + var classNameA = toString.call(a), + classNameB = toString.call(b); + //如果数据类型不相等,则返回false + if (classNameA !== classNameB) { + return false; + } + //如果数据类型相等,再根据不同数据类型分别判断 + switch (classNameA) { + case '[object RegExp]': + case '[object String]': + //进行字符串转换比较 + return '' + a === '' + b; + case '[object Number]': + //进行数字转换比较,判断是否为NaN + if (+a !== +a) { + return +b !== +b; + } + //判断是否为0或-0 + return +a === 0 ? 1 / +a === 1 / b : +a === +b; + case '[object Date]': + case '[object Boolean]': + return +a === +b; + } + //如果是对象类型 + if (classNameA == '[object Object]') { + //获取a和b的属性长度 + var propsA = Object.getOwnPropertyNames(a), + propsB = Object.getOwnPropertyNames(b); + if (propsA.length != propsB.length) { + return false; + } + for (var i = 0; i < propsA.length; i++) { + var propName = propsA[i]; + //如果对应属性对应值不相等,则返回false + if (a[propName] !== b[propName]) { + return false; + } + } + return true; + } + //如果是数组类型 + if (classNameA == '[object Array]') { + if (a.toString() == b.toString()) { + return true; + } + return false; + } +} diff --git a/uni_modules/uni-forms/components/uni-forms/validate.js b/uni_modules/uni-forms/components/uni-forms/validate.js new file mode 100644 index 0000000..1834c6c --- /dev/null +++ b/uni_modules/uni-forms/components/uni-forms/validate.js @@ -0,0 +1,486 @@ +var pattern = { + email: /^\S+?@\S+?\.\S+?$/, + idcard: /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/, + url: new RegExp( + "^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$", + 'i') +}; + +const FORMAT_MAPPING = { + "int": 'integer', + "bool": 'boolean', + "double": 'number', + "long": 'number', + "password": 'string' + // "fileurls": 'array' +} + +function formatMessage(args, resources = '') { + var defaultMessage = ['label'] + defaultMessage.forEach((item) => { + if (args[item] === undefined) { + args[item] = '' + } + }) + + let str = resources + for (let key in args) { + let reg = new RegExp('{' + key + '}') + str = str.replace(reg, args[key]) + } + return str +} + +function isEmptyValue(value, type) { + if (value === undefined || value === null) { + return true; + } + + if (typeof value === 'string' && !value) { + return true; + } + + if (Array.isArray(value) && !value.length) { + return true; + } + + if (type === 'object' && !Object.keys(value).length) { + return true; + } + + return false; +} + +const types = { + integer(value) { + return types.number(value) && parseInt(value, 10) === value; + }, + string(value) { + return typeof value === 'string'; + }, + number(value) { + if (isNaN(value)) { + return false; + } + return typeof value === 'number'; + }, + "boolean": function(value) { + return typeof value === 'boolean'; + }, + "float": function(value) { + return types.number(value) && !types.integer(value); + }, + array(value) { + return Array.isArray(value); + }, + object(value) { + return typeof value === 'object' && !types.array(value); + }, + date(value) { + return value instanceof Date; + }, + timestamp(value) { + if (!this.integer(value) || Math.abs(value).toString().length > 16) { + return false + } + return true; + }, + file(value) { + return typeof value.url === 'string'; + }, + email(value) { + return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255; + }, + url(value) { + return typeof value === 'string' && !!value.match(pattern.url); + }, + pattern(reg, value) { + try { + return new RegExp(reg).test(value); + } catch (e) { + return false; + } + }, + method(value) { + return typeof value === 'function'; + }, + idcard(value) { + return typeof value === 'string' && !!value.match(pattern.idcard); + }, + 'url-https'(value) { + return this.url(value) && value.startsWith('https://'); + }, + 'url-scheme'(value) { + return value.startsWith('://'); + }, + 'url-web'(value) { + return false; + } +} + +class RuleValidator { + + constructor(message) { + this._message = message + } + + async validateRule(fieldKey, fieldValue, value, data, allData) { + var result = null + + let rules = fieldValue.rules + + let hasRequired = rules.findIndex((item) => { + return item.required + }) + if (hasRequired < 0) { + if (value === null || value === undefined) { + return result + } + if (typeof value === 'string' && !value.length) { + return result + } + } + + var message = this._message + + if (rules === undefined) { + return message['default'] + } + + for (var i = 0; i < rules.length; i++) { + let rule = rules[i] + let vt = this._getValidateType(rule) + + Object.assign(rule, { + label: fieldValue.label || `["${fieldKey}"]` + }) + + if (RuleValidatorHelper[vt]) { + result = RuleValidatorHelper[vt](rule, value, message) + if (result != null) { + break + } + } + + if (rule.validateExpr) { + let now = Date.now() + let resultExpr = rule.validateExpr(value, allData, now) + if (resultExpr === false) { + result = this._getMessage(rule, rule.errorMessage || this._message['default']) + break + } + } + + if (rule.validateFunction) { + result = await this.validateFunction(rule, value, data, allData, vt) + if (result !== null) { + break + } + } + } + + if (result !== null) { + result = message.TAG + result + } + + return result + } + + async validateFunction(rule, value, data, allData, vt) { + let result = null + try { + let callbackMessage = null + const res = await rule.validateFunction(rule, value, allData || data, (message) => { + callbackMessage = message + }) + if (callbackMessage || (typeof res === 'string' && res) || res === false) { + result = this._getMessage(rule, callbackMessage || res, vt) + } + } catch (e) { + result = this._getMessage(rule, e.message, vt) + } + return result + } + + _getMessage(rule, message, vt) { + return formatMessage(rule, message || rule.errorMessage || this._message[vt] || message['default']) + } + + _getValidateType(rule) { + var result = '' + if (rule.required) { + result = 'required' + } else if (rule.format) { + result = 'format' + } else if (rule.arrayType) { + result = 'arrayTypeFormat' + } else if (rule.range) { + result = 'range' + } else if (rule.maximum !== undefined || rule.minimum !== undefined) { + result = 'rangeNumber' + } else if (rule.maxLength !== undefined || rule.minLength !== undefined) { + result = 'rangeLength' + } else if (rule.pattern) { + result = 'pattern' + } else if (rule.validateFunction) { + result = 'validateFunction' + } + return result + } +} + +const RuleValidatorHelper = { + required(rule, value, message) { + if (rule.required && isEmptyValue(value, rule.format || typeof value)) { + return formatMessage(rule, rule.errorMessage || message.required); + } + + return null + }, + + range(rule, value, message) { + const { + range, + errorMessage + } = rule; + + let list = new Array(range.length); + for (let i = 0; i < range.length; i++) { + const item = range[i]; + if (types.object(item) && item.value !== undefined) { + list[i] = item.value; + } else { + list[i] = item; + } + } + + let result = false + if (Array.isArray(value)) { + result = (new Set(value.concat(list)).size === list.length); + } else { + if (list.indexOf(value) > -1) { + result = true; + } + } + + if (!result) { + return formatMessage(rule, errorMessage || message['enum']); + } + + return null + }, + + rangeNumber(rule, value, message) { + if (!types.number(value)) { + return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); + } + + let { + minimum, + maximum, + exclusiveMinimum, + exclusiveMaximum + } = rule; + let min = exclusiveMinimum ? value <= minimum : value < minimum; + let max = exclusiveMaximum ? value >= maximum : value > maximum; + + if (minimum !== undefined && min) { + return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMinimum ? + 'exclusiveMinimum' : 'minimum' + ]) + } else if (maximum !== undefined && max) { + return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMaximum ? + 'exclusiveMaximum' : 'maximum' + ]) + } else if (minimum !== undefined && maximum !== undefined && (min || max)) { + return formatMessage(rule, rule.errorMessage || message['number'].range) + } + + return null + }, + + rangeLength(rule, value, message) { + if (!types.string(value) && !types.array(value)) { + return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); + } + + let min = rule.minLength; + let max = rule.maxLength; + let val = value.length; + + if (min !== undefined && val < min) { + return formatMessage(rule, rule.errorMessage || message['length'].minLength) + } else if (max !== undefined && val > max) { + return formatMessage(rule, rule.errorMessage || message['length'].maxLength) + } else if (min !== undefined && max !== undefined && (val < min || val > max)) { + return formatMessage(rule, rule.errorMessage || message['length'].range) + } + + return null + }, + + pattern(rule, value, message) { + if (!types['pattern'](rule.pattern, value)) { + return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); + } + + return null + }, + + format(rule, value, message) { + var customTypes = Object.keys(types); + var format = FORMAT_MAPPING[rule.format] ? FORMAT_MAPPING[rule.format] : (rule.format || rule.arrayType); + + if (customTypes.indexOf(format) > -1) { + if (!types[format](value)) { + return formatMessage(rule, rule.errorMessage || message.typeError); + } + } + + return null + }, + + arrayTypeFormat(rule, value, message) { + if (!Array.isArray(value)) { + return formatMessage(rule, rule.errorMessage || message.typeError); + } + + for (let i = 0; i < value.length; i++) { + const element = value[i]; + let formatResult = this.format(rule, element, message) + if (formatResult !== null) { + return formatResult + } + } + + return null + } +} + +class SchemaValidator extends RuleValidator { + + constructor(schema, options) { + super(SchemaValidator.message); + + this._schema = schema + this._options = options || null + } + + updateSchema(schema) { + this._schema = schema + } + + async validate(data, allData) { + let result = this._checkFieldInSchema(data) + if (!result) { + result = await this.invokeValidate(data, false, allData) + } + return result.length ? result[0] : null + } + + async validateAll(data, allData) { + let result = this._checkFieldInSchema(data) + if (!result) { + result = await this.invokeValidate(data, true, allData) + } + return result + } + + async validateUpdate(data, allData) { + let result = this._checkFieldInSchema(data) + if (!result) { + result = await this.invokeValidateUpdate(data, false, allData) + } + return result.length ? result[0] : null + } + + async invokeValidate(data, all, allData) { + let result = [] + let schema = this._schema + for (let key in schema) { + let value = schema[key] + let errorMessage = await this.validateRule(key, value, data[key], data, allData) + if (errorMessage != null) { + result.push({ + key, + errorMessage + }) + if (!all) break + } + } + return result + } + + async invokeValidateUpdate(data, all, allData) { + let result = [] + for (let key in data) { + let errorMessage = await this.validateRule(key, this._schema[key], data[key], data, allData) + if (errorMessage != null) { + result.push({ + key, + errorMessage + }) + if (!all) break + } + } + return result + } + + _checkFieldInSchema(data) { + var keys = Object.keys(data) + var keys2 = Object.keys(this._schema) + if (new Set(keys.concat(keys2)).size === keys2.length) { + return '' + } + + var noExistFields = keys.filter((key) => { + return keys2.indexOf(key) < 0; + }) + var errorMessage = formatMessage({ + field: JSON.stringify(noExistFields) + }, SchemaValidator.message.TAG + SchemaValidator.message['defaultInvalid']) + return [{ + key: 'invalid', + errorMessage + }] + } +} + +function Message() { + return { + TAG: "", + default: '验证错误', + defaultInvalid: '提交的字段{field}在数据库中并不存在', + validateFunction: '验证无效', + required: '{label}必填', + 'enum': '{label}超出范围', + timestamp: '{label}格式无效', + whitespace: '{label}不能为空', + typeError: '{label}类型无效', + date: { + format: '{label}日期{value}格式无效', + parse: '{label}日期无法解析,{value}无效', + invalid: '{label}日期{value}无效' + }, + length: { + minLength: '{label}长度不能少于{minLength}', + maxLength: '{label}长度不能超过{maxLength}', + range: '{label}必须介于{minLength}和{maxLength}之间' + }, + number: { + minimum: '{label}不能小于{minimum}', + maximum: '{label}不能大于{maximum}', + exclusiveMinimum: '{label}不能小于等于{minimum}', + exclusiveMaximum: '{label}不能大于等于{maximum}', + range: '{label}必须介于{minimum}and{maximum}之间' + }, + pattern: { + mismatch: '{label}格式不匹配' + } + }; +} + + +SchemaValidator.message = new Message(); + +export default SchemaValidator diff --git a/uni_modules/uni-forms/package.json b/uni_modules/uni-forms/package.json new file mode 100644 index 0000000..0d72c63 --- /dev/null +++ b/uni_modules/uni-forms/package.json @@ -0,0 +1,89 @@ +{ + "id": "uni-forms", + "displayName": "uni-forms 表单", + "version": "1.4.13", + "description": "由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据", + "keywords": [ + "uni-ui", + "表单", + "校验", + "表单校验", + "表单验证" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-forms/readme.md b/uni_modules/uni-forms/readme.md new file mode 100644 index 0000000..63d5a04 --- /dev/null +++ b/uni_modules/uni-forms/readme.md @@ -0,0 +1,23 @@ + + +## Forms 表单 + +> **组件名:uni-forms** +> 代码块: `uForms`、`uni-forms-item` +> 关联组件:`uni-forms-item`、`uni-easyinput`、`uni-data-checkbox`、`uni-group`。 + + +uni-app的内置组件已经有了 `<form>`组件,用于提交表单内容。 + +然而几乎每个表单都需要做表单验证,为了方便做表单验证,减少重复开发,`uni ui` 又基于 `<form>`组件封装了 `<uni-forms>`组件,内置了表单验证功能。 + +`<uni-forms>` 提供了 `rules`属性来描述校验规则、`<uni-forms-item>`子组件来包裹具体的表单项,以及给原生或三方组件提供了 `binddata()` 来设置表单值。 + +每个要校验的表单项,不管input还是checkbox,都必须放在`<uni-forms-item>`组件中,且一个`<uni-forms-item>`组件只能放置一个表单项。 + +`<uni-forms-item>`组件内部预留了显示error message的区域,默认是在表单项的底部。 + +另外,`<uni-forms>`组件下面的各个表单项,可以通过`<uni-group>`包裹为不同的分组。同一`<uni-group>`下的不同表单项目将聚拢在一起,同其他group保持垂直间距。`<uni-group>`仅影响视觉效果。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-forms) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-goods-nav/changelog.md b/uni_modules/uni-goods-nav/changelog.md new file mode 100644 index 0000000..c6264c6 --- /dev/null +++ b/uni_modules/uni-goods-nav/changelog.md @@ -0,0 +1,18 @@ +## 1.2.1(2022-05-30) +- 新增 stat属性,是否开启uni统计功能 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-goods-nav](https://uniapp.dcloud.io/component/uniui/uni-goods-nav) +## 1.1.1(2021-08-24) +- 新增 支持国际化 +## 1.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-05-12) +- 新增 组件示例地址 +## 1.0.6(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.5(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.4(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/en.json b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/en.json new file mode 100644 index 0000000..dcdba41 --- /dev/null +++ b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/en.json @@ -0,0 +1,6 @@ +{ + "uni-goods-nav.options.shop": "shop", + "uni-goods-nav.options.cart": "cart", + "uni-goods-nav.buttonGroup.addToCart": "add to cart", + "uni-goods-nav.buttonGroup.buyNow": "buy now" +} diff --git a/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/index.js b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/index.js new file mode 100644 index 0000000..de7509c --- /dev/null +++ b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hans.json b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hans.json new file mode 100644 index 0000000..48ee344 --- /dev/null +++ b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hans.json @@ -0,0 +1,6 @@ +{ + "uni-goods-nav.options.shop": "店铺", + "uni-goods-nav.options.cart": "购物车", + "uni-goods-nav.buttonGroup.addToCart": "加入购物车", + "uni-goods-nav.buttonGroup.buyNow": "立即购买" +} diff --git a/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hant.json b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hant.json new file mode 100644 index 0000000..d0a0255 --- /dev/null +++ b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hant.json @@ -0,0 +1,6 @@ +{ + "uni-goods-nav.options.shop": "店鋪", + "uni-goods-nav.options.cart": "購物車", + "uni-goods-nav.buttonGroup.addToCart": "加入購物車", + "uni-goods-nav.buttonGroup.buyNow": "立即購買" +} diff --git a/uni_modules/uni-goods-nav/components/uni-goods-nav/uni-goods-nav.vue b/uni_modules/uni-goods-nav/components/uni-goods-nav/uni-goods-nav.vue new file mode 100644 index 0000000..8a16b17 --- /dev/null +++ b/uni_modules/uni-goods-nav/components/uni-goods-nav/uni-goods-nav.vue @@ -0,0 +1,229 @@ +<template> + <view class="uni-goods-nav"> + <!-- 底部占位 --> + <view class="uni-tab__seat" /> + <view class="uni-tab__cart-box flex"> + <view class="flex uni-tab__cart-sub-left"> + <view v-for="(item,index) in options" :key="index" class="flex uni-tab__cart-button-left uni-tab__shop-cart" @click="onClick(index,item)"> + <view class="uni-tab__icon"> + <uni-icons :type="item.icon" size="20" color="#646566"></uni-icons> + <!-- <image class="image" :src="item.icon" mode="widthFix" /> --> + </view> + <text class="uni-tab__text">{{ item.text }}</text> + <view class="flex uni-tab__dot-box"> + <text v-if="item.info" :class="{ 'uni-tab__dots': item.info > 9 }" class="uni-tab__dot " :style="{'backgroundColor':item.infoBackgroundColor?item.infoBackgroundColor:'#ff0000', + color:item.infoColor?item.infoColor:'#fff' + }">{{ item.info }}</text> + </view> + </view> + </view> + <view :class="{'uni-tab__right':fill}" class="flex uni-tab__cart-sub-right "> + <view v-for="(item,index) in buttonGroup" :key="index" :style="{background:item.backgroundColor,color:item.color}" + class="flex uni-tab__cart-button-right" @click="buttonClick(index,item)"><text :style="{color:item.color}" class="uni-tab__cart-button-right-text">{{ item.text }}</text></view> + </view> + </view> + </view> +</template> + +<script> + import { + initVueI18n + } from '@dcloudio/uni-i18n' + import messages from './i18n/index.js' + const { t } = initVueI18n(messages) + /** + * GoodsNav 商品导航 + * @description 商品加入购物车、立即购买等 + * @tutorial https://ext.dcloud.net.cn/plugin?id=865 + * @property {Array} options 组件参数 + * @property {Array} buttonGroup 组件按钮组参数 + * @property {Boolean} fill = [true | false] 组件按钮组参数 + * @property {Boolean} stat 是否开启统计功能 + * @event {Function} click 左侧点击事件 + * @event {Function} buttonClick 右侧按钮组点击事件 + * @example <uni-goods-nav :fill="true" options="" buttonGroup="buttonGroup" @click="" @buttonClick="" /> + */ + export default { + name: 'UniGoodsNav', + emits:['click','buttonClick'], + props: { + options: { + type: Array, + default () { + return [{ + icon: 'shop', + text: t("uni-goods-nav.options.shop"), + }, { + icon: 'cart', + text: t("uni-goods-nav.options.cart") + }] + } + }, + buttonGroup: { + type: Array, + default () { + return [{ + text: t("uni-goods-nav.buttonGroup.addToCart"), + backgroundColor: 'linear-gradient(90deg, #FFCD1E, #FF8A18)', + color: '#fff' + }, + { + text: t("uni-goods-nav.buttonGroup.buyNow"), + backgroundColor: 'linear-gradient(90deg, #FE6035, #EF1224)', + color: '#fff' + } + ] + } + }, + fill: { + type: Boolean, + default: false + }, + stat:{ + type: Boolean, + default: false + } + }, + methods: { + onClick(index, item) { + this.$emit('click', { + index, + content: item, + }) + }, + buttonClick(index, item) { + if (uni.report && this.stat) { + uni.report(item.text, item.text) + } + this.$emit('buttonClick', { + index, + content: item + }) + } + } + } +</script> + +<style lang="scss" > + .flex { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + } + + .uni-goods-nav { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex: 1; + flex-direction: row; + } + + .uni-tab__cart-box { + flex: 1; + height: 50px; + background-color: #fff; + z-index: 900; + } + + .uni-tab__cart-sub-left { + padding: 0 5px; + } + + .uni-tab__cart-sub-right { + flex: 1; + } + + .uni-tab__right { + margin: 5px 0; + margin-right: 10px; + border-radius: 100px; + overflow: hidden; + } + + .uni-tab__cart-button-left { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + // flex: 1; + position: relative; + justify-content: center; + align-items: center; + flex-direction: column; + margin: 0 10px; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + .uni-tab__icon { + width: 18px; + height: 18px; + } + + .image { + width: 18px; + height: 18px; + } + + .uni-tab__text { + margin-top: 3px; + font-size: 12px; + color: #646566; + } + + .uni-tab__cart-button-right { + /* #ifndef APP-NVUE */ + display: flex; + flex-direction: column; + /* #endif */ + flex: 1; + justify-content: center; + align-items: center; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + .uni-tab__cart-button-right-text { + font-size: 14px; + color: #fff; + } + + .uni-tab__cart-button-right:active { + opacity: 0.7; + } + + .uni-tab__dot-box { + /* #ifndef APP-NVUE */ + display: flex; + flex-direction: column; + /* #endif */ + position: absolute; + right: -2px; + top: 2px; + justify-content: center; + align-items: center; + // width: 0; + // height: 0; + } + + .uni-tab__dot { + // width: 30rpx; + // height: 30rpx; + padding: 0 4px; + line-height: 15px; + color: #ffffff; + text-align: center; + font-size: 12px; + background-color: #ff0000; + border-radius: 15px; + } + + .uni-tab__dots { + padding: 0 4px; + // width: auto; + border-radius: 15px; + } +</style> diff --git a/uni_modules/uni-goods-nav/package.json b/uni_modules/uni-goods-nav/package.json new file mode 100644 index 0000000..636e45e --- /dev/null +++ b/uni_modules/uni-goods-nav/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-goods-nav", + "displayName": "uni-goods-nav 商品导航", + "version": "1.2.1", + "description": "商品导航组件主要用于电商类应用底部导航,可自定义加入购物车,购买等操作", + "keywords": [ + "uni-ui", + "uniui", + "商品导航" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-goods-nav/readme.md b/uni_modules/uni-goods-nav/readme.md new file mode 100644 index 0000000..07df93f --- /dev/null +++ b/uni_modules/uni-goods-nav/readme.md @@ -0,0 +1,10 @@ + + +## GoodsNav 商品导航 +> **组件名:uni-goods-nav** +> 代码块: `uGoodsNav` + +商品加入购物车,立即购买等。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-goods-nav) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-grid/changelog.md b/uni_modules/uni-grid/changelog.md new file mode 100644 index 0000000..d301166 --- /dev/null +++ b/uni_modules/uni-grid/changelog.md @@ -0,0 +1,13 @@ +## 1.4.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-grid](https://uniapp.dcloud.io/component/uniui/uni-grid) +## 1.3.2(2021-11-09) +- 新增 提供组件设计资源,组件样式调整 +## 1.3.1(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.3.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.2.4(2021-05-12) +- 新增 组件示例地址 +## 1.2.3(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-grid/components/uni-grid-item/uni-grid-item.vue b/uni_modules/uni-grid/components/uni-grid-item/uni-grid-item.vue new file mode 100644 index 0000000..19c08d7 --- /dev/null +++ b/uni_modules/uni-grid/components/uni-grid-item/uni-grid-item.vue @@ -0,0 +1,127 @@ +<template> + <view v-if="width" :style="'width:'+width+';'+(square?'height:'+width:'')" class="uni-grid-item"> + <view :class="{ 'uni-grid-item--border': showBorder, 'uni-grid-item--border-top': showBorder && index < column, 'uni-highlight': highlight }" + :style="{'border-right-color': borderColor ,'border-bottom-color': borderColor ,'border-top-color': borderColor }" + class="uni-grid-item__box" @click="_onClick"> + <slot /> + </view> + </view> +</template> + +<script> + /** + * GridItem 宫格 + * @description 宫格组件 + * @tutorial https://ext.dcloud.net.cn/plugin?id=27 + * @property {Number} index 子组件的唯一标识 ,点击gird会返回当前的标识 + */ + export default { + name: 'UniGridItem', + inject: ['grid'], + props: { + index: { + type: Number, + default: 0 + } + }, + data() { + return { + column: 0, + showBorder: true, + square: true, + highlight: true, + left: 0, + top: 0, + openNum: 2, + width: 0, + borderColor: '#e5e5e5' + } + }, + created() { + this.column = this.grid.column + this.showBorder = this.grid.showBorder + this.square = this.grid.square + this.highlight = this.grid.highlight + this.top = this.hor === 0 ? this.grid.hor : this.hor + this.left = this.ver === 0 ? this.grid.ver : this.ver + this.borderColor = this.grid.borderColor + this.grid.children.push(this) + // this.grid.init() + this.width = this.grid.width + }, + beforeDestroy() { + this.grid.children.forEach((item, index) => { + if (item === this) { + this.grid.children.splice(index, 1) + } + }) + }, + methods: { + _onClick() { + this.grid.change({ + detail: { + index: this.index + } + }) + } + } + } +</script> + +<style lang="scss" scoped> + .uni-grid-item { + /* #ifndef APP-NVUE */ + height: 100%; + display: flex; + /* #endif */ + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + .uni-grid-item__box { + /* #ifndef APP-NVUE */ + display: flex; + width: 100%; + /* #endif */ + position: relative; + flex: 1; + flex-direction: column; + // justify-content: center; + // align-items: center; + } + + .uni-grid-item--border { + position: relative; + /* #ifdef APP-NVUE */ + border-bottom-color: #D2D2D2; + border-bottom-style: solid; + border-bottom-width: 0.5px; + border-right-color: #D2D2D2; + border-right-style: solid; + border-right-width: 0.5px; + /* #endif */ + /* #ifndef APP-NVUE */ + z-index: 0; + border-bottom: 1px #D2D2D2 solid; + border-right: 1px #D2D2D2 solid; + /* #endif */ + } + .uni-grid-item--border-top { + position: relative; + /* #ifdef APP-NVUE */ + border-top-color: #D2D2D2; + border-top-style: solid; + border-top-width: 0.5px; + /* #endif */ + /* #ifndef APP-NVUE */ + border-top: 1px #D2D2D2 solid; + z-index: 0; + /* #endif */ + } + + + .uni-highlight:active { + background-color: #f1f1f1; + } +</style> diff --git a/uni_modules/uni-grid/components/uni-grid/uni-grid.vue b/uni_modules/uni-grid/components/uni-grid/uni-grid.vue new file mode 100644 index 0000000..0edc7ff --- /dev/null +++ b/uni_modules/uni-grid/components/uni-grid/uni-grid.vue @@ -0,0 +1,142 @@ +<template> + <view class="uni-grid-wrap"> + <view :id="elId" ref="uni-grid" class="uni-grid" :class="{ 'uni-grid--border': showBorder }" :style="{ 'border-left-color':borderColor}"> + <slot /> + </view> + </view> +</template> + +<script> + // #ifdef APP-NVUE + const dom = uni.requireNativePlugin('dom'); + // #endif + + /** + * Grid 宫格 + * @description 宫格组件 + * @tutorial https://ext.dcloud.net.cn/plugin?id=27 + * @property {Number} column 每列显示个数 + * @property {String} borderColor 边框颜色 + * @property {Boolean} showBorder 是否显示边框 + * @property {Boolean} square 是否方形显示 + * @property {Boolean} Boolean 点击背景是否高亮 + * @event {Function} change 点击 grid 触发,e={detail:{index:0}},index 为当前点击 gird 下标 + */ + export default { + name: 'UniGrid', + emits:['change'], + props: { + // 每列显示个数 + column: { + type: Number, + default: 3 + }, + // 是否显示边框 + showBorder: { + type: Boolean, + default: true + }, + // 边框颜色 + borderColor: { + type: String, + default: '#D2D2D2' + }, + // 是否正方形显示,默认为 true + square: { + type: Boolean, + default: true + }, + highlight: { + type: Boolean, + default: true + } + }, + provide() { + return { + grid: this + } + }, + data() { + const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}` + return { + elId, + width: 0 + } + }, + created() { + this.children = [] + }, + mounted() { + this.$nextTick(()=>{ + this.init() + }) + }, + methods: { + init() { + setTimeout(() => { + this._getSize((width) => { + this.children.forEach((item, index) => { + item.width = width + }) + }) + }, 50) + }, + change(e) { + this.$emit('change', e) + }, + _getSize(fn) { + // #ifndef APP-NVUE + uni.createSelectorQuery() + .in(this) + .select(`#${this.elId}`) + .boundingClientRect() + .exec(ret => { + this.width = parseInt((ret[0].width - 1) / this.column) + 'px' + fn(this.width) + }) + // #endif + // #ifdef APP-NVUE + dom.getComponentRect(this.$refs['uni-grid'], (ret) => { + this.width = parseInt((ret.size.width - 1) / this.column) + 'px' + fn(this.width) + }) + // #endif + } + } + } +</script> + +<style lang="scss" scoped> + .uni-grid-wrap { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex: 1; + flex-direction: column; + /* #ifdef H5 */ + width: 100%; + /* #endif */ + } + + .uni-grid { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + // flex: 1; + flex-direction: row; + flex-wrap: wrap; + } + + .uni-grid--border { + position: relative; + /* #ifdef APP-NVUE */ + border-left-color: #D2D2D2; + border-left-style: solid; + border-left-width: 0.5px; + /* #endif */ + /* #ifndef APP-NVUE */ + z-index: 1; + border-left: 1px #D2D2D2 solid; + /* #endif */ + } +</style> diff --git a/uni_modules/uni-grid/package.json b/uni_modules/uni-grid/package.json new file mode 100644 index 0000000..ccb2c91 --- /dev/null +++ b/uni_modules/uni-grid/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-grid", + "displayName": "uni-grid 宫格", + "version": "1.4.0", + "description": "Grid 宫格组件,提供移动端常见的宫格布局,如九宫格。", + "keywords": [ + "uni-ui", + "uniui", + "九宫格", + "表格" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-scss","uni-icons"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-grid/readme.md b/uni_modules/uni-grid/readme.md new file mode 100644 index 0000000..0aa44cc --- /dev/null +++ b/uni_modules/uni-grid/readme.md @@ -0,0 +1,11 @@ + + +## Grid 宫格 +> **组件名:uni-grid** +> 代码块: `uGrid` + + +宫格组件。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-grid) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-group/changelog.md b/uni_modules/uni-group/changelog.md new file mode 100644 index 0000000..a7024fd --- /dev/null +++ b/uni_modules/uni-group/changelog.md @@ -0,0 +1,16 @@ +## 1.2.2(2022-05-30) +- 新增 stat属性,是否开启uni统计功能 +## 1.2.1(2021-11-22) +- 修复 vue3中某些scss变量无法找到的问题 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-group](https://uniapp.dcloud.io/component/uniui/uni-group) +## 1.1.7(2021-11-08) +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +- 优化 组件文档 +## 1.0.3(2021-05-12) +- 新增 组件示例地址 +## 1.0.2(2021-02-05) +- 调整为uni_modules目录规范 +- 优化 兼容 nvue 页面 diff --git a/uni_modules/uni-group/components/uni-group/uni-group.vue b/uni_modules/uni-group/components/uni-group/uni-group.vue new file mode 100644 index 0000000..3425ecd --- /dev/null +++ b/uni_modules/uni-group/components/uni-group/uni-group.vue @@ -0,0 +1,134 @@ +<template> + <view class="uni-group" :class="['uni-group--'+mode ,margin?'group-margin':'']" :style="{marginTop: `${top}px` }"> + <slot name="title"> + <view v-if="title" class="uni-group__title" :style="{'padding-left':border?'30px':'15px'}"> + <text class="uni-group__title-text">{{ title }}</text> + </view> + </slot> + <view class="uni-group__content" :class="{'group-conent-padding':border}"> + <slot /> + </view> + </view> +</template> + +<script> + /** + * Group 分组 + * @description 表单字段分组 + * @tutorial https://ext.dcloud.net.cn/plugin?id=3281 + * @property {String} title 主标题 + * @property {Number} top 分组间隔 + * @property {Number} mode 模式 + */ + export default { + name: 'uniGroup', + emits:['click'], + props: { + title: { + type: String, + default: '' + }, + top: { + type: [Number, String], + default: 10 + }, + mode: { + type: String, + default: 'default' + }, + stat:{ + type: Boolean, + default: false + } + }, + data() { + return { + margin: false, + border: false + } + }, + watch: { + title(newVal) { + if (uni.report && this.stat && newVal !== '') { + uni.report('title', newVal) + } + } + }, + created() { + this.form = this.getForm() + if (this.form) { + this.margin = true + this.border = this.form.border + } + }, + methods: { + /** + * 获取父元素实例 + */ + getForm() { + let parent = this.$parent; + let parentName = parent.$options.name; + while (parentName !== 'uniForms') { + parent = parent.$parent; + if (!parent) return false + parentName = parent.$options.name; + } + return parent; + }, + onClick() { + this.$emit('click') + } + } + } +</script> +<style lang="scss" > + .uni-group { + background: #fff; + margin-top: 10px; + // border: 1px red solid; + } + + .group-margin { + // margin: 0 -15px; + } + + .uni-group__title { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + align-items: center; + padding-left: 15px; + height: 40px; + background-color: #eee; + font-weight: normal; + color: #666; + } + + .uni-group__content { + padding: 15px; + // padding-bottom: 5px; + // background-color: #FFF; + } + + .group-conent-padding { + padding: 0 15px; + } + + .uni-group__title-text { + font-size: 14px; + color: #666; + } + + .distraction { + flex-direction: row; + align-items: center; + } + + .uni-group--card { + margin: 10px; + border-radius: 5px; + overflow: hidden; + box-shadow: 0 0 5px 1px rgba($color: #000000, $alpha: 0.08); + } +</style> diff --git a/uni_modules/uni-group/package.json b/uni_modules/uni-group/package.json new file mode 100644 index 0000000..ea00a08 --- /dev/null +++ b/uni_modules/uni-group/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-group", + "displayName": "uni-group 分组", + "version": "1.2.2", + "description": "分组组件可用于将组件用于分组,添加间隔,以产生明显的区块", + "keywords": [ + "uni-ui", + "uniui", + "group", + "分组", + "" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-group/readme.md b/uni_modules/uni-group/readme.md new file mode 100644 index 0000000..bae67f4 --- /dev/null +++ b/uni_modules/uni-group/readme.md @@ -0,0 +1,9 @@ + +## Group 分组 +> **组件名:uni-group** +> 代码块: `uGroup` + +分组组件可用于将组件分组,添加间隔,以产生明显的区块。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-group) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-icons/changelog.md b/uni_modules/uni-icons/changelog.md new file mode 100644 index 0000000..0261131 --- /dev/null +++ b/uni_modules/uni-icons/changelog.md @@ -0,0 +1,42 @@ +## 2.0.10(2024-06-07) +- 优化 uni-app x 中,size 属性的类型 +## 2.0.9(2024-01-12) +fix: 修复图标大小默认值错误的问题 +## 2.0.8(2023-12-14) +- 修复 项目未使用 ts 情况下,打包报错的bug +## 2.0.7(2023-12-14) +- 修复 size 属性为 string 时,不加单位导致尺寸异常的bug +## 2.0.6(2023-12-11) +- 优化 兼容老版本icon类型,如 top ,bottom 等 +## 2.0.5(2023-12-11) +- 优化 兼容老版本icon类型,如 top ,bottom 等 +## 2.0.4(2023-12-06) +- 优化 uni-app x 下示例项目图标排序 +## 2.0.3(2023-12-06) +- 修复 nvue下引入组件报错的bug +## 2.0.2(2023-12-05) +-优化 size 属性支持单位 +## 2.0.1(2023-12-05) +- 新增 uni-app x 支持定义图标 +## 1.3.5(2022-01-24) +- 优化 size 属性可以传入不带单位的字符串数值 +## 1.3.4(2022-01-24) +- 优化 size 支持其他单位 +## 1.3.3(2022-01-17) +- 修复 nvue 有些图标不显示的bug,兼容老版本图标 +## 1.3.2(2021-12-01) +- 优化 示例可复制图标名称 +## 1.3.1(2021-11-23) +- 优化 兼容旧组件 type 值 +## 1.3.0(2021-11-19) +- 新增 更多图标 +- 优化 自定义图标使用方式 +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-icons](https://uniapp.dcloud.io/component/uniui/uni-icons) +## 1.1.7(2021-11-08) +## 1.2.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.5(2021-05-12) +- 新增 组件示例地址 +## 1.1.4(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-icons/components/uni-icons/uni-icons.uvue b/uni_modules/uni-icons/components/uni-icons/uni-icons.uvue new file mode 100644 index 0000000..8740559 --- /dev/null +++ b/uni_modules/uni-icons/components/uni-icons/uni-icons.uvue @@ -0,0 +1,91 @@ +<template> + <text class="uni-icons" :style="styleObj"> + <slot>{{unicode}}</slot> + </text> +</template> + +<script> + import { fontData, IconsDataItem } from './uniicons_file' + + /** + * Icons 图标 + * @description 用于展示 icon 图标 + * @tutorial https://ext.dcloud.net.cn/plugin?id=28 + * @property {Number,String} size 图标大小 + * @property {String} type 图标图案,参考示例 + * @property {String} color 图标颜色 + * @property {String} customPrefix 自定义图标 + * @event {Function} click 点击 Icon 触发事件 + */ + export default { + name: "uni-icons", + props: { + type: { + type: String, + default: '' + }, + color: { + type: String, + default: '#333333' + }, + size: { + type: [Number, String], + default: 16 + }, + fontFamily: { + type: String, + default: '' + } + }, + data() { + return {}; + }, + computed: { + unicode() : string { + let codes = fontData.find((item : IconsDataItem) : boolean => { return item.font_class == this.type }) + if (codes !== null) { + return codes.unicode + } + return '' + }, + iconSize() : string { + const size = this.size + if (typeof size == 'string') { + const reg = /^[0-9]*$/g + return reg.test(size as string) ? '' + size + 'px' : '' + size; + // return '' + this.size + } + return this.getFontSize(size as number) + }, + styleObj() : UTSJSONObject { + if (this.fontFamily !== '') { + return { color: this.color, fontSize: this.iconSize, fontFamily: this.fontFamily } + } + return { color: this.color, fontSize: this.iconSize } + } + }, + created() { }, + methods: { + /** + * 字体大小 + */ + getFontSize(size : number) : string { + return size + 'px'; + }, + }, + } +</script> + +<style scoped> + @font-face { + font-family: UniIconsFontFamily; + src: url('./uniicons.ttf'); + } + + .uni-icons { + font-family: UniIconsFontFamily; + font-size: 18px; + font-style: normal; + color: #333; + } +</style> diff --git a/uni_modules/uni-icons/components/uni-icons/uni-icons.vue b/uni_modules/uni-icons/components/uni-icons/uni-icons.vue new file mode 100644 index 0000000..7da5356 --- /dev/null +++ b/uni_modules/uni-icons/components/uni-icons/uni-icons.vue @@ -0,0 +1,110 @@ +<template> + <!-- #ifdef APP-NVUE --> + <text :style="styleObj" class="uni-icons" @click="_onClick">{{unicode}}</text> + <!-- #endif --> + <!-- #ifndef APP-NVUE --> + <text :style="styleObj" class="uni-icons" :class="['uniui-'+type,customPrefix,customPrefix?type:'']" @click="_onClick"> + <slot></slot> + </text> + <!-- #endif --> +</template> + +<script> + import { fontData } from './uniicons_file_vue.js'; + + const getVal = (val) => { + const reg = /^[0-9]*$/g + return (typeof val === 'number' || reg.test(val)) ? val + 'px' : val; + } + + // #ifdef APP-NVUE + var domModule = weex.requireModule('dom'); + import iconUrl from './uniicons.ttf' + domModule.addRule('fontFace', { + 'fontFamily': "uniicons", + 'src': "url('" + iconUrl + "')" + }); + // #endif + + /** + * Icons 图标 + * @description 用于展示 icons 图标 + * @tutorial https://ext.dcloud.net.cn/plugin?id=28 + * @property {Number} size 图标大小 + * @property {String} type 图标图案,参考示例 + * @property {String} color 图标颜色 + * @property {String} customPrefix 自定义图标 + * @event {Function} click 点击 Icon 触发事件 + */ + export default { + name: 'UniIcons', + emits: ['click'], + props: { + type: { + type: String, + default: '' + }, + color: { + type: String, + default: '#333333' + }, + size: { + type: [Number, String], + default: 16 + }, + customPrefix: { + type: String, + default: '' + }, + fontFamily: { + type: String, + default: '' + } + }, + data() { + return { + icons: fontData + } + }, + computed: { + unicode() { + let code = this.icons.find(v => v.font_class === this.type) + if (code) { + return code.unicode + } + return '' + }, + iconSize() { + return getVal(this.size) + }, + styleObj() { + if (this.fontFamily !== '') { + return `color: ${this.color}; font-size: ${this.iconSize}; font-family: ${this.fontFamily};` + } + return `color: ${this.color}; font-size: ${this.iconSize};` + } + }, + methods: { + _onClick() { + this.$emit('click') + } + } + } +</script> + +<style lang="scss"> + /* #ifndef APP-NVUE */ + @import './uniicons.css'; + + @font-face { + font-family: uniicons; + src: url('./uniicons.ttf'); + } + + /* #endif */ + .uni-icons { + font-family: uniicons; + text-decoration: none; + text-align: center; + } +</style> diff --git a/uni_modules/uni-icons/components/uni-icons/uniicons.css b/uni_modules/uni-icons/components/uni-icons/uniicons.css new file mode 100644 index 0000000..0a6b6fe --- /dev/null +++ b/uni_modules/uni-icons/components/uni-icons/uniicons.css @@ -0,0 +1,664 @@ + +.uniui-cart-filled:before { + content: "\e6d0"; +} + +.uniui-gift-filled:before { + content: "\e6c4"; +} + +.uniui-color:before { + content: "\e6cf"; +} + +.uniui-wallet:before { + content: "\e6b1"; +} + +.uniui-settings-filled:before { + content: "\e6ce"; +} + +.uniui-auth-filled:before { + content: "\e6cc"; +} + +.uniui-shop-filled:before { + content: "\e6cd"; +} + +.uniui-staff-filled:before { + content: "\e6cb"; +} + +.uniui-vip-filled:before { + content: "\e6c6"; +} + +.uniui-plus-filled:before { + content: "\e6c7"; +} + +.uniui-folder-add-filled:before { + content: "\e6c8"; +} + +.uniui-color-filled:before { + content: "\e6c9"; +} + +.uniui-tune-filled:before { + content: "\e6ca"; +} + +.uniui-calendar-filled:before { + content: "\e6c0"; +} + +.uniui-notification-filled:before { + content: "\e6c1"; +} + +.uniui-wallet-filled:before { + content: "\e6c2"; +} + +.uniui-medal-filled:before { + content: "\e6c3"; +} + +.uniui-fire-filled:before { + content: "\e6c5"; +} + +.uniui-refreshempty:before { + content: "\e6bf"; +} + +.uniui-location-filled:before { + content: "\e6af"; +} + +.uniui-person-filled:before { + content: "\e69d"; +} + +.uniui-personadd-filled:before { + content: "\e698"; +} + +.uniui-arrowthinleft:before { + content: "\e6d2"; +} + +.uniui-arrowthinup:before { + content: "\e6d3"; +} + +.uniui-arrowthindown:before { + content: "\e6d4"; +} + +.uniui-back:before { + content: "\e6b9"; +} + +.uniui-forward:before { + content: "\e6ba"; +} + +.uniui-arrow-right:before { + content: "\e6bb"; +} + +.uniui-arrow-left:before { + content: "\e6bc"; +} + +.uniui-arrow-up:before { + content: "\e6bd"; +} + +.uniui-arrow-down:before { + content: "\e6be"; +} + +.uniui-arrowthinright:before { + content: "\e6d1"; +} + +.uniui-down:before { + content: "\e6b8"; +} + +.uniui-bottom:before { + content: "\e6b8"; +} + +.uniui-arrowright:before { + content: "\e6d5"; +} + +.uniui-right:before { + content: "\e6b5"; +} + +.uniui-up:before { + content: "\e6b6"; +} + +.uniui-top:before { + content: "\e6b6"; +} + +.uniui-left:before { + content: "\e6b7"; +} + +.uniui-arrowup:before { + content: "\e6d6"; +} + +.uniui-eye:before { + content: "\e651"; +} + +.uniui-eye-filled:before { + content: "\e66a"; +} + +.uniui-eye-slash:before { + content: "\e6b3"; +} + +.uniui-eye-slash-filled:before { + content: "\e6b4"; +} + +.uniui-info-filled:before { + content: "\e649"; +} + +.uniui-reload:before { + content: "\e6b2"; +} + +.uniui-micoff-filled:before { + content: "\e6b0"; +} + +.uniui-map-pin-ellipse:before { + content: "\e6ac"; +} + +.uniui-map-pin:before { + content: "\e6ad"; +} + +.uniui-location:before { + content: "\e6ae"; +} + +.uniui-starhalf:before { + content: "\e683"; +} + +.uniui-star:before { + content: "\e688"; +} + +.uniui-star-filled:before { + content: "\e68f"; +} + +.uniui-calendar:before { + content: "\e6a0"; +} + +.uniui-fire:before { + content: "\e6a1"; +} + +.uniui-medal:before { + content: "\e6a2"; +} + +.uniui-font:before { + content: "\e6a3"; +} + +.uniui-gift:before { + content: "\e6a4"; +} + +.uniui-link:before { + content: "\e6a5"; +} + +.uniui-notification:before { + content: "\e6a6"; +} + +.uniui-staff:before { + content: "\e6a7"; +} + +.uniui-vip:before { + content: "\e6a8"; +} + +.uniui-folder-add:before { + content: "\e6a9"; +} + +.uniui-tune:before { + content: "\e6aa"; +} + +.uniui-auth:before { + content: "\e6ab"; +} + +.uniui-person:before { + content: "\e699"; +} + +.uniui-email-filled:before { + content: "\e69a"; +} + +.uniui-phone-filled:before { + content: "\e69b"; +} + +.uniui-phone:before { + content: "\e69c"; +} + +.uniui-email:before { + content: "\e69e"; +} + +.uniui-personadd:before { + content: "\e69f"; +} + +.uniui-chatboxes-filled:before { + content: "\e692"; +} + +.uniui-contact:before { + content: "\e693"; +} + +.uniui-chatbubble-filled:before { + content: "\e694"; +} + +.uniui-contact-filled:before { + content: "\e695"; +} + +.uniui-chatboxes:before { + content: "\e696"; +} + +.uniui-chatbubble:before { + content: "\e697"; +} + +.uniui-upload-filled:before { + content: "\e68e"; +} + +.uniui-upload:before { + content: "\e690"; +} + +.uniui-weixin:before { + content: "\e691"; +} + +.uniui-compose:before { + content: "\e67f"; +} + +.uniui-qq:before { + content: "\e680"; +} + +.uniui-download-filled:before { + content: "\e681"; +} + +.uniui-pyq:before { + content: "\e682"; +} + +.uniui-sound:before { + content: "\e684"; +} + +.uniui-trash-filled:before { + content: "\e685"; +} + +.uniui-sound-filled:before { + content: "\e686"; +} + +.uniui-trash:before { + content: "\e687"; +} + +.uniui-videocam-filled:before { + content: "\e689"; +} + +.uniui-spinner-cycle:before { + content: "\e68a"; +} + +.uniui-weibo:before { + content: "\e68b"; +} + +.uniui-videocam:before { + content: "\e68c"; +} + +.uniui-download:before { + content: "\e68d"; +} + +.uniui-help:before { + content: "\e679"; +} + +.uniui-navigate-filled:before { + content: "\e67a"; +} + +.uniui-plusempty:before { + content: "\e67b"; +} + +.uniui-smallcircle:before { + content: "\e67c"; +} + +.uniui-minus-filled:before { + content: "\e67d"; +} + +.uniui-micoff:before { + content: "\e67e"; +} + +.uniui-closeempty:before { + content: "\e66c"; +} + +.uniui-clear:before { + content: "\e66d"; +} + +.uniui-navigate:before { + content: "\e66e"; +} + +.uniui-minus:before { + content: "\e66f"; +} + +.uniui-image:before { + content: "\e670"; +} + +.uniui-mic:before { + content: "\e671"; +} + +.uniui-paperplane:before { + content: "\e672"; +} + +.uniui-close:before { + content: "\e673"; +} + +.uniui-help-filled:before { + content: "\e674"; +} + +.uniui-paperplane-filled:before { + content: "\e675"; +} + +.uniui-plus:before { + content: "\e676"; +} + +.uniui-mic-filled:before { + content: "\e677"; +} + +.uniui-image-filled:before { + content: "\e678"; +} + +.uniui-locked-filled:before { + content: "\e668"; +} + +.uniui-info:before { + content: "\e669"; +} + +.uniui-locked:before { + content: "\e66b"; +} + +.uniui-camera-filled:before { + content: "\e658"; +} + +.uniui-chat-filled:before { + content: "\e659"; +} + +.uniui-camera:before { + content: "\e65a"; +} + +.uniui-circle:before { + content: "\e65b"; +} + +.uniui-checkmarkempty:before { + content: "\e65c"; +} + +.uniui-chat:before { + content: "\e65d"; +} + +.uniui-circle-filled:before { + content: "\e65e"; +} + +.uniui-flag:before { + content: "\e65f"; +} + +.uniui-flag-filled:before { + content: "\e660"; +} + +.uniui-gear-filled:before { + content: "\e661"; +} + +.uniui-home:before { + content: "\e662"; +} + +.uniui-home-filled:before { + content: "\e663"; +} + +.uniui-gear:before { + content: "\e664"; +} + +.uniui-smallcircle-filled:before { + content: "\e665"; +} + +.uniui-map-filled:before { + content: "\e666"; +} + +.uniui-map:before { + content: "\e667"; +} + +.uniui-refresh-filled:before { + content: "\e656"; +} + +.uniui-refresh:before { + content: "\e657"; +} + +.uniui-cloud-upload:before { + content: "\e645"; +} + +.uniui-cloud-download-filled:before { + content: "\e646"; +} + +.uniui-cloud-download:before { + content: "\e647"; +} + +.uniui-cloud-upload-filled:before { + content: "\e648"; +} + +.uniui-redo:before { + content: "\e64a"; +} + +.uniui-images-filled:before { + content: "\e64b"; +} + +.uniui-undo-filled:before { + content: "\e64c"; +} + +.uniui-more:before { + content: "\e64d"; +} + +.uniui-more-filled:before { + content: "\e64e"; +} + +.uniui-undo:before { + content: "\e64f"; +} + +.uniui-images:before { + content: "\e650"; +} + +.uniui-paperclip:before { + content: "\e652"; +} + +.uniui-settings:before { + content: "\e653"; +} + +.uniui-search:before { + content: "\e654"; +} + +.uniui-redo-filled:before { + content: "\e655"; +} + +.uniui-list:before { + content: "\e644"; +} + +.uniui-mail-open-filled:before { + content: "\e63a"; +} + +.uniui-hand-down-filled:before { + content: "\e63c"; +} + +.uniui-hand-down:before { + content: "\e63d"; +} + +.uniui-hand-up-filled:before { + content: "\e63e"; +} + +.uniui-hand-up:before { + content: "\e63f"; +} + +.uniui-heart-filled:before { + content: "\e641"; +} + +.uniui-mail-open:before { + content: "\e643"; +} + +.uniui-heart:before { + content: "\e639"; +} + +.uniui-loop:before { + content: "\e633"; +} + +.uniui-pulldown:before { + content: "\e632"; +} + +.uniui-scan:before { + content: "\e62a"; +} + +.uniui-bars:before { + content: "\e627"; +} + +.uniui-checkbox:before { + content: "\e62b"; +} + +.uniui-checkbox-filled:before { + content: "\e62c"; +} + +.uniui-shop:before { + content: "\e62f"; +} + +.uniui-headphones:before { + content: "\e630"; +} + +.uniui-cart:before { + content: "\e631"; +} diff --git a/uni_modules/uni-icons/components/uni-icons/uniicons.ttf b/uni_modules/uni-icons/components/uni-icons/uniicons.ttf new file mode 100644 index 0000000..14696d0 --- /dev/null +++ b/uni_modules/uni-icons/components/uni-icons/uniicons.ttf Binary files differ diff --git a/uni_modules/uni-icons/components/uni-icons/uniicons_file.ts b/uni_modules/uni-icons/components/uni-icons/uniicons_file.ts new file mode 100644 index 0000000..98e93aa --- /dev/null +++ b/uni_modules/uni-icons/components/uni-icons/uniicons_file.ts @@ -0,0 +1,664 @@ + +export type IconsData = { + id : string + name : string + font_family : string + css_prefix_text : string + description : string + glyphs : Array<IconsDataItem> +} + +export type IconsDataItem = { + font_class : string + unicode : string +} + + +export const fontData = [ + { + "font_class": "arrow-down", + "unicode": "\ue6be" + }, + { + "font_class": "arrow-left", + "unicode": "\ue6bc" + }, + { + "font_class": "arrow-right", + "unicode": "\ue6bb" + }, + { + "font_class": "arrow-up", + "unicode": "\ue6bd" + }, + { + "font_class": "auth", + "unicode": "\ue6ab" + }, + { + "font_class": "auth-filled", + "unicode": "\ue6cc" + }, + { + "font_class": "back", + "unicode": "\ue6b9" + }, + { + "font_class": "bars", + "unicode": "\ue627" + }, + { + "font_class": "calendar", + "unicode": "\ue6a0" + }, + { + "font_class": "calendar-filled", + "unicode": "\ue6c0" + }, + { + "font_class": "camera", + "unicode": "\ue65a" + }, + { + "font_class": "camera-filled", + "unicode": "\ue658" + }, + { + "font_class": "cart", + "unicode": "\ue631" + }, + { + "font_class": "cart-filled", + "unicode": "\ue6d0" + }, + { + "font_class": "chat", + "unicode": "\ue65d" + }, + { + "font_class": "chat-filled", + "unicode": "\ue659" + }, + { + "font_class": "chatboxes", + "unicode": "\ue696" + }, + { + "font_class": "chatboxes-filled", + "unicode": "\ue692" + }, + { + "font_class": "chatbubble", + "unicode": "\ue697" + }, + { + "font_class": "chatbubble-filled", + "unicode": "\ue694" + }, + { + "font_class": "checkbox", + "unicode": "\ue62b" + }, + { + "font_class": "checkbox-filled", + "unicode": "\ue62c" + }, + { + "font_class": "checkmarkempty", + "unicode": "\ue65c" + }, + { + "font_class": "circle", + "unicode": "\ue65b" + }, + { + "font_class": "circle-filled", + "unicode": "\ue65e" + }, + { + "font_class": "clear", + "unicode": "\ue66d" + }, + { + "font_class": "close", + "unicode": "\ue673" + }, + { + "font_class": "closeempty", + "unicode": "\ue66c" + }, + { + "font_class": "cloud-download", + "unicode": "\ue647" + }, + { + "font_class": "cloud-download-filled", + "unicode": "\ue646" + }, + { + "font_class": "cloud-upload", + "unicode": "\ue645" + }, + { + "font_class": "cloud-upload-filled", + "unicode": "\ue648" + }, + { + "font_class": "color", + "unicode": "\ue6cf" + }, + { + "font_class": "color-filled", + "unicode": "\ue6c9" + }, + { + "font_class": "compose", + "unicode": "\ue67f" + }, + { + "font_class": "contact", + "unicode": "\ue693" + }, + { + "font_class": "contact-filled", + "unicode": "\ue695" + }, + { + "font_class": "down", + "unicode": "\ue6b8" + }, + { + "font_class": "bottom", + "unicode": "\ue6b8" + }, + { + "font_class": "download", + "unicode": "\ue68d" + }, + { + "font_class": "download-filled", + "unicode": "\ue681" + }, + { + "font_class": "email", + "unicode": "\ue69e" + }, + { + "font_class": "email-filled", + "unicode": "\ue69a" + }, + { + "font_class": "eye", + "unicode": "\ue651" + }, + { + "font_class": "eye-filled", + "unicode": "\ue66a" + }, + { + "font_class": "eye-slash", + "unicode": "\ue6b3" + }, + { + "font_class": "eye-slash-filled", + "unicode": "\ue6b4" + }, + { + "font_class": "fire", + "unicode": "\ue6a1" + }, + { + "font_class": "fire-filled", + "unicode": "\ue6c5" + }, + { + "font_class": "flag", + "unicode": "\ue65f" + }, + { + "font_class": "flag-filled", + "unicode": "\ue660" + }, + { + "font_class": "folder-add", + "unicode": "\ue6a9" + }, + { + "font_class": "folder-add-filled", + "unicode": "\ue6c8" + }, + { + "font_class": "font", + "unicode": "\ue6a3" + }, + { + "font_class": "forward", + "unicode": "\ue6ba" + }, + { + "font_class": "gear", + "unicode": "\ue664" + }, + { + "font_class": "gear-filled", + "unicode": "\ue661" + }, + { + "font_class": "gift", + "unicode": "\ue6a4" + }, + { + "font_class": "gift-filled", + "unicode": "\ue6c4" + }, + { + "font_class": "hand-down", + "unicode": "\ue63d" + }, + { + "font_class": "hand-down-filled", + "unicode": "\ue63c" + }, + { + "font_class": "hand-up", + "unicode": "\ue63f" + }, + { + "font_class": "hand-up-filled", + "unicode": "\ue63e" + }, + { + "font_class": "headphones", + "unicode": "\ue630" + }, + { + "font_class": "heart", + "unicode": "\ue639" + }, + { + "font_class": "heart-filled", + "unicode": "\ue641" + }, + { + "font_class": "help", + "unicode": "\ue679" + }, + { + "font_class": "help-filled", + "unicode": "\ue674" + }, + { + "font_class": "home", + "unicode": "\ue662" + }, + { + "font_class": "home-filled", + "unicode": "\ue663" + }, + { + "font_class": "image", + "unicode": "\ue670" + }, + { + "font_class": "image-filled", + "unicode": "\ue678" + }, + { + "font_class": "images", + "unicode": "\ue650" + }, + { + "font_class": "images-filled", + "unicode": "\ue64b" + }, + { + "font_class": "info", + "unicode": "\ue669" + }, + { + "font_class": "info-filled", + "unicode": "\ue649" + }, + { + "font_class": "left", + "unicode": "\ue6b7" + }, + { + "font_class": "link", + "unicode": "\ue6a5" + }, + { + "font_class": "list", + "unicode": "\ue644" + }, + { + "font_class": "location", + "unicode": "\ue6ae" + }, + { + "font_class": "location-filled", + "unicode": "\ue6af" + }, + { + "font_class": "locked", + "unicode": "\ue66b" + }, + { + "font_class": "locked-filled", + "unicode": "\ue668" + }, + { + "font_class": "loop", + "unicode": "\ue633" + }, + { + "font_class": "mail-open", + "unicode": "\ue643" + }, + { + "font_class": "mail-open-filled", + "unicode": "\ue63a" + }, + { + "font_class": "map", + "unicode": "\ue667" + }, + { + "font_class": "map-filled", + "unicode": "\ue666" + }, + { + "font_class": "map-pin", + "unicode": "\ue6ad" + }, + { + "font_class": "map-pin-ellipse", + "unicode": "\ue6ac" + }, + { + "font_class": "medal", + "unicode": "\ue6a2" + }, + { + "font_class": "medal-filled", + "unicode": "\ue6c3" + }, + { + "font_class": "mic", + "unicode": "\ue671" + }, + { + "font_class": "mic-filled", + "unicode": "\ue677" + }, + { + "font_class": "micoff", + "unicode": "\ue67e" + }, + { + "font_class": "micoff-filled", + "unicode": "\ue6b0" + }, + { + "font_class": "minus", + "unicode": "\ue66f" + }, + { + "font_class": "minus-filled", + "unicode": "\ue67d" + }, + { + "font_class": "more", + "unicode": "\ue64d" + }, + { + "font_class": "more-filled", + "unicode": "\ue64e" + }, + { + "font_class": "navigate", + "unicode": "\ue66e" + }, + { + "font_class": "navigate-filled", + "unicode": "\ue67a" + }, + { + "font_class": "notification", + "unicode": "\ue6a6" + }, + { + "font_class": "notification-filled", + "unicode": "\ue6c1" + }, + { + "font_class": "paperclip", + "unicode": "\ue652" + }, + { + "font_class": "paperplane", + "unicode": "\ue672" + }, + { + "font_class": "paperplane-filled", + "unicode": "\ue675" + }, + { + "font_class": "person", + "unicode": "\ue699" + }, + { + "font_class": "person-filled", + "unicode": "\ue69d" + }, + { + "font_class": "personadd", + "unicode": "\ue69f" + }, + { + "font_class": "personadd-filled", + "unicode": "\ue698" + }, + { + "font_class": "personadd-filled-copy", + "unicode": "\ue6d1" + }, + { + "font_class": "phone", + "unicode": "\ue69c" + }, + { + "font_class": "phone-filled", + "unicode": "\ue69b" + }, + { + "font_class": "plus", + "unicode": "\ue676" + }, + { + "font_class": "plus-filled", + "unicode": "\ue6c7" + }, + { + "font_class": "plusempty", + "unicode": "\ue67b" + }, + { + "font_class": "pulldown", + "unicode": "\ue632" + }, + { + "font_class": "pyq", + "unicode": "\ue682" + }, + { + "font_class": "qq", + "unicode": "\ue680" + }, + { + "font_class": "redo", + "unicode": "\ue64a" + }, + { + "font_class": "redo-filled", + "unicode": "\ue655" + }, + { + "font_class": "refresh", + "unicode": "\ue657" + }, + { + "font_class": "refresh-filled", + "unicode": "\ue656" + }, + { + "font_class": "refreshempty", + "unicode": "\ue6bf" + }, + { + "font_class": "reload", + "unicode": "\ue6b2" + }, + { + "font_class": "right", + "unicode": "\ue6b5" + }, + { + "font_class": "scan", + "unicode": "\ue62a" + }, + { + "font_class": "search", + "unicode": "\ue654" + }, + { + "font_class": "settings", + "unicode": "\ue653" + }, + { + "font_class": "settings-filled", + "unicode": "\ue6ce" + }, + { + "font_class": "shop", + "unicode": "\ue62f" + }, + { + "font_class": "shop-filled", + "unicode": "\ue6cd" + }, + { + "font_class": "smallcircle", + "unicode": "\ue67c" + }, + { + "font_class": "smallcircle-filled", + "unicode": "\ue665" + }, + { + "font_class": "sound", + "unicode": "\ue684" + }, + { + "font_class": "sound-filled", + "unicode": "\ue686" + }, + { + "font_class": "spinner-cycle", + "unicode": "\ue68a" + }, + { + "font_class": "staff", + "unicode": "\ue6a7" + }, + { + "font_class": "staff-filled", + "unicode": "\ue6cb" + }, + { + "font_class": "star", + "unicode": "\ue688" + }, + { + "font_class": "star-filled", + "unicode": "\ue68f" + }, + { + "font_class": "starhalf", + "unicode": "\ue683" + }, + { + "font_class": "trash", + "unicode": "\ue687" + }, + { + "font_class": "trash-filled", + "unicode": "\ue685" + }, + { + "font_class": "tune", + "unicode": "\ue6aa" + }, + { + "font_class": "tune-filled", + "unicode": "\ue6ca" + }, + { + "font_class": "undo", + "unicode": "\ue64f" + }, + { + "font_class": "undo-filled", + "unicode": "\ue64c" + }, + { + "font_class": "up", + "unicode": "\ue6b6" + }, + { + "font_class": "top", + "unicode": "\ue6b6" + }, + { + "font_class": "upload", + "unicode": "\ue690" + }, + { + "font_class": "upload-filled", + "unicode": "\ue68e" + }, + { + "font_class": "videocam", + "unicode": "\ue68c" + }, + { + "font_class": "videocam-filled", + "unicode": "\ue689" + }, + { + "font_class": "vip", + "unicode": "\ue6a8" + }, + { + "font_class": "vip-filled", + "unicode": "\ue6c6" + }, + { + "font_class": "wallet", + "unicode": "\ue6b1" + }, + { + "font_class": "wallet-filled", + "unicode": "\ue6c2" + }, + { + "font_class": "weibo", + "unicode": "\ue68b" + }, + { + "font_class": "weixin", + "unicode": "\ue691" + } +] as IconsDataItem[] + +// export const fontData = JSON.parse<IconsDataItem>(fontDataJson) diff --git a/uni_modules/uni-icons/components/uni-icons/uniicons_file_vue.js b/uni_modules/uni-icons/components/uni-icons/uniicons_file_vue.js new file mode 100644 index 0000000..1cd11e1 --- /dev/null +++ b/uni_modules/uni-icons/components/uni-icons/uniicons_file_vue.js @@ -0,0 +1,649 @@ + +export const fontData = [ + { + "font_class": "arrow-down", + "unicode": "\ue6be" + }, + { + "font_class": "arrow-left", + "unicode": "\ue6bc" + }, + { + "font_class": "arrow-right", + "unicode": "\ue6bb" + }, + { + "font_class": "arrow-up", + "unicode": "\ue6bd" + }, + { + "font_class": "auth", + "unicode": "\ue6ab" + }, + { + "font_class": "auth-filled", + "unicode": "\ue6cc" + }, + { + "font_class": "back", + "unicode": "\ue6b9" + }, + { + "font_class": "bars", + "unicode": "\ue627" + }, + { + "font_class": "calendar", + "unicode": "\ue6a0" + }, + { + "font_class": "calendar-filled", + "unicode": "\ue6c0" + }, + { + "font_class": "camera", + "unicode": "\ue65a" + }, + { + "font_class": "camera-filled", + "unicode": "\ue658" + }, + { + "font_class": "cart", + "unicode": "\ue631" + }, + { + "font_class": "cart-filled", + "unicode": "\ue6d0" + }, + { + "font_class": "chat", + "unicode": "\ue65d" + }, + { + "font_class": "chat-filled", + "unicode": "\ue659" + }, + { + "font_class": "chatboxes", + "unicode": "\ue696" + }, + { + "font_class": "chatboxes-filled", + "unicode": "\ue692" + }, + { + "font_class": "chatbubble", + "unicode": "\ue697" + }, + { + "font_class": "chatbubble-filled", + "unicode": "\ue694" + }, + { + "font_class": "checkbox", + "unicode": "\ue62b" + }, + { + "font_class": "checkbox-filled", + "unicode": "\ue62c" + }, + { + "font_class": "checkmarkempty", + "unicode": "\ue65c" + }, + { + "font_class": "circle", + "unicode": "\ue65b" + }, + { + "font_class": "circle-filled", + "unicode": "\ue65e" + }, + { + "font_class": "clear", + "unicode": "\ue66d" + }, + { + "font_class": "close", + "unicode": "\ue673" + }, + { + "font_class": "closeempty", + "unicode": "\ue66c" + }, + { + "font_class": "cloud-download", + "unicode": "\ue647" + }, + { + "font_class": "cloud-download-filled", + "unicode": "\ue646" + }, + { + "font_class": "cloud-upload", + "unicode": "\ue645" + }, + { + "font_class": "cloud-upload-filled", + "unicode": "\ue648" + }, + { + "font_class": "color", + "unicode": "\ue6cf" + }, + { + "font_class": "color-filled", + "unicode": "\ue6c9" + }, + { + "font_class": "compose", + "unicode": "\ue67f" + }, + { + "font_class": "contact", + "unicode": "\ue693" + }, + { + "font_class": "contact-filled", + "unicode": "\ue695" + }, + { + "font_class": "down", + "unicode": "\ue6b8" + }, + { + "font_class": "bottom", + "unicode": "\ue6b8" + }, + { + "font_class": "download", + "unicode": "\ue68d" + }, + { + "font_class": "download-filled", + "unicode": "\ue681" + }, + { + "font_class": "email", + "unicode": "\ue69e" + }, + { + "font_class": "email-filled", + "unicode": "\ue69a" + }, + { + "font_class": "eye", + "unicode": "\ue651" + }, + { + "font_class": "eye-filled", + "unicode": "\ue66a" + }, + { + "font_class": "eye-slash", + "unicode": "\ue6b3" + }, + { + "font_class": "eye-slash-filled", + "unicode": "\ue6b4" + }, + { + "font_class": "fire", + "unicode": "\ue6a1" + }, + { + "font_class": "fire-filled", + "unicode": "\ue6c5" + }, + { + "font_class": "flag", + "unicode": "\ue65f" + }, + { + "font_class": "flag-filled", + "unicode": "\ue660" + }, + { + "font_class": "folder-add", + "unicode": "\ue6a9" + }, + { + "font_class": "folder-add-filled", + "unicode": "\ue6c8" + }, + { + "font_class": "font", + "unicode": "\ue6a3" + }, + { + "font_class": "forward", + "unicode": "\ue6ba" + }, + { + "font_class": "gear", + "unicode": "\ue664" + }, + { + "font_class": "gear-filled", + "unicode": "\ue661" + }, + { + "font_class": "gift", + "unicode": "\ue6a4" + }, + { + "font_class": "gift-filled", + "unicode": "\ue6c4" + }, + { + "font_class": "hand-down", + "unicode": "\ue63d" + }, + { + "font_class": "hand-down-filled", + "unicode": "\ue63c" + }, + { + "font_class": "hand-up", + "unicode": "\ue63f" + }, + { + "font_class": "hand-up-filled", + "unicode": "\ue63e" + }, + { + "font_class": "headphones", + "unicode": "\ue630" + }, + { + "font_class": "heart", + "unicode": "\ue639" + }, + { + "font_class": "heart-filled", + "unicode": "\ue641" + }, + { + "font_class": "help", + "unicode": "\ue679" + }, + { + "font_class": "help-filled", + "unicode": "\ue674" + }, + { + "font_class": "home", + "unicode": "\ue662" + }, + { + "font_class": "home-filled", + "unicode": "\ue663" + }, + { + "font_class": "image", + "unicode": "\ue670" + }, + { + "font_class": "image-filled", + "unicode": "\ue678" + }, + { + "font_class": "images", + "unicode": "\ue650" + }, + { + "font_class": "images-filled", + "unicode": "\ue64b" + }, + { + "font_class": "info", + "unicode": "\ue669" + }, + { + "font_class": "info-filled", + "unicode": "\ue649" + }, + { + "font_class": "left", + "unicode": "\ue6b7" + }, + { + "font_class": "link", + "unicode": "\ue6a5" + }, + { + "font_class": "list", + "unicode": "\ue644" + }, + { + "font_class": "location", + "unicode": "\ue6ae" + }, + { + "font_class": "location-filled", + "unicode": "\ue6af" + }, + { + "font_class": "locked", + "unicode": "\ue66b" + }, + { + "font_class": "locked-filled", + "unicode": "\ue668" + }, + { + "font_class": "loop", + "unicode": "\ue633" + }, + { + "font_class": "mail-open", + "unicode": "\ue643" + }, + { + "font_class": "mail-open-filled", + "unicode": "\ue63a" + }, + { + "font_class": "map", + "unicode": "\ue667" + }, + { + "font_class": "map-filled", + "unicode": "\ue666" + }, + { + "font_class": "map-pin", + "unicode": "\ue6ad" + }, + { + "font_class": "map-pin-ellipse", + "unicode": "\ue6ac" + }, + { + "font_class": "medal", + "unicode": "\ue6a2" + }, + { + "font_class": "medal-filled", + "unicode": "\ue6c3" + }, + { + "font_class": "mic", + "unicode": "\ue671" + }, + { + "font_class": "mic-filled", + "unicode": "\ue677" + }, + { + "font_class": "micoff", + "unicode": "\ue67e" + }, + { + "font_class": "micoff-filled", + "unicode": "\ue6b0" + }, + { + "font_class": "minus", + "unicode": "\ue66f" + }, + { + "font_class": "minus-filled", + "unicode": "\ue67d" + }, + { + "font_class": "more", + "unicode": "\ue64d" + }, + { + "font_class": "more-filled", + "unicode": "\ue64e" + }, + { + "font_class": "navigate", + "unicode": "\ue66e" + }, + { + "font_class": "navigate-filled", + "unicode": "\ue67a" + }, + { + "font_class": "notification", + "unicode": "\ue6a6" + }, + { + "font_class": "notification-filled", + "unicode": "\ue6c1" + }, + { + "font_class": "paperclip", + "unicode": "\ue652" + }, + { + "font_class": "paperplane", + "unicode": "\ue672" + }, + { + "font_class": "paperplane-filled", + "unicode": "\ue675" + }, + { + "font_class": "person", + "unicode": "\ue699" + }, + { + "font_class": "person-filled", + "unicode": "\ue69d" + }, + { + "font_class": "personadd", + "unicode": "\ue69f" + }, + { + "font_class": "personadd-filled", + "unicode": "\ue698" + }, + { + "font_class": "personadd-filled-copy", + "unicode": "\ue6d1" + }, + { + "font_class": "phone", + "unicode": "\ue69c" + }, + { + "font_class": "phone-filled", + "unicode": "\ue69b" + }, + { + "font_class": "plus", + "unicode": "\ue676" + }, + { + "font_class": "plus-filled", + "unicode": "\ue6c7" + }, + { + "font_class": "plusempty", + "unicode": "\ue67b" + }, + { + "font_class": "pulldown", + "unicode": "\ue632" + }, + { + "font_class": "pyq", + "unicode": "\ue682" + }, + { + "font_class": "qq", + "unicode": "\ue680" + }, + { + "font_class": "redo", + "unicode": "\ue64a" + }, + { + "font_class": "redo-filled", + "unicode": "\ue655" + }, + { + "font_class": "refresh", + "unicode": "\ue657" + }, + { + "font_class": "refresh-filled", + "unicode": "\ue656" + }, + { + "font_class": "refreshempty", + "unicode": "\ue6bf" + }, + { + "font_class": "reload", + "unicode": "\ue6b2" + }, + { + "font_class": "right", + "unicode": "\ue6b5" + }, + { + "font_class": "scan", + "unicode": "\ue62a" + }, + { + "font_class": "search", + "unicode": "\ue654" + }, + { + "font_class": "settings", + "unicode": "\ue653" + }, + { + "font_class": "settings-filled", + "unicode": "\ue6ce" + }, + { + "font_class": "shop", + "unicode": "\ue62f" + }, + { + "font_class": "shop-filled", + "unicode": "\ue6cd" + }, + { + "font_class": "smallcircle", + "unicode": "\ue67c" + }, + { + "font_class": "smallcircle-filled", + "unicode": "\ue665" + }, + { + "font_class": "sound", + "unicode": "\ue684" + }, + { + "font_class": "sound-filled", + "unicode": "\ue686" + }, + { + "font_class": "spinner-cycle", + "unicode": "\ue68a" + }, + { + "font_class": "staff", + "unicode": "\ue6a7" + }, + { + "font_class": "staff-filled", + "unicode": "\ue6cb" + }, + { + "font_class": "star", + "unicode": "\ue688" + }, + { + "font_class": "star-filled", + "unicode": "\ue68f" + }, + { + "font_class": "starhalf", + "unicode": "\ue683" + }, + { + "font_class": "trash", + "unicode": "\ue687" + }, + { + "font_class": "trash-filled", + "unicode": "\ue685" + }, + { + "font_class": "tune", + "unicode": "\ue6aa" + }, + { + "font_class": "tune-filled", + "unicode": "\ue6ca" + }, + { + "font_class": "undo", + "unicode": "\ue64f" + }, + { + "font_class": "undo-filled", + "unicode": "\ue64c" + }, + { + "font_class": "up", + "unicode": "\ue6b6" + }, + { + "font_class": "top", + "unicode": "\ue6b6" + }, + { + "font_class": "upload", + "unicode": "\ue690" + }, + { + "font_class": "upload-filled", + "unicode": "\ue68e" + }, + { + "font_class": "videocam", + "unicode": "\ue68c" + }, + { + "font_class": "videocam-filled", + "unicode": "\ue689" + }, + { + "font_class": "vip", + "unicode": "\ue6a8" + }, + { + "font_class": "vip-filled", + "unicode": "\ue6c6" + }, + { + "font_class": "wallet", + "unicode": "\ue6b1" + }, + { + "font_class": "wallet-filled", + "unicode": "\ue6c2" + }, + { + "font_class": "weibo", + "unicode": "\ue68b" + }, + { + "font_class": "weixin", + "unicode": "\ue691" + } +] + +// export const fontData = JSON.parse<IconsDataItem>(fontDataJson) diff --git a/uni_modules/uni-icons/package.json b/uni_modules/uni-icons/package.json new file mode 100644 index 0000000..6b681b4 --- /dev/null +++ b/uni_modules/uni-icons/package.json @@ -0,0 +1,89 @@ +{ + "id": "uni-icons", + "displayName": "uni-icons 图标", + "version": "2.0.10", + "description": "图标组件,用于展示移动端常见的图标,可自定义颜色、大小。", + "keywords": [ + "uni-ui", + "uniui", + "icon", + "图标" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "^3.2.14" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y", + "app-uvue": "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", + "钉钉": "y", + "快手": "y", + "飞书": "y", + "京东": "y" + }, + "快应用": { + "华为": "y", + "联盟": "y" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-icons/readme.md b/uni_modules/uni-icons/readme.md new file mode 100644 index 0000000..86234ba --- /dev/null +++ b/uni_modules/uni-icons/readme.md @@ -0,0 +1,8 @@ +## Icons 图标 +> **组件名:uni-icons** +> 代码块: `uIcons` + +用于展示 icons 图标 。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-icons) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 diff --git a/uni_modules/uni-indexed-list/changelog.md b/uni_modules/uni-indexed-list/changelog.md new file mode 100644 index 0000000..08fa71c --- /dev/null +++ b/uni_modules/uni-indexed-list/changelog.md @@ -0,0 +1,17 @@ +## 1.2.1(2021-11-22) +- 修复 vue3中某些scss变量无法找到的问题 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-indexed-list](https://uniapp.dcloud.io/component/uniui/uni-indexed-list) +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.11(2021-05-12) +- 新增 组件示例地址 +## 1.0.10(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.9(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.8(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 支持 PC 端 diff --git a/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list-item.vue b/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list-item.vue new file mode 100644 index 0000000..19284bd --- /dev/null +++ b/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list-item.vue @@ -0,0 +1,144 @@ +<template> + <view> + <view v-if="loaded || list.itemIndex < 15" class="uni-indexed-list__title-wrapper"> + <text v-if="list.items && list.items.length > 0" class="uni-indexed-list__title">{{ list.key }}</text> + </view> + <view v-if="(loaded || list.itemIndex < 15) && list.items && list.items.length > 0" class="uni-indexed-list__list"> + <view v-for="(item, index) in list.items" :key="index" class="uni-indexed-list__item" hover-class="uni-indexed-list__item--hover"> + <view class="uni-indexed-list__item-container" @click="onClick(idx, index)"> + <view class="uni-indexed-list__item-border" :class="{'uni-indexed-list__item-border--last':index===list.items.length-1}"> + <view v-if="showSelect" style="margin-right: 20rpx;"> + <uni-icons :type="item.checked ? 'checkbox-filled' : 'circle'" :color="item.checked ? '#007aff' : '#C0C0C0'" size="24" /> + </view> + <text class="uni-indexed-list__item-content">{{ item.name }}</text> + </view> + </view> + </view> + </view> + </view> +</template> + +<script> + export default { + name: 'UniIndexedList', + emits:['itemClick'], + props: { + loaded: { + type: Boolean, + default: false + }, + idx: { + type: Number, + default: 0 + }, + list: { + type: Object, + default () { + return {} + } + }, + showSelect: { + type: Boolean, + default: false + } + }, + methods: { + onClick(idx, index) { + this.$emit("itemClick", { + idx, + index + }) + } + } + } +</script> + +<style lang="scss" scoped> + .uni-indexed-list__list { + background-color: $uni-bg-color; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + border-top-style: solid; + border-top-width: 1px; + border-top-color: #DEDEDE; + } + + .uni-indexed-list__item { + font-size: 14px; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex: 1; + flex-direction: row; + justify-content: space-between; + align-items: center; + } + + .uni-indexed-list__item-container { + padding-left: 15px; + flex: 1; + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + box-sizing: border-box; + /* #endif */ + flex-direction: row; + justify-content: space-between; + align-items: center; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + .uni-indexed-list__item-border { + flex: 1; + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + box-sizing: border-box; + /* #endif */ + flex-direction: row; + justify-content: space-between; + align-items: center; + height: 50px; + padding: 25px; + padding-left: 0; + border-bottom-style: solid; + border-bottom-width: 1px; + border-bottom-color: #DEDEDE; + } + + .uni-indexed-list__item-border--last { + border-bottom-width: 0px; + } + + .uni-indexed-list__item-content { + flex: 1; + font-size: 14px; + color: #191919; + } + + .uni-indexed-list { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + } + + .uni-indexed-list__title-wrapper { + /* #ifndef APP-NVUE */ + display: flex; + width: 100%; + /* #endif */ + background-color: #f7f7f7; + } + + .uni-indexed-list__title { + padding: 6px 12px; + line-height: 24px; + font-size: 16px; + font-weight: 500; + } +</style> diff --git a/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list.vue b/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list.vue new file mode 100644 index 0000000..ee3a7ec --- /dev/null +++ b/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list.vue @@ -0,0 +1,367 @@ +<template> + <view class="uni-indexed-list" ref="list" id="list"> + <!-- #ifdef APP-NVUE --> + <list class="uni-indexed-list__scroll" scrollable="true" show-scrollbar="false"> + <cell v-for="(list, idx) in lists" :key="idx" :ref="'uni-indexed-list-' + idx"> + <!-- #endif --> + <!-- #ifndef APP-NVUE --> + <scroll-view :scroll-into-view="scrollViewId" class="uni-indexed-list__scroll" scroll-y> + <view v-for="(list, idx) in lists" :key="idx" :id="'uni-indexed-list-' + idx"> + <!-- #endif --> + <indexed-list-item :list="list" :loaded="loaded" :idx="idx" :showSelect="showSelect" + @itemClick="onClick"></indexed-list-item> + <!-- #ifndef APP-NVUE --> + </view> + </scroll-view> + <!-- #endif --> + <!-- #ifdef APP-NVUE --> + </cell> + </list> + <!-- #endif --> + <view class="uni-indexed-list__menu" @touchstart="touchStart" @touchmove.stop.prevent="touchMove" + @touchend="touchEnd" @mousedown.stop="mousedown" @mousemove.stop.prevent="mousemove" + @mouseleave.stop="mouseleave"> + <view v-for="(list, key) in lists" :key="key" class="uni-indexed-list__menu-item" + :class="touchmoveIndex == key ? 'uni-indexed-list__menu--active' : ''"> + <text class="uni-indexed-list__menu-text" + :class="touchmoveIndex == key ? 'uni-indexed-list__menu-text--active' : ''">{{ list.key }}</text> + </view> + </view> + <view v-if="touchmove" class="uni-indexed-list__alert-wrapper"> + <text class="uni-indexed-list__alert">{{ lists[touchmoveIndex].key }}</text> + </view> + </view> +</template> +<script> + import indexedListItem from './uni-indexed-list-item.vue' + // #ifdef APP-NVUE + const dom = weex.requireModule('dom'); + // #endif + // #ifdef APP-PLUS + function throttle(func, delay) { + var prev = Date.now(); + return function() { + var context = this; + var args = arguments; + var now = Date.now(); + if (now - prev >= delay) { + func.apply(context, args); + prev = Date.now(); + } + } + } + + function touchMove(e) { + let pageY = e.touches[0].pageY + let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight) + if (this.touchmoveIndex === index) { + return false + } + let item = this.lists[index] + if (item) { + // #ifndef APP-NVUE + this.scrollViewId = 'uni-indexed-list-' + index + this.touchmoveIndex = index + // #endif + // #ifdef APP-NVUE + dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], { + animated: false + }) + this.touchmoveIndex = index + // #endif + } + } + const throttleTouchMove = throttle(touchMove, 40) + // #endif + + /** + * IndexedList 索引列表 + * @description 用于展示索引列表 + * @tutorial https://ext.dcloud.net.cn/plugin?id=375 + * @property {Boolean} showSelect = [true|false] 展示模式 + * @value true 展示模式 + * @value false 选择模式 + * @property {Object} options 索引列表需要的数据对象 + * @event {Function} click 点击列表事件 ,返回当前选择项的事件对象 + * @example <uni-indexed-list options="" showSelect="false" @click=""></uni-indexed-list> + */ + export default { + name: 'UniIndexedList', + components: { + indexedListItem + }, + emits: ['click'], + props: { + options: { + type: Array, + default () { + return [] + } + }, + showSelect: { + type: Boolean, + default: false + } + }, + data() { + return { + lists: [], + winHeight: 0, + itemHeight: 0, + winOffsetY: 0, + touchmove: false, + touchmoveIndex: -1, + scrollViewId: '', + touchmovable: true, + loaded: false, + isPC: false + } + }, + watch: { + options: { + handler: function() { + this.setList() + }, + deep: true + } + }, + mounted() { + // #ifdef H5 + this.isPC = this.IsPC() + // #endif + setTimeout(() => { + this.setList() + }, 50) + setTimeout(() => { + this.loaded = true + }, 300); + }, + methods: { + setList() { + let index = 0; + this.lists = [] + this.options.forEach((value, index) => { + if (value.data.length === 0) { + return + } + let indexBefore = index + let items = value.data.map(item => { + let obj = {} + obj['key'] = value.letter + obj['name'] = item + obj['itemIndex'] = index + index++ + obj.checked = item.checked ? item.checked : false + return obj + }) + this.lists.push({ + title: value.letter, + key: value.letter, + items: items, + itemIndex: indexBefore + }) + }) + // #ifndef APP-NVUE + uni.createSelectorQuery() + .in(this) + .select('#list') + .boundingClientRect() + .exec(ret => { + this.winOffsetY = ret[0].top + this.winHeight = ret[0].height + this.itemHeight = this.winHeight / this.lists.length + }) + // #endif + // #ifdef APP-NVUE + dom.getComponentRect(this.$refs['list'], (res) => { + this.winOffsetY = res.size.top + this.winHeight = res.size.height + this.itemHeight = this.winHeight / this.lists.length + }) + // #endif + }, + touchStart(e) { + this.touchmove = true + let pageY = this.isPC ? e.pageY : e.touches[0].pageY + let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight) + let item = this.lists[index] + if (item) { + this.scrollViewId = 'uni-indexed-list-' + index + this.touchmoveIndex = index + // #ifdef APP-NVUE + dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], { + animated: false + }) + // #endif + } + }, + touchMove(e) { + // #ifndef APP-PLUS + let pageY = this.isPC ? e.pageY : e.touches[0].pageY + let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight) + if (this.touchmoveIndex === index) { + return false + } + let item = this.lists[index] + if (item) { + this.scrollViewId = 'uni-indexed-list-' + index + this.touchmoveIndex = index + } + // #endif + // #ifdef APP-PLUS + throttleTouchMove.call(this, e) + // #endif + }, + touchEnd() { + this.touchmove = false + // this.touchmoveIndex = -1 + }, + + /** + * 兼容 PC @tian + */ + + mousedown(e) { + if (!this.isPC) return + this.touchStart(e) + }, + mousemove(e) { + if (!this.isPC) return + this.touchMove(e) + }, + mouseleave(e) { + if (!this.isPC) return + this.touchEnd(e) + }, + + // #ifdef H5 + IsPC() { + var userAgentInfo = navigator.userAgent; + var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]; + var flag = true; + for (let v = 0; v < Agents.length - 1; v++) { + if (userAgentInfo.indexOf(Agents[v]) > 0) { + flag = false; + break; + } + } + return flag; + }, + // #endif + + + onClick(e) { + let { + idx, + index + } = e + let obj = {} + for (let key in this.lists[idx].items[index]) { + obj[key] = this.lists[idx].items[index][key] + } + let select = [] + if (this.showSelect) { + this.lists[idx].items[index].checked = !this.lists[idx].items[index].checked + this.lists.forEach((value, idx) => { + value.items.forEach((item, index) => { + if (item.checked) { + let obj = {} + for (let key in this.lists[idx].items[index]) { + obj[key] = this.lists[idx].items[index][key] + } + select.push(obj) + } + }) + }) + } + this.$emit('click', { + item: obj, + select: select + }) + } + } + } +</script> +<style lang="scss" scoped> + .uni-indexed-list { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + } + + .uni-indexed-list__scroll { + flex: 1; + } + + .uni-indexed-list__menu { + width: 24px; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + } + + .uni-indexed-list__menu-item { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex: 1; + align-items: center; + justify-content: center; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + .uni-indexed-list__menu-text { + font-size: 12px; + text-align: center; + color: #aaa; + } + + .uni-indexed-list__menu--active { + // background-color: rgb(200, 200, 200); + } + + .uni-indexed-list__menu--active {} + + .uni-indexed-list__menu-text--active { + border-radius: 16px; + width: 16px; + height: 16px; + line-height: 16px; + background-color: #007aff; + color: #fff; + } + + .uni-indexed-list__alert-wrapper { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + align-items: center; + justify-content: center; + } + + .uni-indexed-list__alert { + width: 80px; + height: 80px; + border-radius: 80px; + text-align: center; + line-height: 80px; + font-size: 35px; + color: #fff; + background-color: rgba(0, 0, 0, 0.5); + } +</style> diff --git a/uni_modules/uni-indexed-list/package.json b/uni_modules/uni-indexed-list/package.json new file mode 100644 index 0000000..125c0e7 --- /dev/null +++ b/uni_modules/uni-indexed-list/package.json @@ -0,0 +1,89 @@ +{ + "id": "uni-indexed-list", + "displayName": "uni-indexed-list 索引列表", + "version": "1.2.1", + "description": "索引列表组件,右侧带索引的列表,方便快速定位到具体内容,通常用于城市/机场选择等场景", + "keywords": [ + "uni-ui", + "索引列表", + "索引", + "列表" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-indexed-list/readme.md b/uni_modules/uni-indexed-list/readme.md new file mode 100644 index 0000000..44ad84b --- /dev/null +++ b/uni_modules/uni-indexed-list/readme.md @@ -0,0 +1,11 @@ + + +## IndexedList 索引列表 +> **组件名:uni-indexed-list** +> 代码块: `uIndexedList` + + +用于展示索引列表。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-indexed-list) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 diff --git a/uni_modules/uni-link/changelog.md b/uni_modules/uni-link/changelog.md new file mode 100644 index 0000000..2cfbf59 --- /dev/null +++ b/uni_modules/uni-link/changelog.md @@ -0,0 +1,17 @@ +## 1.0.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-link](https://uniapp.dcloud.io/component/uniui/uni-link) +## 1.1.7(2021-11-08) +## 0.0.7(2021-09-03) +- 修复 在 nvue 下不显示的 bug +## 0.0.6(2021-07-30) +- 新增 支持自定义插槽 +## 0.0.5(2021-06-21) +- 新增 download 属性,H5平台下载文件名 +## 0.0.4(2021-05-12) +- 新增 组件示例地址 +## 0.0.3(2021-03-09) +- 新增 href 属性支持 tel:|mailto: + +## 0.0.2(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-link/components/uni-link/uni-link.vue b/uni_modules/uni-link/components/uni-link/uni-link.vue new file mode 100644 index 0000000..27c5468 --- /dev/null +++ b/uni_modules/uni-link/components/uni-link/uni-link.vue @@ -0,0 +1,128 @@ +<template> + <a v-if="isShowA" class="uni-link" :href="href" + :class="{'uni-link--withline':showUnderLine===true||showUnderLine==='true'}" + :style="{color,fontSize:fontSize+'px'}" :download="download"> + <slot>{{text}}</slot> + </a> + <!-- #ifndef APP-NVUE --> + <text v-else class="uni-link" :class="{'uni-link--withline':showUnderLine===true||showUnderLine==='true'}" + :style="{color,fontSize:fontSize+'px'}" @click="openURL"> + <slot>{{text}}</slot> + </text> + <!-- #endif --> + <!-- #ifdef APP-NVUE --> + <text v-else class="uni-link" :class="{'uni-link--withline':showUnderLine===true||showUnderLine==='true'}" + :style="{color,fontSize:fontSize+'px'}" @click="openURL"> + {{text}} + </text> + <!-- #endif --> +</template> + +<script> + /** + * Link 外部网页超链接组件 + * @description uni-link是一个外部网页超链接组件,在小程序内复制url,在app内打开外部浏览器,在h5端打开新网页 + * @tutorial https://ext.dcloud.net.cn/plugin?id=1182 + * @property {String} href 点击后打开的外部网页url + * @property {String} text 显示的文字 + * @property {String} downlaod H5平台下载文件名 + * @property {Boolean} showUnderLine 是否显示下划线 + * @property {String} copyTips 在小程序端复制链接时显示的提示语 + * @property {String} color 链接文字颜色 + * @property {String} fontSize 链接文字大小 + * @example * <uni-link href="https://ext.dcloud.net.cn" text="https://ext.dcloud.net.cn"></uni-link> + */ + export default { + name: 'uniLink', + props: { + href: { + type: String, + default: '' + }, + text: { + type: String, + default: '' + }, + download: { + type: String, + default: '' + }, + showUnderLine: { + type: [Boolean, String], + default: true + }, + copyTips: { + type: String, + default: '已自动复制网址,请在手机浏览器里粘贴该网址' + }, + color: { + type: String, + default: '#999999' + }, + fontSize: { + type: [Number, String], + default: 14 + } + }, + computed: { + isShowA() { + // #ifdef H5 + this._isH5 = true; + // #endif + if ((this.isMail() || this.isTel()) && this._isH5 === true) { + return true; + } + return false; + } + }, + created() { + this._isH5 = null; + }, + methods: { + isMail() { + return this.href.startsWith('mailto:'); + }, + isTel() { + return this.href.startsWith('tel:'); + }, + openURL() { + // #ifdef APP-PLUS + if (this.isTel()) { + this.makePhoneCall(this.href.replace('tel:', '')); + } else { + plus.runtime.openURL(this.href); + } + // #endif + // #ifdef H5 + window.open(this.href) + // #endif + // #ifdef MP + uni.setClipboardData({ + data: this.href + }); + uni.showModal({ + content: this.copyTips, + showCancel: false + }); + // #endif + }, + makePhoneCall(phoneNumber) { + uni.makePhoneCall({ + phoneNumber + }) + } + } + } +</script> + +<style> + /* #ifndef APP-NVUE */ + .uni-link { + cursor: pointer; + } + + /* #endif */ + .uni-link--withline { + text-decoration: underline; + } +</style> diff --git a/uni_modules/uni-link/package.json b/uni_modules/uni-link/package.json new file mode 100644 index 0000000..77b1986 --- /dev/null +++ b/uni_modules/uni-link/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-link", + "displayName": "uni-link 超链接", + "version": "1.0.0", + "description": "uni-link是一个外部网页超链接组件,在小程序内复制url,在app内打开外部浏览器,在h5端打", + "keywords": [ + "uni-ui", + "uniui", + "link", + "超链接", + "" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "y", + "联盟": "y" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-link/readme.md b/uni_modules/uni-link/readme.md new file mode 100644 index 0000000..7f09e94 --- /dev/null +++ b/uni_modules/uni-link/readme.md @@ -0,0 +1,11 @@ + + +## Link 链接 +> **组件名:uni-link** +> 代码块: `uLink` + + +uni-link是一个外部网页超链接组件,在小程序内复制url,在app内打开外部浏览器,在h5端打开新网页。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-link) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-list/changelog.md b/uni_modules/uni-list/changelog.md new file mode 100644 index 0000000..8254a18 --- /dev/null +++ b/uni_modules/uni-list/changelog.md @@ -0,0 +1,46 @@ +## 1.2.14(2023-04-14) +- 优化 uni-list-chat 具名插槽`header` 非app端套一层元素,方便使用时通过外层元素定位实现样式修改 +## 1.2.13(2023-03-03) +- uni-list-chat 新增 支持具名插槽`header` +## 1.2.12(2023-02-01) +- 新增 列表图标新增 customPrefix 属性 ,用法 [详见](https://uniapp.dcloud.net.cn/component/uniui/uni-icons.html#icons-props) +## 1.2.11(2023-01-31) +- 修复 无反馈效果呈现的bug +## 1.2.9(2022-11-22) +- 修复 uni-list-chat 在vue3下跳转报错的bug +## 1.2.8(2022-11-21) +- 修复 uni-list-chat avatar属性 值为本地路径时错误的问题 +## 1.2.7(2022-11-21) +- 修复 uni-list-chat avatar属性 在腾讯云版uniCloud下错误的问题 +## 1.2.6(2022-11-18) +- 修复 uni-list-chat note属性 支持:“草稿”字样功能 文本少1位的问题 +## 1.2.5(2022-11-15) +- 修复 uni-list-item 的 customStyle 属性 padding值在 H5端 无效的bug +## 1.2.4(2022-11-15) +- 修复 uni-list-item 的 customStyle 属性 padding值在nvue(vue2)下无效的bug +## 1.2.3(2022-11-14) +- uni-list-chat 新增 avatar 支持 fileId +## 1.2.2(2022-11-11) +- uni-list 新增属性 render-reverse 详情参考:[https://uniapp.dcloud.net.cn/component/list.html](https://uniapp.dcloud.net.cn/component/list.html) +- uni-list-chat note属性 支持:“草稿”字样 加红显示 详情参考uni-im:[https://ext.dcloud.net.cn/plugin?name=uni-im](https://ext.dcloud.net.cn/plugin?name=uni-im) +- uni-list-item 新增属性 customStyle 支持设置padding、backgroundColor +## 1.2.1(2022-03-30) +- 删除无用文件 +## 1.2.0(2021-11-23) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-list](https://uniapp.dcloud.io/component/uniui/uni-list) +## 1.1.3(2021-08-30) +- 修复 在vue3中to属性在发行应用的时候报错的bug +## 1.1.2(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.1.1(2021-07-21) +- 修复 与其他组件嵌套使用时,点击失效的Bug +## 1.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.17(2021-05-12) +- 新增 组件示例地址 +## 1.0.16(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 1.0.15(2021-02-05) +- 调整为uni_modules目录规范 +- 修复 uni-list-chat 角标显示不正常的问题 diff --git a/uni_modules/uni-list/components/uni-list-ad/uni-list-ad.vue b/uni_modules/uni-list/components/uni-list-ad/uni-list-ad.vue new file mode 100644 index 0000000..b9349c2 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list-ad/uni-list-ad.vue @@ -0,0 +1,107 @@ +<template> + <!-- #ifdef APP-NVUE --> + <cell> + <!-- #endif --> + <view class="uni-list-ad"> + <view v-if="borderShow" :class="{'uni-list--border':border,'uni-list-item--first':isFirstChild}"></view> + <ad style="width: 200px;height: 300px;border-width: 1px;border-color: red;border-style: solid;" adpid="1111111111" + unit-id="" appid="" apid="" type="feed" @error="aderror" @close="closeAd"></ad> + </view> + <!-- #ifdef APP-NVUE --> + </cell> + <!-- #endif --> + +</template> + +<script> + // #ifdef APP-NVUE + const dom = uni.requireNativePlugin('dom'); + // #endif + export default { + name: 'UniListAd', + props: { + title: { + type: String, + default: '', + + } + }, + // inject: ['list'], + data() { + return { + isFirstChild: false, + border: false, + borderShow: true, + } + }, + + mounted() { + this.list = this.getForm() + if (this.list) { + if (!this.list.firstChildAppend) { + this.list.firstChildAppend = true + this.isFirstChild = true + } + this.border = this.list.border + } + }, + methods: { + /** + * 获取父元素实例 + */ + 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; + }, + aderror(e) { + console.log("aderror: " + JSON.stringify(e.detail)); + }, + closeAd(e) { + this.borderShow = false + } + } + } +</script> + +<style lang="scss" > + .uni-list-ad { + position: relative; + border: 1px red solid; + } + + .uni-list--border { + position: relative; + padding-bottom: 1px; + /* #ifdef APP-PLUS */ + border-top-color: $uni-border-color; + border-top-style: solid; + border-top-width: 0.5px; + /* #endif */ + margin-left: $uni-spacing-row-lg; + } + + /* #ifndef APP-NVUE */ + .uni-list--border:after { + position: absolute; + top: 0; + right: 0; + left: 0; + height: 1px; + content: ''; + -webkit-transform: scaleY(.5); + transform: scaleY(.5); + background-color: $uni-border-color; + } + + .uni-list-item--first:after { + height: 0px; + } + + /* #endif */ +</style> diff --git a/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.scss b/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.scss new file mode 100644 index 0000000..311f8d9 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.scss @@ -0,0 +1,58 @@ +/** + * 这里是 uni-list 组件内置的常用样式变量 + * 如果需要覆盖样式,这里提供了基本的组件样式变量,您可以尝试修改这里的变量,去完成样式替换,而不用去修改源码 + * + */ + +// 背景色 +$background-color : #fff; +// 分割线颜色 +$divide-line-color : #e5e5e5; + +// 默认头像大小,如需要修改此值,注意同步修改 js 中的值 const avatarWidth = xx ,目前只支持方形头像 +// nvue 页面不支持修改头像大小 +$avatar-width : 45px ; + +// 头像边框 +$avatar-border-radius: 5px; +$avatar-border-color: #eee; +$avatar-border-width: 1px; + +// 标题文字样式 +$title-size : 16px; +$title-color : #3b4144; +$title-weight : normal; + +// 描述文字样式 +$note-size : 12px; +$note-color : #999; +$note-weight : normal; + +// 右侧额外内容默认样式 +$right-text-size : 12px; +$right-text-color : #999; +$right-text-weight : normal; + +// 角标样式 +// nvue 页面不支持修改圆点位置以及大小 +// 角标在左侧时,角标的位置,默认为 0 ,负数左/下移动,正数右/上移动 +$badge-left: 0px; +$badge-top: 0px; + +// 显示圆点时,圆点大小 +$dot-width: 10px; +$dot-height: 10px; + +// 显示角标时,角标大小和字体大小 +$badge-size : 18px; +$badge-font : 12px; +// 显示角标时,角标前景色 +$badge-color : #fff; +// 显示角标时,角标背景色 +$badge-background-color : #ff5a5f; +// 显示角标时,角标左右间距 +$badge-space : 6px; + +// 状态样式 +// 选中颜色 +$hover : #f5f5f5; diff --git a/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.vue b/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.vue new file mode 100644 index 0000000..d49fd7c --- /dev/null +++ b/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.vue @@ -0,0 +1,593 @@ +<template> + <!-- #ifdef APP-NVUE --> + <cell> + <!-- #endif --> + <view :hover-class="!clickable && !link ? '' : 'uni-list-chat--hover'" class="uni-list-chat" @click.stop="onClick"> + <view :class="{ 'uni-list--border': border, 'uni-list-chat--first': isFirstChild }"></view> + <view class="uni-list-chat__container"> + <view class="uni-list-chat__header-warp"> + <view v-if="avatarCircle || avatarList.length === 0" class="uni-list-chat__header" :class="{ 'header--circle': avatarCircle }"> + <image class="uni-list-chat__header-image" :class="{ 'header--circle': avatarCircle }" :src="avatarUrl" mode="aspectFill"></image> + </view> + <!-- 头像组 --> + <view v-else class="uni-list-chat__header"> + <view v-for="(item, index) in avatarList" :key="index" class="uni-list-chat__header-box" :class="computedAvatar" + :style="{ width: imageWidth + 'px', height: imageWidth + 'px' }"> + <image class="uni-list-chat__header-image" :style="{ width: imageWidth + 'px', height: imageWidth + 'px' }" :src="item.url" + mode="aspectFill"></image> + </view> + </view> + </view> + <!-- #ifndef APP --> + <view class="slot-header"> + <!-- #endif --> + <slot name="header"></slot> + <!-- #ifndef APP --> + </view> + <!-- #endif --> + <view v-if="badgeText && badgePositon === 'left'" class="uni-list-chat__badge uni-list-chat__badge-pos" :class="[isSingle]"> + <text class="uni-list-chat__badge-text">{{ badgeText === 'dot' ? '' : badgeText }}</text> + </view> + <view class="uni-list-chat__content"> + <view class="uni-list-chat__content-main"> + <text class="uni-list-chat__content-title uni-ellipsis">{{ title }}</text> + <view style="flex-direction: row;"> + <text class="draft" v-if="isDraft">[草稿]</text> + <text class="uni-list-chat__content-note uni-ellipsis">{{isDraft?note.slice(14):note}}</text> + </view> + </view> + <view class="uni-list-chat__content-extra"> + <slot> + <text class="uni-list-chat__content-extra-text">{{ time }}</text> + <view v-if="badgeText && badgePositon === 'right'" class="uni-list-chat__badge" :class="[isSingle, badgePositon === 'right' ? 'uni-list-chat--right' : '']"> + <text class="uni-list-chat__badge-text">{{ badgeText === 'dot' ? '' : badgeText }}</text> + </view> + </slot> + </view> + </view> + </view> + </view> + <!-- #ifdef APP-NVUE --> + </cell> + <!-- #endif --> +</template> + +<script> + // 头像大小 + const avatarWidth = 45; + + /** + * ListChat 聊天列表 + * @description 聊天列表,用于创建聊天类列表 + * @tutorial https://ext.dcloud.net.cn/plugin?id=24 + * @property {String} title 标题 + * @property {String} note 描述 + * @property {Boolean} clickable = [true|false] 是否开启点击反馈,默认为false + * @property {String} badgeText 数字角标内容 + * @property {String} badgePositon = [left|right] 角标位置,默认为 right + * @property {String} link = [false|navigateTo|redirectTo|reLaunch|switchTab] 是否展示右侧箭头并开启点击反馈,默认为false + * @value false 不开启 + * @value navigateTo 同 uni.navigateTo() + * @value redirectTo 同 uni.redirectTo() + * @value reLaunch 同 uni.reLaunch() + * @value switchTab 同 uni.switchTab() + * @property {String | PageURIString} to 跳转目标页面 + * @property {String} time 右侧时间显示 + * @property {Boolean} avatarCircle = [true|false] 是否显示圆形头像,默认为false + * @property {String} avatar 头像地址,avatarCircle 不填时生效 + * @property {Array} avatarList 头像组,格式为 [{url:''}] + * @event {Function} click 点击 uniListChat 触发事件 + */ + export default { + name: 'UniListChat', + emits:['click'], + props: { + title: { + type: String, + default: '' + }, + note: { + type: String, + default: '' + }, + clickable: { + type: Boolean, + default: false + }, + link: { + type: [Boolean, String], + default: false + }, + to: { + type: String, + default: '' + }, + badgeText: { + type: [String, Number], + default: '' + }, + badgePositon: { + type: String, + default: 'right' + }, + time: { + type: String, + default: '' + }, + avatarCircle: { + type: Boolean, + default: false + }, + avatar: { + type: String, + default: '' + }, + avatarList: { + type: Array, + default () { + return []; + } + } + }, + // inject: ['list'], + computed: { + isDraft(){ + return this.note.slice(0,14) == '[uni-im-draft]' + }, + isSingle() { + if (this.badgeText === 'dot') { + return 'uni-badge--dot'; + } else { + const badgeText = this.badgeText.toString(); + if (badgeText.length > 1) { + return 'uni-badge--complex'; + } else { + return 'uni-badge--single'; + } + } + }, + computedAvatar() { + if (this.avatarList.length > 4) { + this.imageWidth = avatarWidth * 0.31; + return 'avatarItem--3'; + } else if (this.avatarList.length > 1) { + this.imageWidth = avatarWidth * 0.47; + return 'avatarItem--2'; + } else { + this.imageWidth = avatarWidth; + return 'avatarItem--1'; + } + } + }, + watch: { + avatar:{ + handler(avatar) { + if(avatar.substr(0,8) == 'cloud://'){ + uniCloud.getTempFileURL({ + fileList: [avatar] + }).then(res=>{ + // console.log(res); + // 兼容uniCloud私有化部署 + let fileList = res.fileList || res.result.fileList + this.avatarUrl = fileList[0].tempFileURL + }) + }else{ + this.avatarUrl = avatar + } + }, + immediate: true + } + }, + data() { + return { + isFirstChild: false, + border: true, + // avatarList: 3, + imageWidth: 50, + avatarUrl:'' + }; + }, + mounted() { + this.list = this.getForm() + if (this.list) { + if (!this.list.firstChildAppend) { + this.list.firstChildAppend = true; + this.isFirstChild = true; + } + this.border = this.list.border; + } + }, + methods: { + /** + * 获取父元素实例 + */ + 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: {} + }); + } + }, + 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" > + $uni-font-size-lg:16px; + $uni-spacing-row-sm: 5px; + $uni-spacing-row-base: 10px; + $uni-spacing-row-lg: 15px; + $background-color: #fff; + $divide-line-color: #e5e5e5; + $avatar-width: 45px; + $avatar-border-radius: 5px; + $avatar-border-color: #eee; + $avatar-border-width: 1px; + $title-size: 16px; + $title-color: #3b4144; + $title-weight: normal; + $note-size: 12px; + $note-color: #999; + $note-weight: normal; + $right-text-size: 12px; + $right-text-color: #999; + $right-text-weight: normal; + $badge-left: 0px; + $badge-top: 0px; + $dot-width: 10px; + $dot-height: 10px; + $badge-size: 18px; + $badge-font: 12px; + $badge-color: #fff; + $badge-background-color: #ff5a5f; + $badge-space: 6px; + $hover: #f5f5f5; + + .uni-list-chat { + font-size: $uni-font-size-lg; + position: relative; + flex-direction: column; + justify-content: space-between; + background-color: $background-color; + } + + // .uni-list-chat--disabled { + // opacity: 0.3; + // } + + .uni-list-chat--hover { + background-color: $hover; + } + + .uni-list--border { + position: relative; + margin-left: $uni-spacing-row-lg; + /* #ifdef APP-PLUS */ + border-top-color: $divide-line-color; + border-top-style: solid; + border-top-width: 0.5px; + /* #endif */ + } + + /* #ifndef APP-NVUE */ + .uni-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: $divide-line-color; + } + + .uni-list-item--first:after { + height: 0px; + } + + /* #endif */ + + .uni-list-chat--first { + border-top-width: 0px; + } + + .uni-ellipsis { + /* #ifndef APP-NVUE */ + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + /* #endif */ + /* #ifdef APP-NVUE */ + lines: 1; + /* #endif */ + } + + .uni-ellipsis-2 { + /* #ifndef APP-NVUE */ + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + /* #endif */ + + /* #ifdef APP-NVUE */ + lines: 2; + /* #endif */ + } + + .uni-list-chat__container { + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + flex: 1; + padding: $uni-spacing-row-base $uni-spacing-row-lg; + position: relative; + overflow: hidden; + } + + .uni-list-chat__header-warp { + position: relative; + } + + .uni-list-chat__header { + /* #ifndef APP-NVUE */ + display: flex; + align-content: center; + /* #endif */ + flex-direction: row; + justify-content: center; + align-items: center; + flex-wrap: wrap-reverse; + /* #ifdef APP-NVUE */ + width: 50px; + height: 50px; + /* #endif */ + /* #ifndef APP-NVUE */ + width: $avatar-width; + height: $avatar-width; + /* #endif */ + + border-radius: $avatar-border-radius; + border-color: $avatar-border-color; + border-width: $avatar-border-width; + border-style: solid; + overflow: hidden; + } + + .uni-list-chat__header-box { + /* #ifndef APP-PLUS */ + box-sizing: border-box; + display: flex; + width: $avatar-width; + height: $avatar-width; + /* #endif */ + /* #ifdef APP-NVUE */ + width: 50px; + height: 50px; + /* #endif */ + overflow: hidden; + border-radius: 2px; + } + + .uni-list-chat__header-image { + margin: 1px; + /* #ifdef APP-NVUE */ + width: 50px; + height: 50px; + /* #endif */ + /* #ifndef APP-NVUE */ + width: $avatar-width; + height: $avatar-width; + /* #endif */ + } + + /* #ifndef APP-NVUE */ + .uni-list-chat__header-image { + display: block; + width: 100%; + height: 100%; + } + + .avatarItem--1 { + width: 100%; + height: 100%; + } + + .avatarItem--2 { + width: 47%; + height: 47%; + } + + .avatarItem--3 { + width: 32%; + height: 32%; + } + + /* #endif */ + .header--circle { + border-radius: 50%; + } + + .uni-list-chat__content { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + flex: 1; + overflow: hidden; + padding: 2px 0; + } + + .uni-list-chat__content-main { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + justify-content: space-between; + padding-left: $uni-spacing-row-base; + flex: 1; + overflow: hidden; + } + + .uni-list-chat__content-title { + font-size: $title-size; + color: $title-color; + font-weight: $title-weight; + overflow: hidden; + } + + .draft ,.uni-list-chat__content-note { + margin-top: 3px; + color: $note-color; + font-size: $note-size; + font-weight: $title-weight; + overflow: hidden; + } + .draft{ + color: #eb3a41; + /* #ifndef APP-NVUE */ + flex-shrink: 0; + /* #endif */ + padding-right: 3px; + } + + .uni-list-chat__content-extra { + /* #ifndef APP-NVUE */ + flex-shrink: 0; + display: flex; + /* #endif */ + flex-direction: column; + justify-content: space-between; + align-items: flex-end; + margin-left: 5px; + } + + .uni-list-chat__content-extra-text { + color: $right-text-color; + font-size: $right-text-size; + font-weight: $right-text-weight; + overflow: hidden; + } + + .uni-list-chat__badge-pos { + position: absolute; + /* #ifdef APP-NVUE */ + left: 55px; + top: 3px; + /* #endif */ + /* #ifndef APP-NVUE */ + left: calc(#{$avatar-width} + 10px - #{$badge-space} + #{$badge-left}); + top: calc(#{$uni-spacing-row-base}/ 2 + 1px + #{$badge-top}); + /* #endif */ + } + + .uni-list-chat__badge { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + justify-content: center; + align-items: center; + border-radius: 100px; + background-color: $badge-background-color; + } + + .uni-list-chat__badge-text { + color: $badge-color; + font-size: $badge-font; + } + + .uni-badge--single { + /* #ifndef APP-NVUE */ + // left: calc(#{$avatar-width} + 7px + #{$badge-left}); + /* #endif */ + width: $badge-size; + height: $badge-size; + } + + .uni-badge--complex { + /* #ifdef APP-NVUE */ + left: 50px; + /* #endif */ + /* #ifndef APP-NVUE */ + width: auto; + /* #endif */ + height: $badge-size; + padding: 0 $badge-space; + } + + .uni-badge--dot { + /* #ifdef APP-NVUE */ + left: 60px; + top: 6px; + /* #endif */ + /* #ifndef APP-NVUE */ + left: calc(#{$avatar-width} + 15px - #{$dot-width}/ 2 + 1px + #{$badge-left}); + /* #endif */ + width: $dot-width; + height: $dot-height; + padding: 0; + } + + .uni-list-chat--right { + /* #ifdef APP-NVUE */ + left: 0; + /* #endif */ + } +</style> diff --git a/uni_modules/uni-list/components/uni-list-item/uni-list-item.vue b/uni_modules/uni-list/components/uni-list-item/uni-list-item.vue new file mode 100644 index 0000000..a274ac8 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list-item/uni-list-item.vue @@ -0,0 +1,534 @@ +<template> + <!-- #ifdef APP-NVUE --> + <cell :keep-scroll-position="keepScrollPosition"> + <!-- #endif --> + <view :class="{ 'uni-list-item--disabled': disabled }" :style="{'background-color':customStyle.backgroundColor}" + :hover-class="(!clickable && !link) || disabled || showSwitch ? '' : 'uni-list-item--hover'" + class="uni-list-item" @click="onClick"> + <view v-if="!isFirstChild" class="border--left" :class="{ 'uni-list--border': border }"></view> + <view class="uni-list-item__container" + :class="{ 'container--right': showArrow || link, 'flex--direction': direction === 'column'}" + :style="{paddingTop:padding.top,paddingLeft:padding.left,paddingRight:padding.right,paddingBottom:padding.bottom}"> + <slot name="header"> + <view class="uni-list-item__header"> + <view v-if="thumb" class="uni-list-item__icon"> + <image :src="thumb" class="uni-list-item__icon-img" :class="['uni-list--' + thumbSize]" /> + </view> + <view v-else-if="showExtraIcon" class="uni-list-item__icon"> + <uni-icons :customPrefix="extraIcon.customPrefix" :color="extraIcon.color" :size="extraIcon.size" :type="extraIcon.type" /> + </view> + </view> + </slot> + <slot name="body"> + <view class="uni-list-item__content" + :class="{ 'uni-list-item__content--center': thumb || showExtraIcon || showBadge || showSwitch }"> + <text v-if="title" class="uni-list-item__content-title" + :class="[ellipsis !== 0 && ellipsis <= 2 ? 'uni-ellipsis-' + ellipsis : '']">{{ title }}</text> + <text v-if="note" class="uni-list-item__content-note">{{ note }}</text> + </view> + </slot> + <slot name="footer"> + <view v-if="rightText || showBadge || showSwitch" class="uni-list-item__extra" + :class="{ 'flex--justify': direction === 'column' }"> + <text v-if="rightText" class="uni-list-item__extra-text">{{ rightText }}</text> + <uni-badge v-if="showBadge" :type="badgeType" :text="badgeText" :custom-style="badgeStyle" /> + <switch v-if="showSwitch" :disabled="disabled" :checked="switchChecked" + @change="onSwitchChange" /> + </view> + </slot> + </view> + <uni-icons v-if="showArrow || link" :size="16" class="uni-icon-wrapper" color="#bbb" type="arrowright" /> + </view> + <!-- #ifdef APP-NVUE --> + </cell> + <!-- #endif --> +</template> + +<script> + /** + * 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} badgeText 数字角标内容 + * @property {String} badgeType 数字角标类型,参考[uni-icons](https://ext.dcloud.net.cn/plugin?id=21) + * @property {Object} badgeStyle 数字角标样式 + * @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 {Boolean} showSwitch = [true|false] 是否显示Switch + * @property {Boolean} switchChecked = [true|false] Switch是否被选中 + * @property {Boolean} showExtraIcon = [true|false] 左侧是否显示扩展图标 + * @property {Object} extraIcon 扩展图标参数,格式为 {color: '#4cd964',size: '22',type: 'spinner'} + * @property {String} direction = [row|column] 排版方向 + * @value row 水平排列 + * @value column 垂直排列 + * @event {Function} click 点击 uniListItem 触发事件 + * @event {Function} switchChange 点击切换 Switch 时触发 + */ + export default { + name: 'UniListItem', + 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: '' + }, + showBadge: { + type: [Boolean, String], + default: false + }, + showSwitch: { + type: [Boolean, String], + default: false + }, + switchChecked: { + type: [Boolean, String], + default: false + }, + badgeText: { + type: String, + default: '' + }, + badgeType: { + type: String, + default: 'success' + }, + badgeStyle: { + 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 { + type: '', + color: '#000000', + size: 20, + customPrefix: '' + }; + } + }, + border: { + type: Boolean, + default: true + }, + customStyle: { + type: Object, + default () { + return { + padding: '', + backgroundColor: '#FFFFFF' + } + } + }, + keepScrollPosition: { + type: Boolean, + default: false + } + }, + watch: { + 'customStyle.padding': { + handler(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 + } + } + }, + immediate: true + } + }, + // inject: ['list'], + data() { + return { + isFirstChild: false, + padding: { + top: "", + right: "", + bottom: "", + left: "" + } + }; + }, + mounted() { + this.list = this.getForm() + // 判断是否存在 uni-list 组件 + if (this.list) { + if (!this.list.firstChildAppend) { + this.list.firstChildAppend = true; + this.isFirstChild = true; + } + } + }, + methods: { + /** + * 获取父元素实例 + */ + 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.detail); + }, + 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"> + $uni-font-size-sm:12px; + $uni-font-size-base:14px; + $uni-font-size-lg:16px; + $uni-spacing-col-lg: 12px; + $uni-spacing-row-lg: 15px; + $uni-img-size-sm:20px; + $uni-img-size-base:26px; + $uni-img-size-lg:40px; + $uni-border-color:#e5e5e5; + $uni-bg-color-hover:#f1f1f1; + $uni-text-color-grey:#999; + $list-item-pd: $uni-spacing-col-lg $uni-spacing-row-lg; + + .uni-list-item { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + font-size: $uni-font-size-lg; + position: relative; + justify-content: space-between; + align-items: center; + background-color: #fff; + flex-direction: row; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + .uni-list-item--disabled { + opacity: 0.3; + } + + .uni-list-item--hover { + background-color: $uni-bg-color-hover !important; + } + + .uni-list-item__container { + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + padding: $list-item-pd; + padding-left: $uni-spacing-row-lg; + flex: 1; + overflow: hidden; + // align-items: center; + } + + .container--right { + padding-right: 0; + } + + // .border--left { + // margin-left: $uni-spacing-row-lg; + // } + .uni-list--border { + position: absolute; + top: 0; + right: 0; + left: 0; + /* #ifdef APP-NVUE */ + border-top-color: $uni-border-color; + border-top-style: solid; + border-top-width: 0.5px; + /* #endif */ + } + + /* #ifndef APP-NVUE */ + .uni-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: $uni-border-color; + } + + /* #endif */ + .uni-list-item__content { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + padding-right: 8px; + flex: 1; + color: #3b4144; + // overflow: hidden; + flex-direction: column; + justify-content: space-between; + overflow: hidden; + } + + .uni-list-item__content--center { + justify-content: center; + } + + .uni-list-item__content-title { + font-size: $uni-font-size-base; + color: #3b4144; + overflow: hidden; + } + + .uni-list-item__content-note { + margin-top: 6rpx; + color: $uni-text-color-grey; + font-size: $uni-font-size-sm; + overflow: hidden; + } + + .uni-list-item__extra { + // width: 25%; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + justify-content: flex-end; + align-items: center; + } + + .uni-list-item__header { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + align-items: center; + } + + .uni-list-item__icon { + margin-right: 18rpx; + flex-direction: row; + justify-content: center; + align-items: center; + } + + .uni-list-item__icon-img { + /* #ifndef APP-NVUE */ + display: block; + /* #endif */ + height: $uni-img-size-base; + width: $uni-img-size-base; + margin-right: 10px; + } + + .uni-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 */ + } + + .uni-list--lg { + height: $uni-img-size-lg; + width: $uni-img-size-lg; + } + + .uni-list--base { + height: $uni-img-size-base; + width: $uni-img-size-base; + } + + .uni-list--sm { + height: $uni-img-size-sm; + width: $uni-img-size-sm; + } + + .uni-list-item__extra-text { + color: $uni-text-color-grey; + font-size: $uni-font-size-sm; + } + + .uni-ellipsis-1 { + /* #ifndef APP-NVUE */ + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + /* #endif */ + /* #ifdef APP-NVUE */ + lines: 1; + text-overflow: ellipsis; + /* #endif */ + } + + .uni-ellipsis-2 { + /* #ifndef APP-NVUE */ + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + /* #endif */ + /* #ifdef APP-NVUE */ + lines: 2; + text-overflow: ellipsis; + /* #endif */ + } +</style> \ No newline at end of file diff --git a/uni_modules/uni-list/components/uni-list/uni-list.vue b/uni_modules/uni-list/components/uni-list/uni-list.vue new file mode 100644 index 0000000..6ef5972 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list/uni-list.vue @@ -0,0 +1,123 @@ +<template> + <!-- #ifndef APP-NVUE --> + <view class="uni-list uni-border-top-bottom"> + <view v-if="border" class="uni-list--border-top"></view> + <slot /> + <view v-if="border" class="uni-list--border-bottom"></view> + </view> + <!-- #endif --> + <!-- #ifdef APP-NVUE --> + <list :bounce="false" :scrollable="true" show-scrollbar :render-reverse="renderReverse" @scroll="scroll" class="uni-list" :class="{ 'uni-list--border': border }" :enableBackToTop="enableBackToTop" + loadmoreoffset="15"> + <slot /> + </list> + <!-- #endif --> +</template> + +<script> + /** + * List 列表 + * @description 列表组件 + * @tutorial https://ext.dcloud.net.cn/plugin?id=24 + * @property {String} border = [true|false] 标题 + */ + export default { + name: 'uniList', + 'mp-weixin': { + options: { + multipleSlots: false + } + }, + props: { + stackFromEnd:{ + type: Boolean, + default:false + }, + enableBackToTop: { + type: [Boolean, String], + default: false + }, + scrollY: { + type: [Boolean, String], + default: false + }, + border: { + type: Boolean, + default: true + }, + renderReverse:{ + type: Boolean, + default: false + } + }, + // provide() { + // return { + // list: this + // }; + // }, + created() { + this.firstChildAppend = false; + }, + methods: { + loadMore(e) { + this.$emit('scrolltolower'); + }, + scroll(e) { + this.$emit('scroll', e); + } + } + }; +</script> +<style lang="scss"> + $uni-bg-color:#ffffff; + $uni-border-color:#e5e5e5; + + .uni-list { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + background-color: $uni-bg-color; + position: relative; + flex-direction: column; + } + + .uni-list--border { + position: relative; + /* #ifdef APP-NVUE */ + border-top-color: $uni-border-color; + border-top-style: solid; + border-top-width: 0.5px; + border-bottom-color: $uni-border-color; + border-bottom-style: solid; + border-bottom-width: 0.5px; + /* #endif */ + z-index: -1; + } + + /* #ifndef APP-NVUE */ + + .uni-list--border-top { + position: absolute; + top: 0; + right: 0; + left: 0; + height: 1px; + -webkit-transform: scaleY(0.5); + transform: scaleY(0.5); + background-color: $uni-border-color; + z-index: 1; + } + + .uni-list--border-bottom { + position: absolute; + bottom: 0; + right: 0; + left: 0; + height: 1px; + -webkit-transform: scaleY(0.5); + transform: scaleY(0.5); + background-color: $uni-border-color; + } + + /* #endif */ +</style> diff --git a/uni_modules/uni-list/components/uni-list/uni-refresh.vue b/uni_modules/uni-list/components/uni-list/uni-refresh.vue new file mode 100644 index 0000000..3b4c5a2 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list/uni-refresh.vue @@ -0,0 +1,65 @@ +<template> + <!-- #ifdef APP-NVUE --> + <refresh :display="display" @refresh="onrefresh" @pullingdown="onpullingdown"> + <slot /> + </refresh> + <!-- #endif --> + <!-- #ifndef APP-NVUE --> + <view ref="uni-refresh" class="uni-refresh" v-show="isShow"> + <slot /> + </view> + <!-- #endif --> +</template> + +<script> + export default { + name: 'UniRefresh', + props: { + display: { + type: [String], + default: "hide" + } + }, + data() { + return { + pulling: false + } + }, + computed: { + isShow() { + if (this.display === "show" || this.pulling === true) { + return true; + } + return false; + } + }, + created() {}, + methods: { + onchange(value) { + this.pulling = value; + }, + onrefresh(e) { + this.$emit("refresh", e); + }, + onpullingdown(e) { + // #ifdef APP-NVUE + this.$emit("pullingdown", e); + // #endif + // #ifndef APP-NVUE + var detail = { + viewHeight: 90, + pullingDistance: e.height + } + this.$emit("pullingdown", detail); + // #endif + } + } + } +</script> + +<style> + .uni-refresh { + height: 0; + overflow: hidden; + } +</style> diff --git a/uni_modules/uni-list/components/uni-list/uni-refresh.wxs b/uni_modules/uni-list/components/uni-list/uni-refresh.wxs new file mode 100644 index 0000000..818a6b7 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list/uni-refresh.wxs @@ -0,0 +1,87 @@ +var pullDown = { + threshold: 95, + maxHeight: 200, + callRefresh: 'onrefresh', + callPullingDown: 'onpullingdown', + refreshSelector: '.uni-refresh' +}; + +function ready(newValue, oldValue, ownerInstance, instance) { + var state = instance.getState() + state.canPullDown = newValue; + // console.log(newValue); +} + +function touchStart(e, instance) { + var state = instance.getState(); + state.refreshInstance = instance.selectComponent(pullDown.refreshSelector); + state.canPullDown = (state.refreshInstance != null && state.refreshInstance != undefined); + if (!state.canPullDown) { + return + } + + // console.log("touchStart"); + + state.height = 0; + state.touchStartY = e.touches[0].pageY || e.changedTouches[0].pageY; + state.refreshInstance.setStyle({ + 'height': 0 + }); + state.refreshInstance.callMethod("onchange", true); +} + +function touchMove(e, ownerInstance) { + var instance = e.instance; + var state = instance.getState(); + if (!state.canPullDown) { + return + } + + var oldHeight = state.height; + var endY = e.touches[0].pageY || e.changedTouches[0].pageY; + var height = endY - state.touchStartY; + if (height > pullDown.maxHeight) { + return; + } + + var refreshInstance = state.refreshInstance; + refreshInstance.setStyle({ + 'height': height + 'px' + }); + + height = height < pullDown.maxHeight ? height : pullDown.maxHeight; + state.height = height; + refreshInstance.callMethod(pullDown.callPullingDown, { + height: height + }); +} + +function touchEnd(e, ownerInstance) { + var state = e.instance.getState(); + if (!state.canPullDown) { + return + } + + state.refreshInstance.callMethod("onchange", false); + + var refreshInstance = state.refreshInstance; + if (state.height > pullDown.threshold) { + refreshInstance.callMethod(pullDown.callRefresh); + return; + } + + refreshInstance.setStyle({ + 'height': 0 + }); +} + +function propObserver(newValue, oldValue, instance) { + pullDown = newValue; +} + +module.exports = { + touchmove: touchMove, + touchstart: touchStart, + touchend: touchEnd, + propObserver: propObserver +} diff --git a/uni_modules/uni-list/package.json b/uni_modules/uni-list/package.json new file mode 100644 index 0000000..8350efc --- /dev/null +++ b/uni_modules/uni-list/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-list", + "displayName": "uni-list 列表", + "version": "1.2.14", + "description": "List 组件 ,帮助使用者快速构建列表。", + "keywords": [ + "", + "uni-ui", + "uniui", + "列表", + "", + "list" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-badge", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-list/readme.md b/uni_modules/uni-list/readme.md new file mode 100644 index 0000000..32c2865 --- /dev/null +++ b/uni_modules/uni-list/readme.md @@ -0,0 +1,346 @@ +## List 列表 +> **组件名:uni-list** +> 代码块: `uList`、`uListItem` +> 关联组件:`uni-list-item`、`uni-badge`、`uni-icons`、`uni-list-chat`、`uni-list-ad` + + +List 列表组件,包含基本列表样式、可扩展插槽机制、长列表性能优化、多端兼容。 + +在vue页面里,它默认使用页面级滚动。在app-nvue页面里,它默认使用原生list组件滚动。这样的长列表,在滚动出屏幕外后,系统会回收不可见区域的渲染内存资源,不会造成滚动越长手机越卡的问题。 + +uni-list组件是父容器,里面的核心是uni-list-item子组件,它代表列表中的一个可重复行,子组件可以无限循环。 + +uni-list-item有很多风格,uni-list-item组件通过内置的属性,满足一些常用的场景。当内置属性不满足需求时,可以通过扩展插槽来自定义列表内容。 + +内置属性可以覆盖的场景包括:导航列表、设置列表、小图标列表、通信录列表、聊天记录列表。 + +涉及很多大图或丰富内容的列表,比如类今日头条的新闻列表、类淘宝的电商列表,需要通过扩展插槽实现。 + +下文均有样例给出。 + +uni-list不包含下拉刷新和上拉翻页。上拉翻页另见组件:[uni-load-more](https://ext.dcloud.net.cn/plugin?id=29) + + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - 组件需要依赖 `sass` 插件 ,请自行手动安装 +> - 组件内部依赖 `'uni-icons'` 、`uni-badge` 组件 +> - `uni-list` 和 `uni-list-item` 需要配套使用,暂不支持单独使用 `uni-list-item` +> - 只有开启点击反馈后,会有点击选中效果 +> - 使用插槽时,可以完全自定义内容 +> - note 、rightText 属性暂时没做限制,不支持文字溢出隐藏,使用时应该控制长度显示或通过默认插槽自行扩展 +> - 支付宝小程序平台需要在支付宝小程序开发者工具里开启 component2 编译模式,开启方式: 详情 --> 项目配置 --> 启用 component2 编译 +> - 如果需要修改 `switch`、`badge` 样式,请使用插槽自定义 +> - 在 `HBuilderX` 低版本中,可能会出现组件显示 `undefined` 的问题,请升级最新的 `HBuilderX` 或者 `cli` +> - 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + +### 基本用法 + +- 设置 `title` 属性,可以显示列表标题 +- 设置 `disabled` 属性,可以禁用当前项 + +```html +<uni-list> + <uni-list-item title="列表文字" ></uni-list-item> + <uni-list-item :disabled="true" title="列表禁用状态" ></uni-list-item> +</uni-list> + +``` + +### 多行内容显示 + +- 设置 `note` 属性 ,可以在第二行显示描述文本信息 + +```html +<uni-list> + <uni-list-item title="列表文字" note="列表描述信息"></uni-list-item> + <uni-list-item :disabled="true" title="列表文字" note="列表禁用状态"></uni-list-item> +</uni-list> + +``` + +### 右侧显示角标、switch + +- 设置 `show-badge` 属性 ,可以显示角标内容 +- 设置 `show-switch` 属性,可以显示 switch 开关 + +```html +<uni-list> + <uni-list-item title="列表右侧显示角标" :show-badge="true" badge-text="12" ></uni-list-item> + <uni-list-item title="列表右侧显示 switch" :show-switch="true" @switchChange="switchChange" ></uni-list-item> +</uni-list> + +``` + +### 左侧显示略缩图、图标 + +- 设置 `thumb` 属性 ,可以在列表左侧显示略缩图 +- 设置 `show-extra-icon` 属性,并指定 `extra-icon` 可以在左侧显示图标 + +```html + <uni-list> + <uni-list-item title="列表左侧带略缩图" note="列表描述信息" thumb="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" + thumb-size="lg" rightText="右侧文字"></uni-list-item> + <uni-list-item :show-extra-icon="true" :extra-icon="extraIcon1" title="列表左侧带扩展图标" ></uni-list-item> +</uni-list> +``` + +### 开启点击反馈和右侧箭头 +- 设置 `clickable` 为 `true` ,则表示这是一个可点击的列表,会默认给一个点击效果,并可以监听 `click` 事件 +- 设置 `link` 属性,会自动开启点击反馈,并给列表右侧添加一个箭头 +- 设置 `to` 属性,可以跳转页面,`link` 的值表示跳转方式,如果不指定,默认为 `navigateTo` + +```html + +<uni-list> + <uni-list-item title="开启点击反馈" clickable @click="onClick" ></uni-list-item> + <uni-list-item title="默认 navigateTo 方式跳转页面" link to="/pages/vue/index/index" @click="onClick($event,1)" ></uni-list-item> + <uni-list-item title="reLaunch 方式跳转页面" link="reLaunch" to="/pages/vue/index/index" @click="onClick($event,1)" ></uni-list-item> +</uni-list> + +``` + + +### 聊天列表示例 +- 设置 `clickable` 为 `true` ,则表示这是一个可点击的列表,会默认给一个点击效果,并可以监听 `click` 事件 +- 设置 `link` 属性,会自动开启点击反馈,`link` 的值表示跳转方式,如果不指定,默认为 `navigateTo` +- 设置 `to` 属性,可以跳转页面 +- `time` 属性,通常会设置成时间显示,但是这个属性不仅仅可以设置时间,你可以传入任何文本,注意文本长度可能会影响显示 +- `avatar` 和 `avatarList` 属性同时只会有一个生效,同时设置的话,`avatarList` 属性的长度大于1 ,`avatar` 属性将失效 +- 可以通过默认插槽自定义列表右侧内容 + +```html + +<uni-list> + <uni-list :border="true"> + <!-- 显示圆形头像 --> + <uni-list-chat :avatar-circle="true" title="uni-app" avatar="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" note="您收到一条新的消息" time="2020-02-02 20:20" ></uni-list-chat> + <!-- 右侧带角标 --> + <uni-list-chat title="uni-app" avatar="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" note="您收到一条新的消息" time="2020-02-02 20:20" badge-text="12" :badge-style="{backgroundColor:'#FF80AB'}"></uni-list-chat> + <!-- 头像显示圆点 --> + <uni-list-chat title="uni-app" avatar="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" note="您收到一条新的消息" time="2020-02-02 20:20" badge-positon="left" badge-text="dot"></uni-list-chat> + <!-- 头像显示角标 --> + <uni-list-chat title="uni-app" avatar="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" note="您收到一条新的消息" time="2020-02-02 20:20" badge-positon="left" badge-text="99"></uni-list-chat> + <!-- 显示多头像 --> + <uni-list-chat title="uni-app" :avatar-list="avatarList" note="您收到一条新的消息" time="2020-02-02 20:20" badge-positon="left" badge-text="dot"></uni-list-chat> + <!-- 自定义右侧内容 --> + <uni-list-chat title="uni-app" :avatar-list="avatarList" note="您收到一条新的消息" time="2020-02-02 20:20" badge-positon="left" badge-text="dot"> + <view class="chat-custom-right"> + <text class="chat-custom-text">刚刚</text> + <!-- 需要使用 uni-icons 请自行引入 --> + <uni-icons type="star-filled" color="#999" size="18"></uni-icons> + </view> + </uni-list-chat> + </uni-list> +</uni-list> + +``` + +```javascript + +export default { + components: {}, + data() { + return { + avatarList: [{ + url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png' + }, { + url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png' + }, { + url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png' + }] + } + } +} + +``` + + +```css + +.chat-custom-right { + flex: 1; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + justify-content: space-between; + align-items: flex-end; +} + +.chat-custom-text { + font-size: 12px; + color: #999; +} + +``` + +## API + +### List Props + +属性名 |类型 |默认值 | 说明 +:-: |:-: |:-: | :-: +border |Boolean |true | 是否显示边框 + + +### ListItem Props + +属性名 |类型 |默认值 | 说明 +:-: |:-: |:-: | :-: +title |String |- | 标题 +note |String |- | 描述 +ellipsis |Number |0 | title 是否溢出隐藏,可选值,0:默认; 1:显示一行; 2:显示两行;【nvue 暂不支持】 +thumb |String |- | 左侧缩略图,若thumb有值,则不会显示扩展图标 +thumbSize |String |medium | 略缩图尺寸,可选值,lg:大图; medium:一般; sm:小图; +showBadge |Boolean |false | 是否显示数字角标 +badgeText |String |- | 数字角标内容 +badgeType |String |- | 数字角标类型,参考[uni-icons](https://ext.dcloud.net.cn/plugin?id=21) +badgeStyle |Object |- | 数字角标样式,使用uni-badge的custom-style参数 +rightText |String |- | 右侧文字内容 +disabled |Boolean |false | 是否禁用 +showArrow |Boolean |true | 是否显示箭头图标 +link |String |navigateTo | 新页面跳转方式,可选值见下表 +to |String |- | 新页面跳转地址,如填写此属性,click 会返回页面是否跳转成功 +clickable |Boolean |false | 是否开启点击反馈 +showSwitch |Boolean |false | 是否显示Switch +switchChecked |Boolean |false | Switch是否被选中 +showExtraIcon |Boolean |false | 左侧是否显示扩展图标 +extraIcon |Object |- | 扩展图标参数,格式为 ``{color: '#4cd964',size: '22',type: 'spinner'}``,参考 [uni-icons](https://ext.dcloud.net.cn/plugin?id=28) +direction | String |row | 排版方向,可选值,row:水平排列; column:垂直排列; 3个插槽是水平排还是垂直排,也受此属性控制 + + +#### Link Options + +属性名 | 说明 +:-: | :-: +navigateTo | 同 uni.navigateTo() +redirectTo | 同 uni.reLaunch() +reLaunch | 同 uni.reLaunch() +switchTab | 同 uni.switchTab() + +### ListItem Events + +事件称名 |说明 |返回参数 +:-: |:-: |:-: +click |点击 uniListItem 触发事件,需开启点击反馈 |- +switchChange |点击切换 Switch 时触发,需显示 switch |e={value:checked} + + + +### ListItem Slots + +名称 | 说明 +:-: | :-: +header | 左/上内容插槽,可完全自定义默认显示 +body | 中间内容插槽,可完全自定义中间内容 +footer | 右/下内容插槽,可完全自定义右侧内容 + + +> **通过插槽扩展** +> 需要注意的是当使用插槽时,内置样式将会失效,只保留排版样式,此时的样式需要开发者自己实现 +> 如果 `uni-list-item` 组件内置属性样式无法满足需求,可以使用插槽来自定义uni-list-item里的内容。 +> uni-list-item提供了3个可扩展的插槽:`header`、`body`、`footer` +> - 当 `direction` 属性为 `row` 时表示水平排列,此时 `header` 表示列表的左边部分,`body` 表示列表的中间部分,`footer` 表示列表的右边部分 +> - 当 `direction` 属性为 `column` 时表示垂直排列,此时 `header` 表示列表的上边部分,`body` 表示列表的中间部分,`footer` 表示列表的下边部分 +> 开发者可以只用1个插槽,也可以3个一起使用。在插槽中可自主编写view标签,实现自己所需的效果。 + + +**示例** + +```html +<uni-list> + <uni-list-item title="自定义右侧插槽" note="列表描述信息" link> + <template slot="header"> + <image class="slot-image" src="/static/logo.png" mode="widthFix"></image> + </template> + </uni-list-item> + <uni-list-item> + <!-- 自定义 header --> + <view slot="header" class="slot-box"><image class="slot-image" src="/static/logo.png" mode="widthFix"></image></view> + <!-- 自定义 body --> + <text slot="body" class="slot-box slot-text">自定义插槽</text> + <!-- 自定义 footer--> + <template slot="footer"> + <image class="slot-image" src="/static/logo.png" mode="widthFix"></image> + </template> + </uni-list-item> +</uni-list> +``` + + + + + +### ListItemChat Props + +属性名 |类型 |默认值 | 说明 +:-: |:-: |:-: | :-: +title |String |- | 标题 +note |String |- | 描述 +clickable |Boolean |false | 是否开启点击反馈 +badgeText |String |- | 数字角标内容,设置为 `dot` 将显示圆点 +badgePositon |String |right | 角标位置 +link |String |navigateTo | 是否展示右侧箭头并开启点击反馈,可选值见下表 +clickable |Boolean |false | 是否开启点击反馈 +to |String |- | 跳转页面地址,如填写此属性,click 会返回页面是否跳转成功 +time |String |- | 右侧时间显示 +avatarCircle |Boolean |false | 是否显示圆形头像 +avatar |String |- | 头像地址,avatarCircle 不填时生效 +avatarList |Array |- | 头像组,格式为 [{url:''}] + +#### Link Options + +属性名 | 说明 +:-: | :-: +navigateTo | 同 uni.navigateTo() +redirectTo | 同 uni.reLaunch() +reLaunch | 同 uni.reLaunch() +switchTab | 同 uni.switchTab() + +### ListItemChat Slots + +名称 | 说明 +:- | :- +default | 自定义列表右侧内容(包括时间和角标显示) + +### ListItemChat Events +事件称名 | 说明 | 返回参数 +:-: | :-: | :-: +@click | 点击 uniListChat 触发事件 | {data:{}} ,如有 to 属性,会返回页面跳转信息 + + + + + + +## 基于uni-list扩展的页面模板 + +通过扩展插槽,可实现多种常见样式的列表 + +**新闻列表类** + +1. 云端一体混合布局:[https://ext.dcloud.net.cn/plugin?id=2546](https://ext.dcloud.net.cn/plugin?id=2546) +2. 云端一体垂直布局,大图模式:[https://ext.dcloud.net.cn/plugin?id=2583](https://ext.dcloud.net.cn/plugin?id=2583) +3. 云端一体垂直布局,多行图文混排:[https://ext.dcloud.net.cn/plugin?id=2584](https://ext.dcloud.net.cn/plugin?id=2584) +4. 云端一体垂直布局,多图模式:[https://ext.dcloud.net.cn/plugin?id=2585](https://ext.dcloud.net.cn/plugin?id=2585) +5. 云端一体水平布局,左图右文:[https://ext.dcloud.net.cn/plugin?id=2586](https://ext.dcloud.net.cn/plugin?id=2586) +6. 云端一体水平布局,左文右图:[https://ext.dcloud.net.cn/plugin?id=2587](https://ext.dcloud.net.cn/plugin?id=2587) +7. 云端一体垂直布局,无图模式,主标题+副标题:[https://ext.dcloud.net.cn/plugin?id=2588](https://ext.dcloud.net.cn/plugin?id=2588) + +**商品列表类** + +1. 云端一体列表/宫格视图互切:[https://ext.dcloud.net.cn/plugin?id=2651](https://ext.dcloud.net.cn/plugin?id=2651) +2. 云端一体列表(宫格模式):[https://ext.dcloud.net.cn/plugin?id=2671](https://ext.dcloud.net.cn/plugin?id=2671) +3. 云端一体列表(列表模式):[https://ext.dcloud.net.cn/plugin?id=2672](https://ext.dcloud.net.cn/plugin?id=2672) + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/list/list](https://hellouniapp.dcloud.net.cn/pages/extUI/list/list) \ No newline at end of file diff --git a/uni_modules/uni-load-more/changelog.md b/uni_modules/uni-load-more/changelog.md new file mode 100644 index 0000000..667abdb --- /dev/null +++ b/uni_modules/uni-load-more/changelog.md @@ -0,0 +1,25 @@ +## 1.3.6(2024-10-15) +- 修复 微信小程序中的getSystemInfo警告 +## 1.3.5(2024-10-12) +- 修复 微信小程序中的getSystemInfo警告 +## 1.3.4(2024-10-12) +- 修复 微信小程序中的getSystemInfo警告 +## 1.3.3(2022-01-20) +- 新增 showText属性 ,是否显示文本 +## 1.3.2(2022-01-19) +- 修复 nvue 平台下不显示文本的bug +## 1.3.1(2022-01-19) +- 修复 微信小程序平台样式选择器报警告的问题 +## 1.3.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-load-more](https://uniapp.dcloud.io/component/uniui/uni-load-more) +## 1.2.1(2021-08-24) +- 新增 支持国际化 +## 1.2.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.8(2021-05-12) +- 新增 组件示例地址 +## 1.1.7(2021-03-30) +- 修复 uni-load-more 在首页使用时,h5 平台报 'uni is not defined' 的 bug +## 1.1.6(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-load-more/components/uni-load-more/i18n/en.json b/uni_modules/uni-load-more/components/uni-load-more/i18n/en.json new file mode 100644 index 0000000..a4f14a5 --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/i18n/en.json @@ -0,0 +1,5 @@ +{ + "uni-load-more.contentdown": "Pull up to show more", + "uni-load-more.contentrefresh": "loading...", + "uni-load-more.contentnomore": "No more data" +} diff --git a/uni_modules/uni-load-more/components/uni-load-more/i18n/index.js b/uni_modules/uni-load-more/components/uni-load-more/i18n/index.js new file mode 100644 index 0000000..de7509c --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json b/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json new file mode 100644 index 0000000..f15d510 --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json @@ -0,0 +1,5 @@ +{ + "uni-load-more.contentdown": "上拉显示更多", + "uni-load-more.contentrefresh": "正在加载...", + "uni-load-more.contentnomore": "没有更多数据了" +} diff --git a/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json b/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json new file mode 100644 index 0000000..a255c6d --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json @@ -0,0 +1,5 @@ +{ + "uni-load-more.contentdown": "上拉顯示更多", + "uni-load-more.contentrefresh": "正在加載...", + "uni-load-more.contentnomore": "沒有更多數據了" +} diff --git a/uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue b/uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue new file mode 100644 index 0000000..a203417 --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue @@ -0,0 +1,404 @@ +<template> + <view class="uni-load-more" @click="onClick"> + <!-- #ifdef APP-NVUE --> + <loading-indicator v-if="!webviewHide && status === 'loading' && showIcon" + :style="{color: color,width:iconSize+'px',height:iconSize+'px'}" :animating="true" + class="uni-load-more__img uni-load-more__img--nvue"></loading-indicator> + <!-- #endif --> + <!-- #ifdef H5 --> + <svg width="24" height="24" viewBox="25 25 50 50" + v-if="!webviewHide && (iconType==='circle' || iconType==='auto' && platform === 'android') && status === 'loading' && showIcon" + :style="{width:iconSize+'px',height:iconSize+'px'}" + class="uni-load-more__img uni-load-more__img--android-H5"> + <circle cx="50" cy="50" r="20" fill="none" :style="{color:color}" :stroke-width="3"></circle> + </svg> + <!-- #endif --> + <!-- #ifndef APP-NVUE || H5 --> + <view + v-if="!webviewHide && (iconType==='circle' || iconType==='auto' && platform === 'android') && status === 'loading' && showIcon" + :style="{width:iconSize+'px',height:iconSize+'px'}" + class="uni-load-more__img uni-load-more__img--android-MP"> + <view class="uni-load-more__img-icon" :style="{borderTopColor:color,borderTopWidth:iconSize/12}"></view> + <view class="uni-load-more__img-icon" :style="{borderTopColor:color,borderTopWidth:iconSize/12}"></view> + <view class="uni-load-more__img-icon" :style="{borderTopColor:color,borderTopWidth:iconSize/12}"></view> + </view> + <!-- #endif --> + <!-- #ifndef APP-NVUE --> + <view v-else-if="!webviewHide && status === 'loading' && showIcon" + :style="{width:iconSize+'px',height:iconSize+'px'}" class="uni-load-more__img uni-load-more__img--ios-H5"> + <image :src="imgBase64" mode="widthFix"></image> + </view> + <!-- #endif --> + <text v-if="showText" class="uni-load-more__text" + :style="{color: color}">{{ status === 'more' ? contentdownText : status === 'loading' ? contentrefreshText : contentnomoreText }}</text> + </view> +</template> + +<script> + let platform + setTimeout(() => { + // #ifdef MP-WEIXIN + platform = uni.getDeviceInfo().platform + // #endif + // #ifndef MP-WEIXIN + platform = uni.getSystemInfoSync().platform + // #endif + }, 16) + + import { + initVueI18n + } from '@dcloudio/uni-i18n' + import messages from './i18n/index.js' + const { + t + } = initVueI18n(messages) + + /** + * LoadMore 加载更多 + * @description 用于列表中,做滚动加载使用,展示 loading 的各种状态 + * @tutorial https://ext.dcloud.net.cn/plugin?id=29 + * @property {String} status = [more|loading|noMore] loading 的状态 + * @value more loading前 + * @value loading loading中 + * @value noMore 没有更多了 + * @property {Number} iconSize 指定图标大小 + * @property {Boolean} iconSize = [true|false] 是否显示 loading 图标 + * @property {String} iconType = [snow|circle|auto] 指定图标样式 + * @value snow ios雪花加载样式 + * @value circle 安卓唤醒加载样式 + * @value auto 根据平台自动选择加载样式 + * @property {String} color 图标和文字颜色 + * @property {Object} contentText 各状态文字说明,值为:{contentdown: "上拉显示更多",contentrefresh: "正在加载...",contentnomore: "没有更多数据了"} + * @event {Function} clickLoadMore 点击加载更多时触发 + */ + export default { + name: 'UniLoadMore', + emits: ['clickLoadMore'], + props: { + status: { + // 上拉的状态:more-loading前;loading-loading中;noMore-没有更多了 + type: String, + default: 'more' + }, + showIcon: { + type: Boolean, + default: true + }, + iconType: { + type: String, + default: 'auto' + }, + iconSize: { + type: Number, + default: 24 + }, + color: { + type: String, + default: '#777777' + }, + contentText: { + type: Object, + default () { + return { + contentdown: '', + contentrefresh: '', + contentnomore: '' + } + } + }, + showText: { + type: Boolean, + default: true + } + }, + data() { + return { + webviewHide: false, + platform: platform, + imgBase64: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QzlBMzU3OTlEOUM0MTFFOUI0NTZDNERBQURBQzI4RkUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QzlBMzU3OUFEOUM0MTFFOUI0NTZDNERBQURBQzI4RkUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDOUEzNTc5N0Q5QzQxMUU5QjQ1NkM0REFBREFDMjhGRSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDOUEzNTc5OEQ5QzQxMUU5QjQ1NkM0REFBREFDMjhGRSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pt+ALSwAAA6CSURBVHja1FsLkFZVHb98LM+F5bHL8khA1iSeiyQBCRM+YGqKUnnJTDLGI0BGZlKDIU2MMglUiDApEZvSsZnQtBRJtKwQNKQMFYeRDR10WOLd8ljYXdh+v8v5fR3Od+797t1dnOnO/Ofce77z+J//+b/P+ZqtXbs2sJ9MJhNUV1cHJ06cCJo3bx7EPc2aNcvpy7pWrVoF+/fvDyoqKoI2bdoE9fX1F7TjN8a+EXBn/fkfvw942Tf+wYMHg9mzZwfjxo0LDhw4EPa1x2MbFw/fOGfPng1qa2tzcCkILsLDydq2bRsunpOTMM7TD/W/tZDZhPdeKD+yGxHhdu3aBV27dg3OnDlzMVANMheLAO3btw8KCwuDmpoaX5OxbgUIMEq7K8IcPnw4KCsrC/r37x8cP378/4cAXAB3vqSkJMuiDhTkw+XcuXNhOWbMmKBly5YhUT8xArhyFvP0BfwRsAuwxJZJsm/nzp2DTp06he/OU+cZ64K6o0ePBkOHDg2GDx8e6gEbJ5Q/NHNuAJQ1hgBeHUDlR7nVTkY8rQAvAi4z34vR/mPs1FoRsaCgIJThI0eOBC1atEiFGGV+5MiRoS45efJkqFjJFXV1dQuA012m2WcwTw98fy6CqBdsaiIO4CScrGPHjvk4odhavPquRtFWXEC25VgkREKOCh/qDSq+vn37htzD/mZTOmOc5U7zKzBPEedygWshcDyWvs30igAbU+6oyMgJBCFhwQE0fccxN60Ay9iebbjoDh06hMowjQxT4fXq1SskArmHZpkArvixp/kWzHdMeArExSJEaiXIjjRjRJ4DaAGWpibLzXN3Fm1vA5teBgh3j1Rv3bp1YgKwPdmf2p9zcyNYYgPKMfY0T5f5nNYdw158nJ8QawW4CLKwiOBSEgO/hok2eBydR+3dYH+PLxA5J8Vv0KBBwenTp0P2JWAx6+yFEBfs8lMY+y0SWMBNI9E4ThKi58VKTg3FQZS1RQF1cz27eC0QHMu+3E0SkUowjhVt5VdaWhp07949ZHv2Qd1EjDXM2cla1M0nl3GxAs3J9yREzyTdFVKVFOaE9qRA8GM0WebRuo9JGZKA7Mv2SeS/Z8+eoQ9BArMfFrLGo6jvxbhHbJZnKX2Rzz1O7QhJJ9Cs2ZMaWIyq/zhdeqPNfIoHd58clIQD+JSXl4dKlyIAuBdVXZwFVWKspSSoxE++h8x4k3uCnEhE4I5KwRiFWGOU0QWKiCYLbdoRMRKAu2kQ9vkfLU6dOhX06NEjlH+yMRZSinnuyWnYosVcji8CEA/6Cg2JF+IIUBqnGKUTCNwtwBN4f89RiK1R96DEgO2o0NDmtEdvVFdVVYV+P3UAPUEs6GFwV3PHmXkD4vh74iDFJysVI/MlaQhwKeBNTLYX5VuA8T4/gZxA4MRGFxDB6R7OmYPfyykGRJbyie+XnGYnQIC/coH9+vULiYrxrkL9ZA9+0ykaHIfEpM7ge8TiJ2CsHYwyMfafAF1yCGBHYIbCVDjDjKt7BeB51D+LgQa6OkG7IDYEEtvQ7lnXLKLtLdLuJBpE4gPUXcW2+PkZwOex+4cGDhwYDBkyRL7/HFcEwUGPo/8uWRUpYnfxGHco8HkewLHLyYmAawAPuIFZxhOpDfJQ8gbUv41yORAptMWBNr6oqMhWird5+u+iHmBb2nhjDV7HWBNQTgK8y11l5NetWzc5ULscAtSj7nbNI0skhWeUZCc0W4nyH/jO4Vz0u1IeYhbk4AiwM6tjxIWByHsoZ9qcIBPJd/y+DwPfBESOmCa/QF3WiZHucLlEDpNxcNhmheEOPgdQNx6/VZFQzFZ5TN08AHXQt2Ii3EdyFuUsPtTcGPhW5iMiCNELvz+Gdn9huG4HUJaW/w3g0wxV0XaG7arG2WeKiUWYM4Y7GO5ezshTARbbWGw/DvXkpp/ivVvE0JVoMxN4rpGzJMhE5Pl+xlATsDIqikP9F9D2z3h9nOksEUFhK+qO4rcPkoalMQ/HqJLIyb3F3JdjrCcw1yZ8joyJLR5gCo54etlag7qIoeNh1N1BRYj3DTFJ0elotxPlVzkGuYAmL0VSJVGAJA41c4Z6A3BzTLfn0HYwYKEI6CUAMzZEWvLsIcQOo1AmmyyM72nHJCfYsogflGV6jEk9vyQZXSuq6w4c16NsGcGZbwOPr+H1RkOk2LEzjNepxQkihHSCQ4ynAYNRx2zMKV92CQMWqj8J0BRE8EShxRFN6YrfCRhC0x3r/Zm4IbQCcmJoV0kMamllccR6FjHqUC5F2R/wS2dcymOlfAKOS4KmzQb5cpNC2MC7JhVn5wjXoJ44rYhLh8n0eXOCorJxa7POjbSlCGVczr34/RsAmrcvo9s+wGp3tzVhntxiXiJ4nvEYb4FJkf0O8HocAePmLvCxnL0AORraVekJk6TYjDabRVXfRE2lCN1h6ZQRN1+InUbsCpKwoBZHh0dODN9JBCUffItXxEavTQkUtnfTVAplCWL3JISz29h4NjotnuSsQKJCk8dF+kJR6RARjrqFVmfPnj3ZbK8cIJ0msd6jgHPGtfVTQ8VLmlvh4mct9sobRmPic0DyDQQnx/NlfYUgyz59+oScsH379pAwXABD32nTpoUHIToESeI5mnbE/UqDdyLcafEBf2MCqgC7NwxIbMREJQ0g4D4sfJwnD+AmRrII05cfMWJE+L1169bQr+fip06dGp4oJ83lmYd5wj/EmMa4TaHivo4EeCguYZBnkB5g2aWA69OIEnUHOaGysjIYMGBAMGnSpODYsWPZwCpFmm4lNq+4gSLQA7jcX8DwtjEyRC8wjabnXEx9kfWnTJkSJkAo90xpJVV+FmcVNeYAF5zWngS4C4O91MBxmAv8blLEpbjI5sz9MTdAhcgkCT1RO8mZkAjfiYpTEvStAS53Uw1vAiUGgZ3GpuQEYvoiBqlIan7kSDHnTwJQFNiPu0+5VxCVYhcZIjNrdXUDdp+Eq5AZ3Gkg8QAyVZRZIk4Tl4QAbF9cXJxNYZMAtAokgs4BrNxEpCtteXg7DDTMDKYNSuQdKsnJBek7HxewvxaosWxLYXtw+cJp18217wql4aKCfBNoEu0O5VU+PhctJ0YeXD4C6JQpyrlpSLTojpGGGN5YwNziChdIZLk4lvLcFJ9jMX3QdiImY9bmGQU+TRUL5CHITTRlgF8D9ouD1MfmLoEPl5xokIumZ2cfgMpHt47IW9N64Hsh7wQYYjyIugWuF5fCqYncXRd5vPMWyizzvhi/32+nvG0dZc9vR6fZOu0md5e+uC408FvKSIOZwXlGvxPv95izA2Vtvg1xKFWARI+vMX66HUhpQQb643uW1bSjuTWyw2SBvDrBvjFic1eGGlz5esq3ko9uSIlBRqPuFcCv8F4WIcN12nVaBd0SaYwI6PDDImR11JkqgHcPmQssjxIn6bUshygDFJUTxPMpHk+jfjPgupgdnYV2R/g7xSjtpah8RJBewhwf0gGK6XI92u4wXFEU40afJ4DN4h5LcAd+40HI3JgJecuT0c062W0i2hQJUTcxan3/CMW1PF2K6bbA+Daz4xRs1D3Br1Cm0OihKCqizW78/nXAF/G5TXrEcVzaNMH6CyMswqsAHqDyDLEyou8lwOXnKF8DjI6KjV3KzMBiXkDH8ij/H214J5A596ekrZ3F0zXlWeL7+P5eUrNo3/QwC15uxthuzidy7DzKRwEDaAViiDgKbTbz7CJnzo0bN7pIfIiid8SuPwn25o3QCmpnyjlZkyxPP8EomCJzrGb7GJMx7tNsq4MT2xMUYaiErZOluTzKsnz3gwCeCZyVRZJfYplNEokEjwrPtxlxjeYAk+F1F74VAzPxQRNYYdtpOUvWs8J1sGhBJMNsb7igN8plJs1eSmLIhLKE4rvaCX27gOhLpLOsIzJ7qn/i+wZzcvSOZ23/du8TZjwV8zHIXoP4R3ifBxiFz1dcVpa3aPntPE+c6TmIWE9EtcMmAcPdWAhYhAXxcLOQi9L1WhD1Sc8p1d2oL7XGiRKp8F4A2i8K/nfI+y/gsTDJ/YC/8+AD5Uh04KHiGl+cIFPnBDDrPMjwRGkLXyxO4VGbfQWnDH2v0bVWE3C9QOXlepbgjEfIJQI6XDG3z5ahD9cw2pS78ipB85wyScNTvsVzlzzhL8/jRrnmVjfFJK/m3m4nj9vbgQTguT8XZTjsm672R5uJKEaQmBI/c58gyus8ZDagLpEVSJBIyHp4jn++xqPV71OgQgJYEWOtZ/haxRtKmWOBu8xdBLftWltsY84zE6WIEy/eIOWL+BaayMx+KHtL7EAkqdNDLiEXmEMUHniedtJqg9HmZtfvt26vNi0BdG3Ft3g8ZOf7PAu59TxtzivLNIekyi+wD1i8CuUiD9FXAa8C+/xS3JPmZnomyc7H+fb4/Se0bk41Fel621r4cgVxbq91V4jVqwB7HTe2M7jgB+QWHavZkDRPmZcASoZEmBx6i75bGjPcMdL4/VKGFAGWZkGzPG0XAbdL9A81G5LOmUnC9hHKJeO7dcUMjblSl12867ElFTtaGl20xvvLGPdVz/8TVuU7y0x1PG7vtNg24oz9Uo/Z412++VFWI7Fcog9tu9Lm6gvRmIPv9x1xmQAu6RDkXtbOtlGEmpgD5Nvnyc0dcv0EE6cfdi1HmhMf9wDF3k3gtRvEedhxjpgfqPb9PU9iEJHnyOUA7bQUXh6kq/D7l2iTjWv7XOD530BDr8jIrus+srXjt4MzumJMHuTsBa63YKE1+RR5lBjEikCCnWKWiHdzOgKO+nRIBAF88za/IFmJ3eMZov4CYxGBabcpGL8EYx+SeMXJeRwHNsV/h+vdxeuhEpN3ZyNY78Gm2fknJxVGhyjixPiQvVkNzT1elD9Py/aTAL64Hb9vcYmC9zfdXdT/C1LeGbg4rnBaAihDFJH12W5ulfNCNe/xTsP3bp8ikzJs5BF+5PNfAQYAPaseTdsEcaYAAAAASUVORK5CYII=' + } + }, + computed: { + iconSnowWidth() { + return (Math.floor(this.iconSize / 24) || 1) * 2 + }, + contentdownText() { + return this.contentText.contentdown || t("uni-load-more.contentdown") + }, + contentrefreshText() { + return this.contentText.contentrefresh || t("uni-load-more.contentrefresh") + }, + contentnomoreText() { + return this.contentText.contentnomore || t("uni-load-more.contentnomore") + } + }, + mounted() { + // #ifdef APP-PLUS + var pages = getCurrentPages(); + var page = pages[pages.length - 1]; + var currentWebview = page.$getAppWebview(); + currentWebview.addEventListener('hide', () => { + this.webviewHide = true + }) + currentWebview.addEventListener('show', () => { + this.webviewHide = false + }) + // #endif + }, + methods: { + onClick() { + this.$emit('clickLoadMore', { + detail: { + status: this.status, + } + }) + } + } + } +</script> + +<style lang="scss" > + .uni-load-more { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + height: 40px; + align-items: center; + justify-content: center; + } + + .uni-load-more__text { + font-size: 14px; + margin-left: 8px; + } + + .uni-load-more__img { + width: 24px; + height: 24px; + // margin-right: 8px; + } + + .uni-load-more__img--nvue { + color: #666666; + } + + .uni-load-more__img--android, + .uni-load-more__img--ios { + width: 24px; + height: 24px; + transform: rotate(0deg); + } + + /* #ifndef APP-NVUE */ + .uni-load-more__img--android { + animation: loading-ios 1s 0s linear infinite; + } + + @keyframes loading-android { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } + } + + .uni-load-more__img--ios-H5 { + position: relative; + animation: loading-ios-H5 1s 0s step-end infinite; + } + + .uni-load-more__img--ios-H5 image { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + } + + @keyframes loading-ios-H5 { + 0% { + transform: rotate(0deg); + } + + 8% { + transform: rotate(30deg); + } + + 16% { + transform: rotate(60deg); + } + + 24% { + transform: rotate(90deg); + } + + 32% { + transform: rotate(120deg); + } + + 40% { + transform: rotate(150deg); + } + + 48% { + transform: rotate(180deg); + } + + 56% { + transform: rotate(210deg); + } + + 64% { + transform: rotate(240deg); + } + + 73% { + transform: rotate(270deg); + } + + 82% { + transform: rotate(300deg); + } + + 91% { + transform: rotate(330deg); + } + + 100% { + transform: rotate(360deg); + } + } + + /* #endif */ + + /* #ifdef H5 */ + .uni-load-more__img--android-H5 { + animation: loading-android-H5-rotate 2s linear infinite; + transform-origin: center center; + } + + .uni-load-more__img--android-H5 circle { + display: inline-block; + animation: loading-android-H5-dash 1.5s ease-in-out infinite; + stroke: currentColor; + stroke-linecap: round; + } + + @keyframes loading-android-H5-rotate { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } + } + + @keyframes loading-android-H5-dash { + 0% { + stroke-dasharray: 1, 200; + stroke-dashoffset: 0; + } + + 50% { + stroke-dasharray: 90, 150; + stroke-dashoffset: -40; + } + + 100% { + stroke-dasharray: 90, 150; + stroke-dashoffset: -120; + } + } + + /* #endif */ + + /* #ifndef APP-NVUE || H5 */ + .uni-load-more__img--android-MP { + position: relative; + width: 24px; + height: 24px; + transform: rotate(0deg); + animation: loading-ios 1s 0s ease infinite; + } + + .uni-load-more__img--android-MP .uni-load-more__img-icon { + position: absolute; + box-sizing: border-box; + width: 100%; + height: 100%; + border-radius: 50%; + border: solid 2px transparent; + border-top: solid 2px #777777; + transform-origin: center; + } + + .uni-load-more__img--android-MP .uni-load-more__img-icon:nth-child(1) { + animation: loading-android-MP-1 1s 0s linear infinite; + } + + .uni-load-more__img--android-MP .uni-load-more__img-icon:nth-child(2) { + animation: loading-android-MP-2 1s 0s linear infinite; + } + + .uni-load-more__img--android-MP .uni-load-more__img-icon:nth-child(3) { + animation: loading-android-MP-3 1s 0s linear infinite; + } + + @keyframes loading-android { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } + } + + @keyframes loading-android-MP-1 { + 0% { + transform: rotate(0deg); + } + + 50% { + transform: rotate(90deg); + } + + 100% { + transform: rotate(360deg); + } + } + + @keyframes loading-android-MP-2 { + 0% { + transform: rotate(0deg); + } + + 50% { + transform: rotate(180deg); + } + + 100% { + transform: rotate(360deg); + } + } + + @keyframes loading-android-MP-3 { + 0% { + transform: rotate(0deg); + } + + 50% { + transform: rotate(270deg); + } + + 100% { + transform: rotate(360deg); + } + } + + /* #endif */ +</style> diff --git a/uni_modules/uni-load-more/package.json b/uni_modules/uni-load-more/package.json new file mode 100644 index 0000000..cf44bff --- /dev/null +++ b/uni_modules/uni-load-more/package.json @@ -0,0 +1,84 @@ +{ + "id": "uni-load-more", + "displayName": "uni-load-more 加载更多", + "version": "1.3.6", + "description": "LoadMore 组件,常用在列表里面,做滚动加载使用。", + "keywords": [ + "uni-ui", + "uniui", + "加载更多", + "load-more" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-load-more/readme.md b/uni_modules/uni-load-more/readme.md new file mode 100644 index 0000000..54dc1fa --- /dev/null +++ b/uni_modules/uni-load-more/readme.md @@ -0,0 +1,14 @@ + + +### LoadMore 加载更多 +> **组件名:uni-load-more** +> 代码块: `uLoadMore` + + +用于列表中,做滚动加载使用,展示 loading 的各种状态。 + + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-load-more) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-nav-bar/changelog.md b/uni_modules/uni-nav-bar/changelog.md new file mode 100644 index 0000000..e3efe12 --- /dev/null +++ b/uni_modules/uni-nav-bar/changelog.md @@ -0,0 +1,57 @@ +## 1.3.14(2024-10-15) +- 修复 微信小程序中的getSystemInfo警告 +## 1.3.13(2024-10-12) +- 修复 微信小程序中的getSystemInfo警告 +## 1.3.12(2024-10-12) +- 修复 微信小程序中的getSystemInfo警告 +## 1.3.11(2023-03-29) +- 修复 自定义状态栏高度闪动BUG +## 1.3.10(2023-03-29) +- 修复 暗黑模式下边线颜色错误的bug +## 1.3.9(2022-10-13) +- 修复 条件编译错误的bug +## 1.3.8(2022-10-12) +- 修复 nvue 环境 fixed 为 true 的情况下,无法置顶的 bug +## 1.3.7(2022-08-11) +- 修复 nvue 环境下 fixed 为 true 的情况下,无法置顶的 bug +## 1.3.6(2022-06-30) +- 修复 组件示例中插槽用法无法显示内容的bug +## 1.3.5(2022-05-24) +- 新增 stat 属性 ,可开启统计title 上报 ,仅使用了title 属性且项目开启了uni统计生效 +## 1.3.4(2022-01-24) +- 更新 组件示例 +## 1.3.3(2022-01-24) +- 新增 left-width/right-width属性 ,可修改左右两侧的宽度 +## 1.3.2(2022-01-18) +- 修复 在vue下,标题不垂直居中的bug +## 1.3.1(2022-01-18) +- 修复 height 属性类型错误 +## 1.3.0(2022-01-18) +- 新增 height 属性,可修改组件高度 +- 新增 dark 属性可可开启暗黑模式 +- 优化 标题字数过多显示省略号 +- 优化 插槽,插入内容可完全覆盖 +## 1.2.1(2022-01-10) +- 修复 color 属性不生效的bug +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-nav-bar](https://uniapp.dcloud.io/component/uniui/uni-nav-bar) +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.11(2021-05-12) +- 新增 组件示例地址 +## 1.0.10(2021-04-30) +- 修复 在nvue下fixed为true,宽度不能撑满的Bug +## 1.0.9(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.8(2021-04-14) +- uni-ui 修复 uni-nav-bar 当 fixed 属性为 true 时铺不满屏幕的 bug + +## 1.0.7(2021-02-25) +- 修复 easycom 下,找不到 uni-status-bar 的bug + +## 1.0.6(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.5(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue b/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue new file mode 100644 index 0000000..46806c1 --- /dev/null +++ b/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue @@ -0,0 +1,357 @@ +<template> + <view class="uni-navbar" :class="{'uni-dark':dark, 'uni-nvue-fixed': fixed}"> + <view class="uni-navbar__content" :class="{ 'uni-navbar--fixed': fixed, 'uni-navbar--shadow': shadow, 'uni-navbar--border': border }" + :style="{ 'background-color': themeBgColor, 'border-bottom-color':themeColor }" > + <status-bar v-if="statusBar" /> + <view :style="{ color: themeColor,backgroundColor: themeBgColor ,height:navbarHeight}" + class="uni-navbar__header"> + <view @tap="onClickLeft" class="uni-navbar__header-btns uni-navbar__header-btns-left" + :style="{width:leftIconWidth}"> + <slot name="left"> + <view class="uni-navbar__content_view" v-if="leftIcon.length > 0"> + <uni-icons :color="themeColor" :type="leftIcon" size="20" /> + </view> + <view :class="{ 'uni-navbar-btn-icon-left': !leftIcon.length > 0 }" class="uni-navbar-btn-text" + v-if="leftText.length"> + <text :style="{ color: themeColor, fontSize: '12px' }">{{ leftText }}</text> + </view> + </slot> + </view> + <view class="uni-navbar__header-container " @tap="onClickTitle"> + <slot> + <view class="uni-navbar__header-container-inner" v-if="title.length>0"> + <text class="uni-nav-bar-text uni-ellipsis-1" + :style="{color: themeColor }">{{ title }}</text> + </view> + </slot> + </view> + <view @click="onClickRight" class="uni-navbar__header-btns uni-navbar__header-btns-right" + :style="{width:rightIconWidth}"> + <slot name="right"> + <view v-if="rightIcon.length"> + <uni-icons :color="themeColor" :type="rightIcon" size="22" /> + </view> + <view class="uni-navbar-btn-text" v-if="rightText.length && !rightIcon.length"> + <text class="uni-nav-bar-right-text" :style="{ color: themeColor}">{{ rightText }}</text> + </view> + </slot> + </view> + </view> + </view> + <!-- #ifndef APP-NVUE --> + <view class="uni-navbar__placeholder" v-if="fixed"> + <status-bar v-if="statusBar" /> + <view class="uni-navbar__placeholder-view" :style="{ height:navbarHeight}" /> + </view> + <!-- #endif --> + </view> +</template> + +<script> + import statusBar from "./uni-status-bar.vue"; + const getVal = (val) => typeof val === 'number' ? val + 'px' : val; + + /** + * + * + * NavBar 自定义导航栏 + * @description 导航栏组件,主要用于头部导航 + * @tutorial https://ext.dcloud.net.cn/plugin?id=52 + * @property {Boolean} dark 开启黑暗模式 + * @property {String} title 标题文字 + * @property {String} leftText 左侧按钮文本 + * @property {String} rightText 右侧按钮文本 + * @property {String} leftIcon 左侧按钮图标(图标类型参考 [Icon 图标](http://ext.dcloud.net.cn/plugin?id=28) type 属性) + * @property {String} rightIcon 右侧按钮图标(图标类型参考 [Icon 图标](http://ext.dcloud.net.cn/plugin?id=28) type 属性) + * @property {String} color 图标和文字颜色 + * @property {String} backgroundColor 导航栏背景颜色 + * @property {Boolean} fixed = [true|false] 是否固定顶部 + * @property {Boolean} statusBar = [true|false] 是否包含状态栏 + * @property {Boolean} shadow = [true|false] 导航栏下是否有阴影 + * @property {Boolean} stat 是否开启统计标题上报 + * @event {Function} clickLeft 左侧按钮点击时触发 + * @event {Function} clickRight 右侧按钮点击时触发 + * @event {Function} clickTitle 中间标题点击时触发 + */ + export default { + name: "UniNavBar", + components: { + statusBar + }, + emits: ['clickLeft', 'clickRight', 'clickTitle'], + props: { + dark: { + type: Boolean, + default: false + }, + title: { + type: String, + default: "" + }, + leftText: { + type: String, + default: "" + }, + rightText: { + type: String, + default: "" + }, + leftIcon: { + type: String, + default: "" + }, + rightIcon: { + type: String, + default: "" + }, + fixed: { + type: [Boolean, String], + default: false + }, + color: { + type: String, + default: "" + }, + backgroundColor: { + type: String, + default: "" + }, + statusBar: { + type: [Boolean, String], + default: false + }, + shadow: { + type: [Boolean, String], + default: false + }, + border: { + type: [Boolean, String], + default: true + }, + height: { + type: [Number, String], + default: 44 + }, + leftWidth: { + type: [Number, String], + default: 60 + }, + rightWidth: { + type: [Number, String], + default: 60 + }, + stat: { + type: [Boolean, String], + default: '' + } + }, + computed: { + themeBgColor() { + if (this.dark) { + // 默认值 + if (this.backgroundColor) { + return this.backgroundColor + } else { + return this.dark ? '#333' : '#FFF' + } + } + return this.backgroundColor || '#FFF' + }, + themeColor() { + if (this.dark) { + // 默认值 + if (this.color) { + return this.color + } else { + return this.dark ? '#fff' : '#333' + } + } + return this.color || '#333' + }, + navbarHeight() { + return getVal(this.height) + }, + leftIconWidth() { + return getVal(this.leftWidth) + }, + rightIconWidth() { + return getVal(this.rightWidth) + } + }, + mounted() { + if (uni.report && this.stat && this.title !== '') { + uni.report('title', this.title) + } + }, + methods: { + onClickLeft() { + this.$emit("clickLeft"); + }, + onClickRight() { + this.$emit("clickRight"); + }, + onClickTitle() { + this.$emit("clickTitle"); + } + } + }; +</script> + +<style lang="scss" scoped> + $nav-height: 44px; + + .uni-nvue-fixed { + /* #ifdef APP-NVUE */ + position: sticky; + /* #endif */ + } + .uni-navbar { + // box-sizing: border-box; + } + + .uni-nav-bar-text { + /* #ifdef APP-PLUS */ + font-size: 34rpx; + /* #endif */ + /* #ifndef APP-PLUS */ + font-size: 14px; + /* #endif */ + } + + .uni-nav-bar-right-text { + font-size: 12px; + } + + .uni-navbar__content { + position: relative; + // background-color: #fff; + // box-sizing: border-box; + background-color: transparent; + } + + .uni-navbar__content_view { + // box-sizing: border-box; + } + + .uni-navbar-btn-text { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + justify-content: flex-start; + align-items: center; + line-height: 12px; + } + + .uni-navbar__header { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + padding: 0 10px; + flex-direction: row; + height: $nav-height; + font-size: 12px; + } + + .uni-navbar__header-btns { + /* #ifndef APP-NVUE */ + overflow: hidden; + display: flex; + /* #endif */ + flex-wrap: nowrap; + flex-direction: row; + width: 120rpx; + // padding: 0 6px; + justify-content: center; + align-items: center; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + .uni-navbar__header-btns-left { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + width: 120rpx; + justify-content: flex-start; + align-items: center; + } + + .uni-navbar__header-btns-right { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + // width: 150rpx; + // padding-right: 30rpx; + justify-content: flex-end; + align-items: center; + } + + .uni-navbar__header-container { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex: 1; + padding: 0 10px; + overflow: hidden; + } + + .uni-navbar__header-container-inner { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex: 1; + flex-direction: row; + align-items: center; + justify-content: center; + font-size: 12px; + overflow: hidden; + // box-sizing: border-box; + } + + + .uni-navbar__placeholder-view { + height: $nav-height; + } + + .uni-navbar--fixed { + position: fixed; + z-index: 99; + /* #ifdef H5 */ + left: var(--window-left); + right: var(--window-right); + /* #endif */ + /* #ifndef H5 */ + left: 0; + right: 0; + /* #endif */ + + } + + .uni-navbar--shadow { + box-shadow: 0 1px 6px #ccc; + } + + .uni-navbar--border { + border-bottom-width: 1rpx; + border-bottom-style: solid; + border-bottom-color: #eee; + } + + .uni-ellipsis-1 { + overflow: hidden; + /* #ifndef APP-NVUE */ + white-space: nowrap; + text-overflow: ellipsis; + /* #endif */ + /* #ifdef APP-NVUE */ + lines: 1; + text-overflow: ellipsis; + /* #endif */ + } + + // 暗主题配置 + .uni-dark {} +</style> diff --git a/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue b/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue new file mode 100644 index 0000000..6185446 --- /dev/null +++ b/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue @@ -0,0 +1,30 @@ +<template> + <view :style="{ height: statusBarHeight }" class="uni-status-bar"> + <slot /> + </view> +</template> + +<script> + export default { + name: 'UniStatusBar', + data() { + return { + // #ifdef MP-WEIXIN + statusBarHeight: uni.getWindowInfo().statusBarHeight + 'px', + // #endif + // #ifndef MP-WEIXIN + statusBarHeight: uni.getSystemInfoSync().statusBarHeight + 'px', + // #endif + + } + } + } +</script> + +<style lang="scss" > + .uni-status-bar { + // width: 750rpx; + height: 20px; + // height: var(--status-bar-height); + } +</style> diff --git a/uni_modules/uni-nav-bar/package.json b/uni_modules/uni-nav-bar/package.json new file mode 100644 index 0000000..d9f53b2 --- /dev/null +++ b/uni_modules/uni-nav-bar/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-nav-bar", + "displayName": "uni-nav-bar 自定义导航栏", + "version": "1.3.14", + "description": "自定义导航栏组件,主要用于头部导航。", + "keywords": [ + "uni-ui", + "导航", + "导航栏", + "自定义导航栏" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-nav-bar/readme.md b/uni_modules/uni-nav-bar/readme.md new file mode 100644 index 0000000..3934b32 --- /dev/null +++ b/uni_modules/uni-nav-bar/readme.md @@ -0,0 +1,15 @@ + + +## NavBar 导航栏 +> **组件名:uni-nav-bar** +> 代码块: `uNavBar` + +导航栏组件,主要用于头部导航。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-nav-bar) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + + + + diff --git a/uni_modules/uni-notice-bar/changelog.md b/uni_modules/uni-notice-bar/changelog.md new file mode 100644 index 0000000..ce50674 --- /dev/null +++ b/uni_modules/uni-notice-bar/changelog.md @@ -0,0 +1,20 @@ +## 1.2.2(2023-12-20) +- 修复动态绑定title时,滚动速度不一致的问题 +## 1.2.1(2022-09-05) +- 新增 属性 fontSize,可修改文字大小。 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-notice-bar](https://uniapp.dcloud.io/component/uniui/uni-notice-bar) +## 1.1.1(2021-11-09) +- 新增 提供组件设计资源,组件样式调整 +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.9(2021-05-12) +- 新增 组件示例地址 +## 1.0.8(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.7(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.6(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue b/uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue new file mode 100644 index 0000000..47fb9b3 --- /dev/null +++ b/uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue @@ -0,0 +1,431 @@ +<template> + <view v-if="show" class="uni-noticebar" :style="{ backgroundColor }" @click="onClick"> + <uni-icons v-if="showIcon === true || showIcon === 'true'" class="uni-noticebar-icon" type="sound" + :color="color" :size="fontSize * 1.5" /> + <view ref="textBox" class="uni-noticebar__content-wrapper" + :class="{ + 'uni-noticebar__content-wrapper--scrollable': scrollable, + 'uni-noticebar__content-wrapper--single': !scrollable && (single || moreText) + }" + :style="{ height: scrollable ? fontSize * 1.5 + 'px' : 'auto' }" + > + <view :id="elIdBox" class="uni-noticebar__content" + :class="{ + 'uni-noticebar__content--scrollable': scrollable, + 'uni-noticebar__content--single': !scrollable && (single || moreText) + }" + > + <text :id="elId" ref="animationEle" class="uni-noticebar__content-text" + :class="{ + 'uni-noticebar__content-text--scrollable': scrollable, + 'uni-noticebar__content-text--single': !scrollable && (single || showGetMore) + }" + :style="{ + color: color, + fontSize: fontSize + 'px', + lineHeight: fontSize * 1.5 + 'px', + width: wrapWidth + 'px', + 'animationDuration': animationDuration, + '-webkit-animationDuration': animationDuration, + animationPlayState: webviewHide ? 'paused' : animationPlayState, + '-webkit-animationPlayState': webviewHide ? 'paused' : animationPlayState, + animationDelay: animationDelay, + '-webkit-animationDelay': animationDelay + }" + >{{text}}</text> + </view> + </view> + <view v-if="isShowGetMore" class="uni-noticebar__more uni-cursor-point" + @click="clickMore"> + <text v-if="moreText.length > 0" :style="{ color: moreColor, fontSize: fontSize + 'px' }">{{ moreText }}</text> + <uni-icons v-else type="right" :color="moreColor" :size="fontSize * 1.1" /> + </view> + <view class="uni-noticebar-close uni-cursor-point" v-if="isShowClose"> + <uni-icons type="closeempty" :color="color" :size="fontSize * 1.1" @click="close" /> + </view> + </view> +</template> + +<script> + // #ifdef APP-NVUE + const dom = weex.requireModule('dom'); + const animation = weex.requireModule('animation'); + // #endif + + /** + * NoticeBar 自定义导航栏 + * @description 通告栏组件 + * @tutorial https://ext.dcloud.net.cn/plugin?id=30 + * @property {Number} speed 文字滚动的速度,默认100px/秒 + * @property {String} text 显示文字 + * @property {String} backgroundColor 背景颜色 + * @property {String} color 文字颜色 + * @property {String} moreColor 查看更多文字的颜色 + * @property {String} moreText 设置“查看更多”的文本 + * @property {Boolean} single = [true|false] 是否单行 + * @property {Boolean} scrollable = [true|false] 是否滚动,为true时,NoticeBar为单行 + * @property {Boolean} showIcon = [true|false] 是否显示左侧喇叭图标 + * @property {Boolean} showClose = [true|false] 是否显示左侧关闭按钮 + * @property {Boolean} showGetMore = [true|false] 是否显示右侧查看更多图标,为true时,NoticeBar为单行 + * @event {Function} click 点击 NoticeBar 触发事件 + * @event {Function} close 关闭 NoticeBar 触发事件 + * @event {Function} getmore 点击”查看更多“时触发事件 + */ + + export default { + name: 'UniNoticeBar', + emits: ['click', 'getmore', 'close'], + props: { + text: { + type: String, + default: '' + }, + moreText: { + type: String, + default: '' + }, + backgroundColor: { + type: String, + default: '#FFF9EA' + }, + speed: { + // 默认1s滚动100px + type: Number, + default: 100 + }, + color: { + type: String, + default: '#FF9A43' + }, + fontSize: { + type: Number, + default: 14 + }, + moreColor: { + type: String, + default: '#FF9A43' + }, + single: { + // 是否单行 + type: [Boolean, String], + default: false + }, + scrollable: { + // 是否滚动,添加后控制单行效果取消 + type: [Boolean, String], + default: false + }, + showIcon: { + // 是否显示左侧icon + type: [Boolean, String], + default: false + }, + showGetMore: { + // 是否显示右侧查看更多 + type: [Boolean, String], + default: false + }, + showClose: { + // 是否显示左侧关闭按钮 + type: [Boolean, String], + default: false + } + }, + data() { + const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}` + const elIdBox = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}` + return { + textWidth: 0, + boxWidth: 0, + wrapWidth: '', + webviewHide: false, + // #ifdef APP-NVUE + stopAnimation: false, + // #endif + elId: elId, + elIdBox: elIdBox, + show: true, + animationDuration: 'none', + animationPlayState: 'paused', + animationDelay: '0s' + } + }, + watch:{ + text:function(newValue,oldValue){ + this.initSize(); + } + }, + computed: { + isShowGetMore() { + return this.showGetMore === true || this.showGetMore === 'true' + }, + isShowClose() { + return (this.showClose === true || this.showClose === 'true') + && (this.showGetMore === false || this.showGetMore === 'false') + } + }, + mounted() { + // #ifdef APP-PLUS + var pages = getCurrentPages(); + var page = pages[pages.length - 1]; + var currentWebview = page.$getAppWebview(); + currentWebview.addEventListener('hide', () => { + this.webviewHide = true + }) + currentWebview.addEventListener('show', () => { + this.webviewHide = false + }) + // #endif + this.$nextTick(() => { + this.initSize() + }) + }, + // #ifdef APP-NVUE + beforeDestroy() { + this.stopAnimation = true + }, + // #endif + methods: { + initSize() { + if (this.scrollable) { + // #ifndef APP-NVUE + let query = [], + boxWidth = 0, + textWidth = 0; + let textQuery = new Promise((resolve, reject) => { + uni.createSelectorQuery() + // #ifndef MP-ALIPAY + .in(this) + // #endif + .select(`#${this.elId}`) + .boundingClientRect() + .exec(ret => { + this.textWidth = ret[0].width + resolve() + }) + }) + let boxQuery = new Promise((resolve, reject) => { + uni.createSelectorQuery() + // #ifndef MP-ALIPAY + .in(this) + // #endif + .select(`#${this.elIdBox}`) + .boundingClientRect() + .exec(ret => { + this.boxWidth = ret[0].width + resolve() + }) + }) + query.push(textQuery) + query.push(boxQuery) + Promise.all(query).then(() => { + this.animationDuration = `${this.textWidth / this.speed}s` + this.animationDelay = `-${this.boxWidth / this.speed}s` + setTimeout(() => { + this.animationPlayState = 'running' + }, 1000) + }) + // #endif + // #ifdef APP-NVUE + dom.getComponentRect(this.$refs['animationEle'], (res) => { + let winWidth = uni.getSystemInfoSync().windowWidth + this.textWidth = res.size.width + animation.transition(this.$refs['animationEle'], { + styles: { + transform: `translateX(-${winWidth}px)` + }, + duration: 0, + timingFunction: 'linear', + delay: 0 + }, () => { + if (!this.stopAnimation) { + animation.transition(this.$refs['animationEle'], { + styles: { + transform: `translateX(-${this.textWidth}px)` + }, + timingFunction: 'linear', + duration: (this.textWidth - winWidth) / this.speed * 1000, + delay: 1000 + }, () => { + if (!this.stopAnimation) { + this.loopAnimation() + } + }); + } + }); + }) + // #endif + } + // #ifdef APP-NVUE + if (!this.scrollable && (this.single || this.moreText)) { + dom.getComponentRect(this.$refs['textBox'], (res) => { + this.wrapWidth = res.size.width + }) + } + // #endif + }, + loopAnimation() { + // #ifdef APP-NVUE + animation.transition(this.$refs['animationEle'], { + styles: { + transform: `translateX(0px)` + }, + duration: 0 + }, () => { + if (!this.stopAnimation) { + animation.transition(this.$refs['animationEle'], { + styles: { + transform: `translateX(-${this.textWidth}px)` + }, + duration: this.textWidth / this.speed * 1000, + timingFunction: 'linear', + delay: 0 + }, () => { + if (!this.stopAnimation) { + this.loopAnimation() + } + }); + } + }); + // #endif + }, + clickMore() { + this.$emit('getmore') + }, + close() { + this.show = false; + this.$emit('close') + }, + onClick() { + this.$emit('click') + } + } + } +</script> + +<style lang="scss" scoped> + .uni-noticebar { + /* #ifndef APP-NVUE */ + display: flex; + width: 100%; + box-sizing: border-box; + /* #endif */ + flex-direction: row; + align-items: center; + padding: 10px 12px; + margin-bottom: 10px; + } + + .uni-cursor-point { + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + .uni-noticebar-close { + margin-left: 8px; + margin-right: 5px; + } + + .uni-noticebar-icon { + margin-right: 5px; + } + + .uni-noticebar__content-wrapper { + flex: 1; + flex-direction: column; + overflow: hidden; + } + + .uni-noticebar__content-wrapper--single { + /* #ifndef APP-NVUE */ + line-height: 18px; + /* #endif */ + } + + .uni-noticebar__content-wrapper--single, + .uni-noticebar__content-wrapper--scrollable { + flex-direction: row; + } + + /* #ifndef APP-NVUE */ + .uni-noticebar__content-wrapper--scrollable { + position: relative; + } + + /* #endif */ + + .uni-noticebar__content--scrollable { + /* #ifdef APP-NVUE */ + flex: 0; + /* #endif */ + /* #ifndef APP-NVUE */ + flex: 1; + display: block; + overflow: hidden; + /* #endif */ + } + + .uni-noticebar__content--single { + /* #ifndef APP-NVUE */ + display: flex; + flex: none; + width: 100%; + justify-content: center; + /* #endif */ + } + + .uni-noticebar__content-text { + font-size: 14px; + line-height: 18px; + /* #ifndef APP-NVUE */ + word-break: break-all; + /* #endif */ + } + + .uni-noticebar__content-text--single { + /* #ifdef APP-NVUE */ + lines: 1; + /* #endif */ + /* #ifndef APP-NVUE */ + display: block; + width: 100%; + white-space: nowrap; + /* #endif */ + overflow: hidden; + text-overflow: ellipsis; + } + + .uni-noticebar__content-text--scrollable { + /* #ifdef APP-NVUE */ + lines: 1; + padding-left: 750rpx; + /* #endif */ + /* #ifndef APP-NVUE */ + position: absolute; + display: block; + height: 18px; + line-height: 18px; + white-space: nowrap; + padding-left: 100%; + animation: notice 10s 0s linear infinite both; + animation-play-state: paused; + /* #endif */ + } + + .uni-noticebar__more { + /* #ifndef APP-NVUE */ + display: inline-flex; + /* #endif */ + flex-direction: row; + flex-wrap: nowrap; + align-items: center; + padding-left: 5px; + } + + @keyframes notice { + 100% { + transform: translate3d(-100%, 0, 0); + } + } +</style> diff --git a/uni_modules/uni-notice-bar/package.json b/uni_modules/uni-notice-bar/package.json new file mode 100644 index 0000000..1e9762c --- /dev/null +++ b/uni_modules/uni-notice-bar/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-notice-bar", + "displayName": "uni-notice-bar 通告栏", + "version": "1.2.2", + "description": "NoticeBar 通告栏组件,常用于展示公告信息,可设为滚动公告", + "keywords": [ + "uni-ui", + "uniui", + "通告栏", + "公告", + "跑马灯" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-notice-bar/readme.md b/uni_modules/uni-notice-bar/readme.md new file mode 100644 index 0000000..fb2ede2 --- /dev/null +++ b/uni_modules/uni-notice-bar/readme.md @@ -0,0 +1,13 @@ + + +## NoticeBar 通告栏 +> **组件名:uni-notice-bar** +> 代码块: `uNoticeBar` + + +通告栏组件 。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-notice-bar) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-number-box/changelog.md b/uni_modules/uni-number-box/changelog.md new file mode 100644 index 0000000..adf9221 --- /dev/null +++ b/uni_modules/uni-number-box/changelog.md @@ -0,0 +1,39 @@ +## 1.2.8(2024-04-26) +- 修复 在vue2下H5黑边的bug +## 1.2.7(2024-04-26) +- 修复 在vue2手动输入后失焦导致清空数值的严重bug +## 1.2.6(2024-02-22) +- 新增 设置宽度属性width(单位:px) +## 1.2.5(2024-02-21) +- 修复 step步长小于1时,键盘类型为number的bug +## 1.2.4(2024-02-02) +- 修复 加减号垂直位置偏移样式问题 +## 1.2.3(2023-05-23) +- 更新示例工程 +## 1.2.2(2023-05-08) +- 修复 change 事件执行顺序错误的问题 +## 1.2.1(2021-11-22) +- 修复 vue3中某些scss变量无法找到的问题 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-number-box](https://uniapp.dcloud.io/component/uniui/uni-number-box) +## 1.1.2(2021-11-09) +- 新增 提供组件设计资源,组件样式调整 +## 1.1.1(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-05-12) +- 新增 组件示例地址 +## 1.0.6(2021-04-20) +- 修复 uni-number-box 浮点数运算不精确的 bug +- 修复 uni-number-box change 事件触发不正确的 bug +- 新增 uni-number-box v-model 双向绑定 +## 1.0.5(2021-02-05) +- 调整为uni_modules目录规范 + +## 1.0.7(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 支持 v-model +- 新增 支持 focus、blur 事件 +- 新增 支持 PC 端 diff --git a/uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue b/uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue new file mode 100644 index 0000000..4e203cc --- /dev/null +++ b/uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue @@ -0,0 +1,232 @@ +<template> + <view class="uni-numbox"> + <view @click="_calcValue('minus')" class="uni-numbox__minus uni-numbox-btns" :style="{background}"> + <text class="uni-numbox--text" :class="{ 'uni-numbox--disabled': inputValue <= min || disabled }" + :style="{color}">-</text> + </view> + <input :disabled="disabled" @focus="_onFocus" @blur="_onBlur" class="uni-numbox__value" + :type="step<1?'digit':'number'" v-model="inputValue" :style="{background, color, width:widthWithPx}" /> + <view @click="_calcValue('plus')" class="uni-numbox__plus uni-numbox-btns" :style="{background}"> + <text class="uni-numbox--text" :class="{ 'uni-numbox--disabled': inputValue >= max || disabled }" + :style="{color}">+</text> + </view> + </view> +</template> +<script> + /** + * NumberBox 数字输入框 + * @description 带加减按钮的数字输入框 + * @tutorial https://ext.dcloud.net.cn/plugin?id=31 + * @property {Number} value 输入框当前值 + * @property {Number} min 最小值 + * @property {Number} max 最大值 + * @property {Number} step 每次点击改变的间隔大小 + * @property {String} background 背景色 + * @property {String} color 字体颜色(前景色) + * @property {Number} width 输入框宽度(单位:px) + * @property {Boolean} disabled = [true|false] 是否为禁用状态 + * @event {Function} change 输入框值改变时触发的事件,参数为输入框当前的 value + * @event {Function} focus 输入框聚焦时触发的事件,参数为 event 对象 + * @event {Function} blur 输入框失焦时触发的事件,参数为 event 对象 + */ + + export default { + name: "UniNumberBox", + emits: ['change', 'input', 'update:modelValue', 'blur', 'focus'], + props: { + value: { + type: [Number, String], + default: 1 + }, + modelValue: { + type: [Number, String], + default: 1 + }, + min: { + type: Number, + default: 0 + }, + max: { + type: Number, + default: 100 + }, + step: { + type: Number, + default: 1 + }, + background: { + type: String, + default: '#f5f5f5' + }, + color: { + type: String, + default: '#333' + }, + disabled: { + type: Boolean, + default: false + }, + width: { + type: Number, + default: 40, + } + }, + data() { + return { + inputValue: 0 + }; + }, + watch: { + value(val) { + this.inputValue = +val; + }, + modelValue(val) { + this.inputValue = +val; + } + }, + computed: { + widthWithPx() { + return this.width + 'px'; + } + }, + created() { + if (this.value === 1) { + this.inputValue = +this.modelValue; + } + if (this.modelValue === 1) { + this.inputValue = +this.value; + } + }, + methods: { + _calcValue(type) { + if (this.disabled) { + return; + } + const scale = this._getDecimalScale(); + let value = this.inputValue * scale; + let step = this.step * scale; + if (type === "minus") { + value -= step; + if (value < (this.min * scale)) { + return; + } + if (value > (this.max * scale)) { + value = this.max * scale + } + } + + if (type === "plus") { + value += step; + if (value > (this.max * scale)) { + return; + } + if (value < (this.min * scale)) { + value = this.min * scale + } + } + + this.inputValue = (value / scale).toFixed(String(scale).length - 1); + // TODO vue2 兼容 + this.$emit("input", +this.inputValue); + // TODO vue3 兼容 + this.$emit("update:modelValue", +this.inputValue); + this.$emit("change", +this.inputValue); + }, + _getDecimalScale() { + + let scale = 1; + // 浮点型 + if (~~this.step !== this.step) { + scale = Math.pow(10, String(this.step).split(".")[1].length); + } + return scale; + }, + _onBlur(event) { + this.$emit('blur', event) + let value = event.detail.value; + if (isNaN(value)) { + this.inputValue = this.value; + return; + } + value = +value; + if (value > this.max) { + value = this.max; + } else if (value < this.min) { + value = this.min; + } + const scale = this._getDecimalScale(); + this.inputValue = value.toFixed(String(scale).length - 1); + this.$emit("input", +this.inputValue); + this.$emit("update:modelValue", +this.inputValue); + this.$emit("change", +this.inputValue); + }, + _onFocus(event) { + this.$emit('focus', event) + } + } + }; +</script> +<style lang="scss"> + $box-height: 26px; + $bg: #f5f5f5; + $br: 2px; + $color: #333; + + .uni-numbox { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + } + + .uni-numbox-btns { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + align-items: center; + justify-content: center; + padding: 0 8px; + background-color: $bg; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + .uni-numbox__value { + margin: 0 2px; + background-color: $bg; + width: 40px; + height: $box-height; + text-align: center; + font-size: 14px; + border-width: 0; + color: $color; + } + + .uni-numbox__minus { + border-top-left-radius: $br; + border-bottom-left-radius: $br; + } + + .uni-numbox__plus { + border-top-right-radius: $br; + border-bottom-right-radius: $br; + } + + .uni-numbox--text { + // fix nvue + line-height: 20px; + margin-bottom: 2px; + font-size: 20px; + font-weight: 300; + color: $color; + } + + .uni-numbox .uni-numbox--disabled { + color: #c0c0c0 !important; + /* #ifdef H5 */ + cursor: not-allowed; + /* #endif */ + } +</style> diff --git a/uni_modules/uni-number-box/package.json b/uni_modules/uni-number-box/package.json new file mode 100644 index 0000000..4ac9047 --- /dev/null +++ b/uni_modules/uni-number-box/package.json @@ -0,0 +1,83 @@ +{ + "id": "uni-number-box", + "displayName": "uni-number-box 数字输入框", + "version": "1.2.8", + "description": "NumberBox 带加减按钮的数字输入框组件,用户可以控制每次点击增加的数值,支持小数。", + "keywords": [ + "uni-ui", + "uniui", + "数字输入框" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-number-box/readme.md b/uni_modules/uni-number-box/readme.md new file mode 100644 index 0000000..affc56f --- /dev/null +++ b/uni_modules/uni-number-box/readme.md @@ -0,0 +1,13 @@ + + +## NumberBox 数字输入框 +> **组件名:uni-number-box** +> 代码块: `uNumberBox` + + +带加减按钮的数字输入框。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-number-box) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-pagination/changelog.md b/uni_modules/uni-pagination/changelog.md new file mode 100644 index 0000000..2e94adc --- /dev/null +++ b/uni_modules/uni-pagination/changelog.md @@ -0,0 +1,27 @@ +## 1.2.4(2022-09-19) +- 修复,未对主题色设置默认色,导致未引入 uni-scss 变量文件报错。 +- 修复,未对移动端当前页文字做主题色适配。 +## 1.2.3(2022-09-15) +- 修复未使用 uni-scss 主题色的 bug。 +## 1.2.2(2022-07-06) +- 修复 es 语言 i18n 错误 +## 1.2.1(2021-11-22) +- 修复 vue3中某些scss变量无法找到的问题 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-pagination](https://uniapp.dcloud.io/component/uniui/uni-pagination) +## 1.1.2(2021-10-08) +- 修复 current 、value 属性未监听,导致高亮样式失效的 bug +## 1.1.1(2021-08-20) +- 新增 支持国际化 +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-05-12) +- 新增 组件示例地址 +## 1.0.6(2021-04-12) +- 新增 PC 和 移动端适配不同的 ui +## 1.0.5(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.4(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-pagination/components/uni-pagination/i18n/en.json b/uni_modules/uni-pagination/components/uni-pagination/i18n/en.json new file mode 100644 index 0000000..d6e2897 --- /dev/null +++ b/uni_modules/uni-pagination/components/uni-pagination/i18n/en.json @@ -0,0 +1,5 @@ +{ + "uni-pagination.prevText": "prev", + "uni-pagination.nextText": "next", + "uni-pagination.piecePerPage": "piece/page" +} diff --git a/uni_modules/uni-pagination/components/uni-pagination/i18n/es.json b/uni_modules/uni-pagination/components/uni-pagination/i18n/es.json new file mode 100644 index 0000000..604a113 --- /dev/null +++ b/uni_modules/uni-pagination/components/uni-pagination/i18n/es.json @@ -0,0 +1,5 @@ +{ + "uni-pagination.prevText": "anterior", + "uni-pagination.nextText": "prxima", + "uni-pagination.piecePerPage": "Art��culo/P��gina" +} diff --git a/uni_modules/uni-pagination/components/uni-pagination/i18n/fr.json b/uni_modules/uni-pagination/components/uni-pagination/i18n/fr.json new file mode 100644 index 0000000..a7a0c77 --- /dev/null +++ b/uni_modules/uni-pagination/components/uni-pagination/i18n/fr.json @@ -0,0 +1,5 @@ +{ + "uni-pagination.prevText": "précédente", + "uni-pagination.nextText": "suivante", + "uni-pagination.piecePerPage": "Articles/Pages" +} diff --git a/uni_modules/uni-pagination/components/uni-pagination/i18n/index.js b/uni_modules/uni-pagination/components/uni-pagination/i18n/index.js new file mode 100644 index 0000000..2469dd0 --- /dev/null +++ b/uni_modules/uni-pagination/components/uni-pagination/i18n/index.js @@ -0,0 +1,12 @@ +import en from './en.json' +import es from './es.json' +import fr from './fr.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + es, + fr, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hans.json b/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hans.json new file mode 100644 index 0000000..782bbe4 --- /dev/null +++ b/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hans.json @@ -0,0 +1,5 @@ +{ + "uni-pagination.prevText": "上一页", + "uni-pagination.nextText": "下一页", + "uni-pagination.piecePerPage": "条/页" +} diff --git a/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hant.json b/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hant.json new file mode 100644 index 0000000..180fddb --- /dev/null +++ b/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hant.json @@ -0,0 +1,5 @@ +{ + "uni-pagination.prevText": "上一頁", + "uni-pagination.nextText": "下一頁", + "uni-pagination.piecePerPage": "條/頁" +} diff --git a/uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue b/uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue new file mode 100644 index 0000000..5305b5f --- /dev/null +++ b/uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue @@ -0,0 +1,465 @@ +<template> + <view class="uni-pagination"> + <!-- #ifndef MP --> + <picker v-if="showPageSize === true || showPageSize === 'true'" class="select-picker" mode="selector" + :value="pageSizeIndex" :range="pageSizeRange" @change="pickerChange" @cancel="pickerClick" + @click.native="pickerClick"> + <button type="default" size="mini" :plain="true"> + <text>{{pageSizeRange[pageSizeIndex]}} {{piecePerPage}}</text> + <uni-icons class="select-picker-icon" type="arrowdown" size="12" color="#999"></uni-icons> + </button> + </picker> + <!-- #endif --> + <!-- #ifndef APP-NVUE --> + <view class="uni-pagination__total is-phone-hide">共 {{ total }} 条</view> + <!-- #endif --> + <view class="uni-pagination__btn" + :class="currentIndex === 1 ? 'uni-pagination--disabled' : 'uni-pagination--enabled'" + :hover-class="currentIndex === 1 ? '' : 'uni-pagination--hover'" :hover-start-time="20" + :hover-stay-time="70" @click="clickLeft"> + <template v-if="showIcon === true || showIcon === 'true'"> + <uni-icons color="#666" size="16" type="left" /> + </template> + <template v-else> + <text class="uni-pagination__child-btn">{{ prevPageText }}</text> + </template> + </view> + <view class="uni-pagination__num uni-pagination__num-flex-none"> + <view class="uni-pagination__num-current"> + <text class="uni-pagination__num-current-text is-pc-hide current-index-text">{{ currentIndex }}</text> + <text class="uni-pagination__num-current-text is-pc-hide">/{{ maxPage || 0 }}</text> + <!-- #ifndef APP-NVUE --> + <view v-for="(item, index) in paper" :key="index" :class="{ 'page--active': item === currentIndex }" + class="uni-pagination__num-tag tag--active is-phone-hide" @click.top="selectPage(item, index)"> + <text>{{ item }}</text> + </view> + <!-- #endif --> + + </view> + </view> + <view class="uni-pagination__btn" + :class="currentIndex >= maxPage ? 'uni-pagination--disabled' : 'uni-pagination--enabled'" + :hover-class="currentIndex === maxPage ? '' : 'uni-pagination--hover'" :hover-start-time="20" + :hover-stay-time="70" @click="clickRight"> + <template v-if="showIcon === true || showIcon === 'true'"> + <uni-icons color="#666" size="16" type="right" /> + </template> + <template v-else> + <text class="uni-pagination__child-btn">{{ nextPageText }}</text> + </template> + </view> + </view> +</template> + +<script> + /** + * Pagination 分页器 + * @description 分页器组件,用于展示页码、请求数据等 + * @tutorial https://ext.dcloud.net.cn/plugin?id=32 + * @property {String} prevText 左侧按钮文字 + * @property {String} nextText 右侧按钮文字 + * @property {String} piecePerPageText 条/页文字 + * @property {Number} current 当前页 + * @property {Number} total 数据总量 + * @property {Number} pageSize 每页数据量 + * @property {Boolean} showIcon = [true|false] 是否以 icon 形式展示按钮 + * @property {Boolean} showPageSize = [true|false] 是否展示每页条数 + * @property {Array} pageSizeRange = [20, 50, 100, 500] 每页条数选框 + * @event {Function} change 点击页码按钮时触发 ,e={type,current} current为当前页,type值为:next/prev,表示点击的是上一页还是下一个 + * * @event {Function} pageSizeChange 当前每页条数改变时触发 ,e={pageSize} pageSize 为当前所选的每页条数 + */ + + import { + initVueI18n + } from '@dcloudio/uni-i18n' + import messages from './i18n/index.js' + const { + t + } = initVueI18n(messages) + export default { + name: 'UniPagination', + emits: ['update:modelValue', 'input', 'change', 'pageSizeChange'], + props: { + value: { + type: [Number, String], + default: 1 + }, + modelValue: { + type: [Number, String], + default: 1 + }, + prevText: { + type: String, + }, + nextText: { + type: String, + }, + piecePerPageText: { + type: String + }, + current: { + type: [Number, String], + default: 1 + }, + total: { + // 数据总量 + type: [Number, String], + default: 0 + }, + pageSize: { + // 每页数据量 + type: [Number, String], + default: 10 + }, + showIcon: { + // 是否以 icon 形式展示按钮 + type: [Boolean, String], + default: false + }, + showPageSize: { + // 是否以 icon 形式展示按钮 + type: [Boolean, String], + default: false + }, + pagerCount: { + type: Number, + default: 7 + }, + pageSizeRange: { + type: Array, + default: () => [20, 50, 100, 500] + } + }, + data() { + return { + pageSizeIndex: 0, + currentIndex: 1, + paperData: [], + pickerShow: false + } + }, + computed: { + piecePerPage() { + return this.piecePerPageText || t('uni-pagination.piecePerPage') + }, + prevPageText() { + return this.prevText || t('uni-pagination.prevText') + }, + nextPageText() { + return this.nextText || t('uni-pagination.nextText') + }, + maxPage() { + let maxPage = 1 + let total = Number(this.total) + let pageSize = Number(this.pageSize) + if (total && pageSize) { + maxPage = Math.ceil(total / pageSize) + } + return maxPage + }, + paper() { + const num = this.currentIndex + // TODO 最大页数 + const pagerCount = this.pagerCount + // const total = 181 + const total = this.total + const pageSize = this.pageSize + let totalArr = [] + let showPagerArr = [] + let pagerNum = Math.ceil(total / pageSize) + for (let i = 0; i < pagerNum; i++) { + totalArr.push(i + 1) + } + showPagerArr.push(1) + const totalNum = totalArr[totalArr.length - (pagerCount + 1) / 2] + totalArr.forEach((item, index) => { + if ((pagerCount + 1) / 2 >= num) { + if (item < pagerCount + 1 && item > 1) { + showPagerArr.push(item) + } + } else if (num + 2 <= totalNum) { + if (item > num - (pagerCount + 1) / 2 && item < num + (pagerCount + 1) / 2) { + showPagerArr.push(item) + } + } else { + if ((item > num - (pagerCount + 1) / 2 || pagerNum - pagerCount < item) && item < totalArr[ + totalArr.length - 1]) { + showPagerArr.push(item) + } + } + }) + if (pagerNum > pagerCount) { + if ((pagerCount + 1) / 2 >= num) { + showPagerArr[showPagerArr.length - 1] = '...' + } else if (num + 2 <= totalNum) { + showPagerArr[1] = '...' + showPagerArr[showPagerArr.length - 1] = '...' + } else { + showPagerArr[1] = '...' + } + showPagerArr.push(totalArr[totalArr.length - 1]) + } else { + if ((pagerCount + 1) / 2 >= num) {} else if (num + 2 <= totalNum) {} else { + showPagerArr.shift() + showPagerArr.push(totalArr[totalArr.length - 1]) + } + } + + return showPagerArr + } + }, + watch: { + current: { + immediate: true, + handler(val, old) { + if (val < 1) { + this.currentIndex = 1 + } else { + this.currentIndex = val + } + } + }, + value: { + immediate: true, + handler(val) { + if (Number(this.current) !== 1) return + if (val < 1) { + this.currentIndex = 1 + } else { + this.currentIndex = val + } + } + }, + pageSizeIndex(val) { + this.$emit('pageSizeChange', this.pageSizeRange[val]) + } + }, + methods: { + pickerChange(e) { + this.pageSizeIndex = e.detail.value + this.pickerClick() + }, + pickerClick() { + // #ifdef H5 + const body = document.querySelector('body') + if (!body) return + + const className = 'uni-pagination-picker-show' + this.pickerShow = !this.pickerShow + + if (this.pickerShow) { + body.classList.add(className) + } else { + setTimeout(() => body.classList.remove(className), 300) + } + // #endif + }, + // 选择标签 + selectPage(e, index) { + if (parseInt(e)) { + this.currentIndex = e + this.change('current') + } else { + let pagerNum = Math.ceil(this.total / this.pageSize) + // let pagerNum = Math.ceil(181 / this.pageSize) + // 上一页 + if (index <= 1) { + if (this.currentIndex - 5 > 1) { + this.currentIndex -= 5 + } else { + this.currentIndex = 1 + } + return + } + // 下一页 + if (index >= 6) { + if (this.currentIndex + 5 > pagerNum) { + this.currentIndex = pagerNum + } else { + this.currentIndex += 5 + } + return + } + } + }, + clickLeft() { + if (Number(this.currentIndex) === 1) { + return + } + this.currentIndex -= 1 + this.change('prev') + }, + clickRight() { + if (Number(this.currentIndex) >= this.maxPage) { + return + } + this.currentIndex += 1 + this.change('next') + }, + change(e) { + this.$emit('input', this.currentIndex) + this.$emit('update:modelValue', this.currentIndex) + this.$emit('change', { + type: e, + current: this.currentIndex + }) + } + } + } +</script> + +<style lang="scss" scoped> + $uni-primary: #2979ff !default; + .uni-pagination { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + position: relative; + overflow: hidden; + flex-direction: row; + justify-content: center; + align-items: center; + } + + .uni-pagination__total { + font-size: 14px; + color: #999; + margin-right: 15px; + } + + .uni-pagination__btn { + /* #ifndef APP-NVUE */ + display: flex; + cursor: pointer; + /* #endif */ + padding: 0 8px; + line-height: 30px; + font-size: 12px; + position: relative; + background-color: #F0F0F0; + flex-direction: row; + justify-content: center; + align-items: center; + text-align: center; + border-radius: 5px; + // border-width: 1px; + // border-style: solid; + // border-color: $uni-border-color; + } + + .uni-pagination__child-btn { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + font-size: 12px; + position: relative; + flex-direction: row; + justify-content: center; + align-items: center; + text-align: center; + color: #666; + font-size: 12px; + } + + .uni-pagination__num { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex: 1; + flex-direction: row; + justify-content: center; + align-items: center; + height: 30px; + line-height: 30px; + font-size: 12px; + color: #666; + margin: 0 5px; + } + + .uni-pagination__num-tag { + /* #ifdef H5 */ + cursor: pointer; + min-width: 30px; + /* #endif */ + margin: 0 5px; + height: 30px; + text-align: center; + line-height: 30px; + // border: 1px red solid; + color: #999; + border-radius: 4px; + // border-width: 1px; + // border-style: solid; + // border-color: $uni-border-color; + } + + .uni-pagination__num-current { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + } + + .uni-pagination__num-current-text { + font-size: 15px; + } + + .current-index-text{ + color: $uni-primary; + } + + .uni-pagination--enabled { + color: #333333; + opacity: 1; + } + + .uni-pagination--disabled { + opacity: 0.5; + /* #ifdef H5 */ + cursor: default; + /* #endif */ + } + + .uni-pagination--hover { + color: rgba(0, 0, 0, 0.6); + background-color: #eee; + } + + .tag--active:hover { + color: $uni-primary; + } + + .page--active { + color: #fff; + background-color: $uni-primary; + } + + .page--active:hover { + color: #fff; + } + + /* #ifndef APP-NVUE */ + .is-pc-hide { + display: block; + } + + .is-phone-hide { + display: none; + } + + @media screen and (min-width: 450px) { + .is-pc-hide { + display: none; + } + + .is-phone-hide { + display: block; + } + + .uni-pagination__num-flex-none { + flex: none; + } + } + + /* #endif */ +</style> diff --git a/uni_modules/uni-pagination/package.json b/uni_modules/uni-pagination/package.json new file mode 100644 index 0000000..862d5ab --- /dev/null +++ b/uni_modules/uni-pagination/package.json @@ -0,0 +1,83 @@ +{ + "id": "uni-pagination", + "displayName": "uni-pagination 分页器", + "version": "1.2.4", + "description": "Pagination 分页器组件,用于展示页码、请求数据等。", + "keywords": [ + "uni-ui", + "uniui", + "分页器", + "页码" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss","uni-icons"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-pagination/readme.md b/uni_modules/uni-pagination/readme.md new file mode 100644 index 0000000..97ea1d6 --- /dev/null +++ b/uni_modules/uni-pagination/readme.md @@ -0,0 +1,11 @@ + + +## Pagination 分页器 +> **组件名:uni-pagination** +> 代码块: `uPagination` + + +分页器组件,用于展示页码、请求数据等。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-pagination) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 diff --git a/uni_modules/uni-popup/changelog.md b/uni_modules/uni-popup/changelog.md new file mode 100644 index 0000000..f559715 --- /dev/null +++ b/uni_modules/uni-popup/changelog.md @@ -0,0 +1,92 @@ +## 1.9.5(2024-10-15) +- 修复 微信小程序中的getSystemInfo警告 +## 1.9.4(2024-10-12) +- 修复 微信小程序中的getSystemInfo警告 +## 1.9.3(2024-10-12) +- 修复 微信小程序中的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-popup-dialog 组件新增 inputType 属性 +## 1.8.1(2022-12-01) +- 修复 nvue 下 v-show 报错 +## 1.8.0(2022-11-29) +- 优化 主题样式 +## 1.7.9(2022-04-02) +- 修复 弹出层内部无法滚动的bug +## 1.7.8(2022-03-28) +- 修复 小程序中高度错误的bug +## 1.7.7(2022-03-17) +- 修复 快速调用open出现问题的Bug +## 1.7.6(2022-02-14) +- 修复 safeArea 属性不能设置为false的bug +## 1.7.5(2022-01-19) +- 修复 isMaskClick 失效的bug +## 1.7.4(2022-01-19) +- 新增 cancelText \ confirmText 属性 ,可自定义文本 +- 新增 maskBackgroundColor 属性 ,可以修改蒙版颜色 +- 优化 maskClick属性 更新为 isMaskClick ,解决微信小程序警告的问题 +## 1.7.3(2022-01-13) +- 修复 设置 safeArea 属性不生效的bug +## 1.7.2(2021-11-26) +- 优化 组件示例 +## 1.7.1(2021-11-26) +- 修复 vuedoc 文字错误 +## 1.7.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-popup](https://uniapp.dcloud.io/component/uniui/uni-popup) +## 1.6.2(2021-08-24) +- 新增 支持国际化 +## 1.6.1(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.6.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.5.0(2021-06-23) +- 新增 mask-click 遮罩层点击事件 +## 1.4.5(2021-06-22) +- 修复 nvue 平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug +## 1.4.4(2021-06-18) +- 修复 H5平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug +## 1.4.3(2021-06-08) +- 修复 错误的 watch 字段 +- 修复 safeArea 属性不生效的问题 +- 修复 点击内容,再点击遮罩无法关闭的Bug +## 1.4.2(2021-05-12) +- 新增 组件示例地址 +## 1.4.1(2021-04-29) +- 修复 组件内放置 input 、textarea 组件,无法聚焦的问题 +## 1.4.0 (2021-04-29) +- 新增 type 属性的 left\right 值,支持左右弹出 +- 新增 open(String:type) 方法参数 ,可以省略 type 属性 ,直接传入类型打开指定弹窗 +- 新增 backgroundColor 属性,可定义主窗口背景色,默认不显示背景色 +- 新增 safeArea 属性,是否适配底部安全区 +- 修复 App\h5\微信小程序底部安全区占位不对的Bug +- 修复 App 端弹出等待的Bug +- 优化 提升低配设备性能,优化动画卡顿问题 +- 优化 更简单的组件自定义方式 +## 1.2.9(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 1.2.8(2021-02-05) +- 调整为uni_modules目录规范 +## 1.2.7(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 支持 PC 端 +- 新增 uni-popup-message 、uni-popup-dialog扩展组件支持 PC 端 diff --git a/uni_modules/uni-popup/components/uni-popup-dialog/keypress.js b/uni_modules/uni-popup/components/uni-popup-dialog/keypress.js new file mode 100644 index 0000000..6ef26a2 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup-dialog/keypress.js @@ -0,0 +1,45 @@ +// #ifdef H5 +export default { + name: 'Keypress', + props: { + disable: { + type: Boolean, + default: false + } + }, + mounted () { + const keyNames = { + esc: ['Esc', 'Escape'], + tab: 'Tab', + enter: 'Enter', + space: [' ', 'Spacebar'], + up: ['Up', 'ArrowUp'], + left: ['Left', 'ArrowLeft'], + right: ['Right', 'ArrowRight'], + down: ['Down', 'ArrowDown'], + delete: ['Backspace', 'Delete', 'Del'] + } + const listener = ($event) => { + if (this.disable) { + return + } + const keyName = Object.keys(keyNames).find(key => { + const keyName = $event.key + const value = keyNames[key] + return value === keyName || (Array.isArray(value) && value.includes(keyName)) + }) + if (keyName) { + // 避免和其他按键事件冲突 + setTimeout(() => { + this.$emit(keyName, {}) + }, 0) + } + } + document.addEventListener('keyup', listener) + this.$once('hook:beforeDestroy', () => { + document.removeEventListener('keyup', listener) + }) + }, + render: () => {} +} +// #endif diff --git a/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue b/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue new file mode 100644 index 0000000..08707d4 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue @@ -0,0 +1,316 @@ +<template> + <view class="uni-popup-dialog"> + <view class="uni-dialog-title"> + <text class="uni-dialog-title-text" :class="['uni-popup__'+dialogType]">{{titleText}}</text> + </view> + <view v-if="mode === 'base'" class="uni-dialog-content"> + <slot> + <text class="uni-dialog-content-text">{{content}}</text> + </slot> + </view> + <view v-else class="uni-dialog-content"> + <slot> + <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" v-if="showClose" @click="closeDialog"> + <text class="uni-dialog-button-text">{{closeText}}</text> + </view> + <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> + + </view> +</template> + +<script> + import popup from '../uni-popup/popup.js' + import { + initVueI18n + } from '@dcloudio/uni-i18n' + import messages from '../uni-popup/i18n/index.js' + 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 提示 + * @value info 消息 + * @value error 错误 + * @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 点击取消按钮触发 + */ + + export default { + name: "uniPopupDialog", + mixins: [popup], + 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: '' + }, + type: { + type: String, + default: 'error' + }, + mode: { + type: String, + default: 'base' + }, + title: { + type: String, + default: '' + }, + content: { + type: String, + default: '' + }, + beforeClose: { + type: Boolean, + default: false + }, + cancelText: { + type: String, + default: '' + }, + confirmText: { + type: String, + default: '' + }, + maxlength: { + type: Number, + default: -1, + }, + focus: { + type: Boolean, + default: true, + } + }, + data() { + return { + dialogType: 'error', + val: "" + } + }, + computed: { + okText() { + return this.confirmText || t("uni-popup.ok") + }, + closeText() { + return this.cancelText || t("uni-popup.cancel") + }, + placeholderText() { + return this.placeholder || t("uni-popup.placeholder") + }, + titleText() { + return this.title || t("uni-popup.title") + } + }, + watch: { + type(val) { + this.dialogType = val + }, + mode(val) { + if (val === 'input') { + this.dialogType = 'info' + } + }, + value(val) { + if (this.maxlength != -1 && this.mode === 'input') { + this.val = val.slice(0, this.maxlength); + } else { + this.val = val + } + }, + val(val) { + // #ifdef VUE2 + // TODO 兼容 vue2 + this.$emit('input', val); + // #endif + // #ifdef VUE3 + // TODO 兼容 vue3 + this.$emit('update:modelValue', val); + // #endif + } + }, + created() { + // 对话框遮罩不可点击 + this.popup.disableMask() + // this.popup.closeMask() + if (this.mode === 'input') { + this.dialogType = 'info' + this.val = this.value; + // #ifdef VUE3 + this.val = this.modelValue; + // #endif + } else { + this.dialogType = this.type + } + }, + methods: { + /** + * 点击确认按钮 + */ + onOk() { + if (this.mode === 'input') { + this.$emit('confirm', this.val) + } else { + this.$emit('confirm') + } + if (this.beforeClose) return + this.popup.close() + }, + /** + * 点击取消按钮 + */ + closeDialog() { + this.$emit('close') + if (this.beforeClose) return + this.popup.close() + }, + close() { + this.popup.close() + } + } + } +</script> + +<style lang="scss"> + .uni-popup-dialog { + width: 300px; + border-radius: 11px; + background-color: #fff; + } + + .uni-dialog-title { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + justify-content: center; + padding-top: 25px; + } + + .uni-dialog-title-text { + font-size: 16px; + font-weight: 500; + } + + .uni-dialog-content { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + justify-content: center; + align-items: center; + padding: 20px; + } + + .uni-dialog-content-text { + font-size: 14px; + color: #6C6C6C; + } + + .uni-dialog-button-group { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + border-top-color: #f5f5f5; + border-top-style: solid; + border-top-width: 1px; + } + + .uni-dialog-button { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + + flex: 1; + flex-direction: row; + justify-content: center; + align-items: center; + height: 45px; + } + + .uni-border-left { + border-left-color: #f0f0f0; + border-left-style: solid; + border-left-width: 1px; + } + + .uni-dialog-button-text { + font-size: 16px; + color: #333; + } + + .uni-button-color { + color: #007aff; + } + + .uni-dialog-input { + flex: 1; + font-size: 14px; + border: 1px #eee solid; + height: 40px; + padding: 0 10px; + border-radius: 5px; + color: #555; + } + + .uni-popup__success { + color: #4cd964; + } + + .uni-popup__warn { + color: #f0ad4e; + } + + .uni-popup__error { + color: #dd524d; + } + + .uni-popup__info { + color: #909399; + } +</style> diff --git a/uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue b/uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue new file mode 100644 index 0000000..91370a8 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue @@ -0,0 +1,143 @@ +<template> + <view class="uni-popup-message"> + <view class="uni-popup-message__box fixforpc-width" :class="'uni-popup__'+type"> + <slot> + <text class="uni-popup-message-text" :class="'uni-popup__'+type+'-text'">{{message}}</text> + </slot> + </view> + </view> +</template> + +<script> + import popup from '../uni-popup/popup.js' + /** + * PopUp 弹出层-消息提示 + * @description 弹出层-消息提示 + * @tutorial https://ext.dcloud.net.cn/plugin?id=329 + * @property {String} type = [success|warning|info|error] 主题样式 + * @value success 成功 + * @value warning 提示 + * @value info 消息 + * @value error 错误 + * @property {String} message 消息提示文字 + * @property {String} duration 显示时间,设置为 0 则不会自动关闭 + */ + + export default { + name: 'uniPopupMessage', + mixins:[popup], + props: { + /** + * 主题 success/warning/info/error 默认 success + */ + type: { + type: String, + default: 'success' + }, + /** + * 消息文字 + */ + message: { + type: String, + default: '' + }, + /** + * 显示时间,设置为 0 则不会自动关闭 + */ + duration: { + type: Number, + default: 3000 + }, + maskShow:{ + type:Boolean, + default:false + } + }, + data() { + return {} + }, + created() { + this.popup.maskShow = this.maskShow + this.popup.messageChild = this + }, + methods: { + timerClose(){ + if(this.duration === 0) return + clearTimeout(this.timer) + this.timer = setTimeout(()=>{ + this.popup.close() + },this.duration) + } + } + } +</script> +<style lang="scss" > + .uni-popup-message { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + justify-content: center; + } + + .uni-popup-message__box { + background-color: #e1f3d8; + padding: 10px 15px; + border-color: #eee; + border-style: solid; + border-width: 1px; + flex: 1; + } + + @media screen and (min-width: 500px) { + .fixforpc-width { + margin-top: 20px; + border-radius: 4px; + flex: none; + min-width: 380px; + /* #ifndef APP-NVUE */ + max-width: 50%; + /* #endif */ + /* #ifdef APP-NVUE */ + max-width: 500px; + /* #endif */ + } + } + + .uni-popup-message-text { + font-size: 14px; + padding: 0; + } + + .uni-popup__success { + background-color: #e1f3d8; + } + + .uni-popup__success-text { + color: #67C23A; + } + + .uni-popup__warn { + background-color: #faecd8; + } + + .uni-popup__warn-text { + color: #E6A23C; + } + + .uni-popup__error { + background-color: #fde2e2; + } + + .uni-popup__error-text { + color: #F56C6C; + } + + .uni-popup__info { + background-color: #F2F6FC; + } + + .uni-popup__info-text { + color: #909399; + } +</style> diff --git a/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue b/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue new file mode 100644 index 0000000..f7e667c --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue @@ -0,0 +1,187 @@ +<template> + <view class="uni-popup-share"> + <view class="uni-share-title"><text class="uni-share-title-text">{{shareTitleText}}</text></view> + <view class="uni-share-content"> + <view class="uni-share-content-box"> + <view class="uni-share-content-item" v-for="(item,index) in bottomData" :key="index" @click.stop="select(item,index)"> + <image class="uni-share-image" :src="item.icon" mode="aspectFill"></image> + <text class="uni-share-text">{{item.text}}</text> + </view> + + </view> + </view> + <view class="uni-share-button-box"> + <button class="uni-share-button" @click="close">{{cancelText}}</button> + </view> + </view> +</template> + +<script> + import popup from '../uni-popup/popup.js' + import { + initVueI18n + } from '@dcloudio/uni-i18n' + import messages from '../uni-popup/i18n/index.js' + const { t } = initVueI18n(messages) + export default { + name: 'UniPopupShare', + mixins:[popup], + emits:['select'], + props: { + title: { + type: String, + default: '' + }, + beforeClose: { + type: Boolean, + default: false + } + }, + data() { + return { + bottomData: [{ + text: '微信', + icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/c2b17470-50be-11eb-b680-7980c8a877b8.png', + name: 'wx' + }, + { + text: '支付宝', + icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/d684ae40-50be-11eb-8ff1-d5dcf8779628.png', + name: 'ali' + }, + { + text: 'QQ', + icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/e7a79520-50be-11eb-b997-9918a5dda011.png', + name: 'qq' + }, + { + text: '新浪', + icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/0dacdbe0-50bf-11eb-8ff1-d5dcf8779628.png', + name: 'sina' + }, + // { + // text: '百度', + // icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/1ec6e920-50bf-11eb-8a36-ebb87efcf8c0.png', + // name: 'copy' + // }, + // { + // text: '其他', + // icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/2e0fdfe0-50bf-11eb-b997-9918a5dda011.png', + // name: 'more' + // } + ] + } + }, + created() {}, + computed: { + cancelText() { + return t("uni-popup.cancel") + }, + shareTitleText() { + return this.title || t("uni-popup.shareTitle") + } + }, + methods: { + /** + * 选择内容 + */ + select(item, index) { + this.$emit('select', { + item, + index + }) + this.close() + + }, + /** + * 关闭窗口 + */ + close() { + if(this.beforeClose) return + this.popup.close() + } + } + } +</script> +<style lang="scss" > + .uni-popup-share { + background-color: #fff; + border-top-left-radius: 11px; + border-top-right-radius: 11px; + } + .uni-share-title { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + align-items: center; + justify-content: center; + height: 40px; + } + .uni-share-title-text { + font-size: 14px; + color: #666; + } + .uni-share-content { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + justify-content: center; + padding-top: 10px; + } + + .uni-share-content-box { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + flex-wrap: wrap; + width: 360px; + } + + .uni-share-content-item { + width: 90px; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + justify-content: center; + padding: 10px 0; + align-items: center; + } + + .uni-share-content-item:active { + background-color: #f5f5f5; + } + + .uni-share-image { + width: 30px; + height: 30px; + } + + .uni-share-text { + margin-top: 10px; + font-size: 14px; + color: #3B4144; + } + + .uni-share-button-box { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + padding: 10px 15px; + } + + .uni-share-button { + flex: 1; + border-radius: 50px; + color: #666; + font-size: 16px; + } + + .uni-share-button::after { + border-radius: 50px; + } +</style> diff --git a/uni_modules/uni-popup/components/uni-popup/i18n/en.json b/uni_modules/uni-popup/components/uni-popup/i18n/en.json new file mode 100644 index 0000000..7f1bd06 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/i18n/en.json @@ -0,0 +1,7 @@ +{ + "uni-popup.cancel": "cancel", + "uni-popup.ok": "ok", + "uni-popup.placeholder": "pleace enter", + "uni-popup.title": "Hint", + "uni-popup.shareTitle": "Share to" +} diff --git a/uni_modules/uni-popup/components/uni-popup/i18n/index.js b/uni_modules/uni-popup/components/uni-popup/i18n/index.js new file mode 100644 index 0000000..de7509c --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json b/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json new file mode 100644 index 0000000..5e3003c --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json @@ -0,0 +1,7 @@ +{ + "uni-popup.cancel": "取消", + "uni-popup.ok": "确定", + "uni-popup.placeholder": "请输入", + "uni-popup.title": "提示", + "uni-popup.shareTitle": "分享到" +} diff --git a/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json b/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json new file mode 100644 index 0000000..13e39eb --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json @@ -0,0 +1,7 @@ +{ + "uni-popup.cancel": "取消", + "uni-popup.ok": "確定", + "uni-popup.placeholder": "請輸入", + "uni-popup.title": "提示", + "uni-popup.shareTitle": "分享到" +} diff --git a/uni_modules/uni-popup/components/uni-popup/keypress.js b/uni_modules/uni-popup/components/uni-popup/keypress.js new file mode 100644 index 0000000..62dda46 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/keypress.js @@ -0,0 +1,45 @@ +// #ifdef H5 +export default { + name: 'Keypress', + props: { + disable: { + type: Boolean, + default: false + } + }, + mounted () { + const keyNames = { + esc: ['Esc', 'Escape'], + tab: 'Tab', + enter: 'Enter', + space: [' ', 'Spacebar'], + up: ['Up', 'ArrowUp'], + left: ['Left', 'ArrowLeft'], + right: ['Right', 'ArrowRight'], + down: ['Down', 'ArrowDown'], + delete: ['Backspace', 'Delete', 'Del'] + } + const listener = ($event) => { + if (this.disable) { + return + } + const keyName = Object.keys(keyNames).find(key => { + const keyName = $event.key + const value = keyNames[key] + return value === keyName || (Array.isArray(value) && value.includes(keyName)) + }) + if (keyName) { + // 避免和其他按键事件冲突 + setTimeout(() => { + this.$emit(keyName, {}) + }, 0) + } + } + document.addEventListener('keyup', listener) + // this.$once('hook:beforeDestroy', () => { + // document.removeEventListener('keyup', listener) + // }) + }, + render: () => {} +} +// #endif diff --git a/uni_modules/uni-popup/components/uni-popup/popup.js b/uni_modules/uni-popup/components/uni-popup/popup.js new file mode 100644 index 0000000..c4e5781 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/popup.js @@ -0,0 +1,26 @@ + +export default { + data() { + return { + + } + }, + created(){ + this.popup = this.getParent() + }, + methods:{ + /** + * 获取父元素实例 + */ + getParent(name = 'uniPopup') { + 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; + }, + } +} diff --git a/uni_modules/uni-popup/components/uni-popup/uni-popup.uvue b/uni_modules/uni-popup/components/uni-popup/uni-popup.uvue new file mode 100644 index 0000000..5eb8d5b --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/uni-popup.uvue @@ -0,0 +1,90 @@ +<template> + <view class="popup-root" v-if="isOpen" v-show="isShow" @click="clickMask"> + <view @click.stop> + <slot></slot> + </view> + </view> +</template> + +<script> + type CloseCallBack = ()=> void; + let closeCallBack:CloseCallBack = () :void => {}; + export default { + emits:["close","clickMask"], + data() { + return { + isShow:false, + isOpen:false + } + }, + props: { + maskClick: { + type: Boolean, + default: true + }, + }, + watch: { + // 设置show = true 时,如果没有 open 需要设置为 open + isShow:{ + handler(isShow) { + // console.log("isShow",isShow) + if(isShow && this.isOpen == false){ + this.isOpen = true + } + }, + immediate:true + }, + // 设置isOpen = true 时,如果没有 isShow 需要设置为 isShow + isOpen:{ + handler(isOpen) { + // console.log("isOpen",isOpen) + if(isOpen && this.isShow == false){ + this.isShow = true + } + }, + immediate:true + } + }, + methods:{ + open(){ + // ...funs : CloseCallBack[] + // if(funs.length > 0){ + // closeCallBack = funs[0] + // } + this.isOpen = true; + }, + clickMask(){ + if(this.maskClick == true){ + this.$emit('clickMask') + this.close() + } + }, + close(): void{ + this.isOpen = false; + this.$emit('close') + closeCallBack() + }, + hiden(){ + this.isShow = false + }, + show(){ + this.isShow = true + } + } + } +</script> + +<style> +.popup-root { + position: fixed; + top: 0; + left: 0; + width: 750rpx; + height: 100%; + flex: 1; + background-color: rgba(0, 0, 0, 0.3); + justify-content: center; + align-items: center; + z-index: 99; +} +</style> \ No newline at end of file diff --git a/uni_modules/uni-popup/components/uni-popup/uni-popup.vue b/uni_modules/uni-popup/components/uni-popup/uni-popup.vue new file mode 100644 index 0000000..5af55e0 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/uni-popup.vue @@ -0,0 +1,518 @@ +<template> + <view v-if="showPopup" class="uni-popup" :class="[popupstyle, isDesktop ? 'fixforpc-z-index' : '']"> + <view @touchstart="touchstart"> + <uni-transition key="1" v-if="maskShow" name="mask" mode-class="fade" :styles="maskClass" + :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="getStyles" :class="[popupstyle]" @click="clear"> + <slot /> + </view> + </uni-transition> + </view> + <!-- #ifdef H5 --> + <keypress v-if="maskShow" @esc="onTap" /> + <!-- #endif --> + </view> +</template> + +<script> + // #ifdef H5 + import keypress from './keypress.js' + // #endif + + /** + * PopUp 弹出层 + * @description 弹出层组件,为了解决遮罩弹层的问题 + * @tutorial https://ext.dcloud.net.cn/plugin?id=329 + * @property {String} type = [top|center|bottom|left|right|message|dialog|share] 弹出方式 + * @value top 顶部弹出 + * @value center 中间弹出 + * @value bottom 底部弹出 + * @value left 左侧弹出 + * @value right 右侧弹出 + * @value message 消息提示 + * @value dialog 对话框 + * @value share 底部分享示例 + * @property {Boolean} animation = [true|false] 是否开启动画 + * @property {Boolean} maskClick = [true|false] 蒙版点击是否关闭弹窗(废弃) + * @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 点击遮罩触发 + */ + + export default { + name: 'uniPopup', + components: { + // #ifdef H5 + keypress + // #endif + }, + emits: ['change', 'maskClick'], + props: { + // 开启动画 + animation: { + type: Boolean, + default: true + }, + // 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层 + // message: 消息提示 ; dialog : 对话框 + type: { + type: String, + default: 'center' + }, + // maskClick + isMaskClick: { + type: Boolean, + default: null + }, + // TODO 2 个版本后废弃属性 ,使用 isMaskClick + maskClick: { + type: Boolean, + default: null + }, + backgroundColor: { + type: String, + default: 'none' + }, + safeArea: { + type: Boolean, + default: true + }, + maskBackgroundColor: { + type: String, + default: 'rgba(0, 0, 0, 0.4)' + }, + borderRadius:{ + type: String, + } + }, + + watch: { + /** + * 监听type类型 + */ + type: { + handler: function(type) { + if (!this.config[type]) return + this[this.config[type]](true) + }, + immediate: true + }, + isDesktop: { + handler: function(newVal) { + if (!this.config[newVal]) return + this[this.config[this.type]](true) + }, + immediate: true + }, + /** + * 监听遮罩是否可点击 + * @param {Object} val + */ + maskClick: { + handler: function(val) { + this.mkclick = val + }, + immediate: true + }, + isMaskClick: { + handler: function(val) { + this.mkclick = val + }, + immediate: true + }, + // H5 下禁止底部滚动 + showPopup(show) { + // #ifdef H5 + // fix by mehaotian 处理 h5 滚动穿透的问题 + document.getElementsByTagName('body')[0].style.overflow = show ? 'hidden' : 'visible' + // #endif + } + }, + data() { + return { + duration: 300, + ani: [], + showPopup: false, + showTrans: false, + popupWidth: 0, + popupHeight: 0, + config: { + top: 'top', + bottom: 'bottom', + center: 'center', + left: 'left', + right: 'right', + message: 'top', + dialog: 'center', + share: 'bottom' + }, + maskClass: { + position: 'fixed', + bottom: 0, + top: 0, + left: 0, + right: 0, + 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: '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 + }, + bg() { + if (this.backgroundColor === '' || this.backgroundColor === 'none') { + return 'transparent' + } + return this.backgroundColor + } + }, + mounted() { + const fixSize = () => { + // #ifdef MP-WEIXIN + const { + windowWidth, + windowHeight, + windowTop, + safeArea, + screenHeight, + safeAreaInsets + } = uni.getWindowInfo() + // #endif + // #ifndef MP-WEIXIN + const { + windowWidth, + windowHeight, + windowTop, + safeArea, + screenHeight, + safeAreaInsets + } = uni.getSystemInfoSync() + // #endif + this.popupWidth = windowWidth + this.popupHeight = windowHeight + (windowTop || 0) + // TODO fix by mehaotian 是否适配底部安全区 ,目前微信ios 、和 app ios 计算有差异,需要框架修复 + if (safeArea && this.safeArea) { + // #ifdef MP-WEIXIN + this.safeAreaInsets = screenHeight - safeArea.bottom + // #endif + // #ifndef MP-WEIXIN + this.safeAreaInsets = safeAreaInsets.bottom + // #endif + } else { + this.safeAreaInsets = 0 + } + } + fixSize() + // #ifdef H5 + // window.addEventListener('resize', fixSize) + // this.$once('hook:beforeDestroy', () => { + // window.removeEventListener('resize', fixSize) + // }) + // #endif + }, + // #ifndef VUE3 + // TODO vue2 + destroyed() { + this.setH5Visible() + }, + // #endif + // #ifdef VUE3 + // TODO vue3 + unmounted() { + 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) { + this.mkclick = true + } else { + this.mkclick = this.isMaskClick !== null ? this.isMaskClick : this.maskClick + } + if (this.animation) { + this.duration = 300 + } else { + this.duration = 0 + } + // TODO 处理 message 组件生命周期异常的问题 + this.messageChild = null + // TODO 解决头条冒泡的问题 + this.clearPropagation = false + this.maskClass.backgroundColor = this.maskBackgroundColor + }, + methods: { + setH5Visible(visible = true) { + // #ifdef H5 + // fix by mehaotian 处理 h5 滚动穿透的问题 + document.getElementsByTagName('body')[0].style.overflow = visible ? "visible" : "hidden"; + // #endif + }, + /** + * 公用方法,不显示遮罩层 + */ + closeMask() { + this.maskShow = false + }, + /** + * 公用方法,遮罩层禁止点击 + */ + disableMask() { + this.mkclick = false + }, + // TODO nvue 取消冒泡 + clear(e) { + // #ifndef APP-NVUE + e.stopPropagation() + // #endif + this.clearPropagation = true + }, + + open(direction) { + // fix by mehaotian 处理快速打开关闭的情况 + if (this.showPopup) { + return + } + let innerType = ['top', 'center', 'bottom', 'left', 'right', 'message', 'dialog', 'share'] + if (!(direction && innerType.indexOf(direction) !== -1)) { + direction = this.type + } + if (!this.config[direction]) { + console.error('缺少类型:', direction) + return + } + this[this.config[direction]]() + this.$emit('change', { + show: true, + type: direction + }) + }, + close(type) { + this.showTrans = false + this.$emit('change', { + show: false, + type: this.type + }) + clearTimeout(this.timer) + // // 自定义关闭事件 + // this.customOpen && this.customClose() + this.timer = setTimeout(() => { + this.showPopup = false + }, 300) + }, + // TODO 处理冒泡事件,头条的冒泡事件有问题 ,先这样兼容 + touchstart() { + this.clearPropagation = false + }, + + onTap() { + if (this.clearPropagation) { + // fix by mehaotian 兼容 nvue + this.clearPropagation = false + return + } + this.$emit('maskClick') + if (!this.mkclick) return + this.close() + }, + /** + * 顶部弹出样式处理 + */ + top(type) { + this.popupstyle = this.isDesktop ? 'fixforpc-top' : 'top' + this.ani = ['slide-top'] + this.transClass = { + position: 'fixed', + left: 0, + right: 0, + 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() + } + }) + }, + /** + * 底部弹出样式处理 + */ + bottom(type) { + this.popupstyle = 'bottom' + this.ani = ['slide-bottom'] + this.transClass = { + position: 'fixed', + left: 0, + right: 0, + bottom: 0, + paddingBottom: this.safeAreaInsets + 'px', + backgroundColor: this.bg, + borderRadius:this.borderRadius || "0", + } + // TODO 兼容 type 属性 ,后续会废弃 + if (type) return + 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 */ + display: 'flex', + flexDirection: 'column', + /* #endif */ + bottom: 0, + left: 0, + right: 0, + top: 0, + justifyContent: 'center', + alignItems: 'center', + borderRadius:this.borderRadius || "0" + } + // TODO 兼容 type 属性 ,后续会废弃 + if (type) return + this.showPoptrans() + }, + left(type) { + this.popupstyle = 'left' + this.ani = ['slide-left'] + this.transClass = { + position: 'fixed', + left: 0, + bottom: 0, + top: 0, + backgroundColor: this.bg, + borderRadius:this.borderRadius || "0", + /* #ifndef APP-NVUE */ + display: 'flex', + flexDirection: 'column' + /* #endif */ + } + // TODO 兼容 type 属性 ,后续会废弃 + if (type) return + this.showPoptrans() + }, + right(type) { + this.popupstyle = 'right' + this.ani = ['slide-right'] + this.transClass = { + position: 'fixed', + bottom: 0, + right: 0, + top: 0, + backgroundColor: this.bg, + borderRadius:this.borderRadius || "0", + /* #ifndef APP-NVUE */ + display: 'flex', + flexDirection: 'column' + /* #endif */ + } + // TODO 兼容 type 属性 ,后续会废弃 + if (type) return + this.showPoptrans() + }, + showPoptrans(){ + this.$nextTick(()=>{ + this.showPopup = true + this.showTrans = true + }) + } + } + } +</script> +<style lang="scss"> + .uni-popup { + position: fixed; + /* #ifndef APP-NVUE */ + z-index: 99; + + /* #endif */ + &.top, + &.left, + &.right { + /* #ifdef H5 */ + top: var(--window-top); + /* #endif */ + /* #ifndef H5 */ + top: 0; + /* #endif */ + } + + .uni-popup__wrapper { + /* #ifndef APP-NVUE */ + display: block; + /* #endif */ + position: relative; + + /* iphonex 等安全区设置,底部安全区适配 */ + /* #ifndef APP-NVUE */ + // padding-bottom: constant(safe-area-inset-bottom); + // padding-bottom: env(safe-area-inset-bottom); + /* #endif */ + &.left, + &.right { + /* #ifdef H5 */ + padding-top: var(--window-top); + /* #endif */ + /* #ifndef H5 */ + padding-top: 0; + /* #endif */ + flex: 1; + } + } + } + + .fixforpc-z-index { + /* #ifndef APP-NVUE */ + z-index: 999; + /* #endif */ + } + + .fixforpc-top { + top: 0; + } +</style> diff --git a/uni_modules/uni-popup/package.json b/uni_modules/uni-popup/package.json new file mode 100644 index 0000000..2750803 --- /dev/null +++ b/uni_modules/uni-popup/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-popup", + "displayName": "uni-popup 弹出层", + "version": "1.9.5", + "description": " Popup 组件,提供常用的弹层", + "keywords": [ + "uni-ui", + "弹出层", + "弹窗", + "popup", + "弹框" + ], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-transition" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-popup/readme.md b/uni_modules/uni-popup/readme.md new file mode 100644 index 0000000..fdad4b3 --- /dev/null +++ b/uni_modules/uni-popup/readme.md @@ -0,0 +1,17 @@ + + +## Popup 弹出层 +> **组件名:uni-popup** +> 代码块: `uPopup` +> 关联组件:`uni-transition` + + +弹出层组件,在应用中弹出一个消息提示窗口、提示框等 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-popup) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + + + + diff --git a/uni_modules/uni-rate/changelog.md b/uni_modules/uni-rate/changelog.md new file mode 100644 index 0000000..8a98a61 --- /dev/null +++ b/uni_modules/uni-rate/changelog.md @@ -0,0 +1,25 @@ +## 1.3.1(2022-02-25) +- 修复 条件判断 `NaN` 错误的 bug +## 1.3.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-rate](https://uniapp.dcloud.io/component/uniui/uni-rate) +## 1.2.2(2021-09-10) +- 优化 默认值修改为 0 颗星 +## 1.2.1(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.2.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.2(2021-05-12) +- 新增 组件示例地址 +## 1.1.1(2021-04-21) +- 修复 布局变化后 uni-rate 星星计算不准确的 bug +- 优化 添加依赖 uni-icons, 导入 uni-rate 自动下载依赖 +## 1.1.0(2021-04-16) +- 修复 uni-rate 属性 margin 值为 string 组件失效的 bug + +## 1.0.9(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.8(2021-02-05) +- 调整为uni_modules目录规范 +- 支持 pc 端 diff --git a/uni_modules/uni-rate/components/uni-rate/uni-rate.vue b/uni_modules/uni-rate/components/uni-rate/uni-rate.vue new file mode 100644 index 0000000..857f5f9 --- /dev/null +++ b/uni_modules/uni-rate/components/uni-rate/uni-rate.vue @@ -0,0 +1,361 @@ +<template> + <view> + <view ref="uni-rate" class="uni-rate"> + <view class="uni-rate__icon" :class="{'uni-cursor-not-allowed': disabled}" + :style="{ 'margin-right': marginNumber + 'px' }" v-for="(star, index) in stars" :key="index" + @touchstart.stop="touchstart" @touchmove.stop="touchmove" @mousedown.stop="mousedown" + @mousemove.stop="mousemove" @mouseleave="mouseleave"> + <uni-icons :color="color" :size="size" :type="isFill ? 'star-filled' : 'star'" /> + <!-- #ifdef APP-NVUE --> + <view :style="{ width: star.activeWitch.replace('%','')*size/100+'px'}" class="uni-rate__icon-on"> + <uni-icons style="text-align: left;" :color="disabled?'#ccc':activeColor" :size="size" + type="star-filled" /> + </view> + <!-- #endif --> + <!-- #ifndef APP-NVUE --> + <view :style="{ width: star.activeWitch}" class="uni-rate__icon-on"> + <uni-icons :color="disabled?disabledColor:activeColor" :size="size" type="star-filled" /> + </view> + <!-- #endif --> + </view> + </view> + </view> +</template> + +<script> + // #ifdef APP-NVUE + const dom = uni.requireNativePlugin('dom'); + // #endif + /** + * Rate 评分 + * @description 评分组件 + * @tutorial https://ext.dcloud.net.cn/plugin?id=33 + * @property {Boolean} isFill = [true|false] 星星的类型,是否为实心类型, 默认为实心 + * @property {String} color 未选中状态的星星颜色,默认为 "#ececec" + * @property {String} activeColor 选中状态的星星颜色,默认为 "#ffca3e" + * @property {String} disabledColor 禁用状态的星星颜色,默认为 "#c0c0c0" + * @property {Number} size 星星的大小 + * @property {Number} value/v-model 当前评分 + * @property {Number} max 最大评分评分数量,目前一分一颗星 + * @property {Number} margin 星星的间距,单位 px + * @property {Boolean} disabled = [true|false] 是否为禁用状态,默认为 false + * @property {Boolean} readonly = [true|false] 是否为只读状态,默认为 false + * @property {Boolean} allowHalf = [true|false] 是否实现半星,默认为 false + * @property {Boolean} touchable = [true|false] 是否支持滑动手势,默认为 true + * @event {Function} change uniRate 的 value 改变时触发事件,e={value:Number} + */ + + export default { + name: "UniRate", + props: { + isFill: { + // 星星的类型,是否镂空 + type: [Boolean, String], + default: true + }, + color: { + // 星星未选中的颜色 + type: String, + default: "#ececec" + }, + activeColor: { + // 星星选中状态颜色 + type: String, + default: "#ffca3e" + }, + disabledColor: { + // 星星禁用状态颜色 + type: String, + default: "#c0c0c0" + }, + size: { + // 星星的大小 + type: [Number, String], + default: 24 + }, + value: { + // 当前评分 + type: [Number, String], + default: 0 + }, + modelValue: { + // 当前评分 + type: [Number, String], + default: 0 + }, + max: { + // 最大评分 + type: [Number, String], + default: 5 + }, + margin: { + // 星星的间距 + type: [Number, String], + default: 0 + }, + disabled: { + // 是否可点击 + type: [Boolean, String], + default: false + }, + readonly: { + // 是否只读 + type: [Boolean, String], + default: false + }, + allowHalf: { + // 是否显示半星 + type: [Boolean, String], + default: false + }, + touchable: { + // 是否支持滑动手势 + type: [Boolean, String], + default: true + } + }, + data() { + return { + valueSync: "", + userMouseFristMove: true, + userRated: false, + userLastRate: 1 + }; + }, + watch: { + value(newVal) { + this.valueSync = Number(newVal); + }, + modelValue(newVal) { + this.valueSync = Number(newVal); + }, + }, + computed: { + stars() { + const value = this.valueSync ? this.valueSync : 0; + const starList = []; + const floorValue = Math.floor(value); + const ceilValue = Math.ceil(value); + for (let i = 0; i < this.max; i++) { + if (floorValue > i) { + starList.push({ + activeWitch: "100%" + }); + } else if (ceilValue - 1 === i) { + starList.push({ + activeWitch: (value - floorValue) * 100 + "%" + }); + } else { + starList.push({ + activeWitch: "0" + }); + } + } + return starList; + }, + + marginNumber() { + return Number(this.margin) + } + }, + created() { + this.valueSync = Number(this.value || this.modelValue); + this._rateBoxLeft = 0 + this._oldValue = null + }, + mounted() { + setTimeout(() => { + this._getSize() + }, 100) + // #ifdef H5 + this.PC = this.IsPC() + // #endif + }, + methods: { + touchstart(e) { + // #ifdef H5 + if (this.IsPC()) return + // #endif + if (this.readonly || this.disabled) return + const { + clientX, + screenX + } = e.changedTouches[0] + // TODO 做一下兼容,只有 Nvue 下才有 screenX,其他平台式 clientX + this._getRateCount(clientX || screenX) + }, + touchmove(e) { + // #ifdef H5 + if (this.IsPC()) return + // #endif + if (this.readonly || this.disabled || !this.touchable) return + const { + clientX, + screenX + } = e.changedTouches[0] + this._getRateCount(clientX || screenX) + }, + + /** + * 兼容 PC @tian + */ + + mousedown(e) { + // #ifdef H5 + if (!this.IsPC()) return + if (this.readonly || this.disabled) return + const { + clientX, + } = e + this.userLastRate = this.valueSync + this._getRateCount(clientX) + this.userRated = true + // #endif + }, + mousemove(e) { + // #ifdef H5 + if (!this.IsPC()) return + if (this.userRated) return + if (this.userMouseFristMove) { + console.log('---mousemove----', this.valueSync); + this.userLastRate = this.valueSync + this.userMouseFristMove = false + } + if (this.readonly || this.disabled || !this.touchable) return + const { + clientX, + } = e + this._getRateCount(clientX) + // #endif + }, + mouseleave(e) { + // #ifdef H5 + if (!this.IsPC()) return + if (this.readonly || this.disabled || !this.touchable) return + if (this.userRated) { + this.userRated = false + return + } + this.valueSync = this.userLastRate + // #endif + }, + // #ifdef H5 + IsPC() { + var userAgentInfo = navigator.userAgent; + var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]; + var flag = true; + for (let v = 0; v < Agents.length - 1; v++) { + if (userAgentInfo.indexOf(Agents[v]) > 0) { + flag = false; + break; + } + } + return flag; + }, + // #endif + + /** + * 获取星星个数 + */ + _getRateCount(clientX) { + this._getSize() + const size = Number(this.size) + if (isNaN(size)) { + return new Error('size 属性只能设置为数字') + } + const rateMoveRange = clientX - this._rateBoxLeft + let index = parseInt(rateMoveRange / (size + this.marginNumber)) + index = index < 0 ? 0 : index; + index = index > this.max ? this.max : index; + const range = parseInt(rateMoveRange - (size + this.marginNumber) * index); + let value = 0; + if (this._oldValue === index && !this.PC) return; + this._oldValue = index; + if (this.allowHalf) { + if (range > (size / 2)) { + value = index + 1 + } else { + value = index + 0.5 + } + } else { + value = index + 1 + } + + value = Math.max(0.5, Math.min(value, this.max)) + this.valueSync = value + this._onChange() + }, + + /** + * 触发动态修改 + */ + _onChange() { + + this.$emit("input", this.valueSync); + this.$emit("update:modelValue", this.valueSync); + this.$emit("change", { + value: this.valueSync + }); + }, + /** + * 获取星星距离屏幕左侧距离 + */ + _getSize() { + // #ifndef APP-NVUE + uni.createSelectorQuery() + .in(this) + .select('.uni-rate') + .boundingClientRect() + .exec(ret => { + if (ret) { + this._rateBoxLeft = ret[0].left + } + }) + // #endif + // #ifdef APP-NVUE + dom.getComponentRect(this.$refs['uni-rate'], (ret) => { + const size = ret.size + if (size) { + this._rateBoxLeft = size.left + } + }) + // #endif + } + } + }; +</script> + +<style lang="scss"> + .uni-rate { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + line-height: 1; + font-size: 0; + flex-direction: row; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + .uni-rate__icon { + position: relative; + line-height: 1; + font-size: 0; + } + + .uni-rate__icon-on { + overflow: hidden; + position: absolute; + top: 0; + left: 0; + line-height: 1; + text-align: left; + } + + .uni-cursor-not-allowed { + /* #ifdef H5 */ + cursor: not-allowed !important; + /* #endif */ + } +</style> diff --git a/uni_modules/uni-rate/package.json b/uni_modules/uni-rate/package.json new file mode 100644 index 0000000..64e8e33 --- /dev/null +++ b/uni_modules/uni-rate/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-rate", + "displayName": "uni-rate 评分", + "version": "1.3.1", + "description": "Rate 评分组件,可自定义评分星星图标的大小、间隔、评分数。", + "keywords": [ + "uni-ui", + "uniui", + "评分" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-rate/readme.md b/uni_modules/uni-rate/readme.md new file mode 100644 index 0000000..eae7b5c --- /dev/null +++ b/uni_modules/uni-rate/readme.md @@ -0,0 +1,12 @@ + + +## Rate 评分 +> **组件名:uni-rate** +> 代码块: `uRate` +> 关联组件:`uni-icons` + + +评分组件,多用于购买商品后,对商品进行评价等场景 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-rate) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-row/changelog.md b/uni_modules/uni-row/changelog.md new file mode 100644 index 0000000..5b465bc --- /dev/null +++ b/uni_modules/uni-row/changelog.md @@ -0,0 +1,10 @@ +## 1.0.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-row](https://uniapp.dcloud.io/component/uniui/uni-row) +## 0.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 0.0.4(2021-05-12) +- 新增 组件示例地址 +## 0.0.3(2021-02-05) +- 调整为uni_modules目录规范 +- 新增uni-row组件 diff --git a/uni_modules/uni-row/components/uni-col/uni-col.vue b/uni_modules/uni-row/components/uni-col/uni-col.vue new file mode 100644 index 0000000..84e2deb --- /dev/null +++ b/uni_modules/uni-row/components/uni-col/uni-col.vue @@ -0,0 +1,317 @@ +<template> + <!-- #ifndef APP-NVUE --> + <view :class="['uni-col', sizeClass, pointClassList]" :style="{ + paddingLeft:`${Number(gutter)}rpx`, + paddingRight:`${Number(gutter)}rpx`, + }"> + <slot></slot> + </view> + <!-- #endif --> + <!-- #ifdef APP-NVUE --> + <!-- 在nvue上,类名样式不生效,换为style --> + <!-- 设置right正值失效,设置 left 负值 --> + <view :class="['uni-col']" :style="{ + paddingLeft:`${Number(gutter)}rpx`, + paddingRight:`${Number(gutter)}rpx`, + width:`${nvueWidth}rpx`, + position:'relative', + marginLeft:`${marginLeft}rpx`, + left:`${right === 0 ? left : -right}rpx` + }"> + <slot></slot> + </view> + <!-- #endif --> +</template> + +<script> + /** + * Col 布局-列 + * @description 搭配uni-row使用,构建布局。 + * @tutorial https://ext.dcloud.net.cn/plugin?id=3958 + * + * @property {span} type = Number 栅格占据的列数 + * 默认 24 + * @property {offset} type = Number 栅格左侧的间隔格数 + * @property {push} type = Number 栅格向右移动格数 + * @property {pull} type = Number 栅格向左移动格数 + * @property {xs} type = [Number, Object] <768px 响应式栅格数或者栅格属性对象 + * @description Number时表示在此屏幕宽度下,栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4} + * @property {sm} type = [Number, Object] ≥768px 响应式栅格数或者栅格属性对象 + * @description Number时表示在此屏幕宽度下,栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4} + * @property {md} type = [Number, Object] ≥992px 响应式栅格数或者栅格属性对象 + * @description Number时表示在此屏幕宽度下,栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4} + * @property {lg} type = [Number, Object] ≥1200px 响应式栅格数或者栅格属性对象 + * @description Number时表示在此屏幕宽度下,栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4} + * @property {xl} type = [Number, Object] ≥1920px 响应式栅格数或者栅格属性对象 + * @description Number时表示在此屏幕宽度下,栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4} + */ + const ComponentClass = 'uni-col'; + + // -1 默认值,因为在微信小程序端只给Number会有默认值0 + export default { + name: 'uniCol', + // #ifdef MP-WEIXIN + options: { + virtualHost: true // 在微信小程序中将组件节点渲染为虚拟节点,更加接近Vue组件的表现 + }, + // #endif + props: { + span: { + type: Number, + default: 24 + }, + offset: { + type: Number, + default: -1 + }, + pull: { + type: Number, + default: -1 + }, + push: { + type: Number, + default: -1 + }, + xs: [Number, Object], + sm: [Number, Object], + md: [Number, Object], + lg: [Number, Object], + xl: [Number, Object] + }, + data() { + return { + gutter: 0, + sizeClass: '', + parentWidth: 0, + nvueWidth: 0, + marginLeft: 0, + right: 0, + left: 0 + } + }, + created() { + // 字节小程序中,在computed中读取$parent为undefined + let parent = this.$parent; + + while (parent && parent.$options.componentName !== 'uniRow') { + parent = parent.$parent; + } + + this.updateGutter(parent.gutter) + parent.$watch('gutter', (gutter) => { + this.updateGutter(gutter) + }) + + // #ifdef APP-NVUE + this.updateNvueWidth(parent.width) + parent.$watch('width', (width) => { + this.updateNvueWidth(width) + }) + // #endif + }, + computed: { + sizeList() { + let { + span, + offset, + pull, + push + } = this; + + return { + span, + offset, + pull, + push + } + }, + // #ifndef APP-NVUE + pointClassList() { + let classList = []; + + ['xs', 'sm', 'md', 'lg', 'xl'].forEach(point => { + const props = this[point]; + if (typeof props === 'number') { + classList.push(`${ComponentClass}-${point}-${props}`) + } else if (typeof props === 'object' && props) { + Object.keys(props).forEach(pointProp => { + classList.push( + pointProp === 'span' ? + `${ComponentClass}-${point}-${props[pointProp]}` : + `${ComponentClass}-${point}-${pointProp}-${props[pointProp]}` + ) + }) + } + }); + + // 支付宝小程序使用 :class=[ ['a','b'] ],渲染错误 + return classList.join(' '); + } + // #endif + }, + methods: { + updateGutter(parentGutter) { + parentGutter = Number(parentGutter); + if (!isNaN(parentGutter)) { + this.gutter = parentGutter / 2 + } + }, + // #ifdef APP-NVUE + updateNvueWidth(width) { + // 用于在nvue端,span,offset,pull,push的计算 + this.parentWidth = width; + ['span', 'offset', 'pull', 'push'].forEach(size => { + const curSize = this[size]; + if ((curSize || curSize === 0) && curSize !== -1) { + let RPX = 1 / 24 * curSize * width + RPX = Number(RPX); + switch (size) { + case 'span': + this.nvueWidth = RPX + break; + case 'offset': + this.marginLeft = RPX + break; + case 'pull': + this.right = RPX + break; + case 'push': + this.left = RPX + break; + } + } + }); + } + // #endif + }, + watch: { + sizeList: { + immediate: true, + handler(newVal) { + // #ifndef APP-NVUE + let classList = []; + for (let size in newVal) { + const curSize = newVal[size]; + if ((curSize || curSize === 0) && curSize !== -1) { + classList.push( + size === 'span' ? + `${ComponentClass}-${curSize}` : + `${ComponentClass}-${size}-${curSize}` + ) + } + } + // 支付宝小程序使用 :class=[ ['a','b'] ],渲染错误 + this.sizeClass = classList.join(' '); + // #endif + // #ifdef APP-NVUE + this.updateNvueWidth(this.parentWidth); + // #endif + } + } + } + } +</script> + +<style lang='scss' scoped> + /* breakpoints */ + $--sm: 768px !default; + $--md: 992px !default; + $--lg: 1200px !default; + $--xl: 1920px !default; + + $breakpoints: ('xs' : (max-width: $--sm - 1), + 'sm' : (min-width: $--sm), + 'md' : (min-width: $--md), + 'lg' : (min-width: $--lg), + 'xl' : (min-width: $--xl)); + + $layout-namespace: ".uni-"; + $col: $layout-namespace+"col"; + + @function getSize($size) { + /* TODO 1/24 * $size * 100 * 1%; 使用计算后的值,为了解决 vue3 控制台报错 */ + @return 0.04166666666 * $size * 100 * 1%; + } + + @mixin res($key, $map:$breakpoints) { + @if map-has-key($map, $key) { + @media screen and #{inspect(map-get($map,$key))} { + @content; + } + } + + @else { + @warn "Undeinfed point: `#{$key}`"; + } + } + + /* #ifndef APP-NVUE */ + #{$col} { + float: left; + box-sizing: border-box; + } + + #{$col}-0 { + /* #ifdef APP-NVUE */ + width: 0; + height: 0; + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + margin-left: 0; + /* #endif */ + /* #ifndef APP-NVUE */ + display: none; + /* #endif */ + } + + @for $i from 0 through 24 { + #{$col}-#{$i} { + width: getSize($i); + } + + #{$col}-offset-#{$i} { + margin-left: getSize($i); + } + + #{$col}-pull-#{$i} { + position: relative; + right: getSize($i); + } + + #{$col}-push-#{$i} { + position: relative; + left: getSize($i); + } + } + + @each $point in map-keys($breakpoints) { + @include res($point) { + #{$col}-#{$point}-0 { + display: none; + } + + @for $i from 0 through 24 { + #{$col}-#{$point}-#{$i} { + width: getSize($i); + } + + #{$col}-#{$point}-offset-#{$i} { + margin-left: getSize($i); + } + + #{$col}-#{$point}-pull-#{$i} { + position: relative; + right: getSize($i); + } + + #{$col}-#{$point}-push-#{$i} { + position: relative; + left: getSize($i); + } + } + } + } + + /* #endif */ +</style> diff --git a/uni_modules/uni-row/components/uni-row/uni-row.vue b/uni_modules/uni-row/components/uni-row/uni-row.vue new file mode 100644 index 0000000..f8e8542 --- /dev/null +++ b/uni_modules/uni-row/components/uni-row/uni-row.vue @@ -0,0 +1,190 @@ +<template> + <view :class="[ 'uni-row', typeClass , justifyClass, alignClass, ]" :style="{ + marginLeft:`${Number(marginValue)}rpx`, + marginRight:`${Number(marginValue)}rpx`, + }"> + <slot></slot> + </view> +</template> + +<script> + const ComponentClass = 'uni-row'; + const modifierSeparator = '--'; + /** + * Row 布局-行 + * @description 流式栅格系统,随着屏幕或视口分为 24 份,可以迅速简便地创建布局。 + * @tutorial https://ext.dcloud.net.cn/plugin?id=3958 + * + * @property {gutter} type = Number 栅格间隔 + * @property {justify} type = String flex 布局下的水平排列方式 + * 可选 start/end/center/space-around/space-between start + * 默认值 start + * @property {align} type = String flex 布局下的垂直排列方式 + * 可选 top/middle/bottom + * 默认值 top + * @property {width} type = String|Number nvue下需要自行配置宽度用于计算 + * 默认值 750 + */ + + + export default { + name: 'uniRow', + componentName: 'uniRow', + // #ifdef MP-WEIXIN + options: { + virtualHost: true // 在微信小程序中将组件节点渲染为虚拟节点,更加接近Vue组件的表现,可使用flex布局 + }, + // #endif + props: { + type: String, + gutter: Number, + justify: { + type: String, + default: 'start' + }, + align: { + type: String, + default: 'top' + }, + // nvue如果使用span等属性,需要配置宽度 + width: { + type: [String, Number], + default: 750 + } + }, + created() { + // #ifdef APP-NVUE + this.type = 'flex'; + // #endif + }, + computed: { + marginValue() { + // #ifndef APP-NVUE + if (this.gutter) { + return -(this.gutter / 2); + } + // #endif + return 0; + }, + typeClass() { + return this.type === 'flex' ? `${ComponentClass + modifierSeparator}flex` : ''; + }, + justifyClass() { + return this.justify !== 'start' ? `${ComponentClass + modifierSeparator}flex-justify-${this.justify}` : '' + }, + alignClass() { + return this.align !== 'top' ? `${ComponentClass + modifierSeparator}flex-align-${this.align}` : '' + } + } + }; +</script> + +<style lang="scss"> + $layout-namespace: ".uni-"; + $row:$layout-namespace+"row"; + $modifier-separator: "--"; + + @mixin utils-clearfix { + $selector: &; + + @at-root { + + /* #ifndef APP-NVUE */ + #{$selector}::before, + #{$selector}::after { + display: table; + content: ""; + } + + #{$selector}::after { + clear: both; + } + + /* #endif */ + } + + } + + @mixin utils-flex ($direction: row) { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: $direction; + } + + @mixin set-flex($state) { + @at-root &-#{$state} { + @content + } + } + + #{$row} { + position: relative; + flex-direction: row; + + /* #ifdef APP-NVUE */ + flex: 1; + /* #endif */ + + /* #ifndef APP-NVUE */ + box-sizing: border-box; + /* #endif */ + + // 非nvue使用float布局 + @include utils-clearfix; + + // 在QQ、字节、百度小程序平台,编译后使用shadow dom,不可使用flex布局,使用float + @at-root { + + /* #ifndef MP-QQ || MP-TOUTIAO || MP-BAIDU */ + &#{$modifier-separator}flex { + @include utils-flex; + flex-wrap: wrap; + flex: 1; + + &:before, + &:after { + /* #ifndef APP-NVUE */ + display: none; + /* #endif */ + } + + @include set-flex(justify-center) { + justify-content: center; + } + + @include set-flex(justify-end) { + justify-content: flex-end; + } + + @include set-flex(justify-space-between) { + justify-content: space-between; + } + + @include set-flex(justify-space-around) { + justify-content: space-around; + } + + @include set-flex(align-middle) { + align-items: center; + } + + @include set-flex(align-bottom) { + align-items: flex-end; + } + } + + /* #endif */ + } + + } + + // 字节、QQ配置后不生效 + // 此处用法无法使用scoped + /* #ifdef MP-WEIXIN || MP-TOUTIAO || MP-QQ */ + :host { + display: block; + } + + /* #endif */ +</style> diff --git a/uni_modules/uni-row/package.json b/uni_modules/uni-row/package.json new file mode 100644 index 0000000..3f52fa6 --- /dev/null +++ b/uni_modules/uni-row/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-row", + "displayName": "uni-row 布局-行", + "version": "1.0.0", + "description": "流式栅格系统,随着屏幕或视口分为 24 份,可以迅速简便地创建布局。", + "keywords": [ + "uni-ui", + "uniui", + "栅格", + "布局", + "layout" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-row/readme.md b/uni_modules/uni-row/readme.md new file mode 100644 index 0000000..3c9c8b9 --- /dev/null +++ b/uni_modules/uni-row/readme.md @@ -0,0 +1,10 @@ +## Layout 布局 + +> **组件名 uni-row、uni-col** +> 代码块: `uRow`、`uCol` + + +流式栅格系统,随着屏幕或视口分为 24 份,可以迅速简便地创建布局。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-row) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-scss/changelog.md b/uni_modules/uni-scss/changelog.md new file mode 100644 index 0000000..b863bb0 --- /dev/null +++ b/uni_modules/uni-scss/changelog.md @@ -0,0 +1,8 @@ +## 1.0.3(2022-01-21) +- 优化 组件示例 +## 1.0.2(2021-11-22) +- 修复 / 符号在 vue 不同版本兼容问题引起的报错问题 +## 1.0.1(2021-11-22) +- 修复 vue3中scss语法兼容问题 +## 1.0.0(2021-11-18) +- init diff --git a/uni_modules/uni-scss/index.scss b/uni_modules/uni-scss/index.scss new file mode 100644 index 0000000..1744a5f --- /dev/null +++ b/uni_modules/uni-scss/index.scss @@ -0,0 +1 @@ +@import './styles/index.scss'; diff --git a/uni_modules/uni-scss/package.json b/uni_modules/uni-scss/package.json new file mode 100644 index 0000000..7cc0ccb --- /dev/null +++ b/uni_modules/uni-scss/package.json @@ -0,0 +1,82 @@ +{ + "id": "uni-scss", + "displayName": "uni-scss 辅助样式", + "version": "1.0.3", + "description": "uni-sass是uni-ui提供的一套全局样式 ,通过一些简单的类名和sass变量,实现简单的页面布局操作,比如颜色、边距、圆角等。", + "keywords": [ + "uni-scss", + "uni-ui", + "辅助样式" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "JS SDK", + "通用 SDK" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "n", + "联盟": "n" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-scss/readme.md b/uni_modules/uni-scss/readme.md new file mode 100644 index 0000000..b7d1c25 --- /dev/null +++ b/uni_modules/uni-scss/readme.md @@ -0,0 +1,4 @@ +`uni-sass` 是 `uni-ui`提供的一套全局样式 ,通过一些简单的类名和`sass`变量,实现简单的页面布局操作,比如颜色、边距、圆角等。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-sass) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-scss/styles/index.scss b/uni_modules/uni-scss/styles/index.scss new file mode 100644 index 0000000..ffac4fe --- /dev/null +++ b/uni_modules/uni-scss/styles/index.scss @@ -0,0 +1,7 @@ +@import './setting/_variables.scss'; +@import './setting/_border.scss'; +@import './setting/_color.scss'; +@import './setting/_space.scss'; +@import './setting/_radius.scss'; +@import './setting/_text.scss'; +@import './setting/_styles.scss'; diff --git a/uni_modules/uni-scss/styles/setting/_border.scss b/uni_modules/uni-scss/styles/setting/_border.scss new file mode 100644 index 0000000..12a11c3 --- /dev/null +++ b/uni_modules/uni-scss/styles/setting/_border.scss @@ -0,0 +1,3 @@ +.uni-border { + border: 1px $uni-border-1 solid; +} \ No newline at end of file diff --git a/uni_modules/uni-scss/styles/setting/_color.scss b/uni_modules/uni-scss/styles/setting/_color.scss new file mode 100644 index 0000000..1ededd9 --- /dev/null +++ b/uni_modules/uni-scss/styles/setting/_color.scss @@ -0,0 +1,66 @@ + +// TODO 暂时不需要 class ,需要用户使用变量实现 ,如果使用类名其实并不推荐 +// @mixin get-styles($k,$c) { +// @if $k == size or $k == weight{ +// font-#{$k}:#{$c} +// }@else{ +// #{$k}:#{$c} +// } +// } +$uni-ui-color:( + // 主色 + primary: $uni-primary, + primary-disable: $uni-primary-disable, + primary-light: $uni-primary-light, + // 辅助色 + success: $uni-success, + success-disable: $uni-success-disable, + success-light: $uni-success-light, + warning: $uni-warning, + warning-disable: $uni-warning-disable, + warning-light: $uni-warning-light, + error: $uni-error, + error-disable: $uni-error-disable, + error-light: $uni-error-light, + info: $uni-info, + info-disable: $uni-info-disable, + info-light: $uni-info-light, + // 中性色 + main-color: $uni-main-color, + base-color: $uni-base-color, + secondary-color: $uni-secondary-color, + extra-color: $uni-extra-color, + // 背景色 + bg-color: $uni-bg-color, + // 边框颜色 + border-1: $uni-border-1, + border-2: $uni-border-2, + border-3: $uni-border-3, + border-4: $uni-border-4, + // 黑色 + black:$uni-black, + // 白色 + white:$uni-white, + // 透明 + transparent:$uni-transparent +) !default; +@each $key, $child in $uni-ui-color { + .uni-#{"" + $key} { + color: $child; + } + .uni-#{"" + $key}-bg { + background-color: $child; + } +} +.uni-shadow-sm { + box-shadow: $uni-shadow-sm; +} +.uni-shadow-base { + box-shadow: $uni-shadow-base; +} +.uni-shadow-lg { + box-shadow: $uni-shadow-lg; +} +.uni-mask { + background-color:$uni-mask; +} diff --git a/uni_modules/uni-scss/styles/setting/_radius.scss b/uni_modules/uni-scss/styles/setting/_radius.scss new file mode 100644 index 0000000..9a0428b --- /dev/null +++ b/uni_modules/uni-scss/styles/setting/_radius.scss @@ -0,0 +1,55 @@ +@mixin radius($r,$d:null ,$important: false){ + $radius-value:map-get($uni-radius, $r) if($important, !important, null); + // Key exists within the $uni-radius variable + @if (map-has-key($uni-radius, $r) and $d){ + @if $d == t { + border-top-left-radius:$radius-value; + border-top-right-radius:$radius-value; + }@else if $d == r { + border-top-right-radius:$radius-value; + border-bottom-right-radius:$radius-value; + }@else if $d == b { + border-bottom-left-radius:$radius-value; + border-bottom-right-radius:$radius-value; + }@else if $d == l { + border-top-left-radius:$radius-value; + border-bottom-left-radius:$radius-value; + }@else if $d == tl { + border-top-left-radius:$radius-value; + }@else if $d == tr { + border-top-right-radius:$radius-value; + }@else if $d == br { + border-bottom-right-radius:$radius-value; + }@else if $d == bl { + border-bottom-left-radius:$radius-value; + } + }@else{ + border-radius:$radius-value; + } +} + +@each $key, $child in $uni-radius { + @if($key){ + .uni-radius-#{"" + $key} { + @include radius($key) + } + }@else{ + .uni-radius { + @include radius($key) + } + } +} + +@each $direction in t, r, b, l,tl, tr, br, bl { + @each $key, $child in $uni-radius { + @if($key){ + .uni-radius-#{"" + $direction}-#{"" + $key} { + @include radius($key,$direction,false) + } + }@else{ + .uni-radius-#{$direction} { + @include radius($key,$direction,false) + } + } + } +} diff --git a/uni_modules/uni-scss/styles/setting/_space.scss b/uni_modules/uni-scss/styles/setting/_space.scss new file mode 100644 index 0000000..3c89528 --- /dev/null +++ b/uni_modules/uni-scss/styles/setting/_space.scss @@ -0,0 +1,56 @@ + +@mixin fn($space,$direction,$size,$n) { + @if $n { + #{$space}-#{$direction}: #{$size*$uni-space-root}px + } @else { + #{$space}-#{$direction}: #{-$size*$uni-space-root}px + } +} +@mixin get-styles($direction,$i,$space,$n){ + @if $direction == t { + @include fn($space, top,$i,$n); + } + @if $direction == r { + @include fn($space, right,$i,$n); + } + @if $direction == b { + @include fn($space, bottom,$i,$n); + } + @if $direction == l { + @include fn($space, left,$i,$n); + } + @if $direction == x { + @include fn($space, left,$i,$n); + @include fn($space, right,$i,$n); + } + @if $direction == y { + @include fn($space, top,$i,$n); + @include fn($space, bottom,$i,$n); + } + @if $direction == a { + @if $n { + #{$space}:#{$i*$uni-space-root}px; + } @else { + #{$space}:#{-$i*$uni-space-root}px; + } + } +} + +@each $orientation in m,p { + $space: margin; + @if $orientation == m { + $space: margin; + } @else { + $space: padding; + } + @for $i from 0 through 16 { + @each $direction in t, r, b, l, x, y, a { + .uni-#{$orientation}#{$direction}-#{$i} { + @include get-styles($direction,$i,$space,true); + } + .uni-#{$orientation}#{$direction}-n#{$i} { + @include get-styles($direction,$i,$space,false); + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-scss/styles/setting/_styles.scss b/uni_modules/uni-scss/styles/setting/_styles.scss new file mode 100644 index 0000000..689afec --- /dev/null +++ b/uni_modules/uni-scss/styles/setting/_styles.scss @@ -0,0 +1,167 @@ +/* #ifndef APP-NVUE */ + +$-color-white:#fff; +$-color-black:#000; +@mixin base-style($color) { + color: #fff; + background-color: $color; + border-color: mix($-color-black, $color, 8%); + &:not([hover-class]):active { + background: mix($-color-black, $color, 10%); + border-color: mix($-color-black, $color, 20%); + color: $-color-white; + outline: none; + } +} +@mixin is-color($color) { + @include base-style($color); + &[loading] { + @include base-style($color); + &::before { + margin-right:5px; + } + } + &[disabled] { + &, + &[loading], + &:not([hover-class]):active { + color: $-color-white; + border-color: mix(darken($color,10%), $-color-white); + background-color: mix($color, $-color-white); + } + } + +} +@mixin base-plain-style($color) { + color:$color; + background-color: mix($-color-white, $color, 90%); + border-color: mix($-color-white, $color, 70%); + &:not([hover-class]):active { + background: mix($-color-white, $color, 80%); + color: $color; + outline: none; + border-color: mix($-color-white, $color, 50%); + } +} +@mixin is-plain($color){ + &[plain] { + @include base-plain-style($color); + &[loading] { + @include base-plain-style($color); + &::before { + margin-right:5px; + } + } + &[disabled] { + &, + &:active { + color: mix($-color-white, $color, 40%); + background-color: mix($-color-white, $color, 90%); + border-color: mix($-color-white, $color, 80%); + } + } + } +} + + +.uni-btn { + margin: 5px; + color: #393939; + border:1px solid #ccc; + font-size: 16px; + font-weight: 200; + background-color: #F9F9F9; + // TODO 暂时处理边框隐藏一边的问题 + overflow: visible; + &::after{ + border: none; + } + + &:not([type]),&[type=default] { + color: #999; + &[loading] { + background: none; + &::before { + margin-right:5px; + } + } + + + + &[disabled]{ + color: mix($-color-white, #999, 60%); + &, + &[loading], + &:active { + color: mix($-color-white, #999, 60%); + background-color: mix($-color-white,$-color-black , 98%); + border-color: mix($-color-white, #999, 85%); + } + } + + &[plain] { + color: #999; + background: none; + border-color: $uni-border-1; + &:not([hover-class]):active { + background: none; + color: mix($-color-white, $-color-black, 80%); + border-color: mix($-color-white, $-color-black, 90%); + outline: none; + } + &[disabled]{ + &, + &[loading], + &:active { + background: none; + color: mix($-color-white, #999, 60%); + border-color: mix($-color-white, #999, 85%); + } + } + } + } + + &:not([hover-class]):active { + color: mix($-color-white, $-color-black, 50%); + } + + &[size=mini] { + font-size: 16px; + font-weight: 200; + border-radius: 8px; + } + + + + &.uni-btn-small { + font-size: 14px; + } + &.uni-btn-mini { + font-size: 12px; + } + + &.uni-btn-radius { + border-radius: 999px; + } + &[type=primary] { + @include is-color($uni-primary); + @include is-plain($uni-primary) + } + &[type=success] { + @include is-color($uni-success); + @include is-plain($uni-success) + } + &[type=error] { + @include is-color($uni-error); + @include is-plain($uni-error) + } + &[type=warning] { + @include is-color($uni-warning); + @include is-plain($uni-warning) + } + &[type=info] { + @include is-color($uni-info); + @include is-plain($uni-info) + } +} +/* #endif */ diff --git a/uni_modules/uni-scss/styles/setting/_text.scss b/uni_modules/uni-scss/styles/setting/_text.scss new file mode 100644 index 0000000..a34d08f --- /dev/null +++ b/uni_modules/uni-scss/styles/setting/_text.scss @@ -0,0 +1,24 @@ +@mixin get-styles($k,$c) { + @if $k == size or $k == weight{ + font-#{$k}:#{$c} + }@else{ + #{$k}:#{$c} + } +} + +@each $key, $child in $uni-headings { + /* #ifndef APP-NVUE */ + .uni-#{$key} { + @each $k, $c in $child { + @include get-styles($k,$c) + } + } + /* #endif */ + /* #ifdef APP-NVUE */ + .container .uni-#{$key} { + @each $k, $c in $child { + @include get-styles($k,$c) + } + } + /* #endif */ +} diff --git a/uni_modules/uni-scss/styles/setting/_variables.scss b/uni_modules/uni-scss/styles/setting/_variables.scss new file mode 100644 index 0000000..557d3d7 --- /dev/null +++ b/uni_modules/uni-scss/styles/setting/_variables.scss @@ -0,0 +1,146 @@ +// @use "sass:math"; +@import '../tools/functions.scss'; +// 间距基础倍数 +$uni-space-root: 2 !default; +// 边框半径默认值 +$uni-radius-root:5px !default; +$uni-radius: () !default; +// 边框半径断点 +$uni-radius: map-deep-merge( + ( + 0: 0, + // TODO 当前版本暂时不支持 sm 属性 + // 'sm': math.div($uni-radius-root, 2), + null: $uni-radius-root, + 'lg': $uni-radius-root * 2, + 'xl': $uni-radius-root * 6, + 'pill': 9999px, + 'circle': 50% + ), + $uni-radius +); +// 字体家族 +$body-font-family: 'Roboto', sans-serif !default; +// 文本 +$heading-font-family: $body-font-family !default; +$uni-headings: () !default; +$letterSpacing: -0.01562em; +$uni-headings: map-deep-merge( + ( + 'h1': ( + size: 32px, + weight: 300, + line-height: 50px, + // letter-spacing:-0.01562em + ), + 'h2': ( + size: 28px, + weight: 300, + line-height: 40px, + // letter-spacing: -0.00833em + ), + 'h3': ( + size: 24px, + weight: 400, + line-height: 32px, + // letter-spacing: normal + ), + 'h4': ( + size: 20px, + weight: 400, + line-height: 30px, + // letter-spacing: 0.00735em + ), + 'h5': ( + size: 16px, + weight: 400, + line-height: 24px, + // letter-spacing: normal + ), + 'h6': ( + size: 14px, + weight: 500, + line-height: 18px, + // letter-spacing: 0.0125em + ), + 'subtitle': ( + size: 12px, + weight: 400, + line-height: 20px, + // letter-spacing: 0.00937em + ), + 'body': ( + font-size: 14px, + font-weight: 400, + line-height: 22px, + // letter-spacing: 0.03125em + ), + 'caption': ( + 'size': 12px, + 'weight': 400, + 'line-height': 20px, + // 'letter-spacing': 0.03333em, + // 'text-transform': false + ) + ), + $uni-headings +); + + + +// 主色 +$uni-primary: #2979ff !default; +$uni-primary-disable:lighten($uni-primary,20%) !default; +$uni-primary-light: lighten($uni-primary,25%) !default; + +// 辅助色 +// 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。 +$uni-success: #18bc37 !default; +$uni-success-disable:lighten($uni-success,20%) !default; +$uni-success-light: lighten($uni-success,25%) !default; + +$uni-warning: #f3a73f !default; +$uni-warning-disable:lighten($uni-warning,20%) !default; +$uni-warning-light: lighten($uni-warning,25%) !default; + +$uni-error: #e43d33 !default; +$uni-error-disable:lighten($uni-error,20%) !default; +$uni-error-light: lighten($uni-error,25%) !default; + +$uni-info: #8f939c !default; +$uni-info-disable:lighten($uni-info,20%) !default; +$uni-info-light: lighten($uni-info,25%) !default; + +// 中性色 +// 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。 +$uni-main-color: #3a3a3a !default; // 主要文字 +$uni-base-color: #6a6a6a !default; // 常规文字 +$uni-secondary-color: #909399 !default; // 次要文字 +$uni-extra-color: #c7c7c7 !default; // 辅助说明 + +// 边框颜色 +$uni-border-1: #F0F0F0 !default; +$uni-border-2: #EDEDED !default; +$uni-border-3: #DCDCDC !default; +$uni-border-4: #B9B9B9 !default; + +// 常规色 +$uni-black: #000000 !default; +$uni-white: #ffffff !default; +$uni-transparent: rgba($color: #000000, $alpha: 0) !default; + +// 背景色 +$uni-bg-color: #f7f7f7 !default; + +/* 水平间距 */ +$uni-spacing-sm: 8px !default; +$uni-spacing-base: 15px !default; +$uni-spacing-lg: 30px !default; + +// 阴影 +$uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5) !default; +$uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2) !default; +$uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5) !default; + +// 蒙版 +$uni-mask: rgba($color: #000000, $alpha: 0.4) !default; diff --git a/uni_modules/uni-scss/styles/tools/functions.scss b/uni_modules/uni-scss/styles/tools/functions.scss new file mode 100644 index 0000000..ac6f63e --- /dev/null +++ b/uni_modules/uni-scss/styles/tools/functions.scss @@ -0,0 +1,19 @@ +// 合并 map +@function map-deep-merge($parent-map, $child-map){ + $result: $parent-map; + @each $key, $child in $child-map { + $parent-has-key: map-has-key($result, $key); + $parent-value: map-get($result, $key); + $parent-type: type-of($parent-value); + $child-type: type-of($child); + $parent-is-map: $parent-type == map; + $child-is-map: $child-type == map; + + @if (not $parent-has-key) or ($parent-type != $child-type) or (not ($parent-is-map and $child-is-map)){ + $result: map-merge($result, ( $key: $child )); + }@else { + $result: map-merge($result, ( $key: map-deep-merge($parent-value, $child) )); + } + } + @return $result; +}; diff --git a/uni_modules/uni-scss/theme.scss b/uni_modules/uni-scss/theme.scss new file mode 100644 index 0000000..80ee62f --- /dev/null +++ b/uni_modules/uni-scss/theme.scss @@ -0,0 +1,31 @@ +// 间距基础倍数 +$uni-space-root: 2; +// 边框半径默认值 +$uni-radius-root:5px; +// 主色 +$uni-primary: #2979ff; +// 辅助色 +$uni-success: #4cd964; +// 警告色 +$uni-warning: #f0ad4e; +// 错误色 +$uni-error: #dd524d; +// 描述色 +$uni-info: #909399; +// 中性色 +$uni-main-color: #303133; +$uni-base-color: #606266; +$uni-secondary-color: #909399; +$uni-extra-color: #C0C4CC; +// 背景色 +$uni-bg-color: #f5f5f5; +// 边框颜色 +$uni-border-1: #DCDFE6; +$uni-border-2: #E4E7ED; +$uni-border-3: #EBEEF5; +$uni-border-4: #F2F6FC; + +// 常规色 +$uni-black: #000000; +$uni-white: #ffffff; +$uni-transparent: rgba($color: #000000, $alpha: 0); diff --git a/uni_modules/uni-scss/variables.scss b/uni_modules/uni-scss/variables.scss new file mode 100644 index 0000000..1c062d4 --- /dev/null +++ b/uni_modules/uni-scss/variables.scss @@ -0,0 +1,62 @@ +@import './styles/setting/_variables.scss'; +// 间距基础倍数 +$uni-space-root: 2; +// 边框半径默认值 +$uni-radius-root:5px; + +// 主色 +$uni-primary: #2979ff; +$uni-primary-disable:mix(#fff,$uni-primary,50%); +$uni-primary-light: mix(#fff,$uni-primary,80%); + +// 辅助色 +// 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。 +$uni-success: #18bc37; +$uni-success-disable:mix(#fff,$uni-success,50%); +$uni-success-light: mix(#fff,$uni-success,80%); + +$uni-warning: #f3a73f; +$uni-warning-disable:mix(#fff,$uni-warning,50%); +$uni-warning-light: mix(#fff,$uni-warning,80%); + +$uni-error: #e43d33; +$uni-error-disable:mix(#fff,$uni-error,50%); +$uni-error-light: mix(#fff,$uni-error,80%); + +$uni-info: #8f939c; +$uni-info-disable:mix(#fff,$uni-info,50%); +$uni-info-light: mix(#fff,$uni-info,80%); + +// 中性色 +// 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。 +$uni-main-color: #3a3a3a; // 主要文字 +$uni-base-color: #6a6a6a; // 常规文字 +$uni-secondary-color: #909399; // 次要文字 +$uni-extra-color: #c7c7c7; // 辅助说明 + +// 边框颜色 +$uni-border-1: #F0F0F0; +$uni-border-2: #EDEDED; +$uni-border-3: #DCDCDC; +$uni-border-4: #B9B9B9; + +// 常规色 +$uni-black: #000000; +$uni-white: #ffffff; +$uni-transparent: rgba($color: #000000, $alpha: 0); + +// 背景色 +$uni-bg-color: #f7f7f7; + +/* 水平间距 */ +$uni-spacing-sm: 8px; +$uni-spacing-base: 15px; +$uni-spacing-lg: 30px; + +// 阴影 +$uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5); +$uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2); +$uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5); + +// 蒙版 +$uni-mask: rgba($color: #000000, $alpha: 0.4); diff --git a/uni_modules/uni-search-bar/changelog.md b/uni_modules/uni-search-bar/changelog.md new file mode 100644 index 0000000..2c6571c --- /dev/null +++ b/uni_modules/uni-search-bar/changelog.md @@ -0,0 +1,47 @@ +## 1.3.0(2024-04-22) +- 修复 textColor默认值导致的文字不显示的bug +## 1.2.9(2024-04-17) +- 修复 textColor不生效的bug +## 1.2.8(2024-02-22) +- 修复 清空按钮emit值错误的bug +## 1.2.7(2024-02-21) +- 新增 设置输入框字体颜色:textColor +## 1.2.6(2024-02-20) +- 修复 uni-search-bar在支付宝小程序下样式兼容问题 +## 1.2.5(2024-01-31) +- 修复 uni-search-bar居中问题,现在默认居左,并修复样式偏移问题 +## 1.2.4(2023-05-09) +- 修复 i18n 国际化不正确的 Bug +## 1.2.3(2022-05-24) +- 新增 readonly 属性,组件只读 +## 1.2.2(2022-05-06) +- 修复 vue3 input 事件不生效的bug +## 1.2.1(2022-05-06) +- 修复 多余代码导致的bug +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-search-bar](https://uniapp.dcloud.io/component/uniui/uni-search-bar) +## 1.1.2(2021-08-30) +- 修复 value 属性与 modelValue 属性不兼容的Bug +## 1.1.1(2021-08-24) +- 新增 支持国际化 +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.9(2021-05-12) +- 新增 项目示例地址 +## 1.0.8(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.7(2021-04-15) +- uni-ui 新增 uni-search-bar 的 focus 事件 + +## 1.0.6(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.5(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 支持双向绑定 +- 更改 input 事件的返回值,e={value:Number} --> e=value +- 新增 支持图标插槽 +- 新增 支持 clear、blur 事件 +- 新增 支持 focus 属性 +- 去掉组件背景色 diff --git a/uni_modules/uni-search-bar/components/uni-search-bar/i18n/en.json b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/en.json new file mode 100644 index 0000000..dd083a5 --- /dev/null +++ b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/en.json @@ -0,0 +1,4 @@ +{ + "uni-search-bar.cancel": "cancel", + "uni-search-bar.placeholder": "Search enter content" +} \ No newline at end of file diff --git a/uni_modules/uni-search-bar/components/uni-search-bar/i18n/index.js b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/index.js new file mode 100644 index 0000000..de7509c --- /dev/null +++ b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hans.json b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hans.json new file mode 100644 index 0000000..d2a1ced --- /dev/null +++ b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hans.json @@ -0,0 +1,4 @@ +{ + "uni-search-bar.cancel": "取消", + "uni-search-bar.placeholder": "请输入搜索内容" +} diff --git a/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hant.json b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hant.json new file mode 100644 index 0000000..f1c96bc --- /dev/null +++ b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hant.json @@ -0,0 +1,4 @@ +{ + "uni-search-bar.cancel": "取消", + "uni-search-bar.placeholder": "請輸入搜索內容" +} diff --git a/uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue b/uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue new file mode 100644 index 0000000..6b9b9c1 --- /dev/null +++ b/uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue @@ -0,0 +1,309 @@ +<template> + <view class="uni-searchbar"> + <view :style="{borderRadius:radius+'px',backgroundColor: bgColor}" class="uni-searchbar__box" + @click="searchClick"> + <view class="uni-searchbar__box-icon-search"> + <slot name="searchIcon"> + <uni-icons color="#c0c4cc" size="18" type="search" /> + </slot> + </view> + <input v-if="show || searchVal" :focus="showSync" :disabled="readonly" :placeholder="placeholderText" :maxlength="maxlength" + class="uni-searchbar__box-search-input" confirm-type="search" type="text" v-model="searchVal" :style="{color:textColor}" + @confirm="confirm" @blur="blur" @focus="emitFocus"/> + <text v-else class="uni-searchbar__text-placeholder">{{ placeholder }}</text> + <view v-if="show && (clearButton==='always'||clearButton==='auto'&&searchVal!=='') &&!readonly" + class="uni-searchbar__box-icon-clear" @click="clear"> + <slot name="clearIcon"> + <uni-icons color="#c0c4cc" size="20" type="clear" /> + </slot> + </view> + </view> + <text @click="cancel" class="uni-searchbar__cancel" + v-if="cancelButton ==='always' || show && cancelButton ==='auto'">{{cancelTextI18n}}</text> + </view> +</template> + +<script> + import { + initVueI18n + } from '@dcloudio/uni-i18n' + import messages from './i18n/index.js' + const { + t + } = initVueI18n(messages) + + /** + * SearchBar 搜索栏 + * @description 搜索栏组件,通常用于搜索商品、文章等 + * @tutorial https://ext.dcloud.net.cn/plugin?id=866 + * @property {Number} radius 搜索栏圆角 + * @property {Number} maxlength 输入最大长度 + * @property {String} placeholder 搜索栏Placeholder + * @property {String} clearButton = [always|auto|none] 是否显示清除按钮 + * @value always 一直显示 + * @value auto 输入框不为空时显示 + * @value none 一直不显示 + * @property {String} cancelButton = [always|auto|none] 是否显示取消按钮 + * @value always 一直显示 + * @value auto 输入框不为空时显示 + * @value none 一直不显示 + * @property {String} cancelText 取消按钮的文字 + * @property {String} bgColor 输入框背景颜色 + * @property {String} textColor 输入文字颜色 + * @property {Boolean} focus 是否自动聚焦 + * @property {Boolean} readonly 组件只读,不能有任何操作,只做展示 + * @event {Function} confirm uniSearchBar 的输入框 confirm 事件,返回参数为uniSearchBar的value,e={value:Number} + * @event {Function} input uniSearchBar 的 value 改变时触发事件,返回参数为uniSearchBar的value,e=value + * @event {Function} cancel 点击取消按钮时触发事件,返回参数为uniSearchBar的value,e={value:Number} + * @event {Function} clear 点击清除按钮时触发事件,返回参数为uniSearchBar的value,e={value:Number} + * @event {Function} blur input失去焦点时触发事件,返回参数为uniSearchBar的value,e={value:Number} + */ + + export default { + name: "UniSearchBar", + emits: ['input', 'update:modelValue', 'clear', 'cancel', 'confirm', 'blur', 'focus'], + props: { + placeholder: { + type: String, + default: "" + }, + radius: { + type: [Number, String], + default: 5 + }, + clearButton: { + type: String, + default: "auto" + }, + cancelButton: { + type: String, + default: "auto" + }, + cancelText: { + type: String, + default: "" + }, + bgColor: { + type: String, + default: "#F8F8F8" + }, + textColor: { + type: String, + default: "#000000" + }, + maxlength: { + type: [Number, String], + default: 100 + }, + value: { + type: [Number, String], + default: "" + }, + modelValue: { + type: [Number, String], + default: "" + }, + focus: { + type: Boolean, + default: false + }, + readonly: { + type: Boolean, + default: false + } + }, + data() { + return { + show: false, + showSync: false, + searchVal: '' + } + }, + computed: { + cancelTextI18n() { + return this.cancelText || t("uni-search-bar.cancel") + }, + placeholderText() { + return this.placeholder || t("uni-search-bar.placeholder") + } + }, + watch: { + // #ifndef VUE3 + value: { + immediate: true, + handler(newVal) { + this.searchVal = newVal + if (newVal) { + this.show = true + } + } + }, + // #endif + // #ifdef VUE3 + modelValue: { + immediate: true, + handler(newVal) { + this.searchVal = newVal + if (newVal) { + this.show = true + } + } + }, + // #endif + focus: { + immediate: true, + handler(newVal) { + if (newVal) { + if(this.readonly) return + this.show = true; + this.$nextTick(() => { + this.showSync = true + }) + } + } + }, + searchVal(newVal, oldVal) { + this.$emit("input", newVal) + // #ifdef VUE3 + this.$emit("update:modelValue", newVal) + // #endif + } + }, + methods: { + searchClick() { + if(this.readonly) return + if (this.show) { + return + } + this.show = true; + this.$nextTick(() => { + this.showSync = true + }) + }, + clear() { + this.searchVal = "" + this.$nextTick(() => { + this.$emit("clear", { value: "" }) + }) + }, + cancel() { + if(this.readonly) return + this.$emit("cancel", { + value: this.searchVal + }); + this.searchVal = "" + this.show = false + this.showSync = false + // #ifndef APP-PLUS + uni.hideKeyboard() + // #endif + // #ifdef APP-PLUS + plus.key.hideSoftKeybord() + // #endif + }, + confirm() { + // #ifndef APP-PLUS + uni.hideKeyboard(); + // #endif + // #ifdef APP-PLUS + plus.key.hideSoftKeybord() + // #endif + this.$emit("confirm", { + value: this.searchVal + }) + }, + blur() { + // #ifndef APP-PLUS + uni.hideKeyboard(); + // #endif + // #ifdef APP-PLUS + plus.key.hideSoftKeybord() + // #endif + this.$emit("blur", { + value: this.searchVal + }) + }, + emitFocus(e) { + this.$emit("focus", e.detail) + } + } + }; +</script> + +<style lang="scss"> + $uni-searchbar-height: 36px; + + .uni-searchbar { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + position: relative; + padding: 10px; + // background-color: #fff; + } + + .uni-searchbar__box { + /* #ifndef APP-NVUE */ + display: flex; + box-sizing: border-box; + justify-content: left; + /* #endif */ + overflow: hidden; + position: relative; + flex: 1; + flex-direction: row; + align-items: center; + height: $uni-searchbar-height; + padding: 5px 8px 5px 0px; + } + + .uni-searchbar__box-icon-search { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + // width: 32px; + padding: 0 8px; + justify-content: center; + align-items: center; + color: #B3B3B3; + } + + .uni-searchbar__box-search-input { + flex: 1; + font-size: 14px; + color: #333; + margin-left: 5px; + margin-top: 1px; + /* #ifndef APP-NVUE */ + background-color: inherit; + /* #endif */ + } + + .uni-searchbar__box-icon-clear { + align-items: center; + line-height: 24px; + padding-left: 8px; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + .uni-searchbar__text-placeholder { + font-size: 14px; + color: #B3B3B3; + margin-left: 5px; + text-align: left; + } + + .uni-searchbar__cancel { + padding-left: 10px; + line-height: $uni-searchbar-height; + font-size: 14px; + color: #333333; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } +</style> diff --git a/uni_modules/uni-search-bar/package.json b/uni_modules/uni-search-bar/package.json new file mode 100644 index 0000000..1730d9d --- /dev/null +++ b/uni_modules/uni-search-bar/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-search-bar", + "displayName": "uni-search-bar 搜索栏", + "version": "1.3.0", + "description": "搜索栏组件,通常用于搜索商品、文章等", + "keywords": [ + "uni-ui", + "uniui", + "搜索框", + "搜索栏" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-search-bar/readme.md b/uni_modules/uni-search-bar/readme.md new file mode 100644 index 0000000..253092f --- /dev/null +++ b/uni_modules/uni-search-bar/readme.md @@ -0,0 +1,14 @@ + + +## SearchBar 搜索栏 + +> **组件名:uni-search-bar** +> 代码块: `uSearchBar` + + +搜索栏组件 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-search-bar) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-section/changelog.md b/uni_modules/uni-section/changelog.md new file mode 100644 index 0000000..738f2b3 --- /dev/null +++ b/uni_modules/uni-section/changelog.md @@ -0,0 +1,2 @@ +## 0.0.1(2022-07-22) +- 初始化 diff --git a/uni_modules/uni-section/components/uni-section/uni-section.vue b/uni_modules/uni-section/components/uni-section/uni-section.vue new file mode 100644 index 0000000..9a52e0b --- /dev/null +++ b/uni_modules/uni-section/components/uni-section/uni-section.vue @@ -0,0 +1,167 @@ +<template> + <view class="uni-section"> + <view class="uni-section-header" @click="onClick"> + <view class="uni-section-header__decoration" v-if="type" :class="type" /> + <slot v-else name="decoration"></slot> + + <view class="uni-section-header__content"> + <text :style="{'font-size':titleFontSize,'color':titleColor}" class="uni-section__content-title" :class="{'distraction':!subTitle}">{{ title }}</text> + <text v-if="subTitle" :style="{'font-size':subTitleFontSize,'color':subTitleColor}" class="uni-section-header__content-sub">{{ subTitle }}</text> + </view> + + <view class="uni-section-header__slot-right"> + <slot name="right"></slot> + </view> + </view> + + <view class="uni-section-content" :style="{padding: _padding}"> + <slot /> + </view> + </view> +</template> + +<script> + + /** + * Section 标题栏 + * @description 标题栏 + * @property {String} type = [line|circle|square] 标题装饰类型 + * @value line 竖线 + * @value circle 圆形 + * @value square 正方形 + * @property {String} title 主标题 + * @property {String} titleFontSize 主标题字体大小 + * @property {String} titleColor 主标题字体颜色 + * @property {String} subTitle 副标题 + * @property {String} subTitleFontSize 副标题字体大小 + * @property {String} subTitleColor 副标题字体颜色 + * @property {String} padding 默认插槽 padding + */ + + export default { + name: 'UniSection', + emits:['click'], + props: { + type: { + type: String, + default: '' + }, + title: { + type: String, + required: true, + default: '' + }, + titleFontSize: { + type: String, + default: '14px' + }, + titleColor:{ + type: String, + default: '#333' + }, + subTitle: { + type: String, + default: '' + }, + subTitleFontSize: { + type: String, + default: '12px' + }, + subTitleColor: { + type: String, + default: '#999' + }, + padding: { + type: [Boolean, String], + default: false + } + }, + computed:{ + _padding(){ + if(typeof this.padding === 'string'){ + return this.padding + } + + return this.padding?'10px':'' + } + }, + watch: { + title(newVal) { + if (uni.report && newVal !== '') { + uni.report('title', newVal) + } + } + }, + methods: { + onClick() { + this.$emit('click') + } + } + } +</script> +<style lang="scss" > + $uni-primary: #2979ff !default; + + .uni-section { + background-color: #fff; + .uni-section-header { + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + align-items: center; + padding: 12px 10px; + font-weight: normal; + + &__decoration{ + margin-right: 6px; + background-color: $uni-primary; + &.line { + width: 4px; + height: 12px; + border-radius: 10px; + } + + &.circle { + width: 8px; + height: 8px; + border-top-right-radius: 50px; + border-top-left-radius: 50px; + border-bottom-left-radius: 50px; + border-bottom-right-radius: 50px; + } + + &.square { + width: 8px; + height: 8px; + } + } + + &__content { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + flex: 1; + color: #333; + + .distraction { + flex-direction: row; + align-items: center; + } + &-sub { + margin-top: 2px; + } + } + + &__slot-right{ + font-size: 14px; + } + } + + .uni-section-content{ + font-size: 14px; + } + } +</style> diff --git a/uni_modules/uni-section/package.json b/uni_modules/uni-section/package.json new file mode 100644 index 0000000..0a31fb5 --- /dev/null +++ b/uni_modules/uni-section/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-section", + "displayName": "uni-section 标题栏", + "version": "0.0.1", + "description": "标题栏组件", + "keywords": [ + "uni-ui", + "uniui", + "标题栏" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-scss" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-section/readme.md b/uni_modules/uni-section/readme.md new file mode 100644 index 0000000..d47faab --- /dev/null +++ b/uni_modules/uni-section/readme.md @@ -0,0 +1,8 @@ +## Section 标题栏 +> **组件名:uni-section** +> 代码块: `uSection` + +uni-section 组件主要用于文章、列表详情等标题展示 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-section) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 diff --git a/uni_modules/uni-segmented-control/changelog.md b/uni_modules/uni-segmented-control/changelog.md new file mode 100644 index 0000000..02d0c8a --- /dev/null +++ b/uni_modules/uni-segmented-control/changelog.md @@ -0,0 +1,15 @@ +## 1.2.3(2024-04-02) +- 修复 修复在微信小程序下inactiveColor失效bug +## 1.2.2(2024-03-28) +- 修复 在vue2下:style动态绑定导致编译失败的bug +## 1.2.1(2024-03-20) +- 新增 inActiveColor属性,可供配置未激活时的颜色 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-segmented-control](https://uniapp.dcloud.io/component/uniui/uni-segmented-control) +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.5(2021-05-12) +- 新增 项目示例地址 +## 1.0.4(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-segmented-control/components/uni-segmented-control/uni-segmented-control.vue b/uni_modules/uni-segmented-control/components/uni-segmented-control/uni-segmented-control.vue new file mode 100644 index 0000000..a69366a --- /dev/null +++ b/uni_modules/uni-segmented-control/components/uni-segmented-control/uni-segmented-control.vue @@ -0,0 +1,146 @@ +<template> + <view :class="[styleType === 'text'?'segmented-control--text' : 'segmented-control--button' ]" + :style="{ borderColor: styleType === 'text' ? '' : activeColor }" class="segmented-control"> + <view v-for="(item, index) in values" :class="[styleType === 'text' ? '' : 'segmented-control__item--button', + index === 0 && styleType === 'button' ? 'segmented-control__item--button--first' : '', + index === values.length - 1 && styleType === 'button' ? 'segmented-control__item--button--last':'']" :key="index" + :style="{backgroundColor: index === currentIndex && styleType === 'button' ? activeColor : styleType === 'button' ?inActiveColor:'transparent', borderColor: index === currentIndex && styleType === 'text' || styleType === 'button' ? activeColor : inActiveColor}" + class="segmented-control__item" @click="_onClick(index)"> + <view> + <text + :style="{color:index === currentIndex? styleType === 'text'? activeColor: '#fff': styleType === 'text'? '#000': activeColor}" + class="segmented-control__text" + :class="styleType === 'text' && index === currentIndex ? 'segmented-control__item--text': ''">{{ item }}</text> + </view> + + </view> + </view> +</template> + +<script> + /** + * SegmentedControl 分段器 + * @description 用作不同视图的显示 + * @tutorial https://ext.dcloud.net.cn/plugin?id=54 + * @property {Number} current 当前选中的tab索引值,从0计数 + * @property {String} styleType = [button|text] 分段器样式类型 + * @value button 按钮类型 + * @value text 文字类型 + * @property {String} activeColor 选中的标签背景色与边框颜色 + * @property {String} inActiveColor 未选中的标签背景色与边框颜色 + * @property {Array} values 选项数组 + * @event {Function} clickItem 组件触发点击事件时触发,e={currentIndex} + */ + + export default { + name: 'UniSegmentedControl', + emits: ['clickItem'], + props: { + current: { + type: Number, + default: 0 + }, + values: { + type: Array, + default () { + return [] + } + }, + activeColor: { + type: String, + default: '#2979FF' + }, + inActiveColor: { + type: String, + default: 'transparent' + }, + styleType: { + type: String, + default: 'button' + } + }, + data() { + return { + currentIndex: 0 + } + }, + watch: { + current(val) { + if (val !== this.currentIndex) { + this.currentIndex = val + } + } + }, + computed: {}, + created() { + this.currentIndex = this.current + }, + methods: { + _onClick(index) { + if (this.currentIndex !== index) { + this.currentIndex = index + this.$emit('clickItem', { + currentIndex: index + }) + } + } + } + } +</script> + +<style lang="scss" scoped> + .segmented-control { + /* #ifndef APP-NVUE */ + display: flex; + box-sizing: border-box; + /* #endif */ + flex-direction: row; + height: 36px; + overflow: hidden; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + .segmented-control__item { + /* #ifndef APP-NVUE */ + display: inline-flex; + box-sizing: border-box; + /* #endif */ + position: relative; + flex: 1; + justify-content: center; + align-items: center; + } + + .segmented-control__item--button { + border-style: solid; + border-top-width: 1px; + border-bottom-width: 1px; + border-right-width: 1px; + border-left-width: 0; + } + + .segmented-control__item--button--first { + border-left-width: 1px; + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; + } + + .segmented-control__item--button--last { + border-top-right-radius: 5px; + border-bottom-right-radius: 5px; + } + + .segmented-control__item--text { + border-bottom-style: solid; + border-bottom-width: 2px; + padding: 6px 0; + } + + .segmented-control__text { + font-size: 14px; + line-height: 20px; + text-align: center; + } +</style> diff --git a/uni_modules/uni-segmented-control/package.json b/uni_modules/uni-segmented-control/package.json new file mode 100644 index 0000000..49f9eff --- /dev/null +++ b/uni_modules/uni-segmented-control/package.json @@ -0,0 +1,85 @@ +{ + "id": "uni-segmented-control", + "displayName": "uni-segmented-control 分段器", + "version": "1.2.3", + "description": "分段器由至少 2 个分段控件组成,用作不同视图的显示", + "keywords": [ + "uni-ui", + "uniui", + "分段器", + "segement", + "顶部选择" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-segmented-control/readme.md b/uni_modules/uni-segmented-control/readme.md new file mode 100644 index 0000000..3527b03 --- /dev/null +++ b/uni_modules/uni-segmented-control/readme.md @@ -0,0 +1,13 @@ + + +## SegmentedControl 分段器 +> **组件名:uni-segmented-control** +> 代码块: `uSegmentedControl` + + +用作不同视图的显示 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-segmented-control) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-steps/changelog.md b/uni_modules/uni-steps/changelog.md new file mode 100644 index 0000000..04367d8 --- /dev/null +++ b/uni_modules/uni-steps/changelog.md @@ -0,0 +1,18 @@ +## 1.1.2(2024-03-28) +- 修复 uni-steps为竖排列时,文本长度过长引起点错乱的bug +## 1.1.1(2021-11-22) +- 修复 vue3中某些scss变量无法找到的问题 +## 1.1.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-steps](https://uniapp.dcloud.io/component/uniui/uni-steps) +## 1.0.8(2021-05-12) +- 新增 项目示例地址 +## 1.0.7(2021-05-06) +- 修复 uni-steps 横向布局时,多行文字高度不合理的 bug +## 1.0.6(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.5(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.4(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-steps/components/uni-steps/uni-steps.vue b/uni_modules/uni-steps/components/uni-steps/uni-steps.vue new file mode 100644 index 0000000..81017fc --- /dev/null +++ b/uni_modules/uni-steps/components/uni-steps/uni-steps.vue @@ -0,0 +1,280 @@ +<template> + <view class="uni-steps"> + <view :class="[direction==='column'?'uni-steps__column':'uni-steps__row']"> + <view :class="[direction==='column'?'uni-steps__column-text-container':'uni-steps__row-text-container']"> + <view v-for="(item,index) in options" :key="index" + :class="[direction==='column'?'uni-steps__column-text':'uni-steps__row-text']"> + <text :style="{color:index === active?activeColor:deactiveColor}" + :class="[direction==='column'?'uni-steps__column-title':'uni-steps__row-title']">{{item.title}}</text> + <text :style="{color: deactiveColor}" + :class="[direction==='column'?'uni-steps__column-desc':'uni-steps__row-desc']">{{item.desc}}</text> + </view> + </view> + <view :class="[direction==='column'?'uni-steps__column-container':'uni-steps__row-container']"> + <view :class="[direction==='column'?'uni-steps__column-line-item':'uni-steps__row-line-item']" + v-for="(item,index) in options" :key="index" :style="{height: direction === 'column'?heightArr[index]+'px':'14px'}"> + <view + :class="[direction==='column'?'uni-steps__column-line':'uni-steps__row-line',direction==='column'?'uni-steps__column-line--before':'uni-steps__row-line--before']" + :style="{backgroundColor:index<=active&&index!==0?activeColor:index===0?'transparent':deactiveColor}"> + </view> + <view :class="[direction==='column'?'uni-steps__column-check':'uni-steps__row-check']" + v-if="index === active"> + <uni-icons :color="activeColor" :type="activeIcon" size="14" /> + </view> + <view v-else :class="[direction==='column'?'uni-steps__column-circle':'uni-steps__row-circle']" + :style="{backgroundColor:index<active?activeColor:deactiveColor}" /> + <view + :class="[direction==='column'?'uni-steps__column-line':'uni-steps__row-line',direction==='column'?'uni-steps__column-line--after':'uni-steps__row-line--after']" + :style="{backgroundColor:index<active&&index!==options.length-1?activeColor:index===options.length-1?'transparent':deactiveColor}" /> + </view> + </view> + </view> + </view> +</template> + +<script> + /** + * Steps 步骤条 + * @description 评分组件 + * @tutorial https://ext.dcloud.net.cn/plugin?id=34 + * @property {Number} active 当前步骤 + * @property {String} direction = [row|column] 当前步骤 + * @value row 横向 + * @value column 纵向 + * @property {String} activeColor 选中状态的颜色 + * @property {Array} options 数据源,格式为:[{title:'xxx',desc:'xxx'},{title:'xxx',desc:'xxx'}] + */ + + export default { + name: 'UniSteps', + props: { + direction: { + // 排列方向 row column + type: String, + default: 'row' + }, + activeColor: { + // 激活状态颜色 + type: String, + default: '#2979FF' + }, + deactiveColor: { + // 未激活状态颜色 + type: String, + default: '#B7BDC6' + }, + active: { + // 当前步骤 + type: Number, + default: 0 + }, + activeIcon: { + // 当前步骤 + type: String, + default: 'checkbox-filled' + }, + options: { + type: Array, + default () { + return [] + } + } // 数据 + }, + data() { + return { + heightArr: [], + } + }, + mounted() { + //根据内容设置步骤条的长度 + if (this.direction === 'column') { + let that = this; + //只能用类选择器,用id选择器所获取的元素信息不准确 + uni.createSelectorQuery().in(this).selectAll('.uni-steps__column-text').boundingClientRect(data => { + that.heightArr = data.map(item => item.height + 1); + }).exec() + } + }, + } +</script> + +<style lang="scss"> + $uni-primary: #2979ff !default; + $uni-border-color: #EDEDED; + + .uni-steps { + /* #ifndef APP-NVUE */ + display: flex; + width: 100%; + /* #endif */ + /* #ifdef APP-NVUE */ + flex: 1; + /* #endif */ + flex-direction: column; + } + + .uni-steps__row { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + } + + .uni-steps__column { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row-reverse; + } + + .uni-steps__row-text-container { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + align-items: flex-end; + margin-bottom: 8px; + } + + .uni-steps__column-text-container { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + flex: 1; + } + + .uni-steps__row-text { + /* #ifndef APP-NVUE */ + display: inline-flex; + /* #endif */ + flex: 1; + flex-direction: column; + } + + .uni-steps__column-text { + padding: 6px 0px; + border-bottom-style: solid; + border-bottom-width: 1px; + border-bottom-color: $uni-border-color; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + } + + .uni-steps__row-title { + font-size: 14px; + line-height: 16px; + text-align: center; + } + + .uni-steps__column-title { + font-size: 14px; + text-align: left; + line-height: 18px; + } + + .uni-steps__row-desc { + font-size: 12px; + line-height: 14px; + text-align: center; + } + + .uni-steps__column-desc { + font-size: 12px; + text-align: left; + line-height: 18px; + } + + .uni-steps__row-container { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + } + + .uni-steps__column-container { + /* #ifndef APP-NVUE */ + display: inline-flex; + /* #endif */ + width: 30px; + flex-direction: column; + } + + .uni-steps__row-line-item { + /* #ifndef APP-NVUE */ + display: inline-flex; + /* #endif */ + flex-direction: row; + flex: 1; + height: 14px; + line-height: 14px; + align-items: center; + justify-content: center; + } + + .uni-steps__column-line-item { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + align-items: center; + justify-content: center; + } + + .uni-steps__row-line { + flex: 1; + height: 1px; + background-color: #B7BDC6; + } + + .uni-steps__column-line { + width: 1px; + background-color: #B7BDC6; + } + + .uni-steps__row-line--after { + transform: translateX(1px); + } + + .uni-steps__column-line--after { + flex: 1; + transform: translate(0px, 1px); + } + + .uni-steps__row-line--before { + transform: translateX(-1px); + } + + .uni-steps__column-line--before { + height: 6px; + transform: translate(0px, -13px); + } + + .uni-steps__row-circle { + width: 5px; + height: 5px; + border-radius: 50%; + background-color: #B7BDC6; + margin: 0px 3px; + } + + .uni-steps__column-circle { + width: 5px; + height: 5px; + border-radius: 50%; + background-color: #B7BDC6; + margin: 4px 0px 5px 0px; + } + + .uni-steps__row-check { + margin: 0px 6px; + } + + .uni-steps__column-check { + height: 14px; + line-height: 14px; + margin: 2px 0px; + } +</style> diff --git a/uni_modules/uni-steps/package.json b/uni_modules/uni-steps/package.json new file mode 100644 index 0000000..4145ce9 --- /dev/null +++ b/uni_modules/uni-steps/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-steps", + "displayName": "uni-steps 步骤条", + "version": "1.1.2", + "description": "步骤条组件,提供横向和纵向两种布局格式。", + "keywords": [ + "uni-ui", + "uniui", + "步骤条", + "时间轴" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-steps/readme.md b/uni_modules/uni-steps/readme.md new file mode 100644 index 0000000..da7a4bf --- /dev/null +++ b/uni_modules/uni-steps/readme.md @@ -0,0 +1,13 @@ + + +## Steps 步骤条 +> **组件名:uni-steps** +> 代码块: `uSteps` + + +步骤条,常用于显示进度 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-steps) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-swipe-action/changelog.md b/uni_modules/uni-swipe-action/changelog.md new file mode 100644 index 0000000..e28472f --- /dev/null +++ b/uni_modules/uni-swipe-action/changelog.md @@ -0,0 +1,47 @@ +## 1.3.10(2024-01-17) +- 修复 点击按钮时,按钮会被点击穿透导致自动收缩的 bug(兼容阿里/百度/抖音小程序) +## 1.3.9(2024-01-17) +- 修复 点击按钮时,按钮会被点击穿透导致自动收缩的 bug +## 1.3.8(2023-04-13) +- 修复`uni-swipe-action`和`uni-swipe-action-item`不同时使用导致 closeOther 方法报错的 bug +## 1.3.7(2022-06-06) +- 修复 vue3 下使用组件不能正常运行的Bug +## 1.3.6(2022-05-31) +- 修复 h5端点击click触发两次的Bug +## 1.3.5(2022-05-23) +- 修复 isPC 找不到的Bug +## 1.3.4(2022-05-19) +- 修复 在 nvue 下 disabled 失效的bug +## 1.3.3(2022-03-31) +- 修复 按钮字体大小不能设置的bug +## 1.3.2(2022-03-16) +- 修复 h5和app端下报el错误的bug +## 1.3.1(2022-03-07) +- 修复 HBuilderX 1.4.X 版本中,h5和app端下报错的bug +## 1.3.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-swipe-action](https://uniapp.dcloud.io/component/uniui/uni-swipe-action) +## 1.2.4(2021-08-20) +- 优化 close-all 方法 +## 1.2.3(2021-08-20) +- 新增 close-all 方法,关闭所有已打开的组件 +## 1.2.2(2021-08-17) +- 新增 resize() 方法,在非微信小程序、h5、app-vue端出现不能滑动的问题的时候,重置组件 +- 修复 app 端偶尔出现类似 Page[x][-x,xx;-x,xx,x,x-x] 的问题 +- 优化 微信小程序、h5、app-vue 滑动逻辑,避免出现动态新增组件后不能滑动的问题 +## 1.2.1(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +- 修复 跨页面修改组件数据 ,导致不能滑动的问题 +## 1.1.10(2021-06-17) +- 修复 按钮点击执行两次的bug +## 1.1.9(2021-05-12) +- 新增 项目示例地址 +## 1.1.8(2021-03-26) +- 修复 微信小程序 nv_navigator is not defined 报错的bug +## 1.1.7(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 左侧滑动 +- 新增 插槽使用方式 +- 新增 threshold 属性,可以控制滑动缺省值 +- 优化 长列表滚动性能 +- 修复 滚动页面时触发组件滑动的Bug diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/bindingx.js b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/bindingx.js new file mode 100644 index 0000000..707e432 --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/bindingx.js @@ -0,0 +1,302 @@ +let bindIngXMixins = {} + +// #ifdef APP-NVUE +const BindingX = uni.requireNativePlugin('bindingx'); +const dom = uni.requireNativePlugin('dom'); +const animation = uni.requireNativePlugin('animation'); + +bindIngXMixins = { + data() { + return {} + }, + + watch: { + show(newVal) { + if (this.autoClose) return + if (this.stop) return + this.stop = true + if (newVal) { + this.open(newVal) + } else { + this.close() + } + }, + leftOptions() { + this.getSelectorQuery() + this.init() + }, + rightOptions(newVal) { + this.init() + } + }, + created() { + this.swipeaction = this.getSwipeAction() + if (this.swipeaction && Array.isArray(this.swipeaction.children)) { + this.swipeaction.children.push(this) + } + }, + mounted() { + this.box = this.getEl(this.$refs['selector-box--hock']) + this.selector = this.getEl(this.$refs['selector-content--hock']); + this.leftButton = this.getEl(this.$refs['selector-left-button--hock']); + this.rightButton = this.getEl(this.$refs['selector-right-button--hock']); + this.init() + }, + // beforeDestroy() { + // this.swipeaction.children.forEach((item, index) => { + // if (item === this) { + // this.swipeaction.children.splice(index, 1) + // } + // }) + // }, + methods: { + init() { + this.$nextTick(() => { + this.x = 0 + this.button = { + show: false + } + setTimeout(() => { + this.getSelectorQuery() + }, 200) + }) + }, + onClick(index, item, position) { + this.$emit('click', { + content: item, + index, + position + }) + }, + touchstart(e) { + // fix by mehaotian 禁止滑动 + if (this.disabled) return + // 每次只触发一次,避免多次监听造成闪烁 + if (this.stop) return + this.stop = true + if (this.autoClose && this.swipeaction) { + this.swipeaction.closeOther(this) + } + + const leftWidth = this.button.left.width + const rightWidth = this.button.right.width + let expression = this.range(this.x, -rightWidth, leftWidth) + let leftExpression = this.range(this.x - leftWidth, -leftWidth, 0) + let rightExpression = this.range(this.x + rightWidth, 0, rightWidth) + + this.eventpan = BindingX.bind({ + anchor: this.box, + eventType: 'pan', + props: [{ + element: this.selector, + property: 'transform.translateX', + expression + }, { + element: this.leftButton, + property: 'transform.translateX', + expression: leftExpression + }, { + element: this.rightButton, + property: 'transform.translateX', + expression: rightExpression + }, ] + }, (e) => { + // nope + if (e.state === 'end') { + this.x = e.deltaX + this.x; + this.isclick = true + this.bindTiming(e.deltaX) + } + }); + }, + touchend(e) { + if (this.isopen !== 'none' && !this.isclick) { + this.open('none') + } + }, + bindTiming(x) { + const left = this.x + const leftWidth = this.button.left.width + const rightWidth = this.button.right.width + const threshold = this.threshold + if (!this.isopen || this.isopen === 'none') { + if (left > threshold) { + this.open('left') + } else if (left < -threshold) { + this.open('right') + } else { + this.open('none') + } + } else { + if ((x > -leftWidth && x < 0) || x > rightWidth) { + if ((x > -threshold && x < 0) || (x - rightWidth > threshold)) { + this.open('left') + } else { + this.open('none') + } + } else { + if ((x < threshold && x > 0) || (x + leftWidth < -threshold)) { + this.open('right') + } else { + this.open('none') + } + } + } + }, + + /** + * 移动范围 + * @param {Object} num + * @param {Object} mix + * @param {Object} max + */ + range(num, mix, max) { + return `min(max(x+${num}, ${mix}), ${max})` + }, + + /** + * 开启swipe + */ + open(type) { + this.animation(type) + }, + + /** + * 关闭swipe + */ + close() { + this.animation('none') + }, + + /** + * 开启关闭动画 + * @param {Object} type + */ + animation(type) { + const time = 300 + const leftWidth = this.button.left.width + const rightWidth = this.button.right.width + if (this.eventpan && this.eventpan.token) { + BindingX.unbind({ + token: this.eventpan.token, + eventType: 'pan' + }) + } + + switch (type) { + case 'left': + Promise.all([ + this.move(this.selector, leftWidth), + this.move(this.leftButton, 0), + this.move(this.rightButton, rightWidth * 2) + ]).then(() => { + this.setEmit(leftWidth, type) + }) + break + case 'right': + Promise.all([ + this.move(this.selector, -rightWidth), + this.move(this.leftButton, -leftWidth * 2), + this.move(this.rightButton, 0) + ]).then(() => { + this.setEmit(-rightWidth, type) + }) + break + default: + Promise.all([ + this.move(this.selector, 0), + this.move(this.leftButton, -leftWidth), + this.move(this.rightButton, rightWidth) + ]).then(() => { + this.setEmit(0, type) + }) + + } + }, + setEmit(x, type) { + const leftWidth = this.button.left.width + const rightWidth = this.button.right.width + this.isopen = this.isopen || 'none' + this.stop = false + this.isclick = false + // 只有状态不一致才会返回结果 + if (this.isopen !== type && this.x !== x) { + if (type === 'left' && leftWidth > 0) { + this.$emit('change', 'left') + } + if (type === 'right' && rightWidth > 0) { + this.$emit('change', 'right') + } + if (type === 'none') { + this.$emit('change', 'none') + } + } + this.x = x + this.isopen = type + }, + move(ref, value) { + return new Promise((resolve, reject) => { + animation.transition(ref, { + styles: { + transform: `translateX(${value})`, + }, + duration: 150, //ms + timingFunction: 'linear', + needLayout: false, + delay: 0 //ms + }, function(res) { + resolve(res) + }) + }) + + }, + + /** + * 获取ref + * @param {Object} el + */ + getEl(el) { + return el.ref + }, + /** + * 获取节点信息 + */ + getSelectorQuery() { + Promise.all([ + this.getDom('left'), + this.getDom('right'), + ]).then((data) => { + let show = 'none' + if (this.autoClose) { + show = 'none' + } else { + show = this.show + } + + if (show === 'none') { + // this.close() + } else { + this.open(show) + } + + }) + + }, + getDom(str) { + return new Promise((resolve, reject) => { + dom.getComponentRect(this.$refs[`selector-${str}-button--hock`], (data) => { + if (data) { + this.button[str] = data.size + resolve(data) + } else { + reject() + } + }) + }) + } + } +} + +// #endif + +export default bindIngXMixins diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/isPC.js b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/isPC.js new file mode 100644 index 0000000..917cb48 --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/isPC.js @@ -0,0 +1,12 @@ +export function isPC() { + var userAgentInfo = navigator.userAgent; + var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]; + var flag = true; + for (let v = 0; v < Agents.length - 1; v++) { + if (userAgentInfo.indexOf(Agents[v]) > 0) { + flag = false; + break; + } + } + return flag; +} diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpalipay.js b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpalipay.js new file mode 100644 index 0000000..35c796b --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpalipay.js @@ -0,0 +1,195 @@ +export default { + data() { + return { + x: 0, + transition: false, + width: 0, + viewWidth: 0, + swipeShow: 0 + } + }, + watch: { + show(newVal) { + if (this.autoClose) return + if (newVal && newVal !== 'none') { + this.transition = true + this.open(newVal) + } else { + this.close() + } + } + }, + created() { + this.swipeaction = this.getSwipeAction() + if (this.swipeaction && Array.isArray(this.swipeaction.children)) { + this.swipeaction.children.push(this) + } + }, + mounted() { + this.isopen = false + setTimeout(() => { + this.getQuerySelect() + }, 50) + }, + methods: { + appTouchStart(e) { + const { + clientX + } = e.changedTouches[0] + this.clientX = clientX + this.timestamp = new Date().getTime() + }, + appTouchEnd(e, index, item, position) { + const { + clientX + } = e.changedTouches[0] + // fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题 + let diff = Math.abs(this.clientX - clientX) + let time = (new Date().getTime()) - this.timestamp + if (diff < 40 && time < 300) { + this.$emit('click', { + content: item, + index, + position + }) + } + }, + /** + * 移动触发 + * @param {Object} e + */ + onChange(e) { + this.moveX = e.detail.x + this.isclose = false + }, + touchstart(e) { + this.transition = false + this.isclose = true + if (this.autoClose && this.swipeaction) { + this.swipeaction.closeOther(this) + } + }, + touchmove(e) {}, + touchend(e) { + // 0的位置什么都不执行 + if (this.isclose && this.isopen === 'none') return + if (this.isclose && this.isopen !== 'none') { + this.transition = true + this.close() + } else { + this.move(this.moveX + this.leftWidth) + } + }, + + /** + * 移动 + * @param {Object} moveX + */ + move(moveX) { + // 打开关闭的处理逻辑不太一样 + this.transition = true + // 未打开状态 + if (!this.isopen || this.isopen === 'none') { + if (moveX > this.threshold) { + this.open('left') + } else if (moveX < -this.threshold) { + this.open('right') + } else { + this.close() + } + } else { + if (moveX < 0 && moveX < this.rightWidth) { + const rightX = this.rightWidth + moveX + if (rightX < this.threshold) { + this.open('right') + } else { + this.close() + } + } else if (moveX > 0 && moveX < this.leftWidth) { + const leftX = this.leftWidth - moveX + if (leftX < this.threshold) { + this.open('left') + } else { + this.close() + } + } + + } + + }, + + /** + * 打开 + */ + open(type) { + this.x = this.moveX + this.animation(type) + }, + + /** + * 关闭 + */ + close() { + this.x = this.moveX + // TODO 解决 x 值不更新的问题,所以会多触发一次 nextTick ,待优化 + this.$nextTick(() => { + this.x = -this.leftWidth + if (this.isopen !== 'none') { + this.$emit('change', 'none') + } + this.isopen = 'none' + }) + }, + + /** + * 执行结束动画 + * @param {Object} type + */ + animation(type) { + this.$nextTick(() => { + if (type === 'left') { + this.x = 0 + } else { + this.x = -this.rightWidth - this.leftWidth + } + + if (this.isopen !== type) { + this.$emit('change', type) + } + this.isopen = type + }) + + }, + getSlide(x) {}, + getQuerySelect() { + const query = uni.createSelectorQuery().in(this); + query.selectAll('.movable-view--hock').boundingClientRect(data => { + this.leftWidth = data[1].width + this.rightWidth = data[2].width + this.width = data[0].width + this.viewWidth = this.width + this.rightWidth + this.leftWidth + if (this.leftWidth === 0) { + // TODO 疑似bug ,初始化的时候如果x 是0,会导致移动位置错误,所以让元素超出一点 + this.x = -0.1 + } else { + this.x = -this.leftWidth + } + this.moveX = this.x + this.$nextTick(() => { + this.swipeShow = 1 + }) + + if (!this.buttonWidth) { + this.disabledView = true + } + + if (this.autoClose) return + if (this.show !== 'none') { + this.transition = true + this.open(this.shows) + } + }).exec(); + + } + } +} diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpother.js b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpother.js new file mode 100644 index 0000000..d389bce --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpother.js @@ -0,0 +1,260 @@ +let otherMixins = {} + +// #ifndef APP-PLUS|| MP-WEIXIN || H5 +const MIN_DISTANCE = 10; +otherMixins = { + data() { + // TODO 随机生生元素ID,解决百度小程序获取同一个元素位置信息的bug + const elClass = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}` + return { + uniShow: false, + left: 0, + buttonShow: 'none', + ani: false, + moveLeft: '', + elClass + } + }, + watch: { + show(newVal) { + if (this.autoClose) return + this.openState(newVal) + }, + left() { + this.moveLeft = `translateX(${this.left}px)` + }, + buttonShow(newVal) { + if (this.autoClose) return + this.openState(newVal) + }, + leftOptions() { + this.init() + }, + rightOptions() { + this.init() + } + }, + mounted() { + this.swipeaction = this.getSwipeAction() + if (this.swipeaction && Array.isArray(this.swipeaction.children)) { + this.swipeaction.children.push(this) + } + this.init() + }, + methods: { + init() { + clearTimeout(this.timer) + this.timer = setTimeout(() => { + this.getSelectorQuery() + }, 100) + // 移动距离 + this.left = 0 + this.x = 0 + }, + + closeSwipe(e) { + if (this.autoClose && this.swipeaction) { + this.swipeaction.closeOther(this) + } + }, + appTouchStart(e) { + const { + clientX + } = e.changedTouches[0] + this.clientX = clientX + this.timestamp = new Date().getTime() + }, + appTouchEnd(e, index, item, position) { + const { + clientX + } = e.changedTouches[0] + // fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题 + let diff = Math.abs(this.clientX - clientX) + let time = (new Date().getTime()) - this.timestamp + if (diff < 40 && time < 300) { + this.$emit('click', { + content: item, + index, + position + }) + } + }, + touchstart(e) { + if (this.disabled) return + this.ani = false + this.x = this.left || 0 + this.stopTouchStart(e) + this.autoClose && this.closeSwipe() + }, + touchmove(e) { + if (this.disabled) return + // 是否可以滑动页面 + this.stopTouchMove(e); + if (this.direction !== 'horizontal') { + return; + } + this.move(this.x + this.deltaX) + return false + }, + touchend() { + if (this.disabled) return + this.moveDirection(this.left) + }, + /** + * 设置移动距离 + * @param {Object} value + */ + move(value) { + value = value || 0 + const leftWidth = this.leftWidth + const rightWidth = this.rightWidth + // 获取可滑动范围 + this.left = this.range(value, -rightWidth, leftWidth); + }, + + /** + * 获取范围 + * @param {Object} num + * @param {Object} min + * @param {Object} max + */ + range(num, min, max) { + return Math.min(Math.max(num, min), max); + }, + /** + * 移动方向判断 + * @param {Object} left + * @param {Object} value + */ + moveDirection(left) { + const threshold = this.threshold + const isopen = this.isopen || 'none' + const leftWidth = this.leftWidth + const rightWidth = this.rightWidth + if (this.deltaX === 0) { + this.openState('none') + return + } + if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > + 0 && rightWidth + + left < threshold)) { + // right + this.openState('right') + } else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > + 0 && + leftWidth - left < threshold)) { + // left + this.openState('left') + } else { + // default + this.openState('none') + } + }, + + /** + * 开启状态 + * @param {Boolean} type + */ + openState(type) { + const leftWidth = this.leftWidth + const rightWidth = this.rightWidth + let left = '' + this.isopen = this.isopen ? this.isopen : 'none' + switch (type) { + case "left": + left = leftWidth + break + case "right": + left = -rightWidth + break + default: + left = 0 + } + + + if (this.isopen !== type) { + this.throttle = true + this.$emit('change', type) + } + + this.isopen = type + // 添加动画类 + this.ani = true + this.$nextTick(() => { + this.move(left) + }) + // 设置最终移动位置,理论上只要进入到这个函数,肯定是要打开的 + }, + close() { + this.openState('none') + }, + getDirection(x, y) { + if (x > y && x > MIN_DISTANCE) { + return 'horizontal'; + } + if (y > x && y > MIN_DISTANCE) { + return 'vertical'; + } + return ''; + }, + + /** + * 重置滑动状态 + * @param {Object} event + */ + resetTouchStatus() { + this.direction = ''; + this.deltaX = 0; + this.deltaY = 0; + this.offsetX = 0; + this.offsetY = 0; + }, + + /** + * 设置滑动开始位置 + * @param {Object} event + */ + stopTouchStart(event) { + this.resetTouchStatus(); + const touch = event.touches[0]; + this.startX = touch.clientX; + this.startY = touch.clientY; + }, + + /** + * 滑动中,是否禁止打开 + * @param {Object} event + */ + stopTouchMove(event) { + const touch = event.touches[0]; + this.deltaX = touch.clientX - this.startX; + this.deltaY = touch.clientY - this.startY; + this.offsetX = Math.abs(this.deltaX); + this.offsetY = Math.abs(this.deltaY); + this.direction = this.direction || this.getDirection(this.offsetX, this.offsetY); + }, + + getSelectorQuery() { + const views = uni.createSelectorQuery().in(this) + views + .selectAll('.' + this.elClass) + .boundingClientRect(data => { + if (data.length === 0) return + let show = 'none' + if (this.autoClose) { + show = 'none' + } else { + show = this.show + } + this.leftWidth = data[0].width || 0 + this.rightWidth = data[1].width || 0 + this.buttonShow = show + }) + .exec() + } + } +} + +// #endif + +export default otherMixins diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpwxs.js b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpwxs.js new file mode 100644 index 0000000..08de1c9 --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpwxs.js @@ -0,0 +1,84 @@ +let mpMixins = {} +let is_pc = null +// #ifdef H5 +import { + isPC +} from "./isPC" +is_pc = isPC() +// #endif +// #ifdef APP-VUE|| MP-WEIXIN || H5 + +mpMixins = { + data() { + return { + is_show: 'none' + } + }, + watch: { + show(newVal) { + this.is_show = this.show + } + }, + created() { + this.swipeaction = this.getSwipeAction() + if (this.swipeaction && Array.isArray(this.swipeaction.children)) { + this.swipeaction.children.push(this) + } + }, + mounted() { + this.is_show = this.show + }, + methods: { + // wxs 中调用 + closeSwipe(e) { + if (this.autoClose && this.swipeaction) { + this.swipeaction.closeOther(this) + } + }, + + change(e) { + this.$emit('change', e.open) + if (this.is_show !== e.open) { + this.is_show = e.open + } + }, + + appTouchStart(e) { + if (is_pc) return + const { + clientX + } = e.changedTouches[0] + this.clientX = clientX + this.timestamp = new Date().getTime() + }, + appTouchEnd(e, index, item, position) { + if (is_pc) return + const { + clientX + } = e.changedTouches[0] + // fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题 + let diff = Math.abs(this.clientX - clientX) + let time = (new Date().getTime()) - this.timestamp + if (diff < 40 && time < 300) { + this.$emit('click', { + content: item, + index, + position + }) + } + }, + onClickForPC(index, item, position) { + if (!is_pc) return + // #ifdef H5 + this.$emit('click', { + content: item, + index, + position + }) + // #endif + } + } +} + +// #endif +export default mpMixins diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/render.js b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/render.js new file mode 100644 index 0000000..78f0ec6 --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/render.js @@ -0,0 +1,270 @@ +const MIN_DISTANCE = 10; +export default { + showWatch(newVal, oldVal, ownerInstance, instance, self) { + var state = self.state + var $el = ownerInstance.$el || ownerInstance.$vm && ownerInstance.$vm.$el + if (!$el) return + this.getDom(instance, ownerInstance, self) + if (newVal && newVal !== 'none') { + this.openState(newVal, instance, ownerInstance, self) + return + } + + if (state.left) { + this.openState('none', instance, ownerInstance, self) + } + this.resetTouchStatus(instance, self) + }, + + /** + * 开始触摸操作 + * @param {Object} e + * @param {Object} ins + */ + touchstart(e, ownerInstance, self) { + let instance = e.instance; + let disabled = instance.getDataset().disabled + let state = self.state; + this.getDom(instance, ownerInstance, self) + // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 + disabled = this.getDisabledType(disabled) + if (disabled) return + // 开始触摸时移除动画类 + instance.requestAnimationFrame(function() { + instance.removeClass('ani'); + ownerInstance.callMethod('closeSwipe'); + }) + + // 记录上次的位置 + state.x = state.left || 0 + // 计算滑动开始位置 + this.stopTouchStart(e, ownerInstance, self) + }, + + /** + * 开始滑动操作 + * @param {Object} e + * @param {Object} ownerInstance + */ + touchmove(e, ownerInstance, self) { + let instance = e.instance; + // 删除之后已经那不到实例了 + if (!instance) return; + let disabled = instance.getDataset().disabled + let state = self.state + // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 + disabled = this.getDisabledType(disabled) + if (disabled) return + // 是否可以滑动页面 + this.stopTouchMove(e, self); + if (state.direction !== 'horizontal') { + return; + } + if (e.preventDefault) { + // 阻止页面滚动 + e.preventDefault() + } + let x = state.x + state.deltaX + this.move(x, instance, ownerInstance, self) + }, + + /** + * 结束触摸操作 + * @param {Object} e + * @param {Object} ownerInstance + */ + touchend(e, ownerInstance, self) { + let instance = e.instance; + let disabled = instance.getDataset().disabled + let state = self.state + // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 + disabled = this.getDisabledType(disabled) + + if (disabled) return + // 滑动过程中触摸结束,通过阙值判断是开启还是关闭 + // fixed by mehaotian 定时器解决点击按钮,touchend 触发比 click 事件时机早的问题 ,主要是 ios13 + this.moveDirection(state.left, instance, ownerInstance, self) + + }, + + /** + * 设置移动距离 + * @param {Object} value + * @param {Object} instance + * @param {Object} ownerInstance + */ + move(value, instance, ownerInstance, self) { + value = value || 0 + let state = self.state + let leftWidth = state.leftWidth + let rightWidth = state.rightWidth + // 获取可滑动范围 + state.left = this.range(value, -rightWidth, leftWidth); + instance.requestAnimationFrame(function() { + instance.setStyle({ + transform: 'translateX(' + state.left + 'px)', + '-webkit-transform': 'translateX(' + state.left + 'px)' + }) + }) + + }, + + /** + * 获取元素信息 + * @param {Object} instance + * @param {Object} ownerInstance + */ + getDom(instance, ownerInstance, self) { + var state = self.state + var $el = ownerInstance.$el || ownerInstance.$vm && ownerInstance.$vm.$el + var leftDom = $el.querySelector('.button-group--left') + var rightDom = $el.querySelector('.button-group--right') + + state.leftWidth = leftDom.offsetWidth || 0 + state.rightWidth = rightDom.offsetWidth || 0 + state.threshold = instance.getDataset().threshold + }, + + getDisabledType(value) { + return (typeof(value) === 'string' ? JSON.parse(value) : value) || false; + }, + + /** + * 获取范围 + * @param {Object} num + * @param {Object} min + * @param {Object} max + */ + range(num, min, max) { + return Math.min(Math.max(num, min), max); + }, + + + /** + * 移动方向判断 + * @param {Object} left + * @param {Object} value + * @param {Object} ownerInstance + * @param {Object} ins + */ + moveDirection(left, ins, ownerInstance, self) { + var state = self.state + var threshold = state.threshold + var position = state.position + var isopen = state.isopen || 'none' + var leftWidth = state.leftWidth + var rightWidth = state.rightWidth + if (state.deltaX === 0) { + this.openState('none', ins, ownerInstance, self) + return + } + if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 && + rightWidth + + left < threshold)) { + // right + this.openState('right', ins, ownerInstance, self) + } else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 && + leftWidth - left < threshold)) { + // left + this.openState('left', ins, ownerInstance, self) + } else { + // default + this.openState('none', ins, ownerInstance, self) + } + }, + + + /** + * 开启状态 + * @param {Boolean} type + * @param {Object} ins + * @param {Object} ownerInstance + */ + openState(type, ins, ownerInstance, self) { + let state = self.state + let leftWidth = state.leftWidth + let rightWidth = state.rightWidth + let left = '' + state.isopen = state.isopen ? state.isopen : 'none' + switch (type) { + case "left": + left = leftWidth + break + case "right": + left = -rightWidth + break + default: + left = 0 + } + + // && !state.throttle + + if (state.isopen !== type) { + state.throttle = true + ownerInstance.callMethod('change', { + open: type + }) + + } + + state.isopen = type + // 添加动画类 + ins.requestAnimationFrame(() => { + ins.addClass('ani'); + this.move(left, ins, ownerInstance, self) + }) + }, + + + getDirection(x, y) { + if (x > y && x > MIN_DISTANCE) { + return 'horizontal'; + } + if (y > x && y > MIN_DISTANCE) { + return 'vertical'; + } + return ''; + }, + + /** + * 重置滑动状态 + * @param {Object} event + */ + resetTouchStatus(instance, self) { + let state = self.state; + state.direction = ''; + state.deltaX = 0; + state.deltaY = 0; + state.offsetX = 0; + state.offsetY = 0; + }, + + /** + * 设置滑动开始位置 + * @param {Object} event + */ + stopTouchStart(event, ownerInstance, self) { + let instance = event.instance; + let state = self.state + this.resetTouchStatus(instance, self); + var touch = event.touches[0]; + state.startX = touch.clientX; + state.startY = touch.clientY; + }, + + /** + * 滑动中,是否禁止打开 + * @param {Object} event + */ + stopTouchMove(event, self) { + let instance = event.instance; + let state = self.state; + let touch = event.touches[0]; + + state.deltaX = touch.clientX - state.startX; + state.deltaY = touch.clientY - state.startY; + state.offsetY = Math.abs(state.deltaY); + state.offsetX = Math.abs(state.deltaX); + state.direction = state.direction || this.getDirection(state.offsetX, state.offsetY); + } +} diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/uni-swipe-action-item.vue b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/uni-swipe-action-item.vue new file mode 100644 index 0000000..a816e92 --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/uni-swipe-action-item.vue @@ -0,0 +1,348 @@ +<template> + <!-- 在微信小程序 app vue端 h5 使用wxs 实现--> + <!-- #ifdef APP-VUE || MP-WEIXIN || H5 --> + <view class="uni-swipe"> + <!-- #ifdef MP-WEIXIN || VUE3 --> + <view class="uni-swipe_box" :change:prop="wxsswipe.showWatch" :prop="is_show" :data-threshold="threshold" + :data-disabled="disabled" @touchstart="wxsswipe.touchstart" @touchmove="wxsswipe.touchmove" + @touchend="wxsswipe.touchend"> + <!-- #endif --> + <!-- #ifndef MP-WEIXIN || VUE3 --> + <view class="uni-swipe_box" :change:prop="renderswipe.showWatch" :prop="is_show" :data-threshold="threshold" + :data-disabled="disabled+''" @touchstart="renderswipe.touchstart" @touchmove="renderswipe.touchmove" + @touchend="renderswipe.touchend"> + <!-- #endif --> + <!-- 在微信小程序 app vue端 h5 使用wxs 实现--> + <view class="uni-swipe_button-group button-group--left"> + <slot name="left"> + <view v-for="(item,index) in leftOptions" :key="index" :style="{ + backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD' + }" class="uni-swipe_button button-hock" @touchstart.stop="appTouchStart" + @touchend.stop="appTouchEnd($event,index,item,'left')" @click.stop="onClickForPC(index,item,'left')"> + <text class="uni-swipe_button-text" + :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'}">{{ item.text }}</text> + </view> + </slot> + </view> + <view class="uni-swipe_text--center"> + <slot></slot> + </view> + <view class="uni-swipe_button-group button-group--right"> + <slot name="right"> + <view v-for="(item,index) in rightOptions" :key="index" :style="{ + backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD' + }" class="uni-swipe_button button-hock" @touchstart.stop="appTouchStart" + @touchend.stop="appTouchEnd($event,index,item,'right')" @click.stop="onClickForPC(index,item,'right')"><text + class="uni-swipe_button-text" + :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'}">{{ item.text }}</text> + </view> + </slot> + </view> + </view> + </view> + <!-- #endif --> + <!-- app nvue端 使用 bindingx --> + <!-- #ifdef APP-NVUE --> + <view ref="selector-box--hock" class="uni-swipe" @horizontalpan="touchstart" @touchend="touchend"> + <view ref='selector-left-button--hock' class="uni-swipe_button-group button-group--left"> + <slot name="left"> + <view v-for="(item,index) in leftOptions" :key="index" :style="{ + backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD' + }" class="uni-swipe_button button-hock" @click.stop="onClick(index,item,'left')"> + <text class="uni-swipe_button-text" + :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF', fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'}"> + {{ item.text }} + </text> + </view> + </slot> + </view> + <view ref='selector-right-button--hock' class="uni-swipe_button-group button-group--right"> + <slot name="right"> + <view v-for="(item,index) in rightOptions" :key="index" :style="{ + backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD' + }" class="uni-swipe_button button-hock" @click.stop="onClick(index,item,'right')"><text + class="uni-swipe_button-text" + :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'}">{{ item.text }}</text> + </view> + </slot> + </view> + <view ref='selector-content--hock' class="uni-swipe_box"> + <slot></slot> + </view> + </view> + <!-- #endif --> + <!-- 其他平台使用 js ,长列表性能可能会有影响--> + <!-- #ifdef MP-ALIPAY || MP-BAIDU || MP-TOUTIAO || MP-QQ --> + <view class="uni-swipe"> + <view class="uni-swipe_box" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend" + :style="{transform:moveLeft}" :class="{ani:ani}"> + <view class="uni-swipe_button-group button-group--left" :class="[elClass]"> + <slot name="left"> + <view v-for="(item,index) in leftOptions" :key="index" :style="{ + backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD', + fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px' + }" class="uni-swipe_button button-hock" @touchstart.stop="appTouchStart" + @touchend.stop="appTouchEnd($event,index,item,'left')"><text class="uni-swipe_button-text" + :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}">{{ item.text }}</text> + </view> + </slot> + </view> + <slot></slot> + <view class="uni-swipe_button-group button-group--right" :class="[elClass]"> + <slot name="right"> + <view v-for="(item,index) in rightOptions" :key="index" :style="{ + backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD', + fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px' + }" @touchstart.stop="appTouchStart" @touchend.stop="appTouchEnd($event,index,item,'right')" + class="uni-swipe_button button-hock"><text class="uni-swipe_button-text" + :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}">{{ item.text }}</text> + </view> + </slot> + </view> + </view> + </view> + <!-- #endif --> + +</template> +<script src="./wx.wxs" module="wxsswipe" lang="wxs"></script> + +<script module="renderswipe" lang="renderjs"> + import render from './render.js' + export default { + mounted(e, ins, owner) { + this.state = {} + }, + methods: { + showWatch(newVal, oldVal, ownerInstance, instance) { + render.showWatch(newVal, oldVal, ownerInstance, instance, this) + }, + touchstart(e, ownerInstance) { + render.touchstart(e, ownerInstance, this) + }, + touchmove(e, ownerInstance) { + render.touchmove(e, ownerInstance, this) + }, + touchend(e, ownerInstance) { + render.touchend(e, ownerInstance, this) + } + } + } +</script> +<script> + import mpwxs from './mpwxs' + import bindingx from './bindingx.js' + import mpother from './mpother' + + /** + * SwipeActionItem 滑动操作子组件 + * @description 通过滑动触发选项的容器 + * @tutorial https://ext.dcloud.net.cn/plugin?id=181 + * @property {Boolean} show = [left|right|none] 开启关闭组件,auto-close = false 时生效 + * @property {Boolean} disabled = [true|false] 是否禁止滑动 + * @property {Boolean} autoClose = [true|false] 滑动打开当前组件,是否关闭其他组件 + * @property {Number} threshold 滑动缺省值 + * @property {Array} leftOptions 左侧选项内容及样式 + * @property {Array} rgihtOptions 右侧选项内容及样式 + * @event {Function} click 点击选项按钮时触发事件,e = {content,index} ,content(点击内容)、index(下标) + * @event {Function} change 组件打开或关闭时触发,left\right\none + */ + + export default { + mixins: [mpwxs, bindingx, mpother], + emits: ['click', 'change'], + props: { + // 控制开关 + show: { + type: String, + default: 'none' + }, + + // 禁用 + disabled: { + type: Boolean, + default: false + }, + + // 是否自动关闭 + autoClose: { + type: Boolean, + default: true + }, + + // 滑动缺省距离 + threshold: { + type: Number, + default: 20 + }, + + // 左侧按钮内容 + leftOptions: { + type: Array, + default () { + return [] + } + }, + + // 右侧按钮内容 + rightOptions: { + type: Array, + default () { + return [] + } + } + + }, + // #ifndef VUE3 + // TODO vue2 + destroyed() { + if (this.__isUnmounted) return + this.uninstall() + }, + // #endif + // #ifdef VUE3 + // TODO vue3 + unmounted() { + this.__isUnmounted = true + this.uninstall() + }, + // #endif + + methods: { + uninstall() { + if (this.swipeaction) { + this.swipeaction.children.forEach((item, index) => { + if (item === this) { + this.swipeaction.children.splice(index, 1) + } + }) + } + }, + /** + * 获取父元素实例 + */ + getSwipeAction(name = 'uniSwipeAction') { + 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; + } + } + } +</script> +<style lang="scss"> + .uni-swipe { + position: relative; + /* #ifndef APP-NVUE */ + overflow: hidden; + /* #endif */ + } + + .uni-swipe_box { + /* #ifndef APP-NVUE */ + display: flex; + flex-shrink: 0; + // touch-action: none; + /* #endif */ + position: relative; + } + + .uni-swipe_content { + // border: 1px red solid; + } + + .uni-swipe_text--center { + width: 100%; + /* #ifndef APP-NVUE */ + cursor: grab; + /* #endif */ + } + + .uni-swipe_button-group { + /* #ifndef APP-NVUE */ + box-sizing: border-box; + display: flex; + /* #endif */ + flex-direction: row; + position: absolute; + top: 0; + bottom: 0; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + } + + .button-group--left { + left: 0; + transform: translateX(-100%) + } + + .button-group--right { + right: 0; + transform: translateX(100%) + } + + .uni-swipe_button { + /* #ifdef APP-NVUE */ + flex: 1; + /* #endif */ + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + justify-content: center; + align-items: center; + padding: 0 20px; + } + + .uni-swipe_button-text { + /* #ifndef APP-NVUE */ + flex-shrink: 0; + /* #endif */ + font-size: 14px; + } + + .ani { + transition-property: transform; + transition-duration: 0.3s; + transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1); + } + + /* #ifdef MP-ALIPAY */ + .movable-area { + /* width: 100%; */ + height: 45px; + } + + .movable-view { + display: flex; + /* justify-content: center; */ + position: relative; + flex: 1; + height: 45px; + z-index: 2; + } + + .movable-view-button { + display: flex; + flex-shrink: 0; + flex-direction: row; + height: 100%; + background: #C0C0C0; + } + + /* .transition { + transition: all 0.3s; + } */ + + .movable-view-box { + flex-shrink: 0; + height: 100%; + background-color: #fff; + } + + /* #endif */ +</style> diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/wx.wxs b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/wx.wxs new file mode 100644 index 0000000..b394244 --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/wx.wxs @@ -0,0 +1,341 @@ +var MIN_DISTANCE = 10; + +/** + * 判断当前是否为H5、app-vue + */ +var IS_HTML5 = false +if (typeof window === 'object') IS_HTML5 = true + +/** + * 监听页面内值的变化,主要用于动态开关swipe-action + * @param {Object} newValue + * @param {Object} oldValue + * @param {Object} ownerInstance + * @param {Object} instance + */ +function showWatch(newVal, oldVal, ownerInstance, instance) { + var state = instance.getState() + getDom(instance, ownerInstance) + if (newVal && newVal !== 'none') { + openState(newVal, instance, ownerInstance) + return + } + + if (state.left) { + openState('none', instance, ownerInstance) + } + resetTouchStatus(instance) +} + +/** + * 开始触摸操作 + * @param {Object} e + * @param {Object} ins + */ +function touchstart(e, ownerInstance) { + var instance = e.instance; + var disabled = instance.getDataset().disabled + var state = instance.getState(); + getDom(instance, ownerInstance) + // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 + disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false; + if (disabled) return + // 开始触摸时移除动画类 + instance.requestAnimationFrame(function() { + instance.removeClass('ani'); + ownerInstance.callMethod('closeSwipe'); + }) + + // 记录上次的位置 + state.x = state.left || 0 + // 计算滑动开始位置 + stopTouchStart(e, ownerInstance) +} + +/** + * 开始滑动操作 + * @param {Object} e + * @param {Object} ownerInstance + */ +function touchmove(e, ownerInstance) { + var instance = e.instance; + var disabled = instance.getDataset().disabled + var state = instance.getState() + // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 + disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false; + if (disabled) return + // 是否可以滑动页面 + stopTouchMove(e); + if (state.direction !== 'horizontal') { + return; + } + + if (e.preventDefault) { + // 阻止页面滚动 + e.preventDefault() + } + + move(state.x + state.deltaX, instance, ownerInstance) +} + +/** + * 结束触摸操作 + * @param {Object} e + * @param {Object} ownerInstance + */ +function touchend(e, ownerInstance) { + var instance = e.instance; + var disabled = instance.getDataset().disabled + var state = instance.getState() + // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 + disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false; + + if (disabled) return + // 滑动过程中触摸结束,通过阙值判断是开启还是关闭 + // fixed by mehaotian 定时器解决点击按钮,touchend 触发比 click 事件时机早的问题 ,主要是 ios13 + moveDirection(state.left, instance, ownerInstance) + +} + +/** + * 设置移动距离 + * @param {Object} value + * @param {Object} instance + * @param {Object} ownerInstance + */ +function move(value, instance, ownerInstance) { + value = value || 0 + var state = instance.getState() + var leftWidth = state.leftWidth + var rightWidth = state.rightWidth + // 获取可滑动范围 + state.left = range(value, -rightWidth, leftWidth); + instance.requestAnimationFrame(function() { + instance.setStyle({ + transform: 'translateX(' + state.left + 'px)', + '-webkit-transform': 'translateX(' + state.left + 'px)' + }) + }) + +} + +/** + * 获取元素信息 + * @param {Object} instance + * @param {Object} ownerInstance + */ +function getDom(instance, ownerInstance) { + var state = instance.getState() + var leftDom = ownerInstance.selectComponent('.button-group--left') + var rightDom = ownerInstance.selectComponent('.button-group--right') + var leftStyles = { + width: 0 + } + var rightStyles = { + width: 0 + } + leftStyles = leftDom.getBoundingClientRect() + rightStyles = rightDom.getBoundingClientRect() + + state.leftWidth = leftStyles.width || 0 + state.rightWidth = rightStyles.width || 0 + state.threshold = instance.getDataset().threshold +} + +/** + * 获取范围 + * @param {Object} num + * @param {Object} min + * @param {Object} max + */ +function range(num, min, max) { + return Math.min(Math.max(num, min), max); +} + + +/** + * 移动方向判断 + * @param {Object} left + * @param {Object} value + * @param {Object} ownerInstance + * @param {Object} ins + */ +function moveDirection(left, ins, ownerInstance) { + var state = ins.getState() + var threshold = state.threshold + var position = state.position + var isopen = state.isopen || 'none' + var leftWidth = state.leftWidth + var rightWidth = state.rightWidth + if (state.deltaX === 0) { + openState('none', ins, ownerInstance) + return + } + if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 && + rightWidth + + left < threshold)) { + // right + openState('right', ins, ownerInstance) + } else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 && + leftWidth - left < threshold)) { + // left + openState('left', ins, ownerInstance) + } else { + // default + openState('none', ins, ownerInstance) + } +} + + +/** + * 开启状态 + * @param {Boolean} type + * @param {Object} ins + * @param {Object} ownerInstance + */ +function openState(type, ins, ownerInstance) { + var state = ins.getState() + var leftWidth = state.leftWidth + var rightWidth = state.rightWidth + var left = '' + state.isopen = state.isopen ? state.isopen : 'none' + switch (type) { + case "left": + left = leftWidth + break + case "right": + left = -rightWidth + break + default: + left = 0 + } + + // && !state.throttle + + if (state.isopen !== type) { + state.throttle = true + ownerInstance.callMethod('change', { + open: type + }) + + } + + state.isopen = type + // 添加动画类 + ins.requestAnimationFrame(function() { + ins.addClass('ani'); + move(left, ins, ownerInstance) + }) + // 设置最终移动位置,理论上只要进入到这个函数,肯定是要打开的 +} + + +function getDirection(x, y) { + if (x > y && x > MIN_DISTANCE) { + return 'horizontal'; + } + if (y > x && y > MIN_DISTANCE) { + return 'vertical'; + } + return ''; +} + +/** + * 重置滑动状态 + * @param {Object} event + */ +function resetTouchStatus(instance) { + var state = instance.getState(); + state.direction = ''; + state.deltaX = 0; + state.deltaY = 0; + state.offsetX = 0; + state.offsetY = 0; +} + +/** + * 设置滑动开始位置 + * @param {Object} event + */ +function stopTouchStart(event) { + var instance = event.instance; + var state = instance.getState(); + resetTouchStatus(instance); + var touch = event.touches[0]; + if (IS_HTML5 && isPC()) { + touch = event; + } + state.startX = touch.clientX; + state.startY = touch.clientY; +} + +/** + * 滑动中,是否禁止打开 + * @param {Object} event + */ +function stopTouchMove(event) { + var instance = event.instance; + var state = instance.getState(); + var touch = event.touches[0]; + if (IS_HTML5 && isPC()) { + touch = event; + } + state.deltaX = touch.clientX - state.startX; + state.deltaY = touch.clientY - state.startY; + state.offsetY = Math.abs(state.deltaY); + state.offsetX = Math.abs(state.deltaX); + state.direction = state.direction || getDirection(state.offsetX, state.offsetY); +} + +function isPC() { + var userAgentInfo = navigator.userAgent; + var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]; + var flag = true; + for (var v = 0; v < Agents.length - 1; v++) { + if (userAgentInfo.indexOf(Agents[v]) > 0) { + flag = false; + break; + } + } + return flag; +} + +var movable = false + +function mousedown(e, ins) { + if (!IS_HTML5) return + if (!isPC()) return + touchstart(e, ins) + movable = true +} + +function mousemove(e, ins) { + if (!IS_HTML5) return + if (!isPC()) return + if (!movable) return + touchmove(e, ins) +} + +function mouseup(e, ins) { + if (!IS_HTML5) return + if (!isPC()) return + touchend(e, ins) + movable = false +} + +function mouseleave(e, ins) { + if (!IS_HTML5) return + if (!isPC()) return + movable = false +} + +module.exports = { + showWatch: showWatch, + touchstart: touchstart, + touchmove: touchmove, + touchend: touchend, + mousedown: mousedown, + mousemove: mousemove, + mouseup: mouseup, + mouseleave: mouseleave +} diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action/uni-swipe-action.vue b/uni_modules/uni-swipe-action/components/uni-swipe-action/uni-swipe-action.vue new file mode 100644 index 0000000..4971782 --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action/uni-swipe-action.vue @@ -0,0 +1,60 @@ +<template> + <view> + <slot></slot> + </view> +</template> + +<script> + /** + * SwipeAction 滑动操作 + * @description 通过滑动触发选项的容器 + * @tutorial https://ext.dcloud.net.cn/plugin?id=181 + */ + export default { + name:"uniSwipeAction", + data() { + return {}; + }, + created() { + this.children = []; + }, + methods: { + // 公开给用户使用,重制组件样式 + resize(){ + // wxs 会自己计算组件大小,所以无需执行下面代码 + // #ifndef APP-VUE || H5 || MP-WEIXIN + this.children.forEach(vm=>{ + vm.init() + }) + // #endif + }, + // 公开给用户使用,关闭全部 已经打开的组件 + closeAll(){ + this.children.forEach(vm=>{ + // #ifdef APP-VUE || H5 || MP-WEIXIN + vm.is_show = 'none' + // #endif + + // #ifndef APP-VUE || H5 || MP-WEIXIN + vm.close() + // #endif + }) + }, + closeOther(vm) { + if (this.openItem && this.openItem !== vm) { + // #ifdef APP-VUE || H5 || MP-WEIXIN + this.openItem.is_show = 'none' + // #endif + + // #ifndef APP-VUE || H5 || MP-WEIXIN + this.openItem.close() + // #endif + } + // 记录上一个打开的 swipe-action-item ,用于 auto-close + this.openItem = vm + } + } + }; +</script> + +<style></style> diff --git a/uni_modules/uni-swipe-action/package.json b/uni_modules/uni-swipe-action/package.json new file mode 100644 index 0000000..fc5dd8a --- /dev/null +++ b/uni_modules/uni-swipe-action/package.json @@ -0,0 +1,84 @@ +{ + "id": "uni-swipe-action", + "displayName": "uni-swipe-action 滑动操作", + "version": "1.3.10", + "description": "SwipeAction 滑动操作操作组件", + "keywords": [ + "", + "uni-ui", + "uniui", + "滑动删除", + "侧滑删除" + ], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "y", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-swipe-action/readme.md b/uni_modules/uni-swipe-action/readme.md new file mode 100644 index 0000000..93a5cac --- /dev/null +++ b/uni_modules/uni-swipe-action/readme.md @@ -0,0 +1,11 @@ + + +## SwipeAction 滑动操作 +> **组件名:uni-swipe-action** +> 代码块: `uSwipeAction`、`uSwipeActionItem` + + +通过滑动触发选项的容器 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-swipe-action) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-swiper-dot/changelog.md b/uni_modules/uni-swiper-dot/changelog.md new file mode 100644 index 0000000..85cf54d --- /dev/null +++ b/uni_modules/uni-swiper-dot/changelog.md @@ -0,0 +1,12 @@ +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-swiper-dot](https://uniapp.dcloud.io/component/uniui/uni-swiper-dot) +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.6(2021-05-12) +- 新增 示例地址 +- 修复 示例项目缺少组件的Bug +## 1.0.5(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 clickItem 事件,支持指示点控制轮播 +- 新增 支持 pc 可用 diff --git a/uni_modules/uni-swiper-dot/components/uni-swiper-dot/uni-swiper-dot.vue b/uni_modules/uni-swiper-dot/components/uni-swiper-dot/uni-swiper-dot.vue new file mode 100644 index 0000000..e66b6c7 --- /dev/null +++ b/uni_modules/uni-swiper-dot/components/uni-swiper-dot/uni-swiper-dot.vue @@ -0,0 +1,218 @@ +<template> + <view class="uni-swiper__warp"> + <slot /> + <view v-if="mode === 'default'" :style="{'bottom':dots.bottom + 'px'}" class="uni-swiper__dots-box" key='default'> + <view v-for="(item,index) in info" @click="clickItem(index)" :style="{ + 'width': (index === current? dots.width*2:dots.width ) + 'px','height':dots.width/2 +'px' ,'background-color':index !== current?dots.backgroundColor:dots.selectedBackgroundColor,'border-radius':'0px'}" + :key="index" class="uni-swiper__dots-item uni-swiper__dots-bar" /> + </view> + <view v-if="mode === 'dot'" :style="{'bottom':dots.bottom + 'px'}" class="uni-swiper__dots-box" key='dot'> + <view v-for="(item,index) in info" @click="clickItem(index)" :style="{ + 'width': dots.width + 'px','height':dots.height +'px' ,'background-color':index !== current?dots.backgroundColor:dots.selectedBackgroundColor,'border':index !==current ? dots.border:dots.selectedBorder}" + :key="index" class="uni-swiper__dots-item" /> + </view> + <view v-if="mode === 'round'" :style="{'bottom':dots.bottom + 'px'}" class="uni-swiper__dots-box" key='round'> + <view v-for="(item,index) in info" @click="clickItem(index)" :class="[index === current&&'uni-swiper__dots-long']" :style="{ + 'width':(index === current? dots.width*3:dots.width ) + 'px','height':dots.height +'px' ,'background-color':index !== current?dots.backgroundColor:dots.selectedBackgroundColor,'border':index !==current ? dots.border:dots.selectedBorder}" + :key="index" class="uni-swiper__dots-item " /> + </view> + <view v-if="mode === 'nav'" key='nav' :style="{'background-color':dotsStyles.backgroundColor,'bottom':'0'}" class="uni-swiper__dots-box uni-swiper__dots-nav"> + <text :style="{'color':dotsStyles.color}" class="uni-swiper__dots-nav-item">{{ (current+1)+"/"+info.length +' ' +info[current][field] }}</text> + </view> + <view v-if="mode === 'indexes'" key='indexes' :style="{'bottom':dots.bottom + 'px'}" class="uni-swiper__dots-box"> + <view v-for="(item,index) in info" @click="clickItem(index)" :style="{ + 'width':dots.width + 'px','height':dots.height +'px' ,'color':index === current?dots.selectedColor:dots.color,'background-color':index !== current?dots.backgroundColor:dots.selectedBackgroundColor,'border':index !==current ? dots.border:dots.selectedBorder}" + :key="index" class="uni-swiper__dots-item uni-swiper__dots-indexes"><text class="uni-swiper__dots-indexes-text">{{ index+1 }}</text></view> + </view> + </view> +</template> + +<script> + + /** + * SwiperDod 轮播图指示点 + * @description 自定义轮播图指示点 + * @tutorial https://ext.dcloud.net.cn/plugin?id=284 + * @property {Number} current 当前指示点索引,必须是通过 `swiper` 的 `change` 事件获取到的 `e.detail.current` + * @property {String} mode = [default|round|nav|indexes] 指示点的类型 + * @value defualt 默认指示点 + * @value round 圆形指示点 + * @value nav 条形指示点 + * @value indexes 索引指示点 + * @property {String} field mode 为 nav 时,显示的内容字段(mode = nav 时必填) + * @property {String} info 轮播图的数据,通过数组长度决定指示点个数 + * @property {Object} dotsStyles 指示点样式 + * @event {Function} clickItem 组件触发点击事件时触发,e={currentIndex} + */ + + export default { + name: 'UniSwiperDot', + emits:['clickItem'], + props: { + info: { + type: Array, + default () { + return [] + } + }, + current: { + type: Number, + default: 0 + }, + dotsStyles: { + type: Object, + default () { + return {} + } + }, + // 类型 :default(默认) indexes long nav + mode: { + type: String, + default: 'default' + }, + // 只在 nav 模式下生效,变量名称 + field: { + type: String, + default: '' + } + }, + data() { + return { + dots: { + width: 6, + height: 6, + bottom: 10, + color: '#fff', + backgroundColor: 'rgba(0, 0, 0, .3)', + border: '1px rgba(0, 0, 0, .3) solid', + selectedBackgroundColor: '#333', + selectedBorder: '1px rgba(0, 0, 0, .9) solid' + } + } + }, + watch: { + dotsStyles(newVal) { + this.dots = Object.assign(this.dots, this.dotsStyles) + }, + mode(newVal) { + if (newVal === 'indexes') { + this.dots.width = 14 + this.dots.height = 14 + } else { + this.dots.width = 6 + this.dots.height = 6 + } + } + + }, + created() { + if (this.mode === 'indexes') { + this.dots.width = 12 + this.dots.height = 12 + } + this.dots = Object.assign(this.dots, this.dotsStyles) + }, + methods: { + clickItem(index) { + this.$emit('clickItem', index) + } + } + } +</script> + +<style lang="scss" scoped> + .uni-swiper__warp { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex: 1; + flex-direction: column; + position: relative; + overflow: hidden; + } + + .uni-swiper__dots-box { + position: absolute; + bottom: 10px; + left: 0; + right: 0; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex: 1; + flex-direction: row; + justify-content: center; + align-items: center; + } + + .uni-swiper__dots-item { + width: 8px; + border-radius: 100px; + margin-left: 6px; + background-color: rgba(0, 0, 0, 0.4); + /* #ifndef APP-NVUE */ + cursor: pointer; + /* #endif */ + /* #ifdef H5 */ + // border-width: 5px 0; + // border-style: solid; + // border-color: transparent; + // background-clip: padding-box; + /* #endif */ + // transition: width 0.2s linear; 不要取消注释,不然会不能变色 + } + + .uni-swiper__dots-item:first-child { + margin: 0; + } + + .uni-swiper__dots-default { + border-radius: 100px; + } + + .uni-swiper__dots-long { + border-radius: 50px; + } + + .uni-swiper__dots-bar { + border-radius: 50px; + } + + .uni-swiper__dots-nav { + bottom: 0px; + // height: 26px; + padding: 8px 0; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex: 1; + flex-direction: row; + justify-content: flex-start; + align-items: center; + background-color: rgba(0, 0, 0, 0.2); + } + + .uni-swiper__dots-nav-item { + /* overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; */ + font-size: 14px; + color: #fff; + margin: 0 15px; + } + + .uni-swiper__dots-indexes { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + // flex: 1; + justify-content: center; + align-items: center; + } + + .uni-swiper__dots-indexes-text { + color: #fff; + font-size: 12px; + line-height: 14px; + } +</style> diff --git a/uni_modules/uni-swiper-dot/package.json b/uni_modules/uni-swiper-dot/package.json new file mode 100644 index 0000000..f2dd8d2 --- /dev/null +++ b/uni_modules/uni-swiper-dot/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-swiper-dot", + "displayName": "uni-swiper-dot 轮播图指示点", + "version": "1.2.0", + "description": "自定义轮播图指示点组件", + "keywords": [ + "uni-ui", + "uniui", + "轮播图指示点", + "dot", + "swiper" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-swiper-dot/readme.md b/uni_modules/uni-swiper-dot/readme.md new file mode 100644 index 0000000..7d397e2 --- /dev/null +++ b/uni_modules/uni-swiper-dot/readme.md @@ -0,0 +1,11 @@ + + +## SwiperDot 轮播图指示点 +> **组件名:uni-swiper-dot** +> 代码块: `uSwiperDot` + + +自定义轮播图指示点 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-swiper-dot) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-table/changelog.md b/uni_modules/uni-table/changelog.md new file mode 100644 index 0000000..943707b --- /dev/null +++ b/uni_modules/uni-table/changelog.md @@ -0,0 +1,33 @@ +## 1.2.8(2024-10-15) +- 修复 运行到抖音小程序上出现的问题 +## 1.2.7(2024-10-15) +- 修复 微信小程序中的getSystemInfo警告 +## 1.2.4(2023-12-19) +- 修复 uni-tr只有一列时minWidth计算错误,列变化实时计算更新 +## 1.2.3(2023-03-28) +- 修复 在vue3模式下可能会出现错误的问题 +## 1.2.2(2022-11-29) +- 优化 主题样式 +## 1.2.1(2022-06-06) +- 修复 微信小程序存在无使用组件的问题 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-table](https://uniapp.dcloud.io/component/uniui/uni-table) +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-07-08) +- 新增 uni-th 支持 date 日期筛选范围 +## 1.0.6(2021-07-05) +- 新增 uni-th 支持 range 筛选范围 +## 1.0.5(2021-06-28) +- 新增 uni-th 筛选功能 +## 1.0.4(2021-05-12) +- 新增 示例地址 +- 修复 示例项目缺少组件的Bug +## 1.0.3(2021-04-16) +- 新增 sortable 属性,是否开启单列排序 +- 优化 表格多选逻辑 +## 1.0.2(2021-03-22) +- uni-tr 添加 disabled 属性,用于 type=selection 时,设置某行是否可由全选按钮控制 +## 1.0.1(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-table/components/uni-table/uni-table.vue b/uni_modules/uni-table/components/uni-table/uni-table.vue new file mode 100644 index 0000000..3ab0496 --- /dev/null +++ b/uni_modules/uni-table/components/uni-table/uni-table.vue @@ -0,0 +1,460 @@ +<template> + <view class="uni-table-scroll" :class="{ 'table--border': border, 'border-none': !noData }"> + <!-- #ifdef H5 --> + <table class="uni-table" border="0" cellpadding="0" cellspacing="0" :class="{ 'table--stripe': stripe }" :style="{ 'min-width': minWidth + 'px' }"> + <slot></slot> + <tr v-if="noData" class="uni-table-loading"> + <td class="uni-table-text" :class="{ 'empty-border': border }">{{ emptyText }}</td> + </tr> + <view v-if="loading" class="uni-table-mask" :class="{ 'empty-border': border }"><div class="uni-table--loader"></div></view> + </table> + <!-- #endif --> + <!-- #ifndef H5 --> + <view class="uni-table" :style="{ 'min-width': minWidth + 'px' }" :class="{ 'table--stripe': stripe }"> + <slot></slot> + <view v-if="noData" class="uni-table-loading"> + <view class="uni-table-text" :class="{ 'empty-border': border }">{{ emptyText }}</view> + </view> + <view v-if="loading" class="uni-table-mask" :class="{ 'empty-border': border }"><div class="uni-table--loader"></div></view> + </view> + <!-- #endif --> + </view> +</template> + +<script> +/** + * Table 表格 + * @description 用于展示多条结构类似的数据 + * @tutorial https://ext.dcloud.net.cn/plugin?id=3270 + * @property {Boolean} border 是否带有纵向边框 + * @property {Boolean} stripe 是否显示斑马线 + * @property {Boolean} type 是否开启多选 + * @property {String} emptyText 空数据时显示的文本内容 + * @property {Boolean} loading 显示加载中 + * @event {Function} selection-change 开启多选时,当选择项发生变化时会触发该事件 + */ +export default { + name: 'uniTable', + options: { + // #ifdef MP-TOUTIAO + virtualHost: false, + // #endif + // #ifndef MP-TOUTIAO + virtualHost: true + // #endif + }, + emits:['selection-change'], + props: { + data: { + type: Array, + default() { + return [] + } + }, + // 是否有竖线 + border: { + type: Boolean, + default: false + }, + // 是否显示斑马线 + stripe: { + type: Boolean, + default: false + }, + // 多选 + type: { + type: String, + default: '' + }, + // 没有更多数据 + emptyText: { + type: String, + default: '没有更多数据' + }, + loading: { + type: Boolean, + default: false + }, + rowKey: { + type: String, + default: '' + } + }, + data() { + return { + noData: true, + minWidth: 0, + multiTableHeads: [] + } + }, + watch: { + loading(val) {}, + data(newVal) { + let theadChildren = this.theadChildren + let rowspan = 1 + if (this.theadChildren) { + rowspan = this.theadChildren.rowspan + } + + // this.trChildren.length - rowspan + this.noData = false + // this.noData = newVal.length === 0 + } + }, + created() { + // 定义tr的实例数组 + this.trChildren = [] + this.thChildren = [] + this.theadChildren = null + this.backData = [] + this.backIndexData = [] + }, + + methods: { + isNodata() { + let theadChildren = this.theadChildren + let rowspan = 1 + if (this.theadChildren) { + rowspan = this.theadChildren.rowspan + } + this.noData = this.trChildren.length - rowspan <= 0 + }, + /** + * 选中所有 + */ + selectionAll() { + let startIndex = 1 + let theadChildren = this.theadChildren + if (!this.theadChildren) { + theadChildren = this.trChildren[0] + } else { + startIndex = theadChildren.rowspan - 1 + } + let isHaveData = this.data && this.data.length > 0 + theadChildren.checked = true + theadChildren.indeterminate = false + this.trChildren.forEach((item, index) => { + if (!item.disabled) { + item.checked = true + if (isHaveData && item.keyValue) { + const row = this.data.find(v => v[this.rowKey] === item.keyValue) + if (!this.backData.find(v => v[this.rowKey] === row[this.rowKey])) { + this.backData.push(row) + } + } + if (index > (startIndex - 1) && this.backIndexData.indexOf(index - startIndex) === -1) { + this.backIndexData.push(index - startIndex) + } + } + }) + // this.backData = JSON.parse(JSON.stringify(this.data)) + this.$emit('selection-change', { + detail: { + value: this.backData, + index: this.backIndexData + } + }) + }, + /** + * 用于多选表格,切换某一行的选中状态,如果使用了第二个参数,则是设置这一行选中与否(selected 为 true 则选中) + */ + toggleRowSelection(row, selected) { + // if (!this.theadChildren) return + row = [].concat(row) + + this.trChildren.forEach((item, index) => { + // if (item.keyValue) { + + const select = row.findIndex(v => { + // + if (typeof v === 'number') { + return v === index - 1 + } else { + return v[this.rowKey] === item.keyValue + } + }) + let ischeck = item.checked + if (select !== -1) { + if (typeof selected === 'boolean') { + item.checked = selected + } else { + item.checked = !item.checked + } + if (ischeck !== item.checked) { + this.check(item.rowData||item, item.checked, item.rowData?item.keyValue:null, true) + } + } + // } + }) + this.$emit('selection-change', { + detail: { + value: this.backData, + index:this.backIndexData + } + }) + }, + + /** + * 用于多选表格,清空用户的选择 + */ + clearSelection() { + let theadChildren = this.theadChildren + if (!this.theadChildren) { + theadChildren = this.trChildren[0] + } + // if (!this.theadChildren) return + theadChildren.checked = false + theadChildren.indeterminate = false + this.trChildren.forEach(item => { + // if (item.keyValue) { + item.checked = false + // } + }) + this.backData = [] + this.backIndexData = [] + this.$emit('selection-change', { + detail: { + value: [], + index: [] + } + }) + }, + /** + * 用于多选表格,切换所有行的选中状态 + */ + toggleAllSelection() { + let list = [] + let startIndex = 1 + let theadChildren = this.theadChildren + if (!this.theadChildren) { + theadChildren = this.trChildren[0] + } else { + startIndex = theadChildren.rowspan - 1 + } + this.trChildren.forEach((item, index) => { + if (!item.disabled) { + if (index > (startIndex - 1) ) { + list.push(index-startIndex) + } + } + }) + this.toggleRowSelection(list) + }, + + /** + * 选中\取消选中 + * @param {Object} child + * @param {Object} check + * @param {Object} rowValue + */ + check(child, check, keyValue, emit) { + let theadChildren = this.theadChildren + if (!this.theadChildren) { + theadChildren = this.trChildren[0] + } + + + + let childDomIndex = this.trChildren.findIndex((item, index) => child === item) + if(childDomIndex < 0){ + childDomIndex = this.data.findIndex(v=>v[this.rowKey] === keyValue) + 1 + } + const dataLen = this.trChildren.filter(v => !v.disabled && v.keyValue).length + if (childDomIndex === 0) { + check ? this.selectionAll() : this.clearSelection() + return + } + + if (check) { + if (keyValue) { + this.backData.push(child) + } + this.backIndexData.push(childDomIndex - 1) + } else { + const index = this.backData.findIndex(v => v[this.rowKey] === keyValue) + const idx = this.backIndexData.findIndex(item => item === childDomIndex - 1) + if (keyValue) { + this.backData.splice(index, 1) + } + this.backIndexData.splice(idx, 1) + } + + const domCheckAll = this.trChildren.find((item, index) => index > 0 && !item.checked && !item.disabled) + if (!domCheckAll) { + theadChildren.indeterminate = false + theadChildren.checked = true + } else { + theadChildren.indeterminate = true + theadChildren.checked = false + } + + if (this.backIndexData.length === 0) { + theadChildren.indeterminate = false + } + + if (!emit) { + this.$emit('selection-change', { + detail: { + value: this.backData, + index: this.backIndexData + } + }) + } + } + } +} +</script> + +<style lang="scss"> +$border-color: #ebeef5; + +.uni-table-scroll { + width: 100%; + /* #ifndef APP-NVUE */ + overflow-x: auto; + /* #endif */ +} + +.uni-table { + position: relative; + width: 100%; + border-radius: 5px; + // box-shadow: 0px 0px 3px 1px rgba(0, 0, 0, 0.1); + background-color: #fff; + /* #ifndef APP-NVUE */ + box-sizing: border-box; + display: table; + overflow-x: auto; + ::v-deep .uni-table-tr:nth-child(n + 2) { + &:hover { + background-color: #f5f7fa; + } + } + ::v-deep .uni-table-thead { + .uni-table-tr { + // background-color: #f5f7fa; + &:hover { + background-color:#fafafa; + } + } + } + /* #endif */ +} + +.table--border { + border: 1px $border-color solid; + border-right: none; +} + +.border-none { + /* #ifndef APP-NVUE */ + border-bottom: none; + /* #endif */ +} + +.table--stripe { + /* #ifndef APP-NVUE */ + ::v-deep .uni-table-tr:nth-child(2n + 3) { + background-color: #fafafa; + } + /* #endif */ +} + +/* 表格加载、无数据样式 */ +.uni-table-loading { + position: relative; + /* #ifndef APP-NVUE */ + display: table-row; + /* #endif */ + height: 50px; + line-height: 50px; + overflow: hidden; + box-sizing: border-box; +} +.empty-border { + border-right: 1px $border-color solid; +} +.uni-table-text { + position: absolute; + right: 0; + left: 0; + text-align: center; + font-size: 14px; + color: #999; +} + +.uni-table-mask { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background-color: rgba(255, 255, 255, 0.8); + z-index: 99; + /* #ifndef APP-NVUE */ + display: flex; + margin: auto; + transition: all 0.5s; + /* #endif */ + justify-content: center; + align-items: center; +} + +.uni-table--loader { + width: 30px; + height: 30px; + border: 2px solid #aaa; + // border-bottom-color: transparent; + border-radius: 50%; + /* #ifndef APP-NVUE */ + animation: 2s uni-table--loader linear infinite; + /* #endif */ + position: relative; +} + +@keyframes uni-table--loader { + 0% { + transform: rotate(360deg); + } + + 10% { + border-left-color: transparent; + } + + 20% { + border-bottom-color: transparent; + } + + 30% { + border-right-color: transparent; + } + + 40% { + border-top-color: transparent; + } + + 50% { + transform: rotate(0deg); + } + + 60% { + border-top-color: transparent; + } + + 70% { + border-left-color: transparent; + } + + 80% { + border-bottom-color: transparent; + } + + 90% { + border-right-color: transparent; + } + + 100% { + transform: rotate(-360deg); + } +} +</style> diff --git a/uni_modules/uni-table/components/uni-tbody/uni-tbody.vue b/uni_modules/uni-table/components/uni-tbody/uni-tbody.vue new file mode 100644 index 0000000..130f626 --- /dev/null +++ b/uni_modules/uni-table/components/uni-tbody/uni-tbody.vue @@ -0,0 +1,34 @@ +<template> + <!-- #ifdef H5 --> + <tbody> + <slot></slot> + </tbody> + <!-- #endif --> + <!-- #ifndef H5 --> + <view><slot></slot></view> + <!-- #endif --> +</template> + +<script> +export default { + name: 'uniBody', + options: { + // #ifdef MP-TOUTIAO + virtualHost: false, + // #endif + // #ifndef MP-TOUTIAO + virtualHost: true + // #endif + }, + data() { + return { + + } + }, + created() {}, + methods: {} +} +</script> + +<style> +</style> diff --git a/uni_modules/uni-table/components/uni-td/uni-td.vue b/uni_modules/uni-table/components/uni-td/uni-td.vue new file mode 100644 index 0000000..69e5e8a --- /dev/null +++ b/uni_modules/uni-table/components/uni-td/uni-td.vue @@ -0,0 +1,95 @@ +<template> + <!-- #ifdef H5 --> + <td class="uni-table-td" :rowspan="rowspan" :colspan="colspan" :class="{'table--border':border}" :style="{width:width + 'px','text-align':align}"> + <slot></slot> + </td> + <!-- #endif --> + <!-- #ifndef H5 --> + <!-- :class="{'table--border':border}" --> + <view class="uni-table-td" :class="{'table--border':border}" :style="{width:width + 'px','text-align':align}"> + <slot></slot> + </view> + <!-- #endif --> + +</template> + +<script> + /** + * Td 单元格 + * @description 表格中的标准单元格组件 + * @tutorial https://ext.dcloud.net.cn/plugin?id=3270 + * @property {Number} align = [left|center|right] 单元格对齐方式 + */ + export default { + name: 'uniTd', + options: { + // #ifdef MP-TOUTIAO + virtualHost: false, + // #endif + // #ifndef MP-TOUTIAO + virtualHost: true + // #endif + }, + props: { + width: { + type: [String, Number], + default: '' + }, + align: { + type: String, + default: 'left' + }, + rowspan: { + type: [Number,String], + default: 1 + }, + colspan: { + type: [Number,String], + default: 1 + } + }, + data() { + return { + border: false + }; + }, + created() { + this.root = this.getTable() + this.border = this.root.border + }, + methods: { + /** + * 获取父元素实例 + */ + getTable() { + let parent = this.$parent; + let parentName = parent.$options.name; + while (parentName !== 'uniTable') { + parent = parent.$parent; + if (!parent) return false; + parentName = parent.$options.name; + } + return parent; + }, + } + } +</script> + +<style lang="scss"> + $border-color:#EBEEF5; + + .uni-table-td { + display: table-cell; + padding: 8px 10px; + font-size: 14px; + border-bottom: 1px $border-color solid; + font-weight: 400; + color: #606266; + line-height: 23px; + box-sizing: border-box; + } + + .table--border { + border-right: 1px $border-color solid; + } +</style> diff --git a/uni_modules/uni-table/components/uni-th/filter-dropdown.vue b/uni_modules/uni-table/components/uni-th/filter-dropdown.vue new file mode 100644 index 0000000..df22a71 --- /dev/null +++ b/uni_modules/uni-table/components/uni-th/filter-dropdown.vue @@ -0,0 +1,511 @@ +<template> + <view class="uni-filter-dropdown"> + <view class="dropdown-btn" @click="onDropdown"> + <view class="icon-select" :class="{active: canReset}" v-if="isSelect || isRange"></view> + <view class="icon-search" :class="{active: canReset}" v-if="isSearch"> + <view class="icon-search-0"></view> + <view class="icon-search-1"></view> + </view> + <view class="icon-calendar" :class="{active: canReset}" v-if="isDate"> + <view class="icon-calendar-0"></view> + <view class="icon-calendar-1"></view> + </view> + </view> + <view class="uni-dropdown-cover" v-if="isOpened" @click="handleClose"></view> + <view class="dropdown-popup dropdown-popup-right" v-if="isOpened" @click.stop> + <!-- select--> + <view v-if="isSelect" class="list"> + <label class="flex-r a-i-c list-item" v-for="(item,index) in dataList" :key="index" + @click="onItemClick($event, index)"> + <check-box class="check" :checked="item.checked" /> + <view class="checklist-content"> + <text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text> + </view> + </label> + </view> + <view v-if="isSelect" class="flex-r opera-area"> + <view class="flex-f btn btn-default" :class="{disable: !canReset}" @click="handleSelectReset"> + {{resource.reset}}</view> + <view class="flex-f btn btn-submit" @click="handleSelectSubmit">{{resource.submit}}</view> + </view> + <!-- search --> + <view v-if="isSearch" class="search-area"> + <input class="search-input" v-model="filterValue" /> + </view> + <view v-if="isSearch" class="flex-r opera-area"> + <view class="flex-f btn btn-submit" @click="handleSearchSubmit">{{resource.search}}</view> + <view class="flex-f btn btn-default" :class="{disable: !canReset}" @click="handleSearchReset"> + {{resource.reset}}</view> + </view> + <!-- range --> + <view v-if="isRange"> + <view class="input-label">{{resource.gt}}</view> + <input class="input" v-model="gtValue" /> + <view class="input-label">{{resource.lt}}</view> + <input class="input" v-model="ltValue" /> + </view> + <view v-if="isRange" class="flex-r opera-area"> + <view class="flex-f btn btn-default" :class="{disable: !canReset}" @click="handleRangeReset"> + {{resource.reset}}</view> + <view class="flex-f btn btn-submit" @click="handleRangeSubmit">{{resource.submit}}</view> + </view> + <!-- date --> + <view v-if="isDate"> + <uni-datetime-picker ref="datetimepicker" :value="dateRange" type="datetimerange" return-type="timestamp" @change="datetimechange" @maskClick="timepickerclose"> + <view></view> + </uni-datetime-picker> + </view> + </view> + </view> +</template> + +<script> + import checkBox from '../uni-tr/table-checkbox.vue' + + const resource = { + "reset": "重置", + "search": "搜索", + "submit": "确定", + "filter": "筛选", + "gt": "大于等于", + "lt": "小于等于", + "date": "日期范围" + } + + const DropdownType = { + Select: "select", + Search: "search", + Range: "range", + Date: "date", + Timestamp: "timestamp" + } + + export default { + name: 'FilterDropdown', + emits:['change'], + components: { + checkBox + }, + options: { + virtualHost: true + }, + props: { + filterType: { + type: String, + default: DropdownType.Select + }, + filterData: { + type: Array, + default () { + return [] + } + }, + mode: { + type: String, + default: 'default' + }, + map: { + type: Object, + default () { + return { + text: 'text', + value: 'value' + } + } + }, + filterDefaultValue: { + type: [Array,String], + default () { + return "" + } + } + }, + computed: { + canReset() { + if (this.isSearch) { + return this.filterValue.length > 0 + } + if (this.isSelect) { + return this.checkedValues.length > 0 + } + if (this.isRange) { + return (this.gtValue.length > 0 && this.ltValue.length > 0) + } + if (this.isDate) { + return this.dateSelect.length > 0 + } + return false + }, + isSelect() { + return this.filterType === DropdownType.Select + }, + isSearch() { + return this.filterType === DropdownType.Search + }, + isRange() { + return this.filterType === DropdownType.Range + }, + isDate() { + return (this.filterType === DropdownType.Date || this.filterType === DropdownType.Timestamp) + } + }, + watch: { + filterData(newVal) { + this._copyFilters() + }, + indeterminate(newVal) { + this.isIndeterminate = newVal + } + }, + data() { + return { + resource, + enabled: true, + isOpened: false, + dataList: [], + filterValue: this.filterDefaultValue, + checkedValues: [], + gtValue: '', + ltValue: '', + dateRange: [], + dateSelect: [] + }; + }, + created() { + this._copyFilters() + }, + methods: { + _copyFilters() { + let dl = JSON.parse(JSON.stringify(this.filterData)) + for (let i = 0; i < dl.length; i++) { + if (dl[i].checked === undefined) { + dl[i].checked = false + } + } + this.dataList = dl + }, + openPopup() { + this.isOpened = true + if (this.isDate) { + this.$nextTick(() => { + if (!this.dateRange.length) { + this.resetDate() + } + this.$refs.datetimepicker.show() + }) + } + }, + closePopup() { + this.isOpened = false + }, + handleClose(e) { + this.closePopup() + }, + resetDate() { + let date = new Date() + let dateText = date.toISOString().split('T')[0] + this.dateRange = [dateText + ' 0:00:00', dateText + ' 23:59:59'] + }, + onDropdown(e) { + this.openPopup() + }, + onItemClick(e, index) { + let items = this.dataList + let listItem = items[index] + if (listItem.checked === undefined) { + items[index].checked = true + } else { + items[index].checked = !listItem.checked + } + + let checkvalues = [] + for (let i = 0; i < items.length; i++) { + const item = items[i] + if (item.checked) { + checkvalues.push(item.value) + } + } + this.checkedValues = checkvalues + }, + datetimechange(e) { + this.closePopup() + this.dateRange = e + this.dateSelect = e + this.$emit('change', { + filterType: this.filterType, + filter: e + }) + }, + timepickerclose(e) { + this.closePopup() + }, + handleSelectSubmit() { + this.closePopup() + this.$emit('change', { + filterType: this.filterType, + filter: this.checkedValues + }) + }, + handleSelectReset() { + if (!this.canReset) { + return; + } + var items = this.dataList + for (let i = 0; i < items.length; i++) { + let item = items[i] + this.$set(item, 'checked', false) + } + this.checkedValues = [] + this.handleSelectSubmit() + }, + handleSearchSubmit() { + this.closePopup() + this.$emit('change', { + filterType: this.filterType, + filter: this.filterValue + }) + }, + handleSearchReset() { + if (!this.canReset) { + return; + } + this.filterValue = '' + this.handleSearchSubmit() + }, + handleRangeSubmit(isReset) { + this.closePopup() + this.$emit('change', { + filterType: this.filterType, + filter: isReset === true ? [] : [parseInt(this.gtValue), parseInt(this.ltValue)] + }) + }, + handleRangeReset() { + if (!this.canReset) { + return; + } + this.gtValue = '' + this.ltValue = '' + this.handleRangeSubmit(true) + } + } + } +</script> + +<style lang="scss"> + $uni-primary: #1890ff !default; + + .flex-r { + display: flex; + flex-direction: row; + } + + .flex-f { + flex: 1; + } + + .a-i-c { + align-items: center; + } + + .j-c-c { + justify-content: center; + } + + .icon-select { + width: 14px; + height: 16px; + border: solid 6px transparent; + border-top: solid 6px #ddd; + border-bottom: none; + background-color: #ddd; + background-clip: content-box; + box-sizing: border-box; + } + + .icon-select.active { + background-color: $uni-primary; + border-top-color: $uni-primary; + } + + .icon-search { + width: 12px; + height: 16px; + position: relative; + } + + .icon-search-0 { + border: 2px solid #ddd; + border-radius: 8px; + width: 7px; + height: 7px; + } + + .icon-search-1 { + position: absolute; + top: 8px; + right: 0; + width: 1px; + height: 7px; + background-color: #ddd; + transform: rotate(-45deg); + } + + .icon-search.active .icon-search-0 { + border-color: $uni-primary; + } + + .icon-search.active .icon-search-1 { + background-color: $uni-primary; + } + + .icon-calendar { + color: #ddd; + width: 14px; + height: 16px; + } + + .icon-calendar-0 { + height: 4px; + margin-top: 3px; + margin-bottom: 1px; + background-color: #ddd; + border-radius: 2px 2px 1px 1px; + position: relative; + } + .icon-calendar-0:before, .icon-calendar-0:after { + content: ''; + position: absolute; + top: -3px; + width: 4px; + height: 3px; + border-radius: 1px; + background-color: #ddd; + } + .icon-calendar-0:before { + left: 2px; + } + .icon-calendar-0:after { + right: 2px; + } + + .icon-calendar-1 { + height: 9px; + background-color: #ddd; + border-radius: 1px 1px 2px 2px; + } + + .icon-calendar.active { + color: $uni-primary; + } + + .icon-calendar.active .icon-calendar-0, + .icon-calendar.active .icon-calendar-1, + .icon-calendar.active .icon-calendar-0:before, + .icon-calendar.active .icon-calendar-0:after { + background-color: $uni-primary; + } + + .uni-filter-dropdown { + position: relative; + font-weight: normal; + } + + .dropdown-popup { + position: absolute; + top: 100%; + background-color: #fff; + box-shadow: 0 3px 6px -4px #0000001f, 0 6px 16px #00000014, 0 9px 28px 8px #0000000d; + min-width: 150px; + z-index: 1000; + } + + .dropdown-popup-left { + left: 0; + } + + .dropdown-popup-right { + right: 0; + } + + .uni-dropdown-cover { + position: fixed; + left: 0; + top: 0; + right: 0; + bottom: 0; + background-color: transparent; + z-index: 100; + } + + .list { + margin-top: 5px; + margin-bottom: 5px; + } + + .list-item { + padding: 5px 10px; + text-align: left; + } + + .list-item:hover { + background-color: #f0f0f0; + } + + .check { + margin-right: 5px; + } + + .search-area { + padding: 10px; + } + + .search-input { + font-size: 12px; + border: 1px solid #f0f0f0; + border-radius: 3px; + padding: 2px 5px; + min-width: 150px; + text-align: left; + } + + .input-label { + margin: 10px 10px 5px 10px; + text-align: left; + } + + .input { + font-size: 12px; + border: 1px solid #f0f0f0; + border-radius: 3px; + margin: 10px; + padding: 2px 5px; + min-width: 150px; + text-align: left; + } + + .opera-area { + cursor: default; + border-top: 1px solid #ddd; + padding: 5px; + } + + .opera-area .btn { + font-size: 12px; + border-radius: 3px; + margin: 5px; + padding: 4px 4px; + } + + .btn-default { + border: 1px solid #ddd; + } + + .btn-default.disable { + border-color: transparent; + } + + .btn-submit { + background-color: $uni-primary; + color: #ffffff; + } +</style> diff --git a/uni_modules/uni-table/components/uni-th/uni-th.vue b/uni_modules/uni-table/components/uni-th/uni-th.vue new file mode 100644 index 0000000..a39ae6c --- /dev/null +++ b/uni_modules/uni-table/components/uni-th/uni-th.vue @@ -0,0 +1,295 @@ +<template> + <!-- #ifdef H5 --> + <th :rowspan="rowspan" :colspan="colspan" class="uni-table-th" :class="{ 'table--border': border }" :style="{ width: customWidth + 'px', 'text-align': align }"> + <view class="uni-table-th-row"> + <view class="uni-table-th-content" :style="{ 'justify-content': contentAlign }" @click="sort"> + <slot></slot> + <view v-if="sortable" class="arrow-box"> + <text class="arrow up" :class="{ active: ascending }" @click.stop="ascendingFn"></text> + <text class="arrow down" :class="{ active: descending }" @click.stop="descendingFn"></text> + </view> + </view> + <dropdown v-if="filterType || filterData.length" :filterDefaultValue="filterDefaultValue" :filterData="filterData" :filterType="filterType" @change="ondropdown"></dropdown> + </view> + </th> + <!-- #endif --> + <!-- #ifndef H5 --> + <view class="uni-table-th" :class="{ 'table--border': border }" :style="{ width: customWidth + 'px', 'text-align': align }"><slot></slot></view> + <!-- #endif --> +</template> + +<script> + // #ifdef H5 + import dropdown from './filter-dropdown.vue' + // #endif +/** + * Th 表头 + * @description 表格内的表头单元格组件 + * @tutorial https://ext.dcloud.net.cn/plugin?id=3270 + * @property {Number | String} width 单元格宽度(支持纯数字、携带单位px或rpx) + * @property {Boolean} sortable 是否启用排序 + * @property {Number} align = [left|center|right] 单元格对齐方式 + * @value left 单元格文字左侧对齐 + * @value center 单元格文字居中 + * @value right 单元格文字右侧对齐 + * @property {Array} filterData 筛选数据 + * @property {String} filterType [search|select] 筛选类型 + * @value search 关键字搜素 + * @value select 条件选择 + * @event {Function} sort-change 排序触发事件 + */ +export default { + name: 'uniTh', + options: { + // #ifdef MP-TOUTIAO + virtualHost: false, + // #endif + // #ifndef MP-TOUTIAO + virtualHost: true + // #endif + }, + components: { + // #ifdef H5 + dropdown + // #endif + }, + emits:['sort-change','filter-change'], + props: { + width: { + type: [String, Number], + default: '' + }, + align: { + type: String, + default: 'left' + }, + rowspan: { + type: [Number, String], + default: 1 + }, + colspan: { + type: [Number, String], + default: 1 + }, + sortable: { + type: Boolean, + default: false + }, + filterType: { + type: String, + default: "" + }, + filterData: { + type: Array, + default () { + return [] + } + }, + filterDefaultValue: { + type: [Array,String], + default () { + return "" + } + } + }, + data() { + return { + border: false, + ascending: false, + descending: false + } + }, + computed: { + // 根据props中的width属性 自动匹配当前th的宽度(px) + customWidth(){ + if(typeof this.width === 'number'){ + return this.width + } else if(typeof this.width === 'string') { + let regexHaveUnitPx = new RegExp(/^[1-9][0-9]*px$/g) + let regexHaveUnitRpx = new RegExp(/^[1-9][0-9]*rpx$/g) + let regexHaveNotUnit = new RegExp(/^[1-9][0-9]*$/g) + if (this.width.match(regexHaveUnitPx) !== null) { // 携带了 px + return this.width.replace('px', '') + } else if (this.width.match(regexHaveUnitRpx) !== null) { // 携带了 rpx + let numberRpx = Number(this.width.replace('rpx', '')) + // #ifdef MP-WEIXIN + let widthCoe = uni.getWindowInfo().screenWidth / 750 + // #endif + // #ifndef MP-WEIXIN + let widthCoe = uni.getSystemInfoSync().screenWidth / 750 + // #endif + return Math.round(numberRpx * widthCoe) + } else if (this.width.match(regexHaveNotUnit) !== null) { // 未携带 rpx或px 的纯数字 String + return this.width + } else { // 不符合格式 + return '' + } + } else { + return '' + } + }, + contentAlign() { + let align = 'left' + switch (this.align) { + case 'left': + align = 'flex-start' + break + case 'center': + align = 'center' + break + case 'right': + align = 'flex-end' + break + } + return align + } + }, + created() { + this.root = this.getTable('uniTable') + this.rootTr = this.getTable('uniTr') + this.rootTr.minWidthUpdate(this.customWidth ? this.customWidth : 140) + this.border = this.root.border + this.root.thChildren.push(this) + }, + methods: { + sort() { + if (!this.sortable) return + this.clearOther() + if (!this.ascending && !this.descending) { + this.ascending = true + this.$emit('sort-change', { order: 'ascending' }) + return + } + if (this.ascending && !this.descending) { + this.ascending = false + this.descending = true + this.$emit('sort-change', { order: 'descending' }) + return + } + + if (!this.ascending && this.descending) { + this.ascending = false + this.descending = false + this.$emit('sort-change', { order: null }) + } + }, + ascendingFn() { + this.clearOther() + this.ascending = !this.ascending + this.descending = false + this.$emit('sort-change', { order: this.ascending ? 'ascending' : null }) + }, + descendingFn() { + this.clearOther() + this.descending = !this.descending + this.ascending = false + this.$emit('sort-change', { order: this.descending ? 'descending' : null }) + }, + clearOther() { + this.root.thChildren.map(item => { + if (item !== this) { + item.ascending = false + item.descending = false + } + return item + }) + }, + ondropdown(e) { + this.$emit("filter-change", e) + }, + /** + * 获取父元素实例 + */ + getTable(name) { + 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 + } + } +} +</script> + +<style lang="scss"> +$border-color: #ebeef5; +$uni-primary: #007aff !default; + +.uni-table-th { + padding: 12px 10px; + /* #ifndef APP-NVUE */ + display: table-cell; + box-sizing: border-box; + /* #endif */ + font-size: 14px; + font-weight: bold; + color: #909399; + border-bottom: 1px $border-color solid; +} + +.uni-table-th-row { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; +} + +.table--border { + border-right: 1px $border-color solid; +} +.uni-table-th-content { + display: flex; + align-items: center; + flex: 1; +} +.arrow-box { +} +.arrow { + display: block; + position: relative; + width: 10px; + height: 8px; + // border: 1px red solid; + left: 5px; + overflow: hidden; + cursor: pointer; +} +.down { + top: 3px; + ::after { + content: ''; + width: 8px; + height: 8px; + position: absolute; + left: 2px; + top: -5px; + transform: rotate(45deg); + background-color: #ccc; + } + &.active { + ::after { + background-color: $uni-primary; + } + } +} +.up { + ::after { + content: ''; + width: 8px; + height: 8px; + position: absolute; + left: 2px; + top: 5px; + transform: rotate(45deg); + background-color: #ccc; + } + &.active { + ::after { + background-color: $uni-primary; + } + } +} +</style> diff --git a/uni_modules/uni-table/components/uni-thead/uni-thead.vue b/uni_modules/uni-table/components/uni-thead/uni-thead.vue new file mode 100644 index 0000000..53b5c4c --- /dev/null +++ b/uni_modules/uni-table/components/uni-thead/uni-thead.vue @@ -0,0 +1,137 @@ +<template> + <!-- #ifdef H5 --> + <thead class="uni-table-thead"> + <tr class="uni-table-tr"> + <th :rowspan="rowspan" colspan="1" class="checkbox" :class="{ 'tr-table--border': border }"> + <table-checkbox :indeterminate="indeterminate" :checked="checked" + @checkboxSelected="checkboxSelected"></table-checkbox> + </th> + </tr> + <slot></slot> + </thead> + <!-- #endif --> + <!-- #ifndef H5 --> + <view class="uni-table-thead"> + <slot></slot> + </view> + <!-- #endif --> +</template> + +<script> + import tableCheckbox from '../uni-tr/table-checkbox.vue' + export default { + name: 'uniThead', + components: { + tableCheckbox + }, + options: { + // #ifdef MP-TOUTIAO + virtualHost: false, + // #endif + // #ifndef MP-TOUTIAO + virtualHost: true + // #endif + }, + data() { + return { + border: false, + selection: false, + rowspan: 1, + indeterminate: false, + checked: false + } + }, + created() { + this.root = this.getTable() + // #ifdef H5 + this.root.theadChildren = this + // #endif + this.border = this.root.border + this.selection = this.root.type + }, + methods: { + init(self) { + this.rowspan++ + }, + checkboxSelected(e) { + this.indeterminate = false + const backIndexData = this.root.backIndexData + const data = this.root.trChildren.filter(v => !v.disabled && v.keyValue) + if (backIndexData.length === data.length) { + this.checked = false + this.root.clearSelection() + } else { + this.checked = true + this.root.selectionAll() + } + }, + /** + * 获取父元素实例 + */ + getTable(name = 'uniTable') { + 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 + } + } + } +</script> + +<style lang="scss"> + $border-color: #ebeef5; + + .uni-table-thead { + display: table-header-group; + } + + .uni-table-tr { + /* #ifndef APP-NVUE */ + display: table-row; + transition: all 0.3s; + box-sizing: border-box; + /* #endif */ + border: 1px red solid; + background-color: #fafafa; + } + + .checkbox { + padding: 0 8px; + width: 26px; + padding-left: 12px; + /* #ifndef APP-NVUE */ + display: table-cell; + vertical-align: middle; + /* #endif */ + color: #333; + font-weight: 500; + border-bottom: 1px $border-color solid; + font-size: 14px; + // text-align: center; + } + + .tr-table--border { + border-right: 1px $border-color solid; + } + + /* #ifndef APP-NVUE */ + .uni-table-tr { + ::v-deep .uni-table-th { + &.table--border:last-child { + // border-right: none; + } + } + + ::v-deep .uni-table-td { + &.table--border:last-child { + // border-right: none; + } + } + } + + /* #endif */ +</style> diff --git a/uni_modules/uni-table/components/uni-tr/table-checkbox.vue b/uni_modules/uni-table/components/uni-tr/table-checkbox.vue new file mode 100644 index 0000000..1089187 --- /dev/null +++ b/uni_modules/uni-table/components/uni-tr/table-checkbox.vue @@ -0,0 +1,179 @@ +<template> + <view class="uni-table-checkbox" @click="selected"> + <view v-if="!indeterminate" class="checkbox__inner" :class="{'is-checked':isChecked,'is-disable':isDisabled}"> + <view class="checkbox__inner-icon"></view> + </view> + <view v-else class="checkbox__inner checkbox--indeterminate"> + <view class="checkbox__inner-icon"></view> + </view> + </view> +</template> + +<script> + export default { + name: 'TableCheckbox', + emits:['checkboxSelected'], + props: { + indeterminate: { + type: Boolean, + default: false + }, + checked: { + type: [Boolean,String], + default: false + }, + disabled: { + type: Boolean, + default: false + }, + index: { + type: Number, + default: -1 + }, + cellData: { + type: Object, + default () { + return {} + } + } + }, + watch:{ + checked(newVal){ + if(typeof this.checked === 'boolean'){ + this.isChecked = newVal + }else{ + this.isChecked = true + } + }, + indeterminate(newVal){ + this.isIndeterminate = newVal + } + }, + data() { + return { + isChecked: false, + isDisabled: false, + isIndeterminate:false + } + }, + created() { + if(typeof this.checked === 'boolean'){ + this.isChecked = this.checked + } + this.isDisabled = this.disabled + }, + methods: { + selected() { + if (this.isDisabled) return + this.isIndeterminate = false + this.isChecked = !this.isChecked + this.$emit('checkboxSelected', { + checked: this.isChecked, + data: this.cellData + }) + } + } + } +</script> + +<style lang="scss"> + $uni-primary: #007aff !default; + $border-color: #DCDFE6; + $disable:0.4; + + .uni-table-checkbox { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + position: relative; + margin: 5px 0; + cursor: pointer; + + // 多选样式 + .checkbox__inner { + /* #ifndef APP-NVUE */ + flex-shrink: 0; + box-sizing: border-box; + /* #endif */ + position: relative; + width: 16px; + height: 16px; + border: 1px solid $border-color; + border-radius: 2px; + background-color: #fff; + z-index: 1; + + .checkbox__inner-icon { + position: absolute; + /* #ifdef APP-NVUE */ + top: 2px; + /* #endif */ + /* #ifndef APP-NVUE */ + top: 2px; + /* #endif */ + left: 5px; + height: 7px; + width: 3px; + border: 1px solid #fff; + border-left: 0; + border-top: 0; + opacity: 0; + transform-origin: center; + transform: rotate(45deg); + box-sizing: content-box; + } + + &.checkbox--indeterminate { + border-color: $uni-primary; + background-color: $uni-primary; + + .checkbox__inner-icon { + position: absolute; + opacity: 1; + transform: rotate(0deg); + height: 2px; + top: 0; + bottom: 0; + margin: auto; + left: 0px; + right: 0px; + bottom: 0; + width: auto; + border: none; + border-radius: 2px; + transform: scale(0.5); + background-color: #fff; + } + } + &:hover{ + border-color: $uni-primary; + } + // 禁用 + &.is-disable { + /* #ifdef H5 */ + cursor: not-allowed; + /* #endif */ + background-color: #F2F6FC; + border-color: $border-color; + } + + // 选中 + &.is-checked { + border-color: $uni-primary; + background-color: $uni-primary; + + .checkbox__inner-icon { + opacity: 1; + transform: rotate(45deg); + } + + // 选中禁用 + &.is-disable { + opacity: $disable; + } + } + + } + } +</style> diff --git a/uni_modules/uni-table/components/uni-tr/uni-tr.vue b/uni_modules/uni-table/components/uni-tr/uni-tr.vue new file mode 100644 index 0000000..3fb76f4 --- /dev/null +++ b/uni_modules/uni-table/components/uni-tr/uni-tr.vue @@ -0,0 +1,184 @@ +<template> + <!-- #ifdef H5 --> + <tr class="uni-table-tr"> + <th v-if="selection === 'selection' && ishead" class="checkbox" :class="{ 'tr-table--border': border }"> + <table-checkbox :checked="checked" :indeterminate="indeterminate" :disabled="disabled" + @checkboxSelected="checkboxSelected"></table-checkbox> + </th> + <slot></slot> + <!-- <uni-th class="th-fixed">123</uni-th> --> + </tr> + <!-- #endif --> + <!-- #ifndef H5 --> + <view class="uni-table-tr"> + <view v-if="selection === 'selection' " class="checkbox" :class="{ 'tr-table--border': border }"> + <table-checkbox :checked="checked" :indeterminate="indeterminate" :disabled="disabled" + @checkboxSelected="checkboxSelected"></table-checkbox> + </view> + <slot></slot> + </view> + <!-- #endif --> +</template> + +<script> + import tableCheckbox from './table-checkbox.vue' + /** + * Tr 表格行组件 + * @description 表格行组件 仅包含 th,td 组件 + * @tutorial https://ext.dcloud.net.cn/plugin?id= + */ + export default { + name: 'uniTr', + components: { + tableCheckbox + }, + props: { + disabled: { + type: Boolean, + default: false + }, + keyValue: { + type: [String, Number], + default: '' + } + }, + options: { + // #ifdef MP-TOUTIAO + virtualHost: false, + // #endif + // #ifndef MP-TOUTIAO + virtualHost: true + // #endif + }, + data() { + return { + value: false, + border: false, + selection: false, + widthThArr: [], + ishead: true, + checked: false, + indeterminate: false + } + }, + created() { + this.root = this.getTable() + this.head = this.getTable('uniThead') + if (this.head) { + this.ishead = false + this.head.init(this) + } + this.border = this.root.border + this.selection = this.root.type + this.root.trChildren.push(this) + const rowData = this.root.data.find(v => v[this.root.rowKey] === this.keyValue) + if (rowData) { + this.rowData = rowData + } + this.root.isNodata() + }, + mounted() { + if (this.widthThArr.length > 0) { + const selectionWidth = this.selection === 'selection' ? 50 : 0 + this.root.minWidth = Number(this.widthThArr.reduce((a, b) => Number(a) + Number(b))) + selectionWidth; + } + }, + // #ifndef VUE3 + destroyed() { + const index = this.root.trChildren.findIndex(i => i === this) + this.root.trChildren.splice(index, 1) + this.root.isNodata() + }, + // #endif + // #ifdef VUE3 + unmounted() { + const index = this.root.trChildren.findIndex(i => i === this) + this.root.trChildren.splice(index, 1) + this.root.isNodata() + }, + // #endif + methods: { + minWidthUpdate(width) { + this.widthThArr.push(width) + if (this.widthThArr.length > 0) { + const selectionWidth = this.selection === 'selection' ? 50 : 0; + this.root.minWidth = Number(this.widthThArr.reduce((a, b) => Number(a) + Number(b))) + selectionWidth; + } + }, + // 选中 + checkboxSelected(e) { + let rootData = this.root.data.find(v => v[this.root.rowKey] === this.keyValue) + this.checked = e.checked + this.root.check(rootData || this, e.checked, rootData ? this.keyValue : null) + }, + change(e) { + this.root.trChildren.forEach(item => { + if (item === this) { + this.root.check(this, e.detail.value.length > 0 ? true : false) + } + }) + }, + /** + * 获取父元素实例 + */ + getTable(name = 'uniTable') { + 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 + } + } + } +</script> + +<style lang="scss"> + $border-color: #ebeef5; + + .uni-table-tr { + /* #ifndef APP-NVUE */ + display: table-row; + transition: all 0.3s; + box-sizing: border-box; + /* #endif */ + } + + .checkbox { + padding: 0 8px; + width: 26px; + padding-left: 12px; + /* #ifndef APP-NVUE */ + display: table-cell; + vertical-align: middle; + /* #endif */ + color: #333; + font-weight: 500; + border-bottom: 1px $border-color solid; + font-size: 14px; + // text-align: center; + } + + .tr-table--border { + border-right: 1px $border-color solid; + } + + /* #ifndef APP-NVUE */ + .uni-table-tr { + ::v-deep .uni-table-th { + &.table--border:last-child { + // border-right: none; + } + } + + ::v-deep .uni-table-td { + &.table--border:last-child { + // border-right: none; + } + } + } + + /* #endif */ +</style> diff --git a/uni_modules/uni-table/i18n/en.json b/uni_modules/uni-table/i18n/en.json new file mode 100644 index 0000000..e32023c --- /dev/null +++ b/uni_modules/uni-table/i18n/en.json @@ -0,0 +1,9 @@ +{ + "filter-dropdown.reset": "Reset", + "filter-dropdown.search": "Search", + "filter-dropdown.submit": "Submit", + "filter-dropdown.filter": "Filter", + "filter-dropdown.gt": "Greater or equal to", + "filter-dropdown.lt": "Less than or equal to", + "filter-dropdown.date": "Date" +} diff --git a/uni_modules/uni-table/i18n/es.json b/uni_modules/uni-table/i18n/es.json new file mode 100644 index 0000000..9afd04b --- /dev/null +++ b/uni_modules/uni-table/i18n/es.json @@ -0,0 +1,9 @@ +{ + "filter-dropdown.reset": "Reiniciar", + "filter-dropdown.search": "Búsqueda", + "filter-dropdown.submit": "Entregar", + "filter-dropdown.filter": "Filtrar", + "filter-dropdown.gt": "Mayor o igual a", + "filter-dropdown.lt": "Menos que o igual a", + "filter-dropdown.date": "Fecha" +} diff --git a/uni_modules/uni-table/i18n/fr.json b/uni_modules/uni-table/i18n/fr.json new file mode 100644 index 0000000..b006237 --- /dev/null +++ b/uni_modules/uni-table/i18n/fr.json @@ -0,0 +1,9 @@ +{ + "filter-dropdown.reset": "Réinitialiser", + "filter-dropdown.search": "Chercher", + "filter-dropdown.submit": "Soumettre", + "filter-dropdown.filter": "Filtre", + "filter-dropdown.gt": "Supérieur ou égal à", + "filter-dropdown.lt": "Inférieur ou égal à", + "filter-dropdown.date": "Date" +} diff --git a/uni_modules/uni-table/i18n/index.js b/uni_modules/uni-table/i18n/index.js new file mode 100644 index 0000000..2469dd0 --- /dev/null +++ b/uni_modules/uni-table/i18n/index.js @@ -0,0 +1,12 @@ +import en from './en.json' +import es from './es.json' +import fr from './fr.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + es, + fr, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-table/i18n/zh-Hans.json b/uni_modules/uni-table/i18n/zh-Hans.json new file mode 100644 index 0000000..862af17 --- /dev/null +++ b/uni_modules/uni-table/i18n/zh-Hans.json @@ -0,0 +1,9 @@ +{ + "filter-dropdown.reset": "重置", + "filter-dropdown.search": "搜索", + "filter-dropdown.submit": "确定", + "filter-dropdown.filter": "筛选", + "filter-dropdown.gt": "大于等于", + "filter-dropdown.lt": "小于等于", + "filter-dropdown.date": "日期范围" +} diff --git a/uni_modules/uni-table/i18n/zh-Hant.json b/uni_modules/uni-table/i18n/zh-Hant.json new file mode 100644 index 0000000..64f8061 --- /dev/null +++ b/uni_modules/uni-table/i18n/zh-Hant.json @@ -0,0 +1,9 @@ +{ + "filter-dropdown.reset": "重置", + "filter-dropdown.search": "搜索", + "filter-dropdown.submit": "確定", + "filter-dropdown.filter": "篩選", + "filter-dropdown.gt": "大於等於", + "filter-dropdown.lt": "小於等於", + "filter-dropdown.date": "日期範圍" +} diff --git a/uni_modules/uni-table/package.json b/uni_modules/uni-table/package.json new file mode 100644 index 0000000..19454c7 --- /dev/null +++ b/uni_modules/uni-table/package.json @@ -0,0 +1,84 @@ +{ + "id": "uni-table", + "displayName": "uni-table 表格", + "version": "1.2.8", + "description": "表格组件,多用于展示多条结构类似的数据,如", + "keywords": [ + "uni-ui", + "uniui", + "table", + "表格" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss","uni-datetime-picker"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "n" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "n", + "QQ": "y" + }, + "快应用": { + "华为": "n", + "联盟": "n" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-table/readme.md b/uni_modules/uni-table/readme.md new file mode 100644 index 0000000..bb08c79 --- /dev/null +++ b/uni_modules/uni-table/readme.md @@ -0,0 +1,13 @@ + + +## Table 表单 +> 组件名:``uni-table``,代码块: `uTable`。 + +用于展示多条结构类似的数据 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-table) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + + + diff --git a/uni_modules/uni-tag/changelog.md b/uni_modules/uni-tag/changelog.md new file mode 100644 index 0000000..ddee87a --- /dev/null +++ b/uni_modules/uni-tag/changelog.md @@ -0,0 +1,23 @@ +## 2.1.1(2024-03-20) +- 优化 app下边框过窄导致不显示的bug +## 2.1.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-tag](https://uniapp.dcloud.io/component/uniui/uni-tag) +## 2.0.0(2021-11-09) +- 新增 提供组件设计资源,组件样式调整 +- 移除 插槽 +- 移除 type 属性的 royal 选项 +## 1.1.1(2021-08-11) +- type 不是 default 时,size 为 small 字体大小显示不正确 +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-06-18) +- 修复 uni-tag 在字节跳动小程序上 css 类名编译错误的 bug +## 1.0.6(2021-06-04) +- 修复 未定义 sass 变量 "$uni-color-royal" 的bug +## 1.0.5(2021-05-10) +- 修复 royal 类型无效的bug +- 修复 uni-tag 宽度不自适应的bug +- 新增 uni-tag 支持属性 custom-style 自定义样式 +## 1.0.4(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-tag/components/uni-tag/uni-tag.vue b/uni_modules/uni-tag/components/uni-tag/uni-tag.vue new file mode 100644 index 0000000..7274436 --- /dev/null +++ b/uni_modules/uni-tag/components/uni-tag/uni-tag.vue @@ -0,0 +1,252 @@ +<template> + <text class="uni-tag" v-if="text" :class="classes" :style="customStyle" @click="onClick">{{text}}</text> +</template> + +<script> + /** + * Tag 标签 + * @description 用于展示1个或多个文字标签,可点击切换选中、不选中的状态 + * @tutorial https://ext.dcloud.net.cn/plugin?id=35 + * @property {String} text 标签内容 + * @property {String} size = [default|small|mini] 大小尺寸 + * @value default 正常 + * @value small 小尺寸 + * @value mini 迷你尺寸 + * @property {String} type = [default|primary|success|warning|error] 颜色类型 + * @value default 灰色 + * @value primary 蓝色 + * @value success 绿色 + * @value warning 黄色 + * @value error 红色 + * @property {Boolean} disabled = [true|false] 是否为禁用状态 + * @property {Boolean} inverted = [true|false] 是否无需背景颜色(空心标签) + * @property {Boolean} circle = [true|false] 是否为圆角 + * @event {Function} click 点击 Tag 触发事件 + */ + + export default { + name: "UniTag", + emits: ['click'], + props: { + type: { + // 标签类型default、primary、success、warning、error、royal + type: String, + default: "default" + }, + size: { + // 标签大小 normal, small + type: String, + default: "normal" + }, + // 标签内容 + text: { + type: String, + default: "" + }, + disabled: { + // 是否为禁用状态 + type: [Boolean, String], + default: false + }, + inverted: { + // 是否为空心 + type: [Boolean, String], + default: false + }, + circle: { + // 是否为圆角样式 + type: [Boolean, String], + default: false + }, + mark: { + // 是否为标记样式 + type: [Boolean, String], + default: false + }, + customStyle: { + type: String, + default: '' + } + }, + computed: { + classes() { + const { + type, + disabled, + inverted, + circle, + mark, + size, + isTrue + } = this + const classArr = [ + 'uni-tag--' + type, + 'uni-tag--' + size, + isTrue(disabled) ? 'uni-tag--disabled' : '', + isTrue(inverted) ? 'uni-tag--' + type + '--inverted' : '', + isTrue(circle) ? 'uni-tag--circle' : '', + isTrue(mark) ? 'uni-tag--mark' : '', + // type === 'default' ? 'uni-tag--default' : 'uni-tag-text', + isTrue(inverted) ? 'uni-tag--inverted uni-tag-text--' + type : '', + size === 'small' ? 'uni-tag-text--small' : '' + ] + // 返回类的字符串,兼容字节小程序 + return classArr.join(' ') + } + }, + methods: { + isTrue(value) { + return value === true || value === 'true' + }, + onClick() { + if (this.isTrue(this.disabled)) return + this.$emit("click"); + } + } + }; +</script> + +<style lang="scss" scoped> + $uni-primary: #2979ff !default; + $uni-success: #18bc37 !default; + $uni-warning: #f3a73f !default; + $uni-error: #e43d33 !default; + $uni-info: #8f939c !default; + + + $tag-default-pd: 4px 7px; + $tag-small-pd: 2px 5px; + $tag-mini-pd: 1px 3px; + + .uni-tag { + line-height: 14px; + font-size: 12px; + font-weight: 200; + padding: $tag-default-pd; + color: #fff; + border-radius: 3px; + background-color: $uni-info; + border-width: 1px; + border-style: solid; + border-color: $uni-info; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + + // size attr + &--default { + font-size: 12px; + } + + &--default--inverted { + color: $uni-info; + border-color: $uni-info; + } + + &--small { + padding: $tag-small-pd; + font-size: 12px; + border-radius: 2px; + } + + &--mini { + padding: $tag-mini-pd; + font-size: 12px; + border-radius: 2px; + } + + // type attr + &--primary { + background-color: $uni-primary; + border-color: $uni-primary; + color: #fff; + } + + &--success { + color: #fff; + background-color: $uni-success; + border-color: $uni-success; + } + + &--warning { + color: #fff; + background-color: $uni-warning; + border-color: $uni-warning; + } + + &--error { + color: #fff; + background-color: $uni-error; + border-color: $uni-error; + } + + &--primary--inverted { + color: $uni-primary; + border-color: $uni-primary; + } + + &--success--inverted { + color: $uni-success; + border-color: $uni-success; + } + + &--warning--inverted { + color: $uni-warning; + border-color: $uni-warning; + } + + &--error--inverted { + color: $uni-error; + border-color: $uni-error; + } + + &--inverted { + background-color: #fff; + } + + // other attr + &--circle { + border-radius: 15px; + } + + &--mark { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-top-right-radius: 15px; + border-bottom-right-radius: 15px; + } + + &--disabled { + opacity: 0.5; + /* #ifdef H5 */ + cursor: not-allowed; + /* #endif */ + } + } + + + .uni-tag-text { + color: #fff; + font-size: 14px; + + &--primary { + color: $uni-primary; + } + + &--success { + color: $uni-success; + } + + &--warning { + color: $uni-warning; + } + + &--error { + color: $uni-error; + } + + &--small { + font-size: 12px; + } + } +</style> diff --git a/uni_modules/uni-tag/package.json b/uni_modules/uni-tag/package.json new file mode 100644 index 0000000..71b41eb --- /dev/null +++ b/uni_modules/uni-tag/package.json @@ -0,0 +1,84 @@ +{ + "id": "uni-tag", + "displayName": "uni-tag 标签", + "version": "2.1.1", + "description": "Tag 组件,用于展示1个或多个文字标签,可点击切换选中、不选中的状态。", + "keywords": [ + "uni-ui", + "uniui", + "", + "tag", + "标签" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-tag/readme.md b/uni_modules/uni-tag/readme.md new file mode 100644 index 0000000..6e78ff5 --- /dev/null +++ b/uni_modules/uni-tag/readme.md @@ -0,0 +1,13 @@ + + +## Tag 标签 +> **组件名:uni-tag** +> 代码块: `uTag` + + +用于展示1个或多个文字标签,可点击切换选中、不选中的状态 。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-tag) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-title/changelog.md b/uni_modules/uni-title/changelog.md new file mode 100644 index 0000000..7626216 --- /dev/null +++ b/uni_modules/uni-title/changelog.md @@ -0,0 +1,10 @@ +## 1.1.1(2022-05-19) +- 修改组件描述 +## 1.1.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-title](https://uniapp.dcloud.io/component/uniui/uni-title) +## 1.0.2(2021-05-12) +- 新增 示例地址 +- 修复 示例项目缺少组件的Bug +## 1.0.1(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-title/components/uni-title/uni-title.vue b/uni_modules/uni-title/components/uni-title/uni-title.vue new file mode 100644 index 0000000..bf4f926 --- /dev/null +++ b/uni_modules/uni-title/components/uni-title/uni-title.vue @@ -0,0 +1,171 @@ +<template> + <view class="uni-title__box" :style="{'align-items':textAlign}"> + <text class="uni-title__base" :class="['uni-'+type]" :style="{'color':color}">{{title}}</text> + </view> +</template> + +<script> + /** + * Title 标题 + * @description 标题,通常用于记录页面标题,使用当前组件,uni-app 如果开启统计,将会自动统计页面标题 + * @tutorial https://ext.dcloud.net.cn/plugin?id=1066 + * @property {String} type = [h1|h2|h3|h4|h5] 标题类型 + * @value h1 一级标题 + * @value h2 二级标题 + * @value h3 三级标题 + * @value h4 四级标题 + * @value h5 五级标题 + * @property {String} title 标题内容 + * @property {String} align = [left|center|right] 对齐方式 + * @value left 做对齐 + * @value center 居中对齐 + * @value right 右对齐 + * @property {String} color 字体颜色 + * @property {Boolean} stat = [true|false] 是否开启统计功能呢,如不填写type值,默认为开启,填写 type 属性,默认为关闭 + */ + export default { + name:"UniTitle", + props: { + type: { + type: String, + default: '' + }, + title: { + type: String, + default: '' + }, + align: { + type: String, + default: 'left' + }, + color: { + type: String, + default: '#333333' + }, + stat: { + type: [Boolean, String], + default: '' + } + }, + data() { + return { + + }; + }, + computed: { + textAlign() { + let align = 'center'; + switch (this.align) { + case 'left': + align = 'flex-start' + break; + case 'center': + align = 'center' + break; + case 'right': + align = 'flex-end' + break; + } + return align + } + }, + watch: { + title(newVal) { + if (this.isOpenStat()) { + // 上报数据 + if (uni.report) { + uni.report('title', this.title) + } + } + } + }, + mounted() { + if (this.isOpenStat()) { + // 上报数据 + if (uni.report) { + uni.report('title', this.title) + } + } + }, + methods: { + isOpenStat() { + if (this.stat === '') { + this.isStat = false + } + let stat_type = (typeof(this.stat) === 'boolean' && this.stat) || (typeof(this.stat) === 'string' && this.stat !== + '') + if (this.type === "") { + this.isStat = true + if (this.stat.toString() === 'false') { + this.isStat = false + } + } + + if (this.type !== '') { + this.isStat = true + if (stat_type) { + this.isStat = true + } else { + this.isStat = false + } + } + return this.isStat + } + } + } +</script> + +<style> + /* .uni-title { + + } */ + .uni-title__box { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + align-items: flex-start; + justify-content: center; + padding: 8px 0; + flex: 1; + } + + .uni-title__base { + font-size: 15px; + color: #333; + font-weight: 500; + } + + .uni-h1 { + font-size: 20px; + color: #333; + font-weight: bold; + } + + .uni-h2 { + font-size: 18px; + color: #333; + font-weight: bold; + } + + .uni-h3 { + font-size: 16px; + color: #333; + font-weight: bold; + /* font-weight: 400; */ + } + + .uni-h4 { + font-size: 14px; + color: #333; + font-weight: bold; + /* font-weight: 300; */ + } + + .uni-h5 { + font-size: 12px; + color: #333; + font-weight: bold; + /* font-weight: 200; */ + } +</style> diff --git a/uni_modules/uni-title/package.json b/uni_modules/uni-title/package.json new file mode 100644 index 0000000..2249f5a --- /dev/null +++ b/uni_modules/uni-title/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-title", + "displayName": "uni-title 章节标题", + "version": "1.1.1", + "description": "章节标题,通常用于记录页面标题,使用当前组件,uni-app 如果开启统计,将会自动统计页面标题", + "keywords": [ + "uni-ui", + "uniui", + "标题", + "章节", + "章节标题", + "" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-title/readme.md b/uni_modules/uni-title/readme.md new file mode 100644 index 0000000..0e60b1b --- /dev/null +++ b/uni_modules/uni-title/readme.md @@ -0,0 +1,14 @@ + + +## Title 标题 +> **组件名:uni-title** +> 代码块: `uTitle` + + +章节标题,通常用于记录页面标题,使用当前组件,uni-app 如果开启统计,将会自动统计页面标题 。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-title) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + + diff --git a/uni_modules/uni-tooltip/changelog.md b/uni_modules/uni-tooltip/changelog.md new file mode 100644 index 0000000..285b676 --- /dev/null +++ b/uni_modules/uni-tooltip/changelog.md @@ -0,0 +1,16 @@ +## 0.2.4(2024-04-23) +- 修复 弹出位置默认值不一致导致的错位 +## 0.2.3(2024-03-20) +- 修复 弹出位置修正 +## 0.2.2(2024-01-15) +- 新增 placement支持设置四个方向:top bottom left right +## 0.2.1(2022-05-09) +- 修复 content 为空时仍然弹出的bug +## 0.2.0(2022-05-07) +**注意:破坏性更新** +- 更新 text 属性变更为 content +- 更新 移除 width 属性 +## 0.1.1(2022-04-27) +- 修复 组件根 text 嵌套组件 warning +## 0.1.0(2022-04-21) +- 初始化 diff --git a/uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue b/uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue new file mode 100644 index 0000000..476a7dd --- /dev/null +++ b/uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue @@ -0,0 +1,108 @@ +<template> + <view class="uni-tooltip"> + <slot></slot> + <view v-if="content || $slots.content" class="uni-tooltip-popup" :style="initPlacement"> + <slot name="content"> + {{content}} + </slot> + </view> + </view> +</template> + + +<script> + /** + * Tooltip 提示文字 + * @description 常用于展示鼠标 hover 时的提示信息。 + * @tutorial https://uniapp.dcloud.io/component/uniui/uni-tooltip + * @property {String} content 弹出层显示的内容 + * @property {String} placement出现位置, 目前支持:left right top bottom + */ + export default { + name: "uni-tooltip", + data() { + return { + + }; + }, + methods: {}, + computed: { + initPlacement() { + let style = {}; + switch (this.placement) { + case 'left': + style = { + top: '50%', + transform: 'translateY(-50%)', + right: '100%', + "margin-right": '10rpx', + } + break; + case 'right': + style = { + top: '50%', + transform: 'translateY(-50%)', + left: '100%', + "margin-left": '10rpx', + } + break; + case 'top': + style = { + bottom: '100%', + transform: 'translateX(-50%)', + left: '50%', + "margin-bottom": '10rpx', + } + break; + case 'bottom': + style = { + top: '100%', + transform: 'translateX(-50%)', + left: '50%', + "margin-top": '10rpx', + } + break; + } + return style; + } + }, + props: { + content: { + type: String, + default: '' + }, + + placement: { + type: String, + default: 'bottom' + }, + } + } +</script> + +<style> + .uni-tooltip { + position: relative; + cursor: pointer; + display: inline-block; + } + + .uni-tooltip-popup { + z-index: 1; + display: none; + position: absolute; + background-color: #333; + border-radius: 8px; + color: #fff; + font-size: 12px; + text-align: left; + line-height: 16px; + padding: 12px; + overflow: auto; + } + + + .uni-tooltip:hover .uni-tooltip-popup { + display: block; + } +</style> diff --git a/uni_modules/uni-tooltip/package.json b/uni_modules/uni-tooltip/package.json new file mode 100644 index 0000000..44158e1 --- /dev/null +++ b/uni_modules/uni-tooltip/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-tooltip", + "displayName": "uni-tooltip 提示文字", + "version": "0.2.4", + "description": "Tooltip 提示文字", + "keywords": [ + "uni-tooltip", + "uni-ui", + "tooltip", + "tip", + "文字提示" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无 ", + "data": "无", + "permissions": "无" + }, + "npmurl": "", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "Vue": { + "vue2": "y", + "vue3": "y" + }, + "App": { + "app-vue": "y", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-tooltip/readme.md b/uni_modules/uni-tooltip/readme.md new file mode 100644 index 0000000..faafa2e --- /dev/null +++ b/uni_modules/uni-tooltip/readme.md @@ -0,0 +1,8 @@ +## Badge 数字角标 +> **组件名:uni-tooltip** +> 代码块: `uTooltip` + +数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景, + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-tooltip) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 diff --git a/uni_modules/uni-transition/changelog.md b/uni_modules/uni-transition/changelog.md new file mode 100644 index 0000000..faaf336 --- /dev/null +++ b/uni_modules/uni-transition/changelog.md @@ -0,0 +1,24 @@ +## 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) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-transition](https://uniapp.dcloud.io/component/uniui/uni-transition) +## 1.2.1(2021-09-27) +- 修复 init 方法不生效的 Bug +## 1.2.0(2021-07-30) +- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.1(2021-05-12) +- 新增 示例地址 +- 修复 示例项目缺少组件的 Bug +## 1.1.0(2021-04-22) +- 新增 通过方法自定义动画 +- 新增 custom-class 非 NVUE 平台支持自定义 class 定制样式 +- 优化 动画触发逻辑,使动画更流畅 +- 优化 支持单独的动画类型 +- 优化 文档示例 +## 1.0.2(2021-02-05) +- 调整为 uni_modules 目录规范 diff --git a/uni_modules/uni-transition/components/uni-transition/createAnimation.js b/uni_modules/uni-transition/components/uni-transition/createAnimation.js new file mode 100644 index 0000000..8f89b18 --- /dev/null +++ b/uni_modules/uni-transition/components/uni-transition/createAnimation.js @@ -0,0 +1,131 @@ +// const defaultOption = { +// duration: 300, +// timingFunction: 'linear', +// delay: 0, +// transformOrigin: '50% 50% 0' +// } +// #ifdef APP-NVUE +const nvueAnimation = uni.requireNativePlugin('animation') +// #endif +class MPAnimation { + constructor(options, _this) { + this.options = options + // 在iOS10+QQ小程序平台下,传给原生的对象一定是个普通对象而不是Proxy对象,否则会报parameter should be Object instead of ProxyObject的错误 + this.animation = uni.createAnimation({ + ...options + }) + this.currentStepAnimates = {} + this.next = 0 + this.$ = _this + + } + + _nvuePushAnimates(type, args) { + let aniObj = this.currentStepAnimates[this.next] + let styles = {} + if (!aniObj) { + styles = { + styles: {}, + config: {} + } + } else { + styles = aniObj + } + if (animateTypes1.includes(type)) { + if (!styles.styles.transform) { + styles.styles.transform = '' + } + let unit = '' + if(type === 'rotate'){ + unit = 'deg' + } + styles.styles.transform += `${type}(${args+unit}) ` + } else { + styles.styles[type] = `${args}` + } + this.currentStepAnimates[this.next] = styles + } + _animateRun(styles = {}, config = {}) { + let ref = this.$.$refs['ani'].ref + if (!ref) return + return new Promise((resolve, reject) => { + nvueAnimation.transition(ref, { + styles, + ...config + }, res => { + resolve() + }) + }) + } + + _nvueNextAnimate(animates, step = 0, fn) { + let obj = animates[step] + if (obj) { + let { + styles, + config + } = obj + this._animateRun(styles, config).then(() => { + step += 1 + this._nvueNextAnimate(animates, step, fn) + }) + } else { + this.currentStepAnimates = {} + typeof fn === 'function' && fn() + this.isEnd = true + } + } + + step(config = {}) { + // #ifndef APP-NVUE + this.animation.step(config) + // #endif + // #ifdef APP-NVUE + this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config) + this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin + this.next++ + // #endif + return this + } + + run(fn) { + // #ifndef APP-NVUE + this.$.animationData = this.animation.export() + this.$.timer = setTimeout(() => { + typeof fn === 'function' && fn() + }, this.$.durationTime) + // #endif + // #ifdef APP-NVUE + this.isEnd = false + let ref = this.$.$refs['ani'] && this.$.$refs['ani'].ref + if(!ref) return + this._nvueNextAnimate(this.currentStepAnimates, 0, fn) + this.next = 0 + // #endif + } +} + + +const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d', + 'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY', + 'translateZ' +] +const animateTypes2 = ['opacity', 'backgroundColor'] +const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom'] +animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => { + MPAnimation.prototype[type] = function(...args) { + // #ifndef APP-NVUE + this.animation[type](...args) + // #endif + // #ifdef APP-NVUE + this._nvuePushAnimates(type, args) + // #endif + return this + } +}) + +export function createAnimation(option, _this) { + if(!_this) return + clearTimeout(_this.timer) + return new MPAnimation(option, _this) +} diff --git a/uni_modules/uni-transition/components/uni-transition/uni-transition.vue b/uni_modules/uni-transition/components/uni-transition/uni-transition.vue new file mode 100644 index 0000000..f3ddd1f --- /dev/null +++ b/uni_modules/uni-transition/components/uni-transition/uni-transition.vue @@ -0,0 +1,286 @@ +<template> + <!-- #ifndef APP-NVUE --> + <view v-show="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view> + <!-- #endif --> + <!-- #ifdef APP-NVUE --> + <view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view> + <!-- #endif --> +</template> + +<script> +import { createAnimation } from './createAnimation' + +/** + * Transition 过渡动画 + * @description 简单过渡动画组件 + * @tutorial https://ext.dcloud.net.cn/plugin?id=985 + * @property {Boolean} show = [false|true] 控制组件显示或隐藏 + * @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型 + * @value fade 渐隐渐出过渡 + * @value slide-top 由上至下过渡 + * @value slide-right 由右至左过渡 + * @value slide-bottom 由下至上过渡 + * @value slide-left 由左至右过渡 + * @value zoom-in 由小到大过渡 + * @value zoom-out 由大到小过渡 + * @property {Number} duration 过渡动画持续时间 + * @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red` + */ +export default { + name: 'uniTransition', + emits:['click','change'], + props: { + show: { + type: Boolean, + default: false + }, + modeClass: { + type: [Array, String], + default() { + return 'fade' + } + }, + duration: { + type: Number, + default: 300 + }, + styles: { + type: Object, + default() { + return {} + } + }, + customClass:{ + type: String, + default: '' + }, + onceRender:{ + type:Boolean, + default:false + }, + }, + data() { + return { + isShow: false, + transform: '', + opacity: 1, + animationData: {}, + durationTime: 300, + config: {} + } + }, + watch: { + show: { + handler(newVal) { + if (newVal) { + this.open() + } else { + // 避免上来就执行 close,导致动画错乱 + if (this.isShow) { + this.close() + } + } + }, + immediate: true + } + }, + computed: { + // 生成样式数据 + stylesObject() { + let styles = { + ...this.styles, + 'transition-duration': this.duration / 1000 + 's' + } + let transform = '' + for (let i in styles) { + let line = this.toLine(i) + transform += line + ':' + styles[i] + ';' + } + return transform + }, + // 初始化动画条件 + transformStyles() { + return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject + } + }, + created() { + // 动画默认配置 + this.config = { + duration: this.duration, + timingFunction: 'ease', + transformOrigin: '50% 50%', + delay: 0 + } + this.durationTime = this.duration + }, + methods: { + /** + * ref 触发 初始化动画 + */ + init(obj = {}) { + if (obj.duration) { + this.durationTime = obj.duration + } + this.animation = createAnimation(Object.assign(this.config, obj),this) + }, + /** + * 点击组件触发回调 + */ + onClick() { + this.$emit('click', { + detail: this.isShow + }) + }, + /** + * ref 触发 动画分组 + * @param {Object} obj + */ + step(obj, config = {}) { + if (!this.animation) return + for (let i in obj) { + try { + if(typeof obj[i] === 'object'){ + this.animation[i](...obj[i]) + }else{ + this.animation[i](obj[i]) + } + } catch (e) { + console.error(`方法 ${i} 不存在`) + } + } + this.animation.step(config) + return this + }, + /** + * ref 触发 执行动画 + */ + run(fn) { + if (!this.animation) return + this.animation.run(fn) + }, + // 开始过度动画 + open() { + clearTimeout(this.timer) + this.transform = '' + this.isShow = true + let { opacity, transform } = this.styleInit(false) + if (typeof opacity !== 'undefined') { + this.opacity = opacity + } + this.transform = transform + // 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常 + this.$nextTick(() => { + // TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器 + this.timer = setTimeout(() => { + this.animation = createAnimation(this.config, this) + this.tranfromInit(false).step() + this.animation.run() + this.$emit('change', { + detail: this.isShow + }) + }, 20) + }) + }, + // 关闭过度动画 + close(type) { + if (!this.animation) return + this.tranfromInit(true) + .step() + .run(() => { + this.isShow = false + this.animationData = null + this.animation = null + let { opacity, transform } = this.styleInit(false) + this.opacity = opacity || 1 + this.transform = transform + this.$emit('change', { + detail: this.isShow + }) + }) + }, + // 处理动画开始前的默认样式 + styleInit(type) { + let styles = { + transform: '' + } + let buildStyle = (type, mode) => { + if (mode === 'fade') { + styles.opacity = this.animationType(type)[mode] + } else { + styles.transform += this.animationType(type)[mode] + ' ' + } + } + if (typeof this.modeClass === 'string') { + buildStyle(type, this.modeClass) + } else { + this.modeClass.forEach(mode => { + buildStyle(type, mode) + }) + } + return styles + }, + // 处理内置组合动画 + tranfromInit(type) { + let buildTranfrom = (type, mode) => { + let aniNum = null + if (mode === 'fade') { + aniNum = type ? 0 : 1 + } else { + aniNum = type ? '-100%' : '0' + if (mode === 'zoom-in') { + aniNum = type ? 0.8 : 1 + } + if (mode === 'zoom-out') { + aniNum = type ? 1.2 : 1 + } + if (mode === 'slide-right') { + aniNum = type ? '100%' : '0' + } + if (mode === 'slide-bottom') { + aniNum = type ? '100%' : '0' + } + } + this.animation[this.animationMode()[mode]](aniNum) + } + if (typeof this.modeClass === 'string') { + buildTranfrom(type, this.modeClass) + } else { + this.modeClass.forEach(mode => { + buildTranfrom(type, mode) + }) + } + + return this.animation + }, + animationType(type) { + return { + fade: type ? 0 : 1, + 'slide-top': `translateY(${type ? '0' : '-100%'})`, + 'slide-right': `translateX(${type ? '0' : '100%'})`, + 'slide-bottom': `translateY(${type ? '0' : '100%'})`, + 'slide-left': `translateX(${type ? '0' : '-100%'})`, + 'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`, + 'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})` + } + }, + // 内置动画类型与实际动画对应字典 + animationMode() { + return { + fade: 'opacity', + 'slide-top': 'translateY', + 'slide-right': 'translateX', + 'slide-bottom': 'translateY', + 'slide-left': 'translateX', + 'zoom-in': 'scale', + 'zoom-out': 'scale' + } + }, + // 驼峰转中横线 + toLine(name) { + return name.replace(/([A-Z])/g, '-$1').toLowerCase() + } + } +} +</script> + +<style></style> diff --git a/uni_modules/uni-transition/package.json b/uni_modules/uni-transition/package.json new file mode 100644 index 0000000..d5c20e1 --- /dev/null +++ b/uni_modules/uni-transition/package.json @@ -0,0 +1,85 @@ +{ + "id": "uni-transition", + "displayName": "uni-transition 过渡动画", + "version": "1.3.3", + "description": "元素的简单过渡动画", + "keywords": [ + "uni-ui", + "uniui", + "动画", + "过渡", + "过渡动画" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-transition/readme.md b/uni_modules/uni-transition/readme.md new file mode 100644 index 0000000..2f8a77e --- /dev/null +++ b/uni_modules/uni-transition/readme.md @@ -0,0 +1,11 @@ + + +## Transition 过渡动画 +> **组件名:uni-transition** +> 代码块: `uTransition` + + +元素过渡动画 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-transition) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-ui/changelog.md b/uni_modules/uni-ui/changelog.md new file mode 100644 index 0000000..6d477cf --- /dev/null +++ b/uni_modules/uni-ui/changelog.md @@ -0,0 +1,578 @@ +## 1.5.6(2024-07-08) +- uni-datetime-picker 新增 日期点击事件,在点击日期时会触发该事件。 +- uni-datetime-picker 修复 抖音小程序事件传递失效bug +- uni-easyinput 修复 easyinput组件双向绑定问题 +- uni-number-box 修复 在vue2下H5黑边的bug +- uni-number-box 修复 在vue2手动输入后失焦导致清空数值的严重bug +- uni-popup 修复 uni-popup-dialog vue3下使用value无法进行绑定的bug(双向绑定兼容旧写法) +- uni-search-bar 修复 textColor默认值导致的文字不显示的bug +- uni-search-bar 修复 textColor不生效的bug +- uni-segmented-control 修复 修复在微信小程序下inactiveColor失效bug +- uni-tooltip 修复 弹出位置默认值不一致导致的错位 +- uni-transition 修复 当元素会受变量影响自动隐藏的bug +## 1.5.0(2024-01-13) +- 修复 npm包结构目录错误的问题 +- uni-calendar 修复 回到今天时,月份显示不一致问题 +- uni-data-picker 新增 支持 uni-app-x +- uni-datetime-picker 优化 增加noChange事件,当进行日期范围选择时,若有空值,则触发该事件 [详情](https://github.com/dcloudio/uni-ui/issues/815) +- uni-datetime-picker 修复 字节小程序时间选择范围器失效问题 [详情](https://github.com/dcloudio/uni-ui/issues/834) +- uni-datetime-picker 修复 PC端初次修改时间,开始时间未更新的Bug [详情](https://github.com/dcloudio/uni-ui/issues/737) +- uni-datetime-picker 修复 部分情况修改时间,开始、结束时间显示异常的Bug [详情](https://ask.dcloud.net.cn/question/171146) +- uni-datetime-picker 优化 当前月可以选择上月、下月的日期的Bug +- uni-file-picker 新增 微信小程序不再调用chooseImage,而是调用chooseMedia +- uni-file-picker 新增 上传文件至云存储携带本地文件名称 +- uni-forms 优化 labelWidth 描述错误 +fix: 修复图标大小默认值错误的问题 +- uni-icons 修复 项目未使用 ts 情况下,打包报错的bug +- uni-icons 修复 size 属性为 string 时,不加单位导致尺寸异常的bug +- uni-icons 优化 兼容老版本icon类型,如 top ,bottom 等 +- uni-icons 优化 兼容老版本icon类型,如 top ,bottom 等 +- uni-icons 优化 uni-app x 下示例项目图标排序 +- uni-icons 修复 nvue下引入组件报错的bug +-优化 size 属性支持单位 +- uni-icons 新增 uni-app x 支持定义图标 +- uni-notice-bar 修复动态绑定title时,滚动速度不一致的问题 +更新示例工程 +- uni-popup 新增 uni-popup 支持uni-app-x 注意暂时仅支持 `maskClick` `@open` `@close` +- uni-table 修复 uni-tr只有一列时minWidth计算错误,列变化实时计算更新 +## 1.4.27(2023-04-21) +- uni-calendar 修复 某些情况 monthSwitch 未触发的Bug +- uni-calendar 修复 某些情况切换月份错误的Bug +- uni-data-picker 修复 更改 modelValue 报错的 bug +- uni-data-picker 修复 v-for 未使用 key 值控制台 warning +- uni-data-picker 修复代码合并时引发 value 属性为空时不渲染数据的问题 +- uni-data-picker 修复 localdata 不支持动态更新的bug +- uni-data-select 修复 微信小程序点击时会改变背景颜色的 bug +- uni-data-select 修复 禁用时会显示清空按钮 +- uni-data-select 优化 查询条件短期内多次变更只查询最后一次变更后的结果 +- uni-data-select 调整 内部缓存键名调整为 uni-data-select-lastSelectedValue +- uni-datetime-picker 修复 日历 picker 修改年月后,自动选中当月1日 [详情](https://ask.dcloud.net.cn/question/165937) +- uni-datetime-picker 修复 小程序端 低版本 ios NaN [详情](https://ask.dcloud.net.cn/question/162979) +- uni-datetime-picker 修复 firefox 浏览器显示区域点击无法拉起日历弹框的Bug [详情](https://ask.dcloud.net.cn/question/163362) +- uni-datetime-picker 优化 值为空依然选中当天问题 +- uni-datetime-picker 优化 提供 default-value 属性支持配置选择器打开时默认显示的时间 +- uni-datetime-picker 优化 非范围选择未选择日期时间,点击确认按钮选中当前日期时间 +- uni-datetime-picker 优化 字节小程序日期时间范围选择,底部日期换行问题 +- uni-datetime-picker 修复 2.2.18 引起范围选择配置 end 选择无效的Bug [详情](https://github.com/dcloudio/uni-ui/issues/686) +- uni-datetime-picker 修复 移动端范围选择change事件触发异常的Bug [详情](https://github.com/dcloudio/uni-ui/issues/684) +- uni-datetime-picker 优化 PC端输入日期格式错误时返回当前日期时间 +- uni-datetime-picker 优化 PC端输入日期时间超出 start、end 限制的Bug +- uni-datetime-picker 优化 移动端日期时间范围用法时间展示不完整问题 +- uni-datetime-picker 修复 小程序端绑定 Date 类型报错的Bug [详情](https://github.com/dcloudio/uni-ui/issues/679) +- uni-datetime-picker 修复 vue3 time-picker 无法显示绑定时分秒的Bug +- uni-datetime-picker 修复 字节小程序报错的Bug +- uni-datetime-picker 修复 某些情况切换月份错误的Bug +- uni-easyinput 修复 vue3 下 keyboardheightchange 事件报错的bug +- uni-easyinput 优化 trim 属性默认值 +- uni-easyinput 新增 cursor-spacing 属性 +- uni-fab 新增 pattern.icon 属性,可自定义图标 +- uni-file-picker 修复 手动上传删除一个文件后不能再上传的bug +- uni-forms 修复 required 参数无法动态绑定 +- uni-list 优化 uni-list-chat 具名插槽`header` 非app端套一层元素,方便使用时通过外层元素定位实现样式修改 +- uni-list uni-list-chat 新增 支持具名插槽`header` +- uni-list 新增 列表图标新增 customPrefix 属性 ,用法 [详见](https://uniapp.dcloud.net.cn/component/uniui/uni-icons.html#icons-props) +- uni-nav-bar 修复 自定义状态栏高度闪动BUG +- uni-nav-bar 修复 暗黑模式下边线颜色错误的bug +- uni-popup 修复 uni-popup 重复打开时的 bug +- uni-popup uni-popup-dialog 组件新增 inputType 属性 +- uni-swipe-action 修复`uni-swipe-action`和`uni-swipe-action-item`不同时使用导致 closeOther 方法报错的 bug +- uni-table 修复 在vue3模式下可能会出现错误的问题 +## 1.4.26(2023-01-31) +- uni-badge 修复 运行/打包 控制台警告问题 +- uni-calendar 修复 某些情况切换月份错误问题 +- uni-data-select 修复 不关联服务空间报错的问题 +- uni-data-select 新增 属性 `format` 可用于格式化显示选项内容 +- uni-datetime-picker 修复 某些情况切换月份错误问题 +- uni-easyinput 新增 keyboardheightchange 事件,可监听键盘高度变化 +- uni-list 修复 无反馈效果呈现的bug +## 1.4.25(2023-01-11) +- uni-file-picker 新增 sourceType 属性, 可以自定义图片和视频选择的来源 +## 1.4.24(2023-01-11) +- uni-data-select 修复 当where变化时,数据不会自动更新的问题 +- uni-datetime-picker 修复 多次加载组件造成内存占用的 bug +- uni-datetime-picker 修复 vue3 下 i18n 国际化初始值不正确的 bug +- uni-easyinput 修复 props 中背景颜色无默认值的bug +- uni-list 修复 uni-list-chat 在vue3下跳转报错的bug +- uni-list 修复 uni-list-chat avatar属性 值为本地路径时错误的问题 +- uni-list 修复 uni-list-chat avatar属性 在腾讯云版uniCloud下错误的问题 +- uni-list 修复 uni-list-chat note属性 支持:“草稿”字样功能 文本少1位的问题 +- uni-list 修复 uni-list-item 的 customStyle 属性 padding值在 H5端 无效的bug +- uni-list 修复 uni-list-item 的 customStyle 属性 padding值在nvue(vue2)下无效的bug +- uni-list uni-list-chat 新增 avatar 支持 fileId +- uni-list uni-list 新增属性 render-reverse 详情参考:[https://uniapp.dcloud.net.cn/component/list.html](https://uniapp.dcloud.net.cn/component/list.html) +- uni-list uni-list-chat note属性 支持:“草稿”字样 加红显示 详情参考uni-im:[https://ext.dcloud.net.cn/plugin?name=uni-im](https://ext.dcloud.net.cn/plugin?name=uni-im) +- uni-list uni-list-item 新增属性 customStyle 支持设置padding、backgroundColor +- uni-popup 修复 nvue 下 v-show 报错 +## 1.4.23(2022-10-25) +- uni-datetime-picker 修复,支付宝小程序样式错乱,[详情](https://github.com/dcloudio/uni-app/issues/3861) + +- uni-nav-bar 修复 条件编译错误的bug +- uni-nav-bar 修复 nvue 环境 fixed 为 true 的情况下,无法置顶的 bug +## 1.4.22(2022-09-19) +- 优化 部分组件适配 uni-scss 主题色 +- uni-badge 修复 当 text 超过 max-num 时,badge 的宽度计算是根据 text 的长度计算,更改为 css 计算实际展示宽度,详见:[https://ask.dcloud.net.cn/question/150473](https://ask.dcloud.net.cn/question/150473) +- uni-calendar 修复 表头年月切换,导致改变当前日期为选择月1号,且未触发change事件 +- uni-data-select 修复 微信小程序下拉框出现后选择会点击到蒙板后面的输入框 +- uni-data-select 修复 点击的位置不准确 +- uni-data-select 新增 支持 disabled 属性 +- uni-datetime-picker 修复,反向选择日期范围,日期显示异常,[详情](https://ask.dcloud.net.cn/question/153401?item_id=212892&rf=false) +- uni-datetime-picker 修复 close事件无效的 bug +- uni-datetime-picker 修复 移动端 maskClick 无效的 bug,详见:[https://ask.dcloud.net.cn/question/140824?item_id=209458&rf=false](https://ask.dcloud.net.cn/question/140824?item_id=209458&rf=false) +- uni-fab 修复 小程序端由于 style 使用了对象导致报错,[详情](https://ask.dcloud.net.cn/question/152790?item_id=211778&rf=false) +- uni-fab 修复 nvue 环境下,具有 tabBar 时,fab 组件下部位置无法正常获取 --window-bottom 的bug,详见:[https://ask.dcloud.net.cn/question/110638?notification_id=826310](https://ask.dcloud.net.cn/question/110638?notification_id=826310) +- uni-forms 优化 根据 rules 自动添加 required 的问题 +- uni-forms 修复 item 未设置 require 属性,rules 设置 require 后,星号也显示的 bug,详见:[https://ask.dcloud.net.cn/question/151540](https://ask.dcloud.net.cn/question/151540) +- uni-nav-bar 修复 nvue 环境下 fixed 为 true 的情况下,无法置顶的 bug +- uni-notice-bar 新增 属性 fontSize,可修改文字大小。 +- uni-pagination 修复,未对主题色设置默认色,导致未引入 uni-scss 变量文件报错。 +- uni-pagination 修复,未对移动端当前页文字做主题色适配。 +- uni-pagination 修复 es 语言 i18n 错误 +## 1.4.21(2022-09-19) +- 修复,安装时未导入 uni-data-select 和 uni-tooltip 的问题。 +## 1.4.20(2022-07-25) +- uni-section 新增组件 +- uni-forms 修复 model 需要校验的值没有声明对应字段时,导致第一次不触发校验的bug + +## 1.4.19(2022-07-07) +- uni-data-picker 优化 pc端图标位置不正确的问题 +- uni-data-select 修复 pc端宽度异常的bug +## 1.4.18(2022-07-06) +- uni-forms 【重要】组件逻辑重构,部分用法旧版本不兼容,请注意兼容问题 +- uni-forms 【重要】组件使用 Provide/Inject 方式注入依赖,提供了自定义表单组件调用 uni-forms 校验表单的能力 +- uni-forms 新增 更多表单示例 +- uni-forms 新增 model 属性,等同于原 value/modelValue 属性,旧属性即将废弃 +- uni-forms 新增 validateTrigger 属性的 blur 值,仅 uni-easyinput 生效 +- uni-forms 新增 onFieldChange 方法,可以对子表单进行校验,可替代binddata方法 +- uni-forms 新增 子表单的 setRules 方法,配合自定义校验函数使用 +- uni-forms 新增 uni-forms-item 的 setRules 方法,配置动态表单使用可动态更新校验规则 +- uni-forms 修复 由 1.4.0 引发的 label 插槽不生效的bug +- uni-forms 修复 子组件找不到 setValue 报错的bug +- uni-forms 修复 uni-data-picker 在 uni-forms-item 中报错的bug +- uni-forms 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug +- uni-forms 修复 表单校验顺序无序问题 +- uni-forms 优化 子表单组件uni-datetime-picker、uni-data-select、uni-data-picker的显示样式 +- uni-forms 优化 动态表单校验方式,废弃拼接name的方式 +- uni-breadcrumb 修复 微信小程序 separator 不显示问题 +- uni-data-checkbox 优化 在 uni-forms 中的依赖注入方式 +- uni-data-picker 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug +- uni-data-picker 优化 显示样式 +- uni-data-select 优化 显示样式 +- uni-datetime-picker 修复 日历顶部年月及底部确认未国际化 bug +- uni-datetime-picker 优化 组件样式,调整了组件图标大小、高度、颜色等,与uni-ui风格保持一致 +- uni-easyinput 新增 在 uni-forms 1.4.0 中使用可以在 blur 时校验内容 +- uni-easyinput 新增 clear 事件,点击右侧叉号图标触发 +- uni-easyinput 新增 change 事件 ,仅在输入框失去焦点或用户按下回车时触发 +- uni-easyinput 优化 组件样式,组件获取焦点时高亮显示,图标颜色调整等 +- uni-easyinput 优化 clearable 显示策略 +- uni-file-picker 修复 在uni-forms下样式不生效的bug +- uni-nav-bar 修复 组件示例中插槽用法无法显示内容的bug +- uni-swipe-action 修复 vue3 下使用组件不能正常运行的Bug +- uni-swipe-action 修复 h5端点击click触发两次的Bug +- uni-table 修复 微信小程序存在无使用组件的问题 +## 1.4.17(2022-06-30) +- 支持 ios 安全区 +## 1.4.16(2022-06-06) +- uni-breadcrumb 新增 支持 uni.scss 修改颜色 +- uni-data-select 修复 localdata 赋值不生效的 bug +- uni-data-select 新增 支持选项禁用(数据选项设置 disabled: true 即禁用) +- uni-data-select 修复 当 value 为 0 时选择不生效的 bug +- uni-easyinput 修复 关闭图标某些情况下无法取消的bug +- uni-fav 新增 stat 属性 ,是否开启uni统计功能 +- uni-goods-nav 新增 stat属性,是否开启uni统计功能 +- uni-group 新增 stat属性,是否开启uni统计功能 +- uni-nav-bar 新增 stat 属性 ,可开启统计 title 上报 ,仅使用了title 属性且项目开启了uni统计生效 +- uni-search-bar 新增 readonly 属性,组件只读 +- uni-swipe-action 修复 isPC 找不到的Bug +- uni-swipe-action 修复 在 nvue 下 disabled 失效的bug +- uni-tooltip 修复 content 为空时仍然弹出的bug +## 1.4.15(2022-05-07) +- uni-data-picker 修复 字节小程序 本地数据无法选择下一级的Bug +- uni-data-select 新增 记住上次的选项(仅 collection 存在时有效) +- uni-search-bar 修复 vue3 input 事件不生效的bug +- uni-search-bar 修复 多余代码导致的bug +- uni-tooltip 更新 text 属性变更为 content +- uni-tooltip 更新 移除 width 属性 +- uni-tooltip 修复 组件根 text 嵌套组件 warning +## 1.4.14(2022-04-18) +- uni-datetime-picker 修复 Vue3 下动态赋值,单选类型未响应的 bug +- uni-easyinput 修复 默认值不生效的bug +## 1.4.13(2022-04-02) +- uni-calendar 修复 条件编译 nvue 不支持的 css 样式 +- uni-calendar 修复 startDate、 endDate 属性失效的 bug +- uni-data-picker 修复 nvue 不支持的 v-show 的 bug +- uni-data-picker 修复 条件编译 nvue 不支持的 css 样式 +- uni-datetime-picker 修复 Vue3 下动态赋值未响应的 bug +- uni-easyinput 修复 value不能为0的bug +- uni-popup 修复 弹出层内部无法滚动的bug +- uni-popup 修复 小程序中高度错误的bug +- uni-popup 修复 快速调用open出现问题的Bug +- uni-rate 修复 条件判断 `NaN` 错误的 bug +- uni-swipe-action 修复 按钮字体大小不能设置的bug +- uni-swipe-action 修复 h5和app端下报el错误的bug +- uni-swipe-action 修复 HBuilderX 1.4.X 版本中,h5和app端下报错的bug +## 1.4.12(2022-02-19) +- uni-collapse 修复 初始化的时候 ,open 属性失效的bug +- uni-data-checkbox 修复 multiple 为 true 时,v-model 的值为 null 报错的 bug +- uni-icons 优化 size 属性可以传入不带单位的字符串数值 +- uni-icons 优化 size 支持其他单位 +- uni-nav-bar 新增 left-width/right-width属性 ,可修改左右两侧的宽度 +- uni-popup 修复 safeArea 属性不能设置为false的bug +## 1.4.11(2022-01-21) +- uni-collapse 修复 微信小程序resize后组件收起的bug +- uni-countdown 修复 在微信小程序中样式不生效的bug +- uni-countdown 新增 update 方法 ,在动态更新时间后,刷新组件 +- uni-load-more 新增 showText属性 ,是否显示文本 +- uni-load-more 修复 nvue 平台下不显示文本的bug +- uni-load-more 修复 微信小程序平台样式选择器报警告的问题 +- uni-nav-bar 修复 在vue下,标题不垂直居中的bug +- uni-nav-bar 修复 height 属性类型错误 +- uni-nav-bar 新增 height 属性,可修改组件高度 +- uni-nav-bar 新增 dark 属性可可开启暗黑模式 +- uni-nav-bar 优化 标题字数过多显示省略号 +- uni-nav-bar 优化 插槽,插入内容可完全覆盖 +- uni-popup 修复 isMaskClick 失效的bug +- uni-popup 新增 cancelText \ confirmText 属性 ,可自定义文本 +- uni-popup 新增 maskBackgroundColor 属性 ,可以修改蒙版颜色 +- uni-popup 优化 maskClick属性 更新为 isMaskClick ,解决微信小程序警告的问题 + +## 1.4.10(2022-01-17) +- uni-card 修复 在vue页面下略缩图显示不正常的bug +- uni-datetime-picker 修复 clear-icon 属性在小程序平台不生效的 bug +- uni-datetime-picker 修复 日期范围选在小程序平台,必须多点击一次才能取消选中状态的 bug +- uni-fab 更新 组件依赖 +- +- uni-icons 修复 nvue 有些图标不显示的bug,兼容老版本图标 +- uni-icons 优化 示例可复制图标名称 +- uni-nav-bar 修复 color 属性不生效的bug +- uni-popup 修复 设置 safeArea 属性不生效的bug +- uni-popup 优化 组件示例 +- uni-popup 修复 vuedoc 文字错误 +## 1.4.9(2021-11-23) +- uni-ui 修复 vue3中某些scss变量无法找到的问题 +- uni-combox 优化 label、label-width 属性 +- uni-data-picker 修复 由上个版本引发的map、v-model等属性不生效的bug +- uni-file-picker 修复 参数为对象的情况下,url在某些情况显示错误的bug +- uni-icons 优化 兼容旧组件 type 值 +- uni-list 修复 在vue3中to属性在发行应用的时候报错的bug +- uni-scss 修复 vue3中scss语法兼容问题 +- uni-transition 修复 init 方法初始化问题 +## 1.4.8(2021-11-19) +- uni-fab 修复 阴影颜色不正确的bug +## 1.4.7(2021-11-19) +- uni-ui 新增 支持国际化 +- uni-ui 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- uni-ui 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-ui](https://uniapp.dcloud.io/component/uniui/uni-ui) +- uni-badge 修改 size 属性默认值调整为 small +- uni-badge 修改 type 属性,默认值调整为 error,info 替换 default +- uni-badge 修复 在字节小程序上样式不生效的 bug +- uni-calendar 修复 弹出层被 tabbar 遮盖 bug +- uni-card 重构插槽的用法 ,header 替换为 title +- uni-card 新增 actions 插槽 +- uni-card 新增 cover 封面图属性和插槽 +- uni-card 新增 padding 内容默认内边距离 +- uni-card 新增 margin 卡片默认外边距离 +- uni-card 新增 spacing 卡片默认内边距 +- uni-card 新增 shadow 卡片阴影属性 +- uni-card 取消 mode 属性,可使用组合插槽代替 +- uni-card 取消 note 属性 ,使用actions插槽代替 +- uni-collapse 优化 show-arrow 属性默认为true +- uni-collapse 新增 show-arrow 属性,控制是否显示右侧箭头 +- uni-countdown 新增 font-size 支持自定义字体大小 +- uni-data-checkbox 修复 在uni-forms中 modelValue 中不存在当前字段,当前字段必填写也不参与校验的问题 +- uni-data-checkbox 修复 单选 list 模式下 ,icon 为 left 时,选中图标不显示的问题 +- uni-data-checkbox 修复 在 uni-forms 中重置表单,错误信息无法清除的问题 +- uni-dateformat 优化 默认时间不再是当前时间,而是显示'-'字符 +- uni-datetime-picker 修复 hide-second 在移动端的 bug +- uni-datetime-picker 修复 单选赋默认值时,赋值日期未高亮的 bug +- uni-datetime-picker 修复 赋默认值时,移动端未正确显示时间的 bug +- uni-datetime-picker 新增 hide-second 属性,支持只使用时分,隐藏秒 +- uni-datetime-picker 优化 取消选中时(范围选)直接开始下一次选择, 避免多点一次 +- uni-datetime-picker 优化 移动端支持清除按钮,同时支持通过 ref 调用组件的 clear 方法 +- uni-datetime-picker 优化 调整字号大小,美化日历界面 +- uni-datetime-picker 优化 范围选择器在 pc 端过宽的问题 +- uni-datetime-picker 新增 支持作为 uni-forms 子组件相关功能 +- uni-datetime-picker 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的 bug +- uni-datetime-picker 修复 type 属性动态赋值无效的 bug +- uni-datetime-picker 修复 ‘确认’按钮被 tabbar 遮盖 bug +- uni-datetime-picker 修复 组件未赋值时范围选左、右日历相同的 bug +- uni-datetime-picker 修复 范围选未正确显示当前值的 bug +- uni-datetime-picker 修复 h5 平台(移动端)报错 'cale' of undefined 的 bug +- uni-easyinput 修复 在 uni-forms 的动态表单中默认值校验不通过的 bug +- uni-easyinput 修复 在 uni-forms 中重置表单,错误信息无法清除的问题 +- uni-file-picker 新增 参数中返回 fileID 字段 +- uni-file-picker 修复 腾讯云传入fileID 不能回显的bug +- uni-file-picker 修复 选择图片后,不能放大的问题 +- uni-file-picker 修复 由于 0.2.11 版本引起的不能回显图片的Bug +- uni-file-picker 新增 clearFiles(index) 方法,可以手动删除指定文件 +- uni-file-picker 修复 v-model 值设为 null 报错的Bug +- uni-file-picker 修复 return-type="object" 时,无法删除文件的Bug +- uni-file-picker 修复 auto-upload 属性失效的Bug +- uni-forms 修复 label 插槽不生效的bug +- uni-forms 修复 没有添加校验规则的字段依然报错的Bug +- uni-forms 修复 重置表单错误信息无法清除的问题 +- uni-forms 修复 表单验证只生效一次的问题 +- uni-icons 新增 更多图标 +- uni-icons 优化 自定义图标使用方式 +- uni-link 修复 在 nvue 下不显示的 bug +- uni-pagination 修复 current 、value 属性未监听,导致高亮样式失效的 bug +- uni-rate 优化 默认值修改为 0 颗星 +- uni-search-bar 修复 value 属性与 modelValue 属性不兼容的Bug +- uni-swipe-action 新增 close-all 方法,关闭所有已打开的组件 +- uni-swipe-action 新增 resize() 方法,在非微信小程序、h5、app-vue端出现不能滑动的问题的时候,重置组件 +- uni-swipe-action 修复 app 端偶尔出现类似 Page[x][-x,xx;-x,xx,x,x-x] 的问题 +- uni-swipe-action 优化 微信小程序、h5、app-vue 滑动逻辑,避免出现动态新增组件后不能滑动的问题 +- uni-tag 新增 提供组件设计资源,组件样式调整 +- uni-tag 移除 插槽 +- uni-tag 移除 type 属性的 royal 选项 +- uni-tag type 不是 default 时,size 为 small 字体大小显示不正确 +## 1.4.2(2021-08-20) +- 新增 uni-ui 组件支持国际化 i18n +- uni-collapse 优化 show-arrow 属性默认为true +- uni-collapse 新增 show-arrow 属性,控制是否显示右侧箭头 +- uni-data-checkbox 修复 单选 list 模式下 ,icon 为 left 时,选中图标不显示的问题 +- uni-easyinput 修复 在 uni-forms 的动态表单中默认值校验不通过的 bug +- uni-file-picker 修复 由于 0.2.11 版本引起的不能回显图片的Bug +- uni-file-picker 新增 clearFiles(index) 方法,可以手动删除指定文件 +- uni-file-picker 修复 v-model 值设为 null 报错的Bug +- uni-swipe-action 新增 close-all 方法,关闭所有已打开的组件 +- uni-swipe-action 新增 resize() 方法,在非微信小程序、h5、app-vue端出现不能滑动的问题的时候,重置组件 +- uni-swipe-action 修复 app 端偶尔出现类似 Page[x][-x,xx;-x,xx,x,x-x] 的问题 +- uni-swipe-action 优化 微信小程序、h5、app-vue 滑动逻辑,避免出现动态新增组件后不能滑动的问题 +## 1.4.0(2021-08-13) +- uni-calendar 修复 弹出层被 tabbar 遮盖 bug +- uni-data-checkbox 修复 在 uni-forms 中重置表单,错误信息无法清除的问题 +- uni-dateformat 调整 默认时间不再是当前时间,而是显示'-'字符 +- uni-datetime-picker 新增 适配 vue3 +- uni-datetime-picker 新增 支持作为 uni-forms 子组件相关功能 +- uni-datetime-picker 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的 bug +- uni-datetime-picker 修复 type 属性动态赋值无效的 bug +- uni-datetime-picker 修复 ‘确认’按钮被 tabbar 遮盖 bug +- uni-datetime-picker 修复 组件未赋值时范围选左、右日历相同的 bug +- uni-datetime-picker 修复 范围选未正确显示当前值的 bug +- uni-datetime-picker 修复 h5 平台(移动端)报错 'cale' of undefined 的 bug +- uni-easyinput 修复 在 uni-forms 中重置表单,错误信息无法清除的问题 +- uni-file-picker 修复 return-type="object" 时,无法删除文件的Bug +- uni-file-picker 修复 auto-upload 属性失效的Bug +- uni-forms 修复 没有添加校验规则的字段依然报错的Bug +- uni-forms 修复 重置表单错误信息无法清除的问题 +- uni-forms 优化 组件文档 +- uni-forms 修复 表单验证只生效一次的问题 +- uni-tag type 不是 default 时,size 为 small 字体大小显示不正确 +## 1.3.9(2021-08-02) +- uni-datetime-picker 新增 return-type 属性支持返回 date 日期对象 +- uni-file-picker 修复 fileExtname属性不指定值报错的Bug +- uni-file-picker 修复 在某种场景下图片不回显的Bug +- uni-link 支持自定义插槽 +## 1.3.8(2021-07-31) +- uni-ui 组件兼容 vue3 +- uni-collapse 修复 由1.2.0版本引起的 change 事件返回 undefined 的Bug +- uni-collapse 优化 组件示例 +- uni-collapse 新增 组件折叠动画 +- uni-collapse 新增 value\v-model 属性 ,动态修改面板折叠状态 +- uni-collapse 新增 title 插槽 ,可定义面板标题 +- uni-collapse 新增 border 属性 ,显示隐藏面板内容分隔线 +- uni-collapse 新增 title-border 属性 ,显示隐藏面板标题分隔线 +- uni-collapse 修复 resize 方法失效的Bug +- uni-collapse 修复 change 事件返回参数不正确的Bug +- uni-collapse 优化 H5、App 平台自动更具内容更新高度,无需调用 reszie() 方法 +- uni-data-checkbox 优化 在uni-forms组件,与label不对齐的问题 +- uni-data-checkbox 修复 单选默认值为0不能选中的Bug +- uni-easyinput 优化 errorMessage 属性支持 Boolean 类型 +- uni-file-picker 修复 return-type为object下,返回值不正确的Bug +- uni-file-picker 修复(重要) H5 平台下如果和uni-forms组件一同使用导致页面卡死的问题 +- uni-file-picker 优化 h5平台下上传文件导致页面卡死的问题 +- uni-forms 修复 vue2 下条件编译导致destroyed生命周期失效的Bug +- uni-forms 修复 1.2.1 引起的示例在小程序平台报错的Bug +- uni-forms 修复 动态校验表单,默认值为空的情况下校验失效的Bug +- uni-forms 修复 不指定name属性时,运行报错的Bug +- uni-forms 优化 label默认宽度从65调整至70,使required为true且四字时不换行 +- uni-forms 优化 组件示例,新增动态校验示例代码 +- uni-forms 优化 组件文档,使用方式更清晰 +- uni-list 修复 与其他组件嵌套使用时,点击失效的Bug +- uni-swipe-action 修复 跨页面修改组件数据 ,导致不能滑动的问题 +## 1.3.7(2021-07-16) +- uni-ui 兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +- uni-datetime-picker 修复 单选日期类型,初始赋值后不在当前日历的 bug +- uni-datetime-picker 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效) +- uni-datetime-picker 优化 移动端移除显示框的清空按钮,无实际用途 +- uni-datetime-picker 修复 组件赋值为空,界面未更新的 bug +- uni-datetime-picker 修复 start 和 end 不能动态赋值的 bug +- uni-datetime-picker 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的 bug +## 1.3.6(2021-07-09) +- uni-data-checkbox 优化 删除无用日志 +- uni-data-checkbox 修复 由 0.1.9 引起的非 nvue 端图标不显示的问题 +- uni-data-checkbox 修复 nvue 黑框样式问题 +- uni-datetime-picker 修复 范围选择不能动态赋值的 bug +- uni-datetime-picker 修复 范围选择的初始时间在一个月内时,造成无法选择的bug +- uni-datetime-picker 优化 弹出层在超出视窗边缘定位不准确的问题 +- uni-datetime-picker 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的 bug +- uni-datetime-picker 优化 弹出层在超出视窗边缘被遮盖的问题 +- uni-datetime-picker 新增 maskClick 事件 +- uni-datetime-picker 修复 特殊情况日历 rpx 布局错误的 bug,rpx -> px +- uni-datetime-picker 修复 范围选择时清空返回值不合理的bug,['', ''] -> [] +- uni-datetime-picker 新增 日期时间显示框支持插槽 +- uni-file-picker 修复 sourceType 缺少默认值导致 ios 无法选择文件 +- uni-file-picker 优化 解耦与uniCloud的强绑定关系 ,如不绑定服务空间,默认autoUpload为false且不可更改 +- uni-table 新增 uni-th 支持 date 日期筛选范围 +- uni-table 新增 uni-th 支持 range 筛选范围 +- uni-table 新增 uni-th 筛选功能 +## 1.3.5(2021-07-02) +- uni-card 优化 图文卡片无图片加载时,提供占位图标 +- uni-card 新增 header 插槽,自定义卡片头部( 图文卡片 mode="style" 时,不支持) +- uni-card 修复 thumbnail 不存在仍然占位的 bug +- uni-data-checkbox 修复 selectedTextColor 属性不生效的Bug +- uni-datetime-picker 优化 添加 uni-icons 依赖 +- uni-easyinput 修复 confirmType 属性(仅 type="text" 生效)导致多行文本框无法换行的 bug +- uni-file-picker 修复 由 0.0.10 版本引发的 returnType 属性失效的问题 +- uni-file-picker 优化 文件上传后进度条消失时机 +- uni-file-picker 修复 在uni-forms 中,删除文件 ,获取的值不对的Bug +- uni-forms 修复 pattern 属性在微信小程序平台无效的问题 +## 1.3.4(2021-06-25) +- uni-badge 优化 示例项目 +- uni-countdown 修复 uni-countdown 重复赋值跳两秒的 bug +- uni-easyinput 修复 passwordIcon 属性拼写错误的 bug +- uni-forms 修复 validate-trigger属性为submit且err-show-type属性为toast时不能弹出的Bug +- uni-forms 修复 只写setRules方法而导致校验不生效的Bug +- uni-forms 修复 由上个办法引发的错误提示文字错位的Bug +- uni-forms 修复 不设置 label 属性 ,无法设置label插槽的问题 +- uni-forms 修复 不设置label属性,label-width属性不生效的bug +- uni-forms 修复 setRules 方法与rules属性冲突的问题 +- uni-link 新增 download 属性,H5平台下载文件名 +- uni-popup 新增 mask-click 遮罩层点击事件 +- uni-popup 修复 nvue 平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug +- uni-tag 修复 uni-tag 在字节跳动小程序上 css 类名编译错误的 bug +## 1.3.3(2021-06-18) +- uni-easyinput 新增 passwordIcon 属性,当type=password时是否显示小眼睛图标 +- uni-easyinput 修复 confirmType 属性不生效的问题 +- uni-easyinput 修复 disabled 状态可清出内容的 bug +- uni-file-picker 修复 删除文件时无法触发 v-model 的Bug +- uni-popup 修复 H5平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug +- uni-popup 修复 错误的 watch 字段 +- uni-popup 修复 safeArea 属性不生效的问题 +- uni-popup 修复 点击内容,再点击遮罩无法关闭的Bug +## 1.3.2(2021-06-04) +- uni-data-checkbox 新增 map 属性,可以方便映射text/value属性 +- uni-data-checkbox 修复 不关联服务空间的情况下组件报错的Bug +- uni-data-picker 修复 上个版本引出的本地数据无法选择带有children的2级节点 +- uni-forms 修复 动态删减数据导致报错的问题 +- uni-forms 新增 modelValue 属性 ,value 即将废弃 +- uni-forms 新增 uni-forms-item 可以设置单独的 rules +- uni-forms 新增 validate 事件增加 keepitem 参数,可以选择那些字段不过滤 +- uni-forms 优化 submit 事件重命名为 validate +- uni-data-picker 修复 无法加载云端数据的问题 +- uni-data-picker 修复 v-model无效问题 +- uni-data-picker 修复 loaddata 为空数据组时加载时间过长问题 +- uni-datetime-picker 修复 图标在小程序上不显示的 bug +- uni-datetime-picker 优化 重命名引用组件,避免潜在组件命名冲突 +- uni-datetime-picker 优化 代码目录扁平化 +- uni-tag 修复 未定义 sass 变量 "$uni-color-royal" 的bug +## 1.3.1(2021-05-14) +- uni-badge 新增 uni-badge 的 absolute 属性,支持定位 +- uni-badge 新增 uni-badge 的 offset 属性,支持定位偏移 +- uni-badge 新增 uni-badge 的 is-dot 属性,支持仅显示有一个小点 +- uni-badge 新增 uni-badge 的 max-num 属性,支持自定义封顶的数字值,超过 99 显示99+ +- uni-badge 优化 uni-badge 属性 custom-style, 支持以对象形式自定义样式 +- uni-badge 修复 uni-badge 在 App 端,数字小于10时不是圆形的bug +- uni-badge 修复 uni-badge 在父元素不是 flex 布局时,宽度缩小的bug +- uni-badge 新增 uni-badge 属性 custom-style, 支持自定义样式 +- uni-datetime-picker 修复 ios 下不识别 '-' 日期格式的 bug +- uni-datetime-picker 优化 pc 下弹出层添加边框和阴影 +- uni-datetime-picker 修复 在 admin 中获取弹出层定位错误的bug +- uni-datetime-picker 修复 type 属性向下兼容,默认值从 date 变更为 datetime +- uni-datetime-picker 支持日历形式的日期+时间的范围选择 +- uni-steps 修复 uni-steps 横向布局时,多行文字高度不合理的 bug +- uni-countdown 修复 uni-countdown 不能控制倒计时的 bug +- uni-tag 修复 royal 类型无效的bug +- uni-tag 修复 uni-tag 宽度不自适应的bug +- uni-tag 新增 uni-tag 支持属性 custom-style 自定义样式 +- uni-link 新增 href 属性支持 tel:|mailto: +- uni-popup 修复 组件内放置 input 、textarea 组件,无法聚焦的问题 +- uni-popup 新增 type 属性的 left\right 值,支持左右弹出 +- uni-popup 新增 open(String:type) 方法参数 ,可以省略 type 属性 ,直接传入类型打开指定弹窗 +- uni-popup 新增 backgroundColor 属性,可定义主窗口背景色,默认不显示背景色 +- uni-popup 新增 safeArea 属性,是否适配底部安全区 +- uni-popup 修复 App\h5\微信小程序底部安全区占位不对的Bug +- uni-popup 修复 App 端弹出等待的Bug +- uni-popup 优化 提升低配设备性能,优化动画卡顿问题 +- uni-popup 优化 更简单的组件自定义方式 +- uni-table 修复 示例项目缺少组件的Bug +- uni-forms 修复 自定义检验器失效的问题 +- uni-title 修复 示例项目缺少组件的Bug +- uni-transition 修复 示例项目缺少组件的Bug +- uni-swiper-dot 修复 示例项目缺少组件的Bug +- uni-ui 新增 组件示例地址 +## 1.3.0(2021-04-23) +- uni-combox 优化 添加依赖 uni-icons, 导入后自动下载依赖 +- uni-data-picker 修复 非树形数据有 where 属性查询报错的问题 +- uni-fav 优化 添加依赖 uni-icons, 导入后自动下载依赖 +- uni-goods-nav 优化 添加依赖 uni-icons, 导入后自动下载依赖 +- uni-nav-bar 优化 添加依赖 uni-icons, 导入后自动下载依赖 +- uni-notice-bar 优化 添加依赖 uni-icons, 导入后自动下载依赖 +- uni-number-box 修复 uni-number-box 浮点数运算不精确的 bug +- uni-number-box 修复 uni-number-box change 事件触发不正确的 bug +- uni-number-box 新增 uni-number-box v-model 双向绑定 +- uni-rate 修复 布局变化后 uni-rate 星星计算不准确的 bug +- uni-rate 优化 添加依赖 uni-icons, 导入 uni-rate 自动下载依赖 +- uni-search-bar 优化 添加依赖 uni-icons, 导入后自动下载依赖 +- uni-steps 优化 添加依赖 uni-icons, 导入后自动下载依赖 +- uni-transition 新增 通过方法自定义动画 +- uni-transition 新增 custom-class 非 NVUE 平台支持自定义 class 定制样式 +- uni-transition 优化 动画触发逻辑,使动画更流畅 +- uni-transition 优化 支持单独的动画类型 +- uni-transition 优化 文档示例 +## 1.2.13(2021-04-16) +- uni-ui 新增 uni-data-picker 支持云端非树形表结构数据 +- uni-ui 修复 uni-data-checkbox nvue 下无法选中的问题 +- uni-ui 修复 uni-data-picker 根节点 parent_field 字段等于null时选择界面错乱问题 +- uni-ui 修复 uni-file-picker 选择的文件非 file-extname 字段指定的扩展名报错的Bug +- uni-ui 修复 uni-swipe-action 报错 nv_navigator is not defined 的bug +- uni-ui 修复 uni-load-more 在首页使用时,h5 平台报 'uni is not defined' 的 bug +- uni-ui 优化 uni-file-picker file-extname 字段支持字符串写法,多个扩展名需要用逗号分隔 +- uni-ui 优化 uni-pagination PC 和 移动端适配不同的 ui +- uni-ui 更新 uni-file-picker 组件示例 +- uni-ui 修复 uni-nav-bar 当 fixed 属性为 true 时铺不满屏幕的 bug +- uni-ui 新增 uni-search-bar 的 focus 事件 +- uni-ui 修复 uni-rate 属性 margin 值为 string 组件失效的 bug +- uni-data-picker 修复 本地数据概率无法回显时问题 +- uni-table 新增 sortable 属性,是否开启单列排序 +- uni-table 优化 表格多选逻辑 +## 1.2.12(2021-03-23) +- uni-ui 新增 uni-datetime-picker 的 hide-second 属性、border 属性; +- uni-ui 修复 uni-datetime-picker 选择跟显示的日期不一样的 bug, +- uni-ui 修复 uni-datetime-picker change事件触发2次的 bug +- uni-ui 修复 uni-datetime-picker 分、秒 end 范围错误的 bug +- uni-ui 新增 uni-tr selectable 属性,用于 type=selection 时,设置某行是否可由全选按钮控制 +- uni-ui 新增 uni-data-checkbox 新增 disabled属性,支持nvue +- uni-ui 优化 uni-data-checkbox 无选项时提示“暂无数据” +- uni-ui 优化 uni-data-checkbox 默认颜色显示 +- uni-ui 新增 uni-link href 属性支持 tel:|mailto: +- uni-ui 新增 uni-table 示例demo +- uni-ui 修复 uni-data-picker 微信小程序某些情况下无法选择的问题,事件无法触发的问题 +- uni-ui 修复 uni-nav-bar easycom 下,找不到 uni-status-bar 的bug +- uni-ui 修复 uni-easyinput 示例在 qq 小程序上的bug +- uni-ui 修复 uni-forms 动态显示uni-forms-item的情况下,submit 方法获取值错误的Bug +- uni-ui 调整 cli 项目 建议使用 easycom 方式引用组件,如使用按需引用,需手动维护组件内部引用 + +## 1.2.11(2021-02-24) +- 调整为uni_modules目录规范 +- uni-data-picker 新增 数据驱动的picker选择器 +- uni-file-picker 新增 文件选择上传 +- uni-row 新增 栅格系统 +- uni-data-checkbox 优化 支持 nvue +- uni-forms 修复 偶发性获取表单值错误的Bug +- uni-forms 修复 校验 uni-data-picker value 为 0 时,返回值错误的Bug +- uni-forms 修复 uni-forms-item 组件隐藏时依然触发校验的bug +- uni-forms 优化 实时校验 +- uni-forms 优化 兼容nvue页面 +- uni-easyinput 优化 兼容nvue页面 +- uni-group 优化 兼容nvue页面 +- uni-popup 优化 组件适配 PC +- uni-fab 优化 适配 PC +- uni-swiper-dot 优化 适配 PC +- uni-rate 优化 适配 PC +- uni-notice-bar 优化 适配 PC +- uni-indexed-list 优化 适配 PC +- uni-combox 优化 适配 PC +- uni-transition 优化 适配 PC +- uni-nav-bar 优化 适配 PC +- uni-swipe-action 优化 适配 PC diff --git a/uni_modules/uni-ui/components/uni-ui/uni-ui.vue b/uni_modules/uni-ui/components/uni-ui/uni-ui.vue new file mode 100644 index 0000000..0970892 --- /dev/null +++ b/uni_modules/uni-ui/components/uni-ui/uni-ui.vue @@ -0,0 +1,7 @@ +<template> + <view>占位组件,请勿使用</view> +</template> +<script> +</script> +<style> +</style> diff --git a/uni_modules/uni-ui/package.json b/uni_modules/uni-ui/package.json new file mode 100644 index 0000000..d31a390 --- /dev/null +++ b/uni_modules/uni-ui/package.json @@ -0,0 +1,130 @@ +{ + "id": "uni-ui", + "displayName": "uni-ui", + "version": "1.5.6", + "description": "uni-ui 是基于uni-app的、全端兼容的、高性能UI框架", + "keywords": [ + "uni-ui", + "uniui", + "UI组件库", + "ui框架", + "ui库" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "^3.2.10" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-badge", + "uni-calendar", + "uni-card", + "uni-collapse", + "uni-combox", + "uni-countdown", + "uni-data-checkbox", + "uni-data-picker", + "uni-data-select", + "uni-dateformat", + "uni-datetime-picker", + "uni-drawer", + "uni-easyinput", + "uni-fab", + "uni-fav", + "uni-file-picker", + "uni-forms", + "uni-goods-nav", + "uni-grid", + "uni-group", + "uni-icons", + "uni-indexed-list", + "uni-link", + "uni-list", + "uni-load-more", + "uni-nav-bar", + "uni-notice-bar", + "uni-number-box", + "uni-pagination", + "uni-popup", + "uni-rate", + "uni-row", + "uni-search-bar", + "uni-section", + "uni-segmented-control", + "uni-steps", + "uni-swipe-action", + "uni-swiper-dot", + "uni-table", + "uni-tag", + "uni-title", + "uni-tooltip", + "uni-transition" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-ui/readme.md b/uni_modules/uni-ui/readme.md new file mode 100644 index 0000000..23f8456 --- /dev/null +++ b/uni_modules/uni-ui/readme.md @@ -0,0 +1,247 @@ +> 当前插件不包含示例页面 ,如需示例请在 HBuiderX 中新建 `hello uni-app > 扩展组件` 中查看 +> +> 代码示例地址 :[https://ext.dcloud.net.cn/plugin?id=4941](https://ext.dcloud.net.cn/plugin?id=4941) +> +> 组件演示地址:[https://hellouniapp.dcloud.net.cn](https://hellouniapp.dcloud.net.cn/pages/extUI/badge/badge) +> +> 组件文档地址:[https://uniapp.dcloud.io/component/uniui/uni-ui](https://uniapp.dcloud.io/component/uniui/uni-ui) + +# uni-ui 介绍 + +## uni-ui产品特点 + +### 1. 高性能 + +目前为止,在小程序和混合app领域,暂时还没有比 `uni-ui` 更高性能的框架。 +- 自动差量更新数据 + +虽然uni-app支持小程序自定义组件,所有小程序的ui库都可以用。但小程序自定义组件的ui库都需要使用setData手动更新数据,在大数据量时、或高频更新数据时,很容易产生性能问题。 + +而 `uni-ui` 属于vue组件,uni-app引擎底层自动diff更新数据。当然其实插件市场里众多vue组件都具备这个特点。 +- 优化逻辑层和视图层通讯折损 + +非H5,不管是小程序还是App,不管是app的webview渲染还是原生渲染,全都是逻辑层和视图层分离的。这里就有一个逻辑层和视图层通讯的折损问题。 +比如在视图层拖动一个可跟手的组件,由于通讯的损耗,用js监听很难做到实时跟手。 + +这时就需要使用css动画以及平台底层提供的wxs、bindingx等技术。不过这些技术都比较复杂,所以 `uni-ui` 里做了封装,在需要跟手式操作的ui组件,比如swiperaction列表项左滑菜单,就在底层使用了这些技术,实现了高性能的交互体验 +- 背景停止 + +很多ui组件是会一直动的,比如轮播图、跑马灯。即便这个窗体被新窗体挡住,它在背景层仍然在消耗着硬件资源。在Android的webview版本为chrome66以上,背景操作ui会引发很严重的性能问题,造成前台界面明显卡顿。 + +而 `uni-ui` 的组件,会自动判断自己的显示状态,在组件不再可见时,不会再消耗硬件资源。 + +### 2. 全端 + + `uni-ui` 的组件都是多端自适应的,底层会抹平很多小程序平台的差异或bug。 + +比如导航栏navbar组件,会自动处理不同端的状态栏。 +比如swiperaction组件,在app和微信小程序上会使用交互体验更好的wxs技术,但在不支持wxs的其他小程序端会使用js模拟类似效果。 + + `uni-ui` 还支持nvue原生渲染,[详见](https://github.com/dcloudio/uni-ui/tree/nvue-uni-ui) + +未来 `uni-ui` 还会支持pc等大屏设备。 + +### 3. 与uni统计自动集成实现免打点 + +uni统计是优秀的多端统计平台,见[tongji.dcloud.net.cn](https://tongji.dcloud.net.cn)。 + +除了一张报表看全端,它的另一个重要特点是免打点。 +比如使用 `uni-ui` 的navbar标题栏、收藏、购物车等组件,均可实现自动打点,统计页面标题等各种行为数据。 +当然你也可以关闭uni统计,这不是强制的。 + +### 4. 主题扩展 + + `uni-ui` 支持[uni.scss](https://uniapp.dcloud.io/collocation/uni-scss),可以方便的切换App的风格。 + +ui是一种需求非常发散的产品,DCloud官方也无意用 `uni-ui` 压制第三方ui插件的空间,但官方有义务在性能和多端方面提供一个开源的标杆给大家。 + +我们欢迎更多优秀的ui组件出现,也欢迎更多人贡献 `uni-ui` 的主题风格,满足更多用户的需求。 + + +## 快速开始 + +uni-ui支持 HBuilderX直接新建项目模板、npm安装和单独导入个别组件等多种使用方式 + +### 在HBuilderX 新建uni-app项目的模板中,选择uni-ui模板 + + +由于uni-app独特的[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)技术,可以免引用、注册,直接使用各种符合规则的vue组件。 + +在代码区键入`u`,拉出各种内置或uni-ui的组件列表,选择其中一个,即可使用该组件。 + +光标放在组件名称上,按F1,可以查阅组件的文档。 + + + +### 通过 uni_modules 单独安装组件 +如果你没有创建uni-ui项目模板,也可以在你的工程里,通过 uni_modules 单独安装需要的某个组件。下表为uni-ui的扩展组件清单,点击每个组件在详情页面可以导入组件到项目下,导入后直接使用即可,无需import和注册。 + +|组件名|组件说明| +|---|---| +|uni-badge|[数字角标](https://ext.dcloud.net.cn/plugin?name=uni-badge)| +|uni-calendar|[日历](https://ext.dcloud.net.cn/plugin?name=uni-calendar)| +|uni-card|[卡片](https://ext.dcloud.net.cn/plugin?name=uni-card)| +|uni-collapse|[折叠面板](https://ext.dcloud.net.cn/plugin?name=uni-collapse)| +|uni-combox|[组合框](https://ext.dcloud.net.cn/plugin?name=uni-combox)| +|uni-countdown|[倒计时](https://ext.dcloud.net.cn/plugin?name=uni-countdown)| +|uni-data-checkbox|[数据选择器](https://ext.dcloud.net.cn/plugin?name=uni-data-checkbox)| +|uni-data-picker|[数据驱动的picker选择器](https://ext.dcloud.net.cn/plugin?name=uni-data-picker)| +|uni-dateformat|[日期格式化](https://ext.dcloud.net.cn/plugin?name=uni-dateformat)| +|uni-datetime-picker|[日期选择器](https://ext.dcloud.net.cn/plugin?name=uni-datetime-picker)| +|uni-drawer|[抽屉](https://ext.dcloud.net.cn/plugin?name=uni-drawer)| +|uni-easyinput|[增强输入框](https://ext.dcloud.net.cn/plugin?name=uni-easyinput)| +|uni-fab|[悬浮按钮](https://ext.dcloud.net.cn/plugin?name=uni-fab)| +|uni-fav|[收藏按钮](https://ext.dcloud.net.cn/plugin?name=uni-fav)| +|uni-file-picker|[文件选择上传](https://ext.dcloud.net.cn/plugin?name=uni-file-picker)| +|uni-forms|[表单](https://ext.dcloud.net.cn/plugin?name=uni-forms)| +|uni-goods-nav|[商品导航](https://ext.dcloud.net.cn/plugin?name=uni-goods-nav)| +|uni-grid|[宫格](https://ext.dcloud.net.cn/plugin?name=uni-grid)| +|uni-group|[分组](https://ext.dcloud.net.cn/plugin?name=uni-group)| +|uni-icons|[图标](https://ext.dcloud.net.cn/plugin?name=uni-icons)| +|uni-indexed-list|[索引列表](https://ext.dcloud.net.cn/plugin?name=uni-indexed-list)| +|uni-link|[超链接](https://ext.dcloud.net.cn/plugin?name=uni-link)| +|uni-list|[列表](https://ext.dcloud.net.cn/plugin?name=uni-list)| +|uni-load-more|[加载更多](https://ext.dcloud.net.cn/plugin?name=uni-load-more)| +|uni-nav-bar|[自定义导航栏](https://ext.dcloud.net.cn/plugin?name=uni-nav-bar)| +|uni-notice-bar|[通告栏](https://ext.dcloud.net.cn/plugin?name=uni-notice-bar)| +|uni-number-box|[数字输入框](https://ext.dcloud.net.cn/plugin?name=uni-number-box)| +|uni-pagination|[分页器](https://ext.dcloud.net.cn/plugin?name=uni-pagination)| +|uni-popup|[弹出层](https://ext.dcloud.net.cn/plugin?name=uni-popup)| +|uni-rate|[评分](https://ext.dcloud.net.cn/plugin?name=uni-rate)| +|uni-row|[布局-行](https://ext.dcloud.net.cn/plugin?name=uni-row)| +|uni-search-bar|[搜索栏](https://ext.dcloud.net.cn/plugin?name=uni-search-bar)| +|uni-segmented-control|[分段器](https://ext.dcloud.net.cn/plugin?name=uni-segmented-control)| +|uni-steps|[步骤条](https://ext.dcloud.net.cn/plugin?name=uni-steps)| +|uni-swipe-action|[滑动操作](https://ext.dcloud.net.cn/plugin?name=uni-swipe-action)| +|uni-swiper-dot|[轮播图指示点](https://ext.dcloud.net.cn/plugin?name=uni-swiper-dot)| +|uni-table|[表格](https://ext.dcloud.net.cn/plugin?name=uni-table)| +|uni-tag|[标签](https://ext.dcloud.net.cn/plugin?name=uni-tag)| +|uni-title|[章节标题](https://ext.dcloud.net.cn/plugin?name=uni-title)| +|uni-transition|[过渡动画](https://ext.dcloud.net.cn/plugin?name=uni-transition)| + + +使用 `uni_modules` 方式安装组件库,可以直接通过插件市场导入,通过右键菜单快速更新组件,不需要引用、注册,直接在页面中使用 `uni-ui` 组件。[点击安装 uni-ui 组件库](https://ext.dcloud.net.cn/plugin?id=55) + +**注意:下载最新的组件目前仅支持 uni_modules ,非 uni_modules 版本最高支持到组件的1.2.10版本** + +如不能升级到 `uni_modules` 版本,可以使用 `uni_modules` 安装好对应组件,将组件拷贝到对应目录。 + +例如需更新 `uni-list`和`uni-badge` ,将 `uni_modules>uni-list>components`和`uni_modules>uni-badege>components`下所有目录拷贝到如下目录即可: + + +**目录示例** + +```json +┌─components 组件目录 +│ ├─uni-list list 列表目录 +│ │ └─uni-list.vue list 组件文件 +│ ├─uni-list-item list-item 列表目录 +│ │ └─uni-list-item.vue list 组件文件 +│ ├─uni-badge badge 角标目录 +│ │ └─uni-badge.vue badge 组件文件 +│ └─ //.... 更多组件文件 +├─pages 业务页面文件存放的目录 +│ ├─index +│ │ └─index.vue index示例页面 +├─main.js Vue初始化入口文件 +├─App.vue 应用配置,用来配置App全局样式以及监听 应用生命周期 +├─manifest.json 配置应用名称、appid、logo、版本等打包信息,详见 +└─pages.json 配置页 + +``` + +### 通过 `uni_modules` 导入全部组件 +如果想一次把所有uni-ui组件导入到项目中,只需要导入一个 `uni-ui` 组件即可 [点击去导入](https://ext.dcloud.net.cn/plugin?id=55)。 + +如果没有自动导入其他组件,可以在 uni-ui 组件目录上右键选择 `安装三方插件依赖` 即可。 + + + +### npm安装 +在 `vue-cli` 项目中可以使用 `npm` 安装 `uni-ui` 库 ,或者直接在 `HBuilderX` 项目中使用 `npm` 。 + +> **注意** +> cli 项目默认是不编译 `node_modules` 下的组件的,导致条件编译等功能失效 ,导致组件异常 +> 需要在根目录创建 `vue.config.js` 文件 ,增加 `@dcloudio/uni-ui` 包的编译即可正常 +> ```javascript +> // vue.config.js +> module.exports = { +> transpileDependencies:['@dcloudio/uni-ui'] +> } +> ``` + + + +**准备 sass** + +`vue-cli` 项目请先安装 sass 及 sass-loader,如在 HBuliderX 中使用,可跳过此步。 + +- 安装 sass +``` + npm i sass -D 或 yarn add sass -D +``` + +- 安装 sass-loader +``` +npm i sass-loader@10.1.1 -D 或 yarn add sass-loader@10.1.1 -D +``` + +> 如果 `node` 版本小于 16 ,sass-loader 请使用低于 @11.0.0 的版本,[sass-loader@11.0.0 不支持 vue@2.6.12 ](https://stackoverflow.com/questions/66082397/typeerror-this-getoptions-is-not-a-function) +> 如果 `node` 版本大于 16 , `sass-loader` 建议使用 `v8.x` 版本 + +**安装 uni-ui** + +``` +npm i @dcloudio/uni-ui 或 yarn add @dcloudio/uni-ui +``` + + + +**配置easycom** + +使用 `npm` 安装好 `uni-ui` 之后,需要配置 `easycom` 规则,让 `npm` 安装的组件支持 `easycom` + +打开项目根目录下的 `pages.json` 并添加 `easycom` 节点: + +```javascript +// pages.json +{ + "easycom": { + "autoscan": true, + "custom": { + // uni-ui 规则如下配置 + "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue" + } + }, + + // 其他内容 + pages:[ + // ... + ] +} + +``` + +在 ``template`` 中使用组件: + +```html +<uni-badge text="1"></uni-badge> +<uni-badge text="2" type="success" @click="bindClick"></uni-badge> +<uni-badge text="3" type="primary" :inverted="true"></uni-badge> +``` + + **注意** + - uni-ui 现在只推荐使用 `easycom` ,如自己引用组件,可能会出现组件找不到的问题 + - 使用 npm 安装的组件,默认情况下 babel-loader 会忽略所有 node_modules 中的文件 ,导致条件编译失效,需要通过配置 `vue.config.js` 解决: + ```javascript + // 在根目录创建 vue.config.js 文件,并配置如下 + module.exports = { + transpileDependencies: ['@dcloudio/uni-ui'] + } + ``` + - uni-ui 是uni-app内置组件的扩展。注意与web开发不同,uni-ui不包括基础组件,它是基础组件的补充。web开发中有的开发者习惯用一个ui库完成所有开发,但在uni-app体系中,推荐开发者首先使用性能更高的基础组件,然后按需引入必要的扩展组件。 + - `uni-ui` 不支持使用 `Vue.use()` 的方式安装 + + +### 贡献代码 +在使用 `uni-ui` 中,如遇到无法解决的问题,请提 [Issues](https://github.com/dcloudio/uni-ui/issues) 给我们,假如您有更好的点子或更好的实现方式,也欢迎给我们提交 [PR](https://github.com/dcloudio/uni-ui/pulls) \ No newline at end of file diff --git a/unpackage/dist/cache/.vite/deps/_metadata.json b/unpackage/dist/cache/.vite/deps/_metadata.json deleted file mode 100644 index 74eb13c..0000000 --- a/unpackage/dist/cache/.vite/deps/_metadata.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "hash": "675162bf", - "configHash": "14deb472", - "lockfileHash": "8b439a23", - "browserHash": "8f4b50d7", - "optimized": {}, - "chunks": {} -} \ No newline at end of file diff --git a/unpackage/dist/cache/.vite/deps/package.json b/unpackage/dist/cache/.vite/deps/package.json deleted file mode 100644 index 3dbc1ca..0000000 --- a/unpackage/dist/cache/.vite/deps/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "type": "module" -} diff --git a/unpackage/dist/dev/mp-weixin/api/index.js b/unpackage/dist/dev/mp-weixin/api/index.js deleted file mode 100644 index 42471dc..0000000 --- a/unpackage/dist/dev/mp-weixin/api/index.js +++ /dev/null @@ -1,222 +0,0 @@ -"use strict"; -const util_request = require("../util/request.js"); -function wxLoginApi(data) { - return util_request.request("/user/wxLogin", data, "POST"); -} -function getVipInfoApi() { - return util_request.request("/waterCard/getUserCard", {}, "GET"); -} -function getUserInfo() { - return util_request.request("/user/loginUser", {}, "GET"); -} -function getRegionApi(code) { - return util_request.request(`/sysRegion/getListChild?code=${code}`, {}, "GET"); -} -function creatUserArchive(data) { - return util_request.request("/userArchives/realName", data, "POST"); -} -function searchAddress(data) { - return util_request.request("/userAddress/search", data, "POST"); -} -function editAddressApi(data) { - return util_request.request("/userAddress/modify", data, "POST"); -} -function getAddressApi(id) { - return util_request.request(`/userAddress/get?id=${id}`, {}, "GET"); -} -function deleteAddressApi(id) { - return util_request.request(`/userAddress/remove?id=${id}`, {}, "GET"); -} -function setAddressDefaultApi(id) { - return util_request.request(`/userAddress/editDefault?id=${id}`, {}, "GET"); -} -function searchFacilityApi(data) { - return util_request.request("/waterFacility/search", data, "POST"); -} -function siteApi(data) { - return util_request.request("/waterDrinkingSite/getSearchList", data, "POST"); -} -function editUserInfoApi(data) { - return util_request.request("/userArchives/modify", data, "POST"); -} -function wxPayApi(data) { - return util_request.request("/pay/service/wxPay", data, "POST"); -} -function balanceChangeApi(date) { - return util_request.request(`/userCapitalChange/getListByUser?date=${date}`, {}, "GET"); -} -function balanceStatisticsApi(date) { - return util_request.request(`/userCapitalChange/getAmountStatistics?date=${date}`, {}, "GET"); -} -function paymentChangeApi(date) { - return util_request.request(`/paymentRecords/getListByUser?date=${date}`, {}, "GET"); -} -function rechargeChangeApi(date) { - return util_request.request(`/rechargeRecords/getListByUser?date=${date}`, {}, "GET"); -} -function reportLossApi(cardNumber) { - return util_request.request(`/waterCard/reportLoss?cardNumber=${cardNumber}`, {}, "GET"); -} -function removeBindingApi(cardNumber) { - return util_request.request(`/waterCard/removeBinding?cardNumber=${cardNumber}`, {}, "GET"); -} -function infoBreakdownApi(data) { - return util_request.request("/waterFacilityMalfunction/create", data, "POST"); -} -function askShareApi(data) { - return util_request.request("/waterCardShare/askShare", data, "POST"); -} -function askShareListApi(id) { - return util_request.request(`/waterCardShare/getListByUser?userId=${id}`, {}, "GET"); -} -function editShareTypeApi(data) { - return util_request.request("/waterCardShare/editShareType", data, "POST"); -} -function buyWaterApi(data) { - return util_request.request("/api/facility/buyWater", data, "POST"); -} -function getPriceBySnApi(sn) { - return util_request.request(`/waterFacility/getPriceBySn?sn=${sn}`, {}, "GET"); -} -function rechargeApi(data) { - return util_request.request("/rechargeRecords/fees", data, "POST"); -} -function pointsSearchApi(data) { - return util_request.request("/userIntegralAcquire/search", data, "POST"); -} -function pointsExchangeApi(data) { - return util_request.request("/userIntegralConvert/search", data, "POST"); -} -function goodsGetApi(id) { - return util_request.request(`/shopGoods/getListByCategoryId?categoryId=${id}`, {}, "GET"); -} -function goodsGetByIdApi(id) { - return util_request.request(`/shopGoods/get?id=${id}`, {}, "GET"); -} -function goodsSearchTypeApi(data) { - return util_request.request("/shopGoodsCategory/search", data, "POST"); -} -function integralConvertApi(data) { - return util_request.request("/userIntegralConvert/create", data, "POST"); -} -function getRuleDetailApi(data) { - return util_request.request("/userIntegralRuleDetail/search", data, "POST"); -} -function adminLoginApi(data) { - return util_request.request("/admin/user/login", data, "POST"); -} -function adminDetailApi(id) { - return util_request.request(`/admin/user/detail?userId=${id}`, {}, "GET"); -} -function getRunStateApi(data) { - return util_request.request(`/homePage/getFacilityRunState`, data, "POST"); -} -function getTotalRevenueApi(data) { - return util_request.request(`/homePage/getTotalRevenue`, data, "POST"); -} -function getWaterConsumptionApi(data) { - return util_request.request(`/homePage/getWaterConsumption`, data, "POST"); -} -function getParentAreaApi() { - return util_request.request(`/waterArea/getParentArea`, {}, "GET"); -} -function searchWaterFacilityApi(data) { - return util_request.request(`/waterFacility/search`, data, "POST"); -} -function getFacilityApi(id) { - return util_request.request(`/waterFacility/get?id=${id}`, {}, "GET"); -} -function openFacilityApi(data) { - return util_request.request("/api/facility/open", data, "POST"); -} -function closeFacilityApi(data) { - return util_request.request("/api/facility/close", data, "POST"); -} -function getParameterApi(sn) { - return util_request.request(`/waterFacility/getParameter?sn=${sn}`, {}, "GET"); -} -function getParameter1Api(sn) { - return util_request.request(`/waterFacility/getWaterControlParameter?sn=${sn}`, {}, "GET"); -} -function editParameterApi(data) { - return util_request.request("/waterFacility/editParameter", data, "POST"); -} -function editParameter1Api(data) { - return util_request.request("/waterFacility/editWaterControlParameter", data, "POST"); -} -function searchBreakdownApi(data) { - return util_request.request("/waterFacilityMalfunction/search", data, "POST"); -} -function searchMaintainApi(data) { - return util_request.request("/facilityMaintainRecord/search", data, "POST"); -} -function createMaintainApi(data) { - return util_request.request("/facilityMaintainRecord/create", data, "POST"); -} -function searchExamineApi(data) { - return util_request.request("/facilityExamineRecord/search", data, "POST"); -} -function createExamineApi(data) { - return util_request.request("/facilityExamineRecord/create", data, "POST"); -} -function editExamineApi(data) { - return util_request.request("/waterFacilityMalfunction/modify", data, "POST"); -} -function changePasswordApi(data) { - return util_request.request("/admin/user/changePassword", data, "POST"); -} -exports.adminDetailApi = adminDetailApi; -exports.adminLoginApi = adminLoginApi; -exports.askShareApi = askShareApi; -exports.askShareListApi = askShareListApi; -exports.balanceChangeApi = balanceChangeApi; -exports.balanceStatisticsApi = balanceStatisticsApi; -exports.buyWaterApi = buyWaterApi; -exports.changePasswordApi = changePasswordApi; -exports.closeFacilityApi = closeFacilityApi; -exports.creatUserArchive = creatUserArchive; -exports.createExamineApi = createExamineApi; -exports.createMaintainApi = createMaintainApi; -exports.deleteAddressApi = deleteAddressApi; -exports.editAddressApi = editAddressApi; -exports.editExamineApi = editExamineApi; -exports.editParameter1Api = editParameter1Api; -exports.editParameterApi = editParameterApi; -exports.editShareTypeApi = editShareTypeApi; -exports.editUserInfoApi = editUserInfoApi; -exports.getAddressApi = getAddressApi; -exports.getFacilityApi = getFacilityApi; -exports.getParameter1Api = getParameter1Api; -exports.getParameterApi = getParameterApi; -exports.getParentAreaApi = getParentAreaApi; -exports.getPriceBySnApi = getPriceBySnApi; -exports.getRegionApi = getRegionApi; -exports.getRuleDetailApi = getRuleDetailApi; -exports.getRunStateApi = getRunStateApi; -exports.getTotalRevenueApi = getTotalRevenueApi; -exports.getUserInfo = getUserInfo; -exports.getVipInfoApi = getVipInfoApi; -exports.getWaterConsumptionApi = getWaterConsumptionApi; -exports.goodsGetApi = goodsGetApi; -exports.goodsGetByIdApi = goodsGetByIdApi; -exports.goodsSearchTypeApi = goodsSearchTypeApi; -exports.infoBreakdownApi = infoBreakdownApi; -exports.integralConvertApi = integralConvertApi; -exports.openFacilityApi = openFacilityApi; -exports.paymentChangeApi = paymentChangeApi; -exports.pointsExchangeApi = pointsExchangeApi; -exports.pointsSearchApi = pointsSearchApi; -exports.rechargeApi = rechargeApi; -exports.rechargeChangeApi = rechargeChangeApi; -exports.removeBindingApi = removeBindingApi; -exports.reportLossApi = reportLossApi; -exports.searchAddress = searchAddress; -exports.searchBreakdownApi = searchBreakdownApi; -exports.searchExamineApi = searchExamineApi; -exports.searchFacilityApi = searchFacilityApi; -exports.searchMaintainApi = searchMaintainApi; -exports.searchWaterFacilityApi = searchWaterFacilityApi; -exports.setAddressDefaultApi = setAddressDefaultApi; -exports.siteApi = siteApi; -exports.wxLoginApi = wxLoginApi; -exports.wxPayApi = wxPayApi; diff --git a/unpackage/dist/dev/mp-weixin/app.js b/unpackage/dist/dev/mp-weixin/app.js deleted file mode 100644 index 78b067c..0000000 --- a/unpackage/dist/dev/mp-weixin/app.js +++ /dev/null @@ -1,65 +0,0 @@ -"use strict"; -Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); -const common_vendor = require("./common/vendor.js"); -if (!Math) { - "./pages/login/index.js"; - "./pages/index/index.js"; - "./pages/addCard/index.js"; - "./pages/userInfo/index.js"; - "./pages/functionList/balanceRecord/index.js"; - "./pages/functionList/paymentRecord/index.js"; - "./pages/functionList/rechargeRecord/index.js"; - "./pages/functionList/share/cardShare/index.js"; - "./pages/functionList/share/shareConfirm/index.js"; - "./pages/functionList/share/shareList/index.js"; - "./pages/scanWater/index.js"; - "./pages/recharge/index.js"; - "./pages/sendWater/index.js"; - "./pages/preSendWater/index.js"; - "./pages/address/index.js"; - "./pages/addressAdd/index.js"; - "./pages/addressLocate/index.js"; - "./pages/success/index.js"; - "./pages/facilityList/index.js"; - "./pages/station/index.js"; - "./pages/infoBreakdown/index.js"; - "./pages/scanRecharge/index.js"; - "./pages/scanSuccess/index.js"; - "./pages/counterRecharge/index.js"; - "./pages/wxScanWater/index.js"; - "./pagesAdmin/adminPlatform/home.js"; - "./pagesAdmin/adminPlatform/facility.js"; - "./pagesAdmin/adminPlatform/facilityDetail.js"; - "./pagesAdmin/adminPlatform/inspect.js"; - "./pagesAdmin/adminPlatform/params.js"; - "./pagesAdmin/adminPlatform/params1.js"; - "./pagesAdmin/adminPlatform/breakdown.js"; - "./pagesAdmin/adminPlatform/maintain.js"; - "./pagesPoints/pointsMall/index.js"; - "./pagesPoints/pointsMall/pointsGetRecord/index.js"; - "./pagesPoints/pointsMall/pointsExchangeRecord/index.js"; - "./pagesPoints/pointsMall/pointsGoodsDetail/index.js"; - "./pagesPoints/pointsMall/pointsrule/index.js"; -} -const _sfc_main = { - __name: "App", - setup(__props) { - common_vendor.onLaunch(async () => { - console.log("进入APP.vue"); - }); - return () => { - }; - } -}; -const navbar = () => "./components/navbar/navbar.js"; -const pointNavbar = () => "./components/pointNavbar/pointNavbar.js"; -const DaTree = () => "./components/da-tree/index.js"; -function createApp() { - const app = common_vendor.createSSRApp(_sfc_main); - app.component("navbar", navbar).component("pointNavbar", pointNavbar).component("DaTree", DaTree); - return { - app - }; -} -createApp().app.mount("#app"); -exports.createApp = createApp; diff --git a/unpackage/dist/dev/mp-weixin/app.json b/unpackage/dist/dev/mp-weixin/app.json deleted file mode 100644 index 0d8d940..0000000 --- a/unpackage/dist/dev/mp-weixin/app.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "pages": [ - "pages/login/index", - "pages/index/index", - "pages/addCard/index", - "pages/userInfo/index", - "pages/functionList/balanceRecord/index", - "pages/functionList/paymentRecord/index", - "pages/functionList/rechargeRecord/index", - "pages/functionList/share/cardShare/index", - "pages/functionList/share/shareConfirm/index", - "pages/functionList/share/shareList/index", - "pages/scanWater/index", - "pages/recharge/index", - "pages/sendWater/index", - "pages/preSendWater/index", - "pages/address/index", - "pages/addressAdd/index", - "pages/addressLocate/index", - "pages/success/index", - "pages/facilityList/index", - "pages/station/index", - "pages/infoBreakdown/index", - "pages/scanRecharge/index", - "pages/scanSuccess/index", - "pages/counterRecharge/index", - "pages/wxScanWater/index" - ], - "subPackages": [ - { - "root": "pagesAdmin", - "pages": [ - "adminPlatform/home", - "adminPlatform/facility", - "adminPlatform/facilityDetail", - "adminPlatform/inspect", - "adminPlatform/params", - "adminPlatform/params1", - "adminPlatform/breakdown", - "adminPlatform/maintain" - ] - }, - { - "root": "pagesPoints", - "pages": [ - "pointsMall/index", - "pointsMall/pointsGetRecord/index", - "pointsMall/pointsExchangeRecord/index", - "pointsMall/pointsGoodsDetail/index", - "pointsMall/pointsrule/index" - ] - } - ], - "window": { - "navigationStyle": "custom" - }, - "permission": { - "scope.userLocation": { - "desc": "你的位置信息将用于计算直饮水设备与您的距离" - } - }, - "requiredPrivateInfos": [ - "getLocation" - ], - "usingComponents": { - "navbar": "/components/navbar/navbar", - "point-navbar": "/components/pointNavbar/pointNavbar", - "da-tree": "/components/da-tree/index" - } -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/app.wxss b/unpackage/dist/dev/mp-weixin/app.wxss deleted file mode 100644 index c1b6251..0000000 --- a/unpackage/dist/dev/mp-weixin/app.wxss +++ /dev/null @@ -1 +0,0 @@ -page{--status-bar-height:25px;--top-window-height:0px;--window-top:0px;--window-bottom:0px;--window-left:0px;--window-right:0px;--window-magin:0px}[data-c-h="true"]{display: none !important;} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/common/assets.js b/unpackage/dist/dev/mp-weixin/common/assets.js deleted file mode 100644 index 28b3018..0000000 --- a/unpackage/dist/dev/mp-weixin/common/assets.js +++ /dev/null @@ -1,95 +0,0 @@ -"use strict"; -const _imports_0$h = "/static/images/login/logo.png"; -const _imports_1$9 = "/static/images/login/wx.png"; -const _imports_2$2 = "/static/images/login/close_pwd.png"; -const _imports_3$3 = "/static/images/login/open_pwd.png"; -const _imports_4$2 = "/static/images/login/switchto.svg"; -const _imports_0$g = "/static/images/index/head.png"; -const _imports_1$8 = "/static/images/index/back.png"; -const _imports_0$f = "/static/images/index/card11.png"; -const _imports_3$2 = "/static/images/index/home21.svg"; -const _imports_1$7 = "/static/images/index/icon22.svg"; -const _imports_5$1 = "/static/images/index/scan.png"; -const _imports_6$1 = "/static/images/index/point-banner.png"; -const _imports_7 = "/static/images/index/icon51.svg"; -const _imports_8$1 = "/static/images/index/more-info5.png"; -const _imports_9$1 = "/static/images/index/notice5.svg"; -const _imports_0$e = "/static/images/addCard/code.svg"; -const _imports_1$6 = "/static/images/other/more.png"; -const _imports_0$d = "/static/images/other/expand.png"; -const _imports_0$c = "/static/images/other/nodata.png"; -const _imports_0$b = "/static/images/other/code-none.png"; -const _imports_0$a = "/static/images/address/edit.png"; -const _imports_1$5 = "/static/images/address/delete.png"; -const _imports_0$9 = "/static/images/address/expand.png"; -const _imports_0$8 = "/static/images/other/success.png"; -const _imports_0$7 = "/static/images/station/facilityImg.svg"; -const _imports_1$4 = "/static/images/other/img-add.png"; -const _imports_0$6 = "/static/images/other/home.svg"; -const _imports_0$5 = "/static/images/maintain/maintenance-head.svg"; -const _imports_1$3 = "/static/images/maintain/fun-icon1.svg"; -const _imports_2$1 = "/static/images/maintain/fun-icon2.svg"; -const _imports_3$1 = "/static/images/maintain/fun-icon3.svg"; -const _imports_4$1 = "/static/images/maintain/fun-icon4.svg"; -const _imports_0$4 = "/static/images/maintain/expand.svg"; -const _imports_0$3 = "/static/images/maintain/switch-on.png"; -const _imports_1$2 = "/static/images/maintain/switch-off.png"; -const _imports_0$2 = "/static/images/pointMall/back.svg"; -const _imports_1$1 = "/static/images/pointMall/points.svg"; -const _imports_2 = "/static/images/pointMall/points-icon.svg"; -const _imports_3 = "/static/images/pointMall/advert/gift1.svg"; -const _imports_4 = "/static/images/pointMall/advert/gift2.svg"; -const _imports_5 = "/static/images/pointMall/advert/bag.svg"; -const _imports_6 = "/static/images/pointMall/advert/money.svg"; -const _imports_0$1 = "/static/images/pointMall/advert/money1.svg"; -const _imports_8 = "/static/images/pointMall/nav1.svg"; -const _imports_9 = "/static/images/pointMall/nav2.svg"; -const _imports_1 = "/static/images/pointMall/noRecord.svg"; -const _imports_0 = "/static/images/other/navBack.svg"; -exports._imports_0 = _imports_0; -exports._imports_0$1 = _imports_0$2; -exports._imports_0$10 = _imports_0$9; -exports._imports_0$11 = _imports_0$8; -exports._imports_0$12 = _imports_0$7; -exports._imports_0$13 = _imports_0$6; -exports._imports_0$14 = _imports_0$5; -exports._imports_0$15 = _imports_0$4; -exports._imports_0$16 = _imports_0$3; -exports._imports_0$17 = _imports_0$1; -exports._imports_0$2 = _imports_0$h; -exports._imports_0$3 = _imports_0$f; -exports._imports_0$4 = _imports_0$g; -exports._imports_0$5 = _imports_0$e; -exports._imports_0$6 = _imports_0$c; -exports._imports_0$7 = _imports_0$d; -exports._imports_0$8 = _imports_0$b; -exports._imports_0$9 = _imports_0$a; -exports._imports_1 = _imports_1$9; -exports._imports_1$1 = _imports_1$7; -exports._imports_1$2 = _imports_1$8; -exports._imports_1$3 = _imports_1$6; -exports._imports_1$4 = _imports_1$5; -exports._imports_1$5 = _imports_1$4; -exports._imports_1$6 = _imports_1$3; -exports._imports_1$7 = _imports_1$2; -exports._imports_1$8 = _imports_1$1; -exports._imports_1$9 = _imports_1; -exports._imports_2 = _imports_2$2; -exports._imports_2$1 = _imports_2$1; -exports._imports_2$2 = _imports_2; -exports._imports_3 = _imports_3$3; -exports._imports_3$1 = _imports_3$2; -exports._imports_3$2 = _imports_3$1; -exports._imports_3$3 = _imports_3; -exports._imports_4 = _imports_4$2; -exports._imports_4$1 = _imports_4$1; -exports._imports_4$2 = _imports_4; -exports._imports_5 = _imports_5$1; -exports._imports_5$1 = _imports_5; -exports._imports_6 = _imports_6$1; -exports._imports_6$1 = _imports_6; -exports._imports_7 = _imports_7; -exports._imports_8 = _imports_8$1; -exports._imports_8$1 = _imports_8; -exports._imports_9 = _imports_9$1; -exports._imports_9$1 = _imports_9; diff --git a/unpackage/dist/dev/mp-weixin/common/vendor.js b/unpackage/dist/dev/mp-weixin/common/vendor.js deleted file mode 100644 index 03d24d8..0000000 --- a/unpackage/dist/dev/mp-weixin/common/vendor.js +++ /dev/null @@ -1,10953 +0,0 @@ -"use strict"; -const _export_sfc = (sfc, props) => { - const target = sfc.__vccOpts || sfc; - for (const [key, val] of props) { - target[key] = val; - } - return target; -}; -/** -* @vue/shared v3.4.21 -* (c) 2018-present Yuxi (Evan) You and Vue contributors -* @license MIT -**/ -function makeMap(str, expectsLowerCase) { - const set2 = new Set(str.split(",")); - return expectsLowerCase ? (val) => set2.has(val.toLowerCase()) : (val) => set2.has(val); -} -const EMPTY_OBJ = Object.freeze({}); -const EMPTY_ARR = Object.freeze([]); -const NOOP = () => { -}; -const NO = () => false; -const isOn = (key) => key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && // uppercase letter -(key.charCodeAt(2) > 122 || key.charCodeAt(2) < 97); -const isModelListener = (key) => key.startsWith("onUpdate:"); -const extend = Object.assign; -const remove = (arr, el) => { - const i2 = arr.indexOf(el); - if (i2 > -1) { - arr.splice(i2, 1); - } -}; -const hasOwnProperty$2 = Object.prototype.hasOwnProperty; -const hasOwn$1 = (val, key) => hasOwnProperty$2.call(val, key); -const isArray = Array.isArray; -const isMap = (val) => toTypeString(val) === "[object Map]"; -const isSet = (val) => toTypeString(val) === "[object Set]"; -const isFunction = (val) => typeof val === "function"; -const isString = (val) => typeof val === "string"; -const isSymbol = (val) => typeof val === "symbol"; -const isObject$1 = (val) => val !== null && typeof val === "object"; -const isPromise = (val) => { - return (isObject$1(val) || isFunction(val)) && isFunction(val.then) && isFunction(val.catch); -}; -const objectToString = Object.prototype.toString; -const toTypeString = (value) => objectToString.call(value); -const toRawType = (value) => { - return toTypeString(value).slice(8, -1); -}; -const isPlainObject = (val) => toTypeString(val) === "[object Object]"; -const isIntegerKey = (key) => isString(key) && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key; -const isReservedProp = /* @__PURE__ */ makeMap( - // the leading comma is intentional so empty string "" is also included - ",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted" -); -const isBuiltInDirective = /* @__PURE__ */ makeMap( - "bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo" -); -const cacheStringFunction = (fn) => { - const cache = /* @__PURE__ */ Object.create(null); - return (str) => { - const hit = cache[str]; - return hit || (cache[str] = fn(str)); - }; -}; -const camelizeRE = /-(\w)/g; -const camelize = cacheStringFunction((str) => { - return str.replace(camelizeRE, (_2, c2) => c2 ? c2.toUpperCase() : ""); -}); -const hyphenateRE = /\B([A-Z])/g; -const hyphenate = cacheStringFunction( - (str) => str.replace(hyphenateRE, "-$1").toLowerCase() -); -const capitalize = cacheStringFunction((str) => { - return str.charAt(0).toUpperCase() + str.slice(1); -}); -const toHandlerKey = cacheStringFunction((str) => { - const s2 = str ? `on${capitalize(str)}` : ``; - return s2; -}); -const hasChanged = (value, oldValue) => !Object.is(value, oldValue); -const invokeArrayFns$1 = (fns, arg) => { - for (let i2 = 0; i2 < fns.length; i2++) { - fns[i2](arg); - } -}; -const def = (obj, key, value) => { - Object.defineProperty(obj, key, { - configurable: true, - enumerable: false, - value - }); -}; -const looseToNumber = (val) => { - const n2 = parseFloat(val); - return isNaN(n2) ? val : n2; -}; -let _globalThis; -const getGlobalThis = () => { - return _globalThis || (_globalThis = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {}); -}; -function normalizeStyle(value) { - if (isArray(value)) { - const res = {}; - for (let i2 = 0; i2 < value.length; i2++) { - const item = value[i2]; - const normalized = isString(item) ? parseStringStyle(item) : normalizeStyle(item); - if (normalized) { - for (const key in normalized) { - res[key] = normalized[key]; - } - } - } - return res; - } else if (isString(value) || isObject$1(value)) { - return value; - } -} -const listDelimiterRE = /;(?![^(]*\))/g; -const propertyDelimiterRE = /:([^]+)/; -const styleCommentRE = /\/\*[^]*?\*\//g; -function parseStringStyle(cssText) { - const ret = {}; - cssText.replace(styleCommentRE, "").split(listDelimiterRE).forEach((item) => { - if (item) { - const tmp = item.split(propertyDelimiterRE); - tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim()); - } - }); - return ret; -} -function normalizeClass(value) { - let res = ""; - if (isString(value)) { - res = value; - } else if (isArray(value)) { - for (let i2 = 0; i2 < value.length; i2++) { - const normalized = normalizeClass(value[i2]); - if (normalized) { - res += normalized + " "; - } - } - } else if (isObject$1(value)) { - for (const name in value) { - if (value[name]) { - res += name + " "; - } - } - } - return res.trim(); -} -const toDisplayString = (val) => { - return isString(val) ? val : val == null ? "" : isArray(val) || isObject$1(val) && (val.toString === objectToString || !isFunction(val.toString)) ? JSON.stringify(val, replacer, 2) : String(val); -}; -const replacer = (_key, val) => { - if (val && val.__v_isRef) { - return replacer(_key, val.value); - } else if (isMap(val)) { - return { - [`Map(${val.size})`]: [...val.entries()].reduce( - (entries, [key, val2], i2) => { - entries[stringifySymbol(key, i2) + " =>"] = val2; - return entries; - }, - {} - ) - }; - } else if (isSet(val)) { - return { - [`Set(${val.size})`]: [...val.values()].map((v2) => stringifySymbol(v2)) - }; - } else if (isSymbol(val)) { - return stringifySymbol(val); - } else if (isObject$1(val) && !isArray(val) && !isPlainObject(val)) { - return String(val); - } - return val; -}; -const stringifySymbol = (v2, i2 = "") => { - var _a; - return isSymbol(v2) ? `Symbol(${(_a = v2.description) != null ? _a : i2})` : v2; -}; -const SLOT_DEFAULT_NAME = "d"; -const ON_SHOW = "onShow"; -const ON_HIDE = "onHide"; -const ON_LAUNCH = "onLaunch"; -const ON_ERROR = "onError"; -const ON_THEME_CHANGE = "onThemeChange"; -const ON_PAGE_NOT_FOUND = "onPageNotFound"; -const ON_UNHANDLE_REJECTION = "onUnhandledRejection"; -const ON_EXIT = "onExit"; -const ON_LOAD = "onLoad"; -const ON_READY = "onReady"; -const ON_UNLOAD = "onUnload"; -const ON_INIT = "onInit"; -const ON_SAVE_EXIT_STATE = "onSaveExitState"; -const ON_RESIZE = "onResize"; -const ON_BACK_PRESS = "onBackPress"; -const ON_PAGE_SCROLL = "onPageScroll"; -const ON_TAB_ITEM_TAP = "onTabItemTap"; -const ON_REACH_BOTTOM = "onReachBottom"; -const ON_PULL_DOWN_REFRESH = "onPullDownRefresh"; -const ON_SHARE_TIMELINE = "onShareTimeline"; -const ON_ADD_TO_FAVORITES = "onAddToFavorites"; -const ON_SHARE_APP_MESSAGE = "onShareAppMessage"; -const ON_NAVIGATION_BAR_BUTTON_TAP = "onNavigationBarButtonTap"; -const ON_NAVIGATION_BAR_SEARCH_INPUT_CLICKED = "onNavigationBarSearchInputClicked"; -const ON_NAVIGATION_BAR_SEARCH_INPUT_CHANGED = "onNavigationBarSearchInputChanged"; -const ON_NAVIGATION_BAR_SEARCH_INPUT_CONFIRMED = "onNavigationBarSearchInputConfirmed"; -const ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED = "onNavigationBarSearchInputFocusChanged"; -const customizeRE = /:/g; -function customizeEvent(str) { - return camelize(str.replace(customizeRE, "-")); -} -function hasLeadingSlash(str) { - return str.indexOf("/") === 0; -} -function addLeadingSlash(str) { - return hasLeadingSlash(str) ? str : "/" + str; -} -const invokeArrayFns = (fns, arg) => { - let ret; - for (let i2 = 0; i2 < fns.length; i2++) { - ret = fns[i2](arg); - } - return ret; -}; -function once(fn, ctx = null) { - let res; - return (...args) => { - if (fn) { - res = fn.apply(ctx, args); - fn = null; - } - return res; - }; -} -function getValueByDataPath(obj, path) { - if (!isString(path)) { - return; - } - path = path.replace(/\[(\d+)\]/g, ".$1"); - const parts = path.split("."); - let key = parts[0]; - if (!obj) { - obj = {}; - } - if (parts.length === 1) { - return obj[key]; - } - return getValueByDataPath(obj[key], parts.slice(1).join(".")); -} -function sortObject(obj) { - let sortObj = {}; - if (isPlainObject(obj)) { - Object.keys(obj).sort().forEach((key) => { - const _key = key; - sortObj[_key] = obj[_key]; - }); - } - return !Object.keys(sortObj) ? obj : sortObj; -} -const encode = encodeURIComponent; -function stringifyQuery(obj, encodeStr = encode) { - const res = obj ? Object.keys(obj).map((key) => { - let val = obj[key]; - if (typeof val === void 0 || val === null) { - val = ""; - } else if (isPlainObject(val)) { - val = JSON.stringify(val); - } - return encodeStr(key) + "=" + encodeStr(val); - }).filter((x) => x.length > 0).join("&") : null; - return res ? `?${res}` : ""; -} -const PAGE_HOOKS = [ - ON_INIT, - ON_LOAD, - ON_SHOW, - ON_HIDE, - ON_UNLOAD, - ON_BACK_PRESS, - ON_PAGE_SCROLL, - ON_TAB_ITEM_TAP, - ON_REACH_BOTTOM, - ON_PULL_DOWN_REFRESH, - ON_SHARE_TIMELINE, - ON_SHARE_APP_MESSAGE, - ON_ADD_TO_FAVORITES, - ON_SAVE_EXIT_STATE, - ON_NAVIGATION_BAR_BUTTON_TAP, - ON_NAVIGATION_BAR_SEARCH_INPUT_CLICKED, - ON_NAVIGATION_BAR_SEARCH_INPUT_CHANGED, - ON_NAVIGATION_BAR_SEARCH_INPUT_CONFIRMED, - ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED -]; -function isRootHook(name) { - return PAGE_HOOKS.indexOf(name) > -1; -} -const UniLifecycleHooks = [ - ON_SHOW, - ON_HIDE, - ON_LAUNCH, - ON_ERROR, - ON_THEME_CHANGE, - ON_PAGE_NOT_FOUND, - ON_UNHANDLE_REJECTION, - ON_EXIT, - ON_INIT, - ON_LOAD, - ON_READY, - ON_UNLOAD, - ON_RESIZE, - ON_BACK_PRESS, - ON_PAGE_SCROLL, - ON_TAB_ITEM_TAP, - ON_REACH_BOTTOM, - ON_PULL_DOWN_REFRESH, - ON_SHARE_TIMELINE, - ON_ADD_TO_FAVORITES, - ON_SHARE_APP_MESSAGE, - ON_SAVE_EXIT_STATE, - ON_NAVIGATION_BAR_BUTTON_TAP, - ON_NAVIGATION_BAR_SEARCH_INPUT_CLICKED, - ON_NAVIGATION_BAR_SEARCH_INPUT_CHANGED, - ON_NAVIGATION_BAR_SEARCH_INPUT_CONFIRMED, - ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED -]; -const MINI_PROGRAM_PAGE_RUNTIME_HOOKS = /* @__PURE__ */ (() => { - return { - onPageScroll: 1, - onShareAppMessage: 1 << 1, - onShareTimeline: 1 << 2 - }; -})(); -function isUniLifecycleHook(name, value, checkType = true) { - if (checkType && !isFunction(value)) { - return false; - } - if (UniLifecycleHooks.indexOf(name) > -1) { - return true; - } else if (name.indexOf("on") === 0) { - return true; - } - return false; -} -let vueApp; -const createVueAppHooks = []; -function onCreateVueApp(hook) { - if (vueApp) { - return hook(vueApp); - } - createVueAppHooks.push(hook); -} -function invokeCreateVueAppHook(app) { - vueApp = app; - createVueAppHooks.forEach((hook) => hook(app)); -} -const invokeCreateErrorHandler = once((app, createErrorHandler2) => { - if (isFunction(app._component.onError)) { - return createErrorHandler2(app); - } -}); -const E$1 = function() { -}; -E$1.prototype = { - on: function(name, callback, ctx) { - var e2 = this.e || (this.e = {}); - (e2[name] || (e2[name] = [])).push({ - fn: callback, - ctx - }); - return this; - }, - once: function(name, callback, ctx) { - var self2 = this; - function listener() { - self2.off(name, listener); - callback.apply(ctx, arguments); - } - listener._ = callback; - return this.on(name, listener, ctx); - }, - emit: function(name) { - var data = [].slice.call(arguments, 1); - var evtArr = ((this.e || (this.e = {}))[name] || []).slice(); - var i2 = 0; - var len = evtArr.length; - for (i2; i2 < len; i2++) { - evtArr[i2].fn.apply(evtArr[i2].ctx, data); - } - return this; - }, - off: function(name, callback) { - var e2 = this.e || (this.e = {}); - var evts = e2[name]; - var liveEvents = []; - if (evts && callback) { - for (var i2 = evts.length - 1; i2 >= 0; i2--) { - if (evts[i2].fn === callback || evts[i2].fn._ === callback) { - evts.splice(i2, 1); - break; - } - } - liveEvents = evts; - } - liveEvents.length ? e2[name] = liveEvents : delete e2[name]; - return this; - } -}; -var E$1$1 = E$1; -const isObject = (val) => val !== null && typeof val === "object"; -const defaultDelimiters = ["{", "}"]; -class BaseFormatter { - constructor() { - this._caches = /* @__PURE__ */ Object.create(null); - } - interpolate(message, values, delimiters = defaultDelimiters) { - if (!values) { - return [message]; - } - let tokens = this._caches[message]; - if (!tokens) { - tokens = parse(message, delimiters); - this._caches[message] = tokens; - } - return compile$1(tokens, values); - } -} -const RE_TOKEN_LIST_VALUE = /^(?:\d)+/; -const RE_TOKEN_NAMED_VALUE = /^(?:\w)+/; -function parse(format, [startDelimiter, endDelimiter]) { - const tokens = []; - let position = 0; - let text = ""; - while (position < format.length) { - let char = format[position++]; - if (char === startDelimiter) { - if (text) { - tokens.push({ type: "text", value: text }); - } - text = ""; - let sub = ""; - char = format[position++]; - while (char !== void 0 && char !== endDelimiter) { - sub += char; - char = format[position++]; - } - const isClosed = char === endDelimiter; - const type = RE_TOKEN_LIST_VALUE.test(sub) ? "list" : isClosed && RE_TOKEN_NAMED_VALUE.test(sub) ? "named" : "unknown"; - tokens.push({ value: sub, type }); - } else { - text += char; - } - } - text && tokens.push({ type: "text", value: text }); - return tokens; -} -function compile$1(tokens, values) { - const compiled = []; - let index2 = 0; - const mode = Array.isArray(values) ? "list" : isObject(values) ? "named" : "unknown"; - if (mode === "unknown") { - return compiled; - } - while (index2 < tokens.length) { - const token = tokens[index2]; - switch (token.type) { - case "text": - compiled.push(token.value); - break; - case "list": - compiled.push(values[parseInt(token.value, 10)]); - break; - case "named": - if (mode === "named") { - compiled.push(values[token.value]); - } else { - { - console.warn(`Type of token '${token.type}' and format of value '${mode}' don't match!`); - } - } - break; - case "unknown": - { - console.warn(`Detect 'unknown' type of token!`); - } - break; - } - index2++; - } - return compiled; -} -const LOCALE_ZH_HANS = "zh-Hans"; -const LOCALE_ZH_HANT = "zh-Hant"; -const LOCALE_EN = "en"; -const LOCALE_FR = "fr"; -const LOCALE_ES = "es"; -const hasOwnProperty$1 = Object.prototype.hasOwnProperty; -const hasOwn = (val, key) => hasOwnProperty$1.call(val, key); -const defaultFormatter = new BaseFormatter(); -function include(str, parts) { - return !!parts.find((part) => str.indexOf(part) !== -1); -} -function startsWith(str, parts) { - return parts.find((part) => str.indexOf(part) === 0); -} -function normalizeLocale(locale, messages) { - if (!locale) { - return; - } - locale = locale.trim().replace(/_/g, "-"); - if (messages && messages[locale]) { - return locale; - } - locale = locale.toLowerCase(); - if (locale === "chinese") { - return LOCALE_ZH_HANS; - } - if (locale.indexOf("zh") === 0) { - if (locale.indexOf("-hans") > -1) { - return LOCALE_ZH_HANS; - } - if (locale.indexOf("-hant") > -1) { - return LOCALE_ZH_HANT; - } - if (include(locale, ["-tw", "-hk", "-mo", "-cht"])) { - return LOCALE_ZH_HANT; - } - return LOCALE_ZH_HANS; - } - let locales = [LOCALE_EN, LOCALE_FR, LOCALE_ES]; - if (messages && Object.keys(messages).length > 0) { - locales = Object.keys(messages); - } - const lang = startsWith(locale, locales); - if (lang) { - return lang; - } -} -class I18n { - constructor({ locale, fallbackLocale, messages, watcher, formater: formater2 }) { - this.locale = LOCALE_EN; - this.fallbackLocale = LOCALE_EN; - this.message = {}; - this.messages = {}; - this.watchers = []; - if (fallbackLocale) { - this.fallbackLocale = fallbackLocale; - } - this.formater = formater2 || defaultFormatter; - this.messages = messages || {}; - this.setLocale(locale || LOCALE_EN); - if (watcher) { - this.watchLocale(watcher); - } - } - setLocale(locale) { - const oldLocale = this.locale; - this.locale = normalizeLocale(locale, this.messages) || this.fallbackLocale; - if (!this.messages[this.locale]) { - this.messages[this.locale] = {}; - } - this.message = this.messages[this.locale]; - if (oldLocale !== this.locale) { - this.watchers.forEach((watcher) => { - watcher(this.locale, oldLocale); - }); - } - } - getLocale() { - return this.locale; - } - watchLocale(fn) { - const index2 = this.watchers.push(fn) - 1; - return () => { - this.watchers.splice(index2, 1); - }; - } - add(locale, message, override = true) { - const curMessages = this.messages[locale]; - if (curMessages) { - if (override) { - Object.assign(curMessages, message); - } else { - Object.keys(message).forEach((key) => { - if (!hasOwn(curMessages, key)) { - curMessages[key] = message[key]; - } - }); - } - } else { - this.messages[locale] = message; - } - } - f(message, values, delimiters) { - return this.formater.interpolate(message, values, delimiters).join(""); - } - t(key, locale, values) { - let message = this.message; - if (typeof locale === "string") { - locale = normalizeLocale(locale, this.messages); - locale && (message = this.messages[locale]); - } else { - values = locale; - } - if (!hasOwn(message, key)) { - console.warn(`Cannot translate the value of keypath ${key}. Use the value of keypath as default.`); - return key; - } - return this.formater.interpolate(message[key], values).join(""); - } -} -function watchAppLocale(appVm, i18n) { - if (appVm.$watchLocale) { - appVm.$watchLocale((newLocale) => { - i18n.setLocale(newLocale); - }); - } else { - appVm.$watch(() => appVm.$locale, (newLocale) => { - i18n.setLocale(newLocale); - }); - } -} -function getDefaultLocale() { - if (typeof index !== "undefined" && index.getLocale) { - return index.getLocale(); - } - if (typeof global !== "undefined" && global.getLocale) { - return global.getLocale(); - } - return LOCALE_EN; -} -function initVueI18n(locale, messages = {}, fallbackLocale, watcher) { - if (typeof locale !== "string") { - const options = [ - messages, - locale - ]; - locale = options[0]; - messages = options[1]; - } - if (typeof locale !== "string") { - locale = getDefaultLocale(); - } - if (typeof fallbackLocale !== "string") { - fallbackLocale = typeof __uniConfig !== "undefined" && __uniConfig.fallbackLocale || LOCALE_EN; - } - const i18n = new I18n({ - locale, - fallbackLocale, - messages, - watcher - }); - let t2 = (key, values) => { - if (typeof getApp !== "function") { - t2 = function(key2, values2) { - return i18n.t(key2, values2); - }; - } else { - let isWatchedAppLocale = false; - t2 = function(key2, values2) { - const appVm = getApp().$vm; - if (appVm) { - appVm.$locale; - if (!isWatchedAppLocale) { - isWatchedAppLocale = true; - watchAppLocale(appVm, i18n); - } - } - return i18n.t(key2, values2); - }; - } - return t2(key, values); - }; - return { - i18n, - f(message, values, delimiters) { - return i18n.f(message, values, delimiters); - }, - t(key, values) { - return t2(key, values); - }, - add(locale2, message, override = true) { - return i18n.add(locale2, message, override); - }, - watch(fn) { - return i18n.watchLocale(fn); - }, - getLocale() { - return i18n.getLocale(); - }, - setLocale(newLocale) { - return i18n.setLocale(newLocale); - } - }; -} -function getBaseSystemInfo() { - return wx.getSystemInfoSync(); -} -function validateProtocolFail(name, msg) { - console.warn(`${name}: ${msg}`); -} -function validateProtocol(name, data, protocol, onFail) { - if (!onFail) { - onFail = validateProtocolFail; - } - for (const key in protocol) { - const errMsg = validateProp$1(key, data[key], protocol[key], !hasOwn$1(data, key)); - if (isString(errMsg)) { - onFail(name, errMsg); - } - } -} -function validateProtocols(name, args, protocol, onFail) { - if (!protocol) { - return; - } - if (!isArray(protocol)) { - return validateProtocol(name, args[0] || /* @__PURE__ */ Object.create(null), protocol, onFail); - } - const len = protocol.length; - const argsLen = args.length; - for (let i2 = 0; i2 < len; i2++) { - const opts = protocol[i2]; - const data = /* @__PURE__ */ Object.create(null); - if (argsLen > i2) { - data[opts.name] = args[i2]; - } - validateProtocol(name, data, { [opts.name]: opts }, onFail); - } -} -function validateProp$1(name, value, prop, isAbsent) { - if (!isPlainObject(prop)) { - prop = { type: prop }; - } - const { type, required, validator } = prop; - if (required && isAbsent) { - return 'Missing required args: "' + name + '"'; - } - if (value == null && !required) { - return; - } - if (type != null) { - let isValid = false; - const types = isArray(type) ? type : [type]; - const expectedTypes = []; - for (let i2 = 0; i2 < types.length && !isValid; i2++) { - const { valid, expectedType } = assertType$1(value, types[i2]); - expectedTypes.push(expectedType || ""); - isValid = valid; - } - if (!isValid) { - return getInvalidTypeMessage$1(name, value, expectedTypes); - } - } - if (validator) { - return validator(value); - } -} -const isSimpleType$1 = /* @__PURE__ */ makeMap("String,Number,Boolean,Function,Symbol"); -function assertType$1(value, type) { - let valid; - const expectedType = getType$1(type); - if (isSimpleType$1(expectedType)) { - const t2 = typeof value; - valid = t2 === expectedType.toLowerCase(); - if (!valid && t2 === "object") { - valid = value instanceof type; - } - } else if (expectedType === "Object") { - valid = isObject$1(value); - } else if (expectedType === "Array") { - valid = isArray(value); - } else { - { - valid = value instanceof type; - } - } - return { - valid, - expectedType - }; -} -function getInvalidTypeMessage$1(name, value, expectedTypes) { - let message = `Invalid args: type check failed for args "${name}". Expected ${expectedTypes.map(capitalize).join(", ")}`; - const expectedType = expectedTypes[0]; - const receivedType = toRawType(value); - const expectedValue = styleValue$1(value, expectedType); - const receivedValue = styleValue$1(value, receivedType); - if (expectedTypes.length === 1 && isExplicable$1(expectedType) && !isBoolean$1(expectedType, receivedType)) { - message += ` with value ${expectedValue}`; - } - message += `, got ${receivedType} `; - if (isExplicable$1(receivedType)) { - message += `with value ${receivedValue}.`; - } - return message; -} -function getType$1(ctor) { - const match = ctor && ctor.toString().match(/^\s*function (\w+)/); - return match ? match[1] : ""; -} -function styleValue$1(value, type) { - if (type === "String") { - return `"${value}"`; - } else if (type === "Number") { - return `${Number(value)}`; - } else { - return `${value}`; - } -} -function isExplicable$1(type) { - const explicitTypes = ["string", "number", "boolean"]; - return explicitTypes.some((elem) => type.toLowerCase() === elem); -} -function isBoolean$1(...args) { - return args.some((elem) => elem.toLowerCase() === "boolean"); -} -function tryCatch(fn) { - return function() { - try { - return fn.apply(fn, arguments); - } catch (e2) { - console.error(e2); - } - }; -} -let invokeCallbackId = 1; -const invokeCallbacks = {}; -function addInvokeCallback(id, name, callback, keepAlive = false) { - invokeCallbacks[id] = { - name, - keepAlive, - callback - }; - return id; -} -function invokeCallback(id, res, extras) { - if (typeof id === "number") { - const opts = invokeCallbacks[id]; - if (opts) { - if (!opts.keepAlive) { - delete invokeCallbacks[id]; - } - return opts.callback(res, extras); - } - } - return res; -} -const API_SUCCESS = "success"; -const API_FAIL = "fail"; -const API_COMPLETE = "complete"; -function getApiCallbacks(args) { - const apiCallbacks = {}; - for (const name in args) { - const fn = args[name]; - if (isFunction(fn)) { - apiCallbacks[name] = tryCatch(fn); - delete args[name]; - } - } - return apiCallbacks; -} -function normalizeErrMsg(errMsg, name) { - if (!errMsg || errMsg.indexOf(":fail") === -1) { - return name + ":ok"; - } - return name + errMsg.substring(errMsg.indexOf(":fail")); -} -function createAsyncApiCallback(name, args = {}, { beforeAll, beforeSuccess } = {}) { - if (!isPlainObject(args)) { - args = {}; - } - const { success, fail, complete } = getApiCallbacks(args); - const hasSuccess = isFunction(success); - const hasFail = isFunction(fail); - const hasComplete = isFunction(complete); - const callbackId = invokeCallbackId++; - addInvokeCallback(callbackId, name, (res) => { - res = res || {}; - res.errMsg = normalizeErrMsg(res.errMsg, name); - isFunction(beforeAll) && beforeAll(res); - if (res.errMsg === name + ":ok") { - isFunction(beforeSuccess) && beforeSuccess(res, args); - hasSuccess && success(res); - } else { - hasFail && fail(res); - } - hasComplete && complete(res); - }); - return callbackId; -} -const HOOK_SUCCESS = "success"; -const HOOK_FAIL = "fail"; -const HOOK_COMPLETE = "complete"; -const globalInterceptors = {}; -const scopedInterceptors = {}; -function wrapperHook(hook, params) { - return function(data) { - return hook(data, params) || data; - }; -} -function queue$2(hooks, data, params) { - let promise = false; - for (let i2 = 0; i2 < hooks.length; i2++) { - const hook = hooks[i2]; - if (promise) { - promise = Promise.resolve(wrapperHook(hook, params)); - } else { - const res = hook(data, params); - if (isPromise(res)) { - promise = Promise.resolve(res); - } - if (res === false) { - return { - then() { - }, - catch() { - } - }; - } - } - } - return promise || { - then(callback) { - return callback(data); - }, - catch() { - } - }; -} -function wrapperOptions(interceptors2, options = {}) { - [HOOK_SUCCESS, HOOK_FAIL, HOOK_COMPLETE].forEach((name) => { - const hooks = interceptors2[name]; - if (!isArray(hooks)) { - return; - } - const oldCallback = options[name]; - options[name] = function callbackInterceptor(res) { - queue$2(hooks, res, options).then((res2) => { - return isFunction(oldCallback) && oldCallback(res2) || res2; - }); - }; - }); - return options; -} -function wrapperReturnValue(method, returnValue) { - const returnValueHooks = []; - if (isArray(globalInterceptors.returnValue)) { - returnValueHooks.push(...globalInterceptors.returnValue); - } - const interceptor = scopedInterceptors[method]; - if (interceptor && isArray(interceptor.returnValue)) { - returnValueHooks.push(...interceptor.returnValue); - } - returnValueHooks.forEach((hook) => { - returnValue = hook(returnValue) || returnValue; - }); - return returnValue; -} -function getApiInterceptorHooks(method) { - const interceptor = /* @__PURE__ */ Object.create(null); - Object.keys(globalInterceptors).forEach((hook) => { - if (hook !== "returnValue") { - interceptor[hook] = globalInterceptors[hook].slice(); - } - }); - const scopedInterceptor = scopedInterceptors[method]; - if (scopedInterceptor) { - Object.keys(scopedInterceptor).forEach((hook) => { - if (hook !== "returnValue") { - interceptor[hook] = (interceptor[hook] || []).concat(scopedInterceptor[hook]); - } - }); - } - return interceptor; -} -function invokeApi(method, api, options, params) { - const interceptor = getApiInterceptorHooks(method); - if (interceptor && Object.keys(interceptor).length) { - if (isArray(interceptor.invoke)) { - const res = queue$2(interceptor.invoke, options); - return res.then((options2) => { - return api(wrapperOptions(getApiInterceptorHooks(method), options2), ...params); - }); - } else { - return api(wrapperOptions(interceptor, options), ...params); - } - } - return api(options, ...params); -} -function hasCallback(args) { - if (isPlainObject(args) && [API_SUCCESS, API_FAIL, API_COMPLETE].find((cb) => isFunction(args[cb]))) { - return true; - } - return false; -} -function handlePromise(promise) { - return promise; -} -function promisify$1(name, fn) { - return (args = {}, ...rest) => { - if (hasCallback(args)) { - return wrapperReturnValue(name, invokeApi(name, fn, args, rest)); - } - return wrapperReturnValue(name, handlePromise(new Promise((resolve2, reject) => { - invokeApi(name, fn, extend(args, { success: resolve2, fail: reject }), rest); - }))); - }; -} -function formatApiArgs(args, options) { - const params = args[0]; - if (!options || !options.formatArgs || !isPlainObject(options.formatArgs) && isPlainObject(params)) { - return; - } - const formatArgs = options.formatArgs; - const keys = Object.keys(formatArgs); - for (let i2 = 0; i2 < keys.length; i2++) { - const name = keys[i2]; - const formatterOrDefaultValue = formatArgs[name]; - if (isFunction(formatterOrDefaultValue)) { - const errMsg = formatterOrDefaultValue(args[0][name], params); - if (isString(errMsg)) { - return errMsg; - } - } else { - if (!hasOwn$1(params, name)) { - params[name] = formatterOrDefaultValue; - } - } - } -} -function invokeSuccess(id, name, res) { - const result = { - errMsg: name + ":ok" - }; - return invokeCallback(id, extend(res || {}, result)); -} -function invokeFail(id, name, errMsg, errRes = {}) { - const apiErrMsg = name + ":fail" + (errMsg ? " " + errMsg : ""); - delete errRes.errCode; - let res = extend({ errMsg: apiErrMsg }, errRes); - return invokeCallback(id, res); -} -function beforeInvokeApi(name, args, protocol, options) { - { - validateProtocols(name, args, protocol); - } - if (options && options.beforeInvoke) { - const errMsg2 = options.beforeInvoke(args); - if (isString(errMsg2)) { - return errMsg2; - } - } - const errMsg = formatApiArgs(args, options); - if (errMsg) { - return errMsg; - } -} -function parseErrMsg(errMsg) { - if (!errMsg || isString(errMsg)) { - return errMsg; - } - if (errMsg.stack) { - console.error(errMsg.message + "\n" + errMsg.stack); - return errMsg.message; - } - return errMsg; -} -function wrapperTaskApi(name, fn, protocol, options) { - return (args) => { - const id = createAsyncApiCallback(name, args, options); - const errMsg = beforeInvokeApi(name, [args], protocol, options); - if (errMsg) { - return invokeFail(id, name, errMsg); - } - return fn(args, { - resolve: (res) => invokeSuccess(id, name, res), - reject: (errMsg2, errRes) => invokeFail(id, name, parseErrMsg(errMsg2), errRes) - }); - }; -} -function wrapperSyncApi(name, fn, protocol, options) { - return (...args) => { - const errMsg = beforeInvokeApi(name, args, protocol, options); - if (errMsg) { - throw new Error(errMsg); - } - return fn.apply(null, args); - }; -} -function wrapperAsyncApi(name, fn, protocol, options) { - return wrapperTaskApi(name, fn, protocol, options); -} -function defineSyncApi(name, fn, protocol, options) { - return wrapperSyncApi(name, fn, protocol, options); -} -function defineAsyncApi(name, fn, protocol, options) { - return promisify$1(name, wrapperAsyncApi(name, fn, protocol, options)); -} -const API_UPX2PX = "upx2px"; -const Upx2pxProtocol = [ - { - name: "upx", - type: [Number, String], - required: true - } -]; -const EPS = 1e-4; -const BASE_DEVICE_WIDTH = 750; -let isIOS = false; -let deviceWidth = 0; -let deviceDPR = 0; -function checkDeviceWidth() { - const { platform, pixelRatio, windowWidth } = getBaseSystemInfo(); - deviceWidth = windowWidth; - deviceDPR = pixelRatio; - isIOS = platform === "ios"; -} -const upx2px = defineSyncApi(API_UPX2PX, (number, newDeviceWidth) => { - if (deviceWidth === 0) { - checkDeviceWidth(); - } - number = Number(number); - if (number === 0) { - return 0; - } - let width = newDeviceWidth || deviceWidth; - let result = number / BASE_DEVICE_WIDTH * width; - if (result < 0) { - result = -result; - } - result = Math.floor(result + EPS); - if (result === 0) { - if (deviceDPR === 1 || !isIOS) { - result = 1; - } else { - result = 0.5; - } - } - return number < 0 ? -result : result; -}, Upx2pxProtocol); -const API_ADD_INTERCEPTOR = "addInterceptor"; -const API_REMOVE_INTERCEPTOR = "removeInterceptor"; -const AddInterceptorProtocol = [ - { - name: "method", - type: [String, Object], - required: true - } -]; -const RemoveInterceptorProtocol = AddInterceptorProtocol; -function mergeInterceptorHook(interceptors2, interceptor) { - Object.keys(interceptor).forEach((hook) => { - if (isFunction(interceptor[hook])) { - interceptors2[hook] = mergeHook(interceptors2[hook], interceptor[hook]); - } - }); -} -function removeInterceptorHook(interceptors2, interceptor) { - if (!interceptors2 || !interceptor) { - return; - } - Object.keys(interceptor).forEach((name) => { - const hooks = interceptors2[name]; - const hook = interceptor[name]; - if (isArray(hooks) && isFunction(hook)) { - remove(hooks, hook); - } - }); -} -function mergeHook(parentVal, childVal) { - const res = childVal ? parentVal ? parentVal.concat(childVal) : isArray(childVal) ? childVal : [childVal] : parentVal; - return res ? dedupeHooks(res) : res; -} -function dedupeHooks(hooks) { - const res = []; - for (let i2 = 0; i2 < hooks.length; i2++) { - if (res.indexOf(hooks[i2]) === -1) { - res.push(hooks[i2]); - } - } - return res; -} -const addInterceptor = defineSyncApi(API_ADD_INTERCEPTOR, (method, interceptor) => { - if (isString(method) && isPlainObject(interceptor)) { - mergeInterceptorHook(scopedInterceptors[method] || (scopedInterceptors[method] = {}), interceptor); - } else if (isPlainObject(method)) { - mergeInterceptorHook(globalInterceptors, method); - } -}, AddInterceptorProtocol); -const removeInterceptor = defineSyncApi(API_REMOVE_INTERCEPTOR, (method, interceptor) => { - if (isString(method)) { - if (isPlainObject(interceptor)) { - removeInterceptorHook(scopedInterceptors[method], interceptor); - } else { - delete scopedInterceptors[method]; - } - } else if (isPlainObject(method)) { - removeInterceptorHook(globalInterceptors, method); - } -}, RemoveInterceptorProtocol); -const interceptors = {}; -const API_ON = "$on"; -const OnProtocol = [ - { - name: "event", - type: String, - required: true - }, - { - name: "callback", - type: Function, - required: true - } -]; -const API_ONCE = "$once"; -const OnceProtocol = OnProtocol; -const API_OFF = "$off"; -const OffProtocol = [ - { - name: "event", - type: [String, Array] - }, - { - name: "callback", - type: Function - } -]; -const API_EMIT = "$emit"; -const EmitProtocol = [ - { - name: "event", - type: String, - required: true - } -]; -const emitter = new E$1$1(); -const $on = defineSyncApi(API_ON, (name, callback) => { - emitter.on(name, callback); - return () => emitter.off(name, callback); -}, OnProtocol); -const $once = defineSyncApi(API_ONCE, (name, callback) => { - emitter.once(name, callback); - return () => emitter.off(name, callback); -}, OnceProtocol); -const $off = defineSyncApi(API_OFF, (name, callback) => { - if (!name) { - emitter.e = {}; - return; - } - if (!isArray(name)) - name = [name]; - name.forEach((n2) => emitter.off(n2, callback)); -}, OffProtocol); -const $emit = defineSyncApi(API_EMIT, (name, ...args) => { - emitter.emit(name, ...args); -}, EmitProtocol); -let cid; -let cidErrMsg; -let enabled; -function normalizePushMessage(message) { - try { - return JSON.parse(message); - } catch (e2) { - } - return message; -} -function invokePushCallback(args) { - if (args.type === "enabled") { - enabled = true; - } else if (args.type === "clientId") { - cid = args.cid; - cidErrMsg = args.errMsg; - invokeGetPushCidCallbacks(cid, args.errMsg); - } else if (args.type === "pushMsg") { - const message = { - type: "receive", - data: normalizePushMessage(args.message) - }; - for (let i2 = 0; i2 < onPushMessageCallbacks.length; i2++) { - const callback = onPushMessageCallbacks[i2]; - callback(message); - if (message.stopped) { - break; - } - } - } else if (args.type === "click") { - onPushMessageCallbacks.forEach((callback) => { - callback({ - type: "click", - data: normalizePushMessage(args.message) - }); - }); - } -} -const getPushCidCallbacks = []; -function invokeGetPushCidCallbacks(cid2, errMsg) { - getPushCidCallbacks.forEach((callback) => { - callback(cid2, errMsg); - }); - getPushCidCallbacks.length = 0; -} -const API_GET_PUSH_CLIENT_ID = "getPushClientId"; -const getPushClientId = defineAsyncApi(API_GET_PUSH_CLIENT_ID, (_2, { resolve: resolve2, reject }) => { - Promise.resolve().then(() => { - if (typeof enabled === "undefined") { - enabled = false; - cid = ""; - cidErrMsg = "uniPush is not enabled"; - } - getPushCidCallbacks.push((cid2, errMsg) => { - if (cid2) { - resolve2({ cid: cid2 }); - } else { - reject(errMsg); - } - }); - if (typeof cid !== "undefined") { - invokeGetPushCidCallbacks(cid, cidErrMsg); - } - }); -}); -const onPushMessageCallbacks = []; -const onPushMessage = (fn) => { - if (onPushMessageCallbacks.indexOf(fn) === -1) { - onPushMessageCallbacks.push(fn); - } -}; -const offPushMessage = (fn) => { - if (!fn) { - onPushMessageCallbacks.length = 0; - } else { - const index2 = onPushMessageCallbacks.indexOf(fn); - if (index2 > -1) { - onPushMessageCallbacks.splice(index2, 1); - } - } -}; -const SYNC_API_RE = /^\$|getLocale|setLocale|sendNativeEvent|restoreGlobal|requireGlobal|getCurrentSubNVue|getMenuButtonBoundingClientRect|^report|interceptors|Interceptor$|getSubNVueById|requireNativePlugin|upx2px|hideKeyboard|canIUse|^create|Sync$|Manager$|base64ToArrayBuffer|arrayBufferToBase64|getDeviceInfo|getAppBaseInfo|getWindowInfo|getSystemSetting|getAppAuthorizeSetting/; -const CONTEXT_API_RE = /^create|Manager$/; -const CONTEXT_API_RE_EXC = ["createBLEConnection"]; -const ASYNC_API = ["createBLEConnection"]; -const CALLBACK_API_RE = /^on|^off/; -function isContextApi(name) { - return CONTEXT_API_RE.test(name) && CONTEXT_API_RE_EXC.indexOf(name) === -1; -} -function isSyncApi(name) { - return SYNC_API_RE.test(name) && ASYNC_API.indexOf(name) === -1; -} -function isCallbackApi(name) { - return CALLBACK_API_RE.test(name) && name !== "onPush"; -} -function shouldPromise(name) { - if (isContextApi(name) || isSyncApi(name) || isCallbackApi(name)) { - return false; - } - return true; -} -if (!Promise.prototype.finally) { - Promise.prototype.finally = function(onfinally) { - const promise = this.constructor; - return this.then((value) => promise.resolve(onfinally && onfinally()).then(() => value), (reason) => promise.resolve(onfinally && onfinally()).then(() => { - throw reason; - })); - }; -} -function promisify(name, api) { - if (!shouldPromise(name)) { - return api; - } - if (!isFunction(api)) { - return api; - } - return function promiseApi(options = {}, ...rest) { - if (isFunction(options.success) || isFunction(options.fail) || isFunction(options.complete)) { - return wrapperReturnValue(name, invokeApi(name, api, options, rest)); - } - return wrapperReturnValue(name, handlePromise(new Promise((resolve2, reject) => { - invokeApi(name, api, extend({}, options, { - success: resolve2, - fail: reject - }), rest); - }))); - }; -} -const CALLBACKS = ["success", "fail", "cancel", "complete"]; -function initWrapper(protocols2) { - function processCallback(methodName, method, returnValue) { - return function(res) { - return method(processReturnValue(methodName, res, returnValue)); - }; - } - function processArgs(methodName, fromArgs, argsOption = {}, returnValue = {}, keepFromArgs = false) { - if (isPlainObject(fromArgs)) { - const toArgs = keepFromArgs === true ? fromArgs : {}; - if (isFunction(argsOption)) { - argsOption = argsOption(fromArgs, toArgs) || {}; - } - for (const key in fromArgs) { - if (hasOwn$1(argsOption, key)) { - let keyOption = argsOption[key]; - if (isFunction(keyOption)) { - keyOption = keyOption(fromArgs[key], fromArgs, toArgs); - } - if (!keyOption) { - console.warn(`微信小程序 ${methodName} 暂不支持 ${key}`); - } else if (isString(keyOption)) { - toArgs[keyOption] = fromArgs[key]; - } else if (isPlainObject(keyOption)) { - toArgs[keyOption.name ? keyOption.name : key] = keyOption.value; - } - } else if (CALLBACKS.indexOf(key) !== -1) { - const callback = fromArgs[key]; - if (isFunction(callback)) { - toArgs[key] = processCallback(methodName, callback, returnValue); - } - } else { - if (!keepFromArgs && !hasOwn$1(toArgs, key)) { - toArgs[key] = fromArgs[key]; - } - } - } - return toArgs; - } else if (isFunction(fromArgs)) { - fromArgs = processCallback(methodName, fromArgs, returnValue); - } - return fromArgs; - } - function processReturnValue(methodName, res, returnValue, keepReturnValue = false) { - if (isFunction(protocols2.returnValue)) { - res = protocols2.returnValue(methodName, res); - } - return processArgs(methodName, res, returnValue, {}, keepReturnValue); - } - return function wrapper(methodName, method) { - if (!hasOwn$1(protocols2, methodName)) { - return method; - } - const protocol = protocols2[methodName]; - if (!protocol) { - return function() { - console.error(`微信小程序 暂不支持${methodName}`); - }; - } - return function(arg1, arg2) { - let options = protocol; - if (isFunction(protocol)) { - options = protocol(arg1); - } - arg1 = processArgs(methodName, arg1, options.args, options.returnValue); - const args = [arg1]; - if (typeof arg2 !== "undefined") { - args.push(arg2); - } - const returnValue = wx[options.name || methodName].apply(wx, args); - if (isSyncApi(methodName)) { - return processReturnValue(methodName, returnValue, options.returnValue, isContextApi(methodName)); - } - return returnValue; - }; - }; -} -const getLocale = () => { - const app = isFunction(getApp) && getApp({ allowDefault: true }); - if (app && app.$vm) { - return app.$vm.$locale; - } - return normalizeLocale(wx.getSystemInfoSync().language) || LOCALE_EN; -}; -const setLocale = (locale) => { - const app = isFunction(getApp) && getApp(); - if (!app) { - return false; - } - const oldLocale = app.$vm.$locale; - if (oldLocale !== locale) { - app.$vm.$locale = locale; - onLocaleChangeCallbacks.forEach((fn) => fn({ locale })); - return true; - } - return false; -}; -const onLocaleChangeCallbacks = []; -const onLocaleChange = (fn) => { - if (onLocaleChangeCallbacks.indexOf(fn) === -1) { - onLocaleChangeCallbacks.push(fn); - } -}; -if (typeof global !== "undefined") { - global.getLocale = getLocale; -} -const UUID_KEY = "__DC_STAT_UUID"; -let deviceId; -function useDeviceId(global2 = wx) { - return function addDeviceId(_2, toRes) { - deviceId = deviceId || global2.getStorageSync(UUID_KEY); - if (!deviceId) { - deviceId = Date.now() + "" + Math.floor(Math.random() * 1e7); - wx.setStorage({ - key: UUID_KEY, - data: deviceId - }); - } - toRes.deviceId = deviceId; - }; -} -function addSafeAreaInsets(fromRes, toRes) { - if (fromRes.safeArea) { - const safeArea = fromRes.safeArea; - toRes.safeAreaInsets = { - top: safeArea.top, - left: safeArea.left, - right: fromRes.windowWidth - safeArea.right, - bottom: fromRes.screenHeight - safeArea.bottom - }; - } -} -function populateParameters(fromRes, toRes) { - const { brand = "", model = "", system = "", language = "", theme, version: version2, platform, fontSizeSetting, SDKVersion, pixelRatio, deviceOrientation } = fromRes; - let osName = ""; - let osVersion = ""; - { - osName = system.split(" ")[0] || ""; - osVersion = system.split(" ")[1] || ""; - } - let hostVersion = version2; - let deviceType = getGetDeviceType(fromRes, model); - let deviceBrand = getDeviceBrand(brand); - let _hostName = getHostName(fromRes); - let _deviceOrientation = deviceOrientation; - let _devicePixelRatio = pixelRatio; - let _SDKVersion = SDKVersion; - const hostLanguage = language.replace(/_/g, "-"); - const parameters = { - appId: "__UNI__97986D6", - appName: "water-drinking-uniapp", - appVersion: "1.0.0", - appVersionCode: "100", - appLanguage: getAppLanguage(hostLanguage), - uniCompileVersion: "4.28", - uniRuntimeVersion: "4.28", - uniPlatform: "mp-weixin", - deviceBrand, - deviceModel: model, - deviceType, - devicePixelRatio: _devicePixelRatio, - deviceOrientation: _deviceOrientation, - osName: osName.toLocaleLowerCase(), - osVersion, - hostTheme: theme, - hostVersion, - hostLanguage, - hostName: _hostName, - hostSDKVersion: _SDKVersion, - hostFontSizeSetting: fontSizeSetting, - windowTop: 0, - windowBottom: 0, - // TODO - osLanguage: void 0, - osTheme: void 0, - ua: void 0, - hostPackageName: void 0, - browserName: void 0, - browserVersion: void 0 - }; - extend(toRes, parameters); -} -function getGetDeviceType(fromRes, model) { - let deviceType = fromRes.deviceType || "phone"; - { - const deviceTypeMaps = { - ipad: "pad", - windows: "pc", - mac: "pc" - }; - const deviceTypeMapsKeys = Object.keys(deviceTypeMaps); - const _model = model.toLocaleLowerCase(); - for (let index2 = 0; index2 < deviceTypeMapsKeys.length; index2++) { - const _m = deviceTypeMapsKeys[index2]; - if (_model.indexOf(_m) !== -1) { - deviceType = deviceTypeMaps[_m]; - break; - } - } - } - return deviceType; -} -function getDeviceBrand(brand) { - let deviceBrand = brand; - if (deviceBrand) { - deviceBrand = deviceBrand.toLocaleLowerCase(); - } - return deviceBrand; -} -function getAppLanguage(defaultLanguage) { - return getLocale ? getLocale() : defaultLanguage; -} -function getHostName(fromRes) { - const _platform = "WeChat"; - let _hostName = fromRes.hostName || _platform; - { - if (fromRes.environment) { - _hostName = fromRes.environment; - } else if (fromRes.host && fromRes.host.env) { - _hostName = fromRes.host.env; - } - } - return _hostName; -} -const getSystemInfo = { - returnValue: (fromRes, toRes) => { - addSafeAreaInsets(fromRes, toRes); - useDeviceId()(fromRes, toRes); - populateParameters(fromRes, toRes); - } -}; -const getSystemInfoSync = getSystemInfo; -const redirectTo = {}; -const previewImage = { - args(fromArgs, toArgs) { - let currentIndex = parseInt(fromArgs.current); - if (isNaN(currentIndex)) { - return; - } - const urls = fromArgs.urls; - if (!isArray(urls)) { - return; - } - const len = urls.length; - if (!len) { - return; - } - if (currentIndex < 0) { - currentIndex = 0; - } else if (currentIndex >= len) { - currentIndex = len - 1; - } - if (currentIndex > 0) { - toArgs.current = urls[currentIndex]; - toArgs.urls = urls.filter((item, index2) => index2 < currentIndex ? item !== urls[currentIndex] : true); - } else { - toArgs.current = urls[0]; - } - return { - indicator: false, - loop: false - }; - } -}; -const showActionSheet = { - args(fromArgs, toArgs) { - toArgs.alertText = fromArgs.title; - } -}; -const getDeviceInfo = { - returnValue: (fromRes, toRes) => { - const { brand, model } = fromRes; - let deviceType = getGetDeviceType(fromRes, model); - let deviceBrand = getDeviceBrand(brand); - useDeviceId()(fromRes, toRes); - toRes = sortObject(extend(toRes, { - deviceType, - deviceBrand, - deviceModel: model - })); - } -}; -const getAppBaseInfo = { - returnValue: (fromRes, toRes) => { - const { version: version2, language, SDKVersion, theme } = fromRes; - let _hostName = getHostName(fromRes); - let hostLanguage = language.replace(/_/g, "-"); - toRes = sortObject(extend(toRes, { - hostVersion: version2, - hostLanguage, - hostName: _hostName, - hostSDKVersion: SDKVersion, - hostTheme: theme, - appId: "__UNI__97986D6", - appName: "water-drinking-uniapp", - appVersion: "1.0.0", - appVersionCode: "100", - appLanguage: getAppLanguage(hostLanguage) - })); - } -}; -const getWindowInfo = { - returnValue: (fromRes, toRes) => { - addSafeAreaInsets(fromRes, toRes); - toRes = sortObject(extend(toRes, { - windowTop: 0, - windowBottom: 0 - })); - } -}; -const getAppAuthorizeSetting = { - returnValue: function(fromRes, toRes) { - const { locationReducedAccuracy } = fromRes; - toRes.locationAccuracy = "unsupported"; - if (locationReducedAccuracy === true) { - toRes.locationAccuracy = "reduced"; - } else if (locationReducedAccuracy === false) { - toRes.locationAccuracy = "full"; - } - } -}; -const baseApis = { - $on, - $off, - $once, - $emit, - upx2px, - interceptors, - addInterceptor, - removeInterceptor, - onCreateVueApp, - invokeCreateVueAppHook, - getLocale, - setLocale, - onLocaleChange, - getPushClientId, - onPushMessage, - offPushMessage, - invokePushCallback -}; -function initUni(api, protocols2, platform = wx) { - const wrapper = initWrapper(protocols2); - const UniProxyHandlers = { - get(target, key) { - if (hasOwn$1(target, key)) { - return target[key]; - } - if (hasOwn$1(api, key)) { - return promisify(key, api[key]); - } - if (hasOwn$1(baseApis, key)) { - return promisify(key, baseApis[key]); - } - return promisify(key, wrapper(key, platform[key])); - } - }; - return new Proxy({}, UniProxyHandlers); -} -function initGetProvider(providers) { - return function getProvider2({ service, success, fail, complete }) { - let res; - if (providers[service]) { - res = { - errMsg: "getProvider:ok", - service, - provider: providers[service] - }; - isFunction(success) && success(res); - } else { - res = { - errMsg: "getProvider:fail:服务[" + service + "]不存在" - }; - isFunction(fail) && fail(res); - } - isFunction(complete) && complete(res); - }; -} -const objectKeys = [ - "qy", - "env", - "error", - "version", - "lanDebug", - "cloud", - "serviceMarket", - "router", - "worklet", - "__webpack_require_UNI_MP_PLUGIN__" -]; -const singlePageDisableKey = ["lanDebug", "router", "worklet"]; -const launchOption = wx.getLaunchOptionsSync ? wx.getLaunchOptionsSync() : null; -function isWxKey(key) { - if (launchOption && launchOption.scene === 1154 && singlePageDisableKey.includes(key)) { - return false; - } - return objectKeys.indexOf(key) > -1 || typeof wx[key] === "function"; -} -function initWx() { - const newWx = {}; - for (const key in wx) { - if (isWxKey(key)) { - newWx[key] = wx[key]; - } - } - if (typeof globalThis !== "undefined" && typeof requireMiniProgram === "undefined") { - globalThis.wx = newWx; - } - return newWx; -} -const mocks$1 = ["__route__", "__wxExparserNodeId__", "__wxWebviewId__"]; -const getProvider = initGetProvider({ - oauth: ["weixin"], - share: ["weixin"], - payment: ["wxpay"], - push: ["weixin"] -}); -function initComponentMocks(component) { - const res = /* @__PURE__ */ Object.create(null); - mocks$1.forEach((name) => { - res[name] = component[name]; - }); - return res; -} -function createSelectorQuery() { - const query = wx$2.createSelectorQuery(); - const oldIn = query.in; - query.in = function newIn(component) { - return oldIn.call(this, initComponentMocks(component)); - }; - return query; -} -const wx$2 = initWx(); -let baseInfo = wx$2.getAppBaseInfo && wx$2.getAppBaseInfo(); -if (!baseInfo) { - baseInfo = wx$2.getSystemInfoSync(); -} -const host = baseInfo ? baseInfo.host : null; -const shareVideoMessage = host && host.env === "SAAASDK" ? wx$2.miniapp.shareVideoMessage : wx$2.shareVideoMessage; -var shims = /* @__PURE__ */ Object.freeze({ - __proto__: null, - createSelectorQuery, - getProvider, - shareVideoMessage -}); -const compressImage = { - args(fromArgs, toArgs) { - if (fromArgs.compressedHeight && !toArgs.compressHeight) { - toArgs.compressHeight = fromArgs.compressedHeight; - } - if (fromArgs.compressedWidth && !toArgs.compressWidth) { - toArgs.compressWidth = fromArgs.compressedWidth; - } - } -}; -var protocols = /* @__PURE__ */ Object.freeze({ - __proto__: null, - compressImage, - getAppAuthorizeSetting, - getAppBaseInfo, - getDeviceInfo, - getSystemInfo, - getSystemInfoSync, - getWindowInfo, - previewImage, - redirectTo, - showActionSheet -}); -const wx$1 = initWx(); -var index = initUni(shims, protocols, wx$1); -new Set( - /* @__PURE__ */ Object.getOwnPropertyNames(Symbol).filter((key) => key !== "arguments" && key !== "caller").map((key) => Symbol[key]).filter(isSymbol) -); -function toRaw$1(observed) { - const raw = observed && observed["__v_raw"]; - return raw ? toRaw$1(raw) : observed; -} -function isRef$1(r2) { - return !!(r2 && r2.__v_isRef === true); -} -/** -* @vue/runtime-core v3.4.21 -* (c) 2018-present Yuxi (Evan) You and Vue contributors -* @license MIT -**/ -const stack$1 = []; -function pushWarningContext$1(vnode) { - stack$1.push(vnode); -} -function popWarningContext$1() { - stack$1.pop(); -} -function warn$1$1(msg, ...args) { - const instance = stack$1.length ? stack$1[stack$1.length - 1].component : null; - const appWarnHandler = instance && instance.appContext.config.warnHandler; - const trace = getComponentTrace$1(); - if (appWarnHandler) { - callWithErrorHandling$1( - appWarnHandler, - instance, - 11, - [ - msg + args.map((a2) => { - var _a, _b; - return (_b = (_a = a2.toString) == null ? void 0 : _a.call(a2)) != null ? _b : JSON.stringify(a2); - }).join(""), - instance && instance.proxy, - trace.map( - ({ vnode }) => `at <${formatComponentName$1(instance, vnode.type)}>` - ).join("\n"), - trace - ] - ); - } else { - const warnArgs = [`[Vue warn]: ${msg}`, ...args]; - if (trace.length && // avoid spamming console during tests - true) { - warnArgs.push(` -`, ...formatTrace$1(trace)); - } - console.warn(...warnArgs); - } -} -function getComponentTrace$1() { - let currentVNode = stack$1[stack$1.length - 1]; - if (!currentVNode) { - return []; - } - const normalizedStack = []; - while (currentVNode) { - const last = normalizedStack[0]; - if (last && last.vnode === currentVNode) { - last.recurseCount++; - } else { - normalizedStack.push({ - vnode: currentVNode, - recurseCount: 0 - }); - } - const parentInstance = currentVNode.component && currentVNode.component.parent; - currentVNode = parentInstance && parentInstance.vnode; - } - return normalizedStack; -} -function formatTrace$1(trace) { - const logs = []; - trace.forEach((entry, i2) => { - logs.push(...i2 === 0 ? [] : [` -`], ...formatTraceEntry$1(entry)); - }); - return logs; -} -function formatTraceEntry$1({ vnode, recurseCount }) { - const postfix = recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``; - const isRoot = vnode.component ? vnode.component.parent == null : false; - const open = ` at <${formatComponentName$1( - vnode.component, - vnode.type, - isRoot - )}`; - const close = `>` + postfix; - return vnode.props ? [open, ...formatProps$1(vnode.props), close] : [open + close]; -} -function formatProps$1(props) { - const res = []; - const keys = Object.keys(props); - keys.slice(0, 3).forEach((key) => { - res.push(...formatProp$1(key, props[key])); - }); - if (keys.length > 3) { - res.push(` ...`); - } - return res; -} -function formatProp$1(key, value, raw) { - if (isString(value)) { - value = JSON.stringify(value); - return raw ? value : [`${key}=${value}`]; - } else if (typeof value === "number" || typeof value === "boolean" || value == null) { - return raw ? value : [`${key}=${value}`]; - } else if (isRef$1(value)) { - value = formatProp$1(key, toRaw$1(value.value), true); - return raw ? value : [`${key}=Ref<`, value, `>`]; - } else if (isFunction(value)) { - return [`${key}=fn${value.name ? `<${value.name}>` : ``}`]; - } else { - value = toRaw$1(value); - return raw ? value : [`${key}=`, value]; - } -} -const ErrorTypeStrings$1 = { - ["sp"]: "serverPrefetch hook", - ["bc"]: "beforeCreate hook", - ["c"]: "created hook", - ["bm"]: "beforeMount hook", - ["m"]: "mounted hook", - ["bu"]: "beforeUpdate hook", - ["u"]: "updated", - ["bum"]: "beforeUnmount hook", - ["um"]: "unmounted hook", - ["a"]: "activated hook", - ["da"]: "deactivated hook", - ["ec"]: "errorCaptured hook", - ["rtc"]: "renderTracked hook", - ["rtg"]: "renderTriggered hook", - [0]: "setup function", - [1]: "render function", - [2]: "watcher getter", - [3]: "watcher callback", - [4]: "watcher cleanup function", - [5]: "native event handler", - [6]: "component event handler", - [7]: "vnode hook", - [8]: "directive hook", - [9]: "transition hook", - [10]: "app errorHandler", - [11]: "app warnHandler", - [12]: "ref function", - [13]: "async component loader", - [14]: "scheduler flush. This is likely a Vue internals bug. Please open an issue at https://github.com/vuejs/core ." -}; -function callWithErrorHandling$1(fn, instance, type, args) { - try { - return args ? fn(...args) : fn(); - } catch (err) { - handleError$1(err, instance, type); - } -} -function handleError$1(err, instance, type, throwInDev = true) { - const contextVNode = instance ? instance.vnode : null; - if (instance) { - let cur = instance.parent; - const exposedInstance = instance.proxy; - const errorInfo = ErrorTypeStrings$1[type]; - while (cur) { - const errorCapturedHooks = cur.ec; - if (errorCapturedHooks) { - for (let i2 = 0; i2 < errorCapturedHooks.length; i2++) { - if (errorCapturedHooks[i2](err, exposedInstance, errorInfo) === false) { - return; - } - } - } - cur = cur.parent; - } - const appErrorHandler = instance.appContext.config.errorHandler; - if (appErrorHandler) { - callWithErrorHandling$1( - appErrorHandler, - null, - 10, - [err, exposedInstance, errorInfo] - ); - return; - } - } - logError$1(err, type, contextVNode, throwInDev); -} -function logError$1(err, type, contextVNode, throwInDev = true) { - { - const info = ErrorTypeStrings$1[type]; - if (contextVNode) { - pushWarningContext$1(contextVNode); - } - warn$1$1(`Unhandled error${info ? ` during execution of ${info}` : ``}`); - if (contextVNode) { - popWarningContext$1(); - } - if (throwInDev) { - throw err; - } else { - console.error(err); - } - } -} -let isFlushing$1 = false; -let isFlushPending$1 = false; -const queue$1 = []; -let flushIndex$1 = 0; -const pendingPostFlushCbs$1 = []; -let activePostFlushCbs$1 = null; -let postFlushIndex$1 = 0; -const resolvedPromise$1 = /* @__PURE__ */ Promise.resolve(); -const RECURSION_LIMIT$1 = 100; -function findInsertionIndex$1(id) { - let start = flushIndex$1 + 1; - let end = queue$1.length; - while (start < end) { - const middle = start + end >>> 1; - const middleJob = queue$1[middle]; - const middleJobId = getId$1(middleJob); - if (middleJobId < id || middleJobId === id && middleJob.pre) { - start = middle + 1; - } else { - end = middle; - } - } - return start; -} -function queueJob$1(job) { - if (!queue$1.length || !queue$1.includes( - job, - isFlushing$1 && job.allowRecurse ? flushIndex$1 + 1 : flushIndex$1 - )) { - if (job.id == null) { - queue$1.push(job); - } else { - queue$1.splice(findInsertionIndex$1(job.id), 0, job); - } - queueFlush$1(); - } -} -function queueFlush$1() { - if (!isFlushing$1 && !isFlushPending$1) { - isFlushPending$1 = true; - resolvedPromise$1.then(flushJobs$1); - } -} -function queuePostFlushCb$1(cb) { - if (!isArray(cb)) { - if (!activePostFlushCbs$1 || !activePostFlushCbs$1.includes( - cb, - cb.allowRecurse ? postFlushIndex$1 + 1 : postFlushIndex$1 - )) { - pendingPostFlushCbs$1.push(cb); - } - } else { - pendingPostFlushCbs$1.push(...cb); - } - queueFlush$1(); -} -function flushPostFlushCbs$1(seen) { - if (pendingPostFlushCbs$1.length) { - const deduped = [...new Set(pendingPostFlushCbs$1)].sort( - (a2, b2) => getId$1(a2) - getId$1(b2) - ); - pendingPostFlushCbs$1.length = 0; - if (activePostFlushCbs$1) { - activePostFlushCbs$1.push(...deduped); - return; - } - activePostFlushCbs$1 = deduped; - { - seen = seen || /* @__PURE__ */ new Map(); - } - for (postFlushIndex$1 = 0; postFlushIndex$1 < activePostFlushCbs$1.length; postFlushIndex$1++) { - if (checkRecursiveUpdates$1(seen, activePostFlushCbs$1[postFlushIndex$1])) { - continue; - } - activePostFlushCbs$1[postFlushIndex$1](); - } - activePostFlushCbs$1 = null; - postFlushIndex$1 = 0; - } -} -const getId$1 = (job) => job.id == null ? Infinity : job.id; -const comparator$1 = (a2, b2) => { - const diff2 = getId$1(a2) - getId$1(b2); - if (diff2 === 0) { - if (a2.pre && !b2.pre) - return -1; - if (b2.pre && !a2.pre) - return 1; - } - return diff2; -}; -function flushJobs$1(seen) { - isFlushPending$1 = false; - isFlushing$1 = true; - { - seen = seen || /* @__PURE__ */ new Map(); - } - queue$1.sort(comparator$1); - const check = (job) => checkRecursiveUpdates$1(seen, job); - try { - for (flushIndex$1 = 0; flushIndex$1 < queue$1.length; flushIndex$1++) { - const job = queue$1[flushIndex$1]; - if (job && job.active !== false) { - if (check(job)) { - continue; - } - callWithErrorHandling$1(job, null, 14); - } - } - } finally { - flushIndex$1 = 0; - queue$1.length = 0; - flushPostFlushCbs$1(seen); - isFlushing$1 = false; - if (queue$1.length || pendingPostFlushCbs$1.length) { - flushJobs$1(seen); - } - } -} -function checkRecursiveUpdates$1(seen, fn) { - if (!seen.has(fn)) { - seen.set(fn, 1); - } else { - const count = seen.get(fn); - if (count > RECURSION_LIMIT$1) { - const instance = fn.ownerInstance; - const componentName = instance && getComponentName$1(instance.type); - handleError$1( - `Maximum recursive updates exceeded${componentName ? ` in component <${componentName}>` : ``}. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.`, - null, - 10 - ); - return true; - } else { - seen.set(fn, count + 1); - } - } -} -const hmrDirtyComponents = /* @__PURE__ */ new Set(); -{ - getGlobalThis().__VUE_HMR_RUNTIME__ = { - createRecord: tryWrap(createRecord), - rerender: tryWrap(rerender), - reload: tryWrap(reload) - }; -} -const map = /* @__PURE__ */ new Map(); -function createRecord(id, initialDef) { - if (map.has(id)) { - return false; - } - map.set(id, { - initialDef: normalizeClassComponent(initialDef), - instances: /* @__PURE__ */ new Set() - }); - return true; -} -function normalizeClassComponent(component) { - return isClassComponent$1(component) ? component.__vccOpts : component; -} -function rerender(id, newRender) { - const record = map.get(id); - if (!record) { - return; - } - record.initialDef.render = newRender; - [...record.instances].forEach((instance) => { - if (newRender) { - instance.render = newRender; - normalizeClassComponent(instance.type).render = newRender; - } - instance.renderCache = []; - instance.effect.dirty = true; - instance.update(); - }); -} -function reload(id, newComp) { - const record = map.get(id); - if (!record) - return; - newComp = normalizeClassComponent(newComp); - updateComponentDef(record.initialDef, newComp); - const instances = [...record.instances]; - for (const instance of instances) { - const oldComp = normalizeClassComponent(instance.type); - if (!hmrDirtyComponents.has(oldComp)) { - if (oldComp !== record.initialDef) { - updateComponentDef(oldComp, newComp); - } - hmrDirtyComponents.add(oldComp); - } - instance.appContext.propsCache.delete(instance.type); - instance.appContext.emitsCache.delete(instance.type); - instance.appContext.optionsCache.delete(instance.type); - if (instance.ceReload) { - hmrDirtyComponents.add(oldComp); - instance.ceReload(newComp.styles); - hmrDirtyComponents.delete(oldComp); - } else if (instance.parent) { - instance.parent.effect.dirty = true; - queueJob$1(instance.parent.update); - } else if (instance.appContext.reload) { - instance.appContext.reload(); - } else if (typeof window !== "undefined") { - window.location.reload(); - } else { - console.warn( - "[HMR] Root or manually mounted instance modified. Full reload required." - ); - } - } - queuePostFlushCb$1(() => { - for (const instance of instances) { - hmrDirtyComponents.delete( - normalizeClassComponent(instance.type) - ); - } - }); -} -function updateComponentDef(oldComp, newComp) { - extend(oldComp, newComp); - for (const key in oldComp) { - if (key !== "__file" && !(key in newComp)) { - delete oldComp[key]; - } - } -} -function tryWrap(fn) { - return (id, arg) => { - try { - return fn(id, arg); - } catch (e2) { - console.error(e2); - console.warn( - `[HMR] Something went wrong during Vue component hot-reload. Full reload required.` - ); - } - }; -} -{ - const g2 = getGlobalThis(); - const registerGlobalSetter = (key, setter) => { - let setters; - if (!(setters = g2[key])) - setters = g2[key] = []; - setters.push(setter); - return (v2) => { - if (setters.length > 1) - setters.forEach((set2) => set2(v2)); - else - setters[0](v2); - }; - }; - registerGlobalSetter( - `__VUE_INSTANCE_SETTERS__`, - (v2) => v2 - ); - registerGlobalSetter( - `__VUE_SSR_SETTERS__`, - (v2) => v2 - ); -} -const classifyRE$1 = /(?:^|[-_])(\w)/g; -const classify$1 = (str) => str.replace(classifyRE$1, (c2) => c2.toUpperCase()).replace(/[-_]/g, ""); -function getComponentName$1(Component2, includeInferred = true) { - return isFunction(Component2) ? Component2.displayName || Component2.name : Component2.name || includeInferred && Component2.__name; -} -function formatComponentName$1(instance, Component2, isRoot = false) { - let name = getComponentName$1(Component2); - if (!name && Component2.__file) { - const match = Component2.__file.match(/([^/\\]+)\.\w+$/); - if (match) { - name = match[1]; - } - } - if (!name && instance && instance.parent) { - const inferFromRegistry = (registry) => { - for (const key in registry) { - if (registry[key] === Component2) { - return key; - } - } - }; - name = inferFromRegistry( - instance.components || instance.parent.type.components - ) || inferFromRegistry(instance.appContext.components); - } - return name ? classify$1(name) : isRoot ? `App` : `Anonymous`; -} -function isClassComponent$1(value) { - return isFunction(value) && "__vccOpts" in value; -} -/** -* @dcloudio/uni-mp-vue v3.4.21 -* (c) 2018-present Yuxi (Evan) You and Vue contributors -* @license MIT -**/ -function warn$2(msg, ...args) { - console.warn(`[Vue warn] ${msg}`, ...args); -} -let activeEffectScope; -class EffectScope { - constructor(detached = false) { - this.detached = detached; - this._active = true; - this.effects = []; - this.cleanups = []; - this.parent = activeEffectScope; - if (!detached && activeEffectScope) { - this.index = (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push( - this - ) - 1; - } - } - get active() { - return this._active; - } - run(fn) { - if (this._active) { - const currentEffectScope = activeEffectScope; - try { - activeEffectScope = this; - return fn(); - } finally { - activeEffectScope = currentEffectScope; - } - } else { - warn$2(`cannot run an inactive effect scope.`); - } - } - /** - * This should only be called on non-detached scopes - * @internal - */ - on() { - activeEffectScope = this; - } - /** - * This should only be called on non-detached scopes - * @internal - */ - off() { - activeEffectScope = this.parent; - } - stop(fromParent) { - if (this._active) { - let i2, l2; - for (i2 = 0, l2 = this.effects.length; i2 < l2; i2++) { - this.effects[i2].stop(); - } - for (i2 = 0, l2 = this.cleanups.length; i2 < l2; i2++) { - this.cleanups[i2](); - } - if (this.scopes) { - for (i2 = 0, l2 = this.scopes.length; i2 < l2; i2++) { - this.scopes[i2].stop(true); - } - } - if (!this.detached && this.parent && !fromParent) { - const last = this.parent.scopes.pop(); - if (last && last !== this) { - this.parent.scopes[this.index] = last; - last.index = this.index; - } - } - this.parent = void 0; - this._active = false; - } - } -} -function recordEffectScope(effect2, scope = activeEffectScope) { - if (scope && scope.active) { - scope.effects.push(effect2); - } -} -function getCurrentScope() { - return activeEffectScope; -} -let activeEffect; -class ReactiveEffect2 { - constructor(fn, trigger2, scheduler, scope) { - this.fn = fn; - this.trigger = trigger2; - this.scheduler = scheduler; - this.active = true; - this.deps = []; - this._dirtyLevel = 4; - this._trackId = 0; - this._runnings = 0; - this._shouldSchedule = false; - this._depsLength = 0; - recordEffectScope(this, scope); - } - get dirty() { - if (this._dirtyLevel === 2 || this._dirtyLevel === 3) { - this._dirtyLevel = 1; - pauseTracking(); - for (let i2 = 0; i2 < this._depsLength; i2++) { - const dep = this.deps[i2]; - if (dep.computed) { - triggerComputed(dep.computed); - if (this._dirtyLevel >= 4) { - break; - } - } - } - if (this._dirtyLevel === 1) { - this._dirtyLevel = 0; - } - resetTracking(); - } - return this._dirtyLevel >= 4; - } - set dirty(v2) { - this._dirtyLevel = v2 ? 4 : 0; - } - run() { - this._dirtyLevel = 0; - if (!this.active) { - return this.fn(); - } - let lastShouldTrack = shouldTrack; - let lastEffect = activeEffect; - try { - shouldTrack = true; - activeEffect = this; - this._runnings++; - preCleanupEffect(this); - return this.fn(); - } finally { - postCleanupEffect(this); - this._runnings--; - activeEffect = lastEffect; - shouldTrack = lastShouldTrack; - } - } - stop() { - var _a; - if (this.active) { - preCleanupEffect(this); - postCleanupEffect(this); - (_a = this.onStop) == null ? void 0 : _a.call(this); - this.active = false; - } - } -} -function triggerComputed(computed2) { - return computed2.value; -} -function preCleanupEffect(effect2) { - effect2._trackId++; - effect2._depsLength = 0; -} -function postCleanupEffect(effect2) { - if (effect2.deps.length > effect2._depsLength) { - for (let i2 = effect2._depsLength; i2 < effect2.deps.length; i2++) { - cleanupDepEffect(effect2.deps[i2], effect2); - } - effect2.deps.length = effect2._depsLength; - } -} -function cleanupDepEffect(dep, effect2) { - const trackId = dep.get(effect2); - if (trackId !== void 0 && effect2._trackId !== trackId) { - dep.delete(effect2); - if (dep.size === 0) { - dep.cleanup(); - } - } -} -let shouldTrack = true; -let pauseScheduleStack = 0; -const trackStack = []; -function pauseTracking() { - trackStack.push(shouldTrack); - shouldTrack = false; -} -function resetTracking() { - const last = trackStack.pop(); - shouldTrack = last === void 0 ? true : last; -} -function pauseScheduling() { - pauseScheduleStack++; -} -function resetScheduling() { - pauseScheduleStack--; - while (!pauseScheduleStack && queueEffectSchedulers.length) { - queueEffectSchedulers.shift()(); - } -} -function trackEffect(effect2, dep, debuggerEventExtraInfo) { - var _a; - if (dep.get(effect2) !== effect2._trackId) { - dep.set(effect2, effect2._trackId); - const oldDep = effect2.deps[effect2._depsLength]; - if (oldDep !== dep) { - if (oldDep) { - cleanupDepEffect(oldDep, effect2); - } - effect2.deps[effect2._depsLength++] = dep; - } else { - effect2._depsLength++; - } - { - (_a = effect2.onTrack) == null ? void 0 : _a.call(effect2, extend({ effect: effect2 }, debuggerEventExtraInfo)); - } - } -} -const queueEffectSchedulers = []; -function triggerEffects(dep, dirtyLevel, debuggerEventExtraInfo) { - var _a; - pauseScheduling(); - for (const effect2 of dep.keys()) { - let tracking; - if (effect2._dirtyLevel < dirtyLevel && (tracking != null ? tracking : tracking = dep.get(effect2) === effect2._trackId)) { - effect2._shouldSchedule || (effect2._shouldSchedule = effect2._dirtyLevel === 0); - effect2._dirtyLevel = dirtyLevel; - } - if (effect2._shouldSchedule && (tracking != null ? tracking : tracking = dep.get(effect2) === effect2._trackId)) { - { - (_a = effect2.onTrigger) == null ? void 0 : _a.call(effect2, extend({ effect: effect2 }, debuggerEventExtraInfo)); - } - effect2.trigger(); - if ((!effect2._runnings || effect2.allowRecurse) && effect2._dirtyLevel !== 2) { - effect2._shouldSchedule = false; - if (effect2.scheduler) { - queueEffectSchedulers.push(effect2.scheduler); - } - } - } - } - resetScheduling(); -} -const createDep = (cleanup, computed2) => { - const dep = /* @__PURE__ */ new Map(); - dep.cleanup = cleanup; - dep.computed = computed2; - return dep; -}; -const targetMap = /* @__PURE__ */ new WeakMap(); -const ITERATE_KEY = Symbol("iterate"); -const MAP_KEY_ITERATE_KEY = Symbol("Map key iterate"); -function track(target, type, key) { - if (shouldTrack && activeEffect) { - let depsMap = targetMap.get(target); - if (!depsMap) { - targetMap.set(target, depsMap = /* @__PURE__ */ new Map()); - } - let dep = depsMap.get(key); - if (!dep) { - depsMap.set(key, dep = createDep(() => depsMap.delete(key))); - } - trackEffect( - activeEffect, - dep, - { - target, - type, - key - } - ); - } -} -function trigger(target, type, key, newValue, oldValue, oldTarget) { - const depsMap = targetMap.get(target); - if (!depsMap) { - return; - } - let deps = []; - if (type === "clear") { - deps = [...depsMap.values()]; - } else if (key === "length" && isArray(target)) { - const newLength = Number(newValue); - depsMap.forEach((dep, key2) => { - if (key2 === "length" || !isSymbol(key2) && key2 >= newLength) { - deps.push(dep); - } - }); - } else { - if (key !== void 0) { - deps.push(depsMap.get(key)); - } - switch (type) { - case "add": - if (!isArray(target)) { - deps.push(depsMap.get(ITERATE_KEY)); - if (isMap(target)) { - deps.push(depsMap.get(MAP_KEY_ITERATE_KEY)); - } - } else if (isIntegerKey(key)) { - deps.push(depsMap.get("length")); - } - break; - case "delete": - if (!isArray(target)) { - deps.push(depsMap.get(ITERATE_KEY)); - if (isMap(target)) { - deps.push(depsMap.get(MAP_KEY_ITERATE_KEY)); - } - } - break; - case "set": - if (isMap(target)) { - deps.push(depsMap.get(ITERATE_KEY)); - } - break; - } - } - pauseScheduling(); - for (const dep of deps) { - if (dep) { - triggerEffects( - dep, - 4, - { - target, - type, - key, - newValue, - oldValue, - oldTarget - } - ); - } - } - resetScheduling(); -} -const isNonTrackableKeys = /* @__PURE__ */ makeMap(`__proto__,__v_isRef,__isVue`); -const builtInSymbols = new Set( - /* @__PURE__ */ Object.getOwnPropertyNames(Symbol).filter((key) => key !== "arguments" && key !== "caller").map((key) => Symbol[key]).filter(isSymbol) -); -const arrayInstrumentations = /* @__PURE__ */ createArrayInstrumentations(); -function createArrayInstrumentations() { - const instrumentations = {}; - ["includes", "indexOf", "lastIndexOf"].forEach((key) => { - instrumentations[key] = function(...args) { - const arr = toRaw(this); - for (let i2 = 0, l2 = this.length; i2 < l2; i2++) { - track(arr, "get", i2 + ""); - } - const res = arr[key](...args); - if (res === -1 || res === false) { - return arr[key](...args.map(toRaw)); - } else { - return res; - } - }; - }); - ["push", "pop", "shift", "unshift", "splice"].forEach((key) => { - instrumentations[key] = function(...args) { - pauseTracking(); - pauseScheduling(); - const res = toRaw(this)[key].apply(this, args); - resetScheduling(); - resetTracking(); - return res; - }; - }); - return instrumentations; -} -function hasOwnProperty(key) { - const obj = toRaw(this); - track(obj, "has", key); - return obj.hasOwnProperty(key); -} -class BaseReactiveHandler2 { - constructor(_isReadonly = false, _isShallow = false) { - this._isReadonly = _isReadonly; - this._isShallow = _isShallow; - } - get(target, key, receiver) { - const isReadonly2 = this._isReadonly, isShallow2 = this._isShallow; - if (key === "__v_isReactive") { - return !isReadonly2; - } else if (key === "__v_isReadonly") { - return isReadonly2; - } else if (key === "__v_isShallow") { - return isShallow2; - } else if (key === "__v_raw") { - if (receiver === (isReadonly2 ? isShallow2 ? shallowReadonlyMap : readonlyMap : isShallow2 ? shallowReactiveMap : reactiveMap).get(target) || // receiver is not the reactive proxy, but has the same prototype - // this means the reciever is a user proxy of the reactive proxy - Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)) { - return target; - } - return; - } - const targetIsArray = isArray(target); - if (!isReadonly2) { - if (targetIsArray && hasOwn$1(arrayInstrumentations, key)) { - return Reflect.get(arrayInstrumentations, key, receiver); - } - if (key === "hasOwnProperty") { - return hasOwnProperty; - } - } - const res = Reflect.get(target, key, receiver); - if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) { - return res; - } - if (!isReadonly2) { - track(target, "get", key); - } - if (isShallow2) { - return res; - } - if (isRef(res)) { - return targetIsArray && isIntegerKey(key) ? res : res.value; - } - if (isObject$1(res)) { - return isReadonly2 ? readonly(res) : reactive(res); - } - return res; - } -} -class MutableReactiveHandler2 extends BaseReactiveHandler2 { - constructor(isShallow2 = false) { - super(false, isShallow2); - } - set(target, key, value, receiver) { - let oldValue = target[key]; - if (!this._isShallow) { - const isOldValueReadonly = isReadonly(oldValue); - if (!isShallow(value) && !isReadonly(value)) { - oldValue = toRaw(oldValue); - value = toRaw(value); - } - if (!isArray(target) && isRef(oldValue) && !isRef(value)) { - if (isOldValueReadonly) { - return false; - } else { - oldValue.value = value; - return true; - } - } - } - const hadKey = isArray(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn$1(target, key); - const result = Reflect.set(target, key, value, receiver); - if (target === toRaw(receiver)) { - if (!hadKey) { - trigger(target, "add", key, value); - } else if (hasChanged(value, oldValue)) { - trigger(target, "set", key, value, oldValue); - } - } - return result; - } - deleteProperty(target, key) { - const hadKey = hasOwn$1(target, key); - const oldValue = target[key]; - const result = Reflect.deleteProperty(target, key); - if (result && hadKey) { - trigger(target, "delete", key, void 0, oldValue); - } - return result; - } - has(target, key) { - const result = Reflect.has(target, key); - if (!isSymbol(key) || !builtInSymbols.has(key)) { - track(target, "has", key); - } - return result; - } - ownKeys(target) { - track( - target, - "iterate", - isArray(target) ? "length" : ITERATE_KEY - ); - return Reflect.ownKeys(target); - } -} -class ReadonlyReactiveHandler2 extends BaseReactiveHandler2 { - constructor(isShallow2 = false) { - super(true, isShallow2); - } - set(target, key) { - { - warn$2( - `Set operation on key "${String(key)}" failed: target is readonly.`, - target - ); - } - return true; - } - deleteProperty(target, key) { - { - warn$2( - `Delete operation on key "${String(key)}" failed: target is readonly.`, - target - ); - } - return true; - } -} -const mutableHandlers = /* @__PURE__ */ new MutableReactiveHandler2(); -const readonlyHandlers = /* @__PURE__ */ new ReadonlyReactiveHandler2(); -const shallowReactiveHandlers = /* @__PURE__ */ new MutableReactiveHandler2( - true -); -const shallowReadonlyHandlers = /* @__PURE__ */ new ReadonlyReactiveHandler2(true); -const toShallow = (value) => value; -const getProto = (v2) => Reflect.getPrototypeOf(v2); -function get(target, key, isReadonly2 = false, isShallow2 = false) { - target = target["__v_raw"]; - const rawTarget = toRaw(target); - const rawKey = toRaw(key); - if (!isReadonly2) { - if (hasChanged(key, rawKey)) { - track(rawTarget, "get", key); - } - track(rawTarget, "get", rawKey); - } - const { has: has2 } = getProto(rawTarget); - const wrap = isShallow2 ? toShallow : isReadonly2 ? toReadonly : toReactive; - if (has2.call(rawTarget, key)) { - return wrap(target.get(key)); - } else if (has2.call(rawTarget, rawKey)) { - return wrap(target.get(rawKey)); - } else if (target !== rawTarget) { - target.get(key); - } -} -function has(key, isReadonly2 = false) { - const target = this["__v_raw"]; - const rawTarget = toRaw(target); - const rawKey = toRaw(key); - if (!isReadonly2) { - if (hasChanged(key, rawKey)) { - track(rawTarget, "has", key); - } - track(rawTarget, "has", rawKey); - } - return key === rawKey ? target.has(key) : target.has(key) || target.has(rawKey); -} -function size(target, isReadonly2 = false) { - target = target["__v_raw"]; - !isReadonly2 && track(toRaw(target), "iterate", ITERATE_KEY); - return Reflect.get(target, "size", target); -} -function add(value) { - value = toRaw(value); - const target = toRaw(this); - const proto = getProto(target); - const hadKey = proto.has.call(target, value); - if (!hadKey) { - target.add(value); - trigger(target, "add", value, value); - } - return this; -} -function set$1(key, value) { - value = toRaw(value); - const target = toRaw(this); - const { has: has2, get: get2 } = getProto(target); - let hadKey = has2.call(target, key); - if (!hadKey) { - key = toRaw(key); - hadKey = has2.call(target, key); - } else { - checkIdentityKeys(target, has2, key); - } - const oldValue = get2.call(target, key); - target.set(key, value); - if (!hadKey) { - trigger(target, "add", key, value); - } else if (hasChanged(value, oldValue)) { - trigger(target, "set", key, value, oldValue); - } - return this; -} -function deleteEntry(key) { - const target = toRaw(this); - const { has: has2, get: get2 } = getProto(target); - let hadKey = has2.call(target, key); - if (!hadKey) { - key = toRaw(key); - hadKey = has2.call(target, key); - } else { - checkIdentityKeys(target, has2, key); - } - const oldValue = get2 ? get2.call(target, key) : void 0; - const result = target.delete(key); - if (hadKey) { - trigger(target, "delete", key, void 0, oldValue); - } - return result; -} -function clear() { - const target = toRaw(this); - const hadItems = target.size !== 0; - const oldTarget = isMap(target) ? new Map(target) : new Set(target); - const result = target.clear(); - if (hadItems) { - trigger(target, "clear", void 0, void 0, oldTarget); - } - return result; -} -function createForEach(isReadonly2, isShallow2) { - return function forEach(callback, thisArg) { - const observed = this; - const target = observed["__v_raw"]; - const rawTarget = toRaw(target); - const wrap = isShallow2 ? toShallow : isReadonly2 ? toReadonly : toReactive; - !isReadonly2 && track(rawTarget, "iterate", ITERATE_KEY); - return target.forEach((value, key) => { - return callback.call(thisArg, wrap(value), wrap(key), observed); - }); - }; -} -function createIterableMethod(method, isReadonly2, isShallow2) { - return function(...args) { - const target = this["__v_raw"]; - const rawTarget = toRaw(target); - const targetIsMap = isMap(rawTarget); - const isPair = method === "entries" || method === Symbol.iterator && targetIsMap; - const isKeyOnly = method === "keys" && targetIsMap; - const innerIterator = target[method](...args); - const wrap = isShallow2 ? toShallow : isReadonly2 ? toReadonly : toReactive; - !isReadonly2 && track( - rawTarget, - "iterate", - isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY - ); - return { - // iterator protocol - next() { - const { value, done } = innerIterator.next(); - return done ? { value, done } : { - value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value), - done - }; - }, - // iterable protocol - [Symbol.iterator]() { - return this; - } - }; - }; -} -function createReadonlyMethod(type) { - return function(...args) { - { - const key = args[0] ? `on key "${args[0]}" ` : ``; - warn$2( - `${capitalize(type)} operation ${key}failed: target is readonly.`, - toRaw(this) - ); - } - return type === "delete" ? false : type === "clear" ? void 0 : this; - }; -} -function createInstrumentations() { - const mutableInstrumentations2 = { - get(key) { - return get(this, key); - }, - get size() { - return size(this); - }, - has, - add, - set: set$1, - delete: deleteEntry, - clear, - forEach: createForEach(false, false) - }; - const shallowInstrumentations2 = { - get(key) { - return get(this, key, false, true); - }, - get size() { - return size(this); - }, - has, - add, - set: set$1, - delete: deleteEntry, - clear, - forEach: createForEach(false, true) - }; - const readonlyInstrumentations2 = { - get(key) { - return get(this, key, true); - }, - get size() { - return size(this, true); - }, - has(key) { - return has.call(this, key, true); - }, - add: createReadonlyMethod("add"), - set: createReadonlyMethod("set"), - delete: createReadonlyMethod("delete"), - clear: createReadonlyMethod("clear"), - forEach: createForEach(true, false) - }; - const shallowReadonlyInstrumentations2 = { - get(key) { - return get(this, key, true, true); - }, - get size() { - return size(this, true); - }, - has(key) { - return has.call(this, key, true); - }, - add: createReadonlyMethod("add"), - set: createReadonlyMethod("set"), - delete: createReadonlyMethod("delete"), - clear: createReadonlyMethod("clear"), - forEach: createForEach(true, true) - }; - const iteratorMethods = [ - "keys", - "values", - "entries", - Symbol.iterator - ]; - iteratorMethods.forEach((method) => { - mutableInstrumentations2[method] = createIterableMethod(method, false, false); - readonlyInstrumentations2[method] = createIterableMethod(method, true, false); - shallowInstrumentations2[method] = createIterableMethod(method, false, true); - shallowReadonlyInstrumentations2[method] = createIterableMethod( - method, - true, - true - ); - }); - return [ - mutableInstrumentations2, - readonlyInstrumentations2, - shallowInstrumentations2, - shallowReadonlyInstrumentations2 - ]; -} -const [ - mutableInstrumentations, - readonlyInstrumentations, - shallowInstrumentations, - shallowReadonlyInstrumentations -] = /* @__PURE__ */ createInstrumentations(); -function createInstrumentationGetter(isReadonly2, shallow) { - const instrumentations = shallow ? isReadonly2 ? shallowReadonlyInstrumentations : shallowInstrumentations : isReadonly2 ? readonlyInstrumentations : mutableInstrumentations; - return (target, key, receiver) => { - if (key === "__v_isReactive") { - return !isReadonly2; - } else if (key === "__v_isReadonly") { - return isReadonly2; - } else if (key === "__v_raw") { - return target; - } - return Reflect.get( - hasOwn$1(instrumentations, key) && key in target ? instrumentations : target, - key, - receiver - ); - }; -} -const mutableCollectionHandlers = { - get: /* @__PURE__ */ createInstrumentationGetter(false, false) -}; -const shallowCollectionHandlers = { - get: /* @__PURE__ */ createInstrumentationGetter(false, true) -}; -const readonlyCollectionHandlers = { - get: /* @__PURE__ */ createInstrumentationGetter(true, false) -}; -const shallowReadonlyCollectionHandlers = { - get: /* @__PURE__ */ createInstrumentationGetter(true, true) -}; -function checkIdentityKeys(target, has2, key) { - const rawKey = toRaw(key); - if (rawKey !== key && has2.call(target, rawKey)) { - const type = toRawType(target); - warn$2( - `Reactive ${type} contains both the raw and reactive versions of the same object${type === `Map` ? ` as keys` : ``}, which can lead to inconsistencies. Avoid differentiating between the raw and reactive versions of an object and only use the reactive version if possible.` - ); - } -} -const reactiveMap = /* @__PURE__ */ new WeakMap(); -const shallowReactiveMap = /* @__PURE__ */ new WeakMap(); -const readonlyMap = /* @__PURE__ */ new WeakMap(); -const shallowReadonlyMap = /* @__PURE__ */ new WeakMap(); -function targetTypeMap(rawType) { - switch (rawType) { - case "Object": - case "Array": - return 1; - case "Map": - case "Set": - case "WeakMap": - case "WeakSet": - return 2; - default: - return 0; - } -} -function getTargetType(value) { - return value["__v_skip"] || !Object.isExtensible(value) ? 0 : targetTypeMap(toRawType(value)); -} -function reactive(target) { - if (isReadonly(target)) { - return target; - } - return createReactiveObject( - target, - false, - mutableHandlers, - mutableCollectionHandlers, - reactiveMap - ); -} -function shallowReactive(target) { - return createReactiveObject( - target, - false, - shallowReactiveHandlers, - shallowCollectionHandlers, - shallowReactiveMap - ); -} -function readonly(target) { - return createReactiveObject( - target, - true, - readonlyHandlers, - readonlyCollectionHandlers, - readonlyMap - ); -} -function shallowReadonly(target) { - return createReactiveObject( - target, - true, - shallowReadonlyHandlers, - shallowReadonlyCollectionHandlers, - shallowReadonlyMap - ); -} -function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) { - if (!isObject$1(target)) { - { - warn$2(`value cannot be made reactive: ${String(target)}`); - } - return target; - } - if (target["__v_raw"] && !(isReadonly2 && target["__v_isReactive"])) { - return target; - } - const existingProxy = proxyMap.get(target); - if (existingProxy) { - return existingProxy; - } - const targetType = getTargetType(target); - if (targetType === 0) { - return target; - } - const proxy = new Proxy( - target, - targetType === 2 ? collectionHandlers : baseHandlers - ); - proxyMap.set(target, proxy); - return proxy; -} -function isReactive(value) { - if (isReadonly(value)) { - return isReactive(value["__v_raw"]); - } - return !!(value && value["__v_isReactive"]); -} -function isReadonly(value) { - return !!(value && value["__v_isReadonly"]); -} -function isShallow(value) { - return !!(value && value["__v_isShallow"]); -} -function isProxy(value) { - return isReactive(value) || isReadonly(value); -} -function toRaw(observed) { - const raw = observed && observed["__v_raw"]; - return raw ? toRaw(raw) : observed; -} -function markRaw(value) { - if (Object.isExtensible(value)) { - def(value, "__v_skip", true); - } - return value; -} -const toReactive = (value) => isObject$1(value) ? reactive(value) : value; -const toReadonly = (value) => isObject$1(value) ? readonly(value) : value; -const COMPUTED_SIDE_EFFECT_WARN = `Computed is still dirty after getter evaluation, likely because a computed is mutating its own dependency in its getter. State mutations in computed getters should be avoided. Check the docs for more details: https://vuejs.org/guide/essentials/computed.html#getters-should-be-side-effect-free`; -class ComputedRefImpl { - constructor(getter, _setter, isReadonly2, isSSR) { - this.getter = getter; - this._setter = _setter; - this.dep = void 0; - this.__v_isRef = true; - this["__v_isReadonly"] = false; - this.effect = new ReactiveEffect2( - () => getter(this._value), - () => triggerRefValue( - this, - this.effect._dirtyLevel === 2 ? 2 : 3 - ) - ); - this.effect.computed = this; - this.effect.active = this._cacheable = !isSSR; - this["__v_isReadonly"] = isReadonly2; - } - get value() { - const self2 = toRaw(this); - if ((!self2._cacheable || self2.effect.dirty) && hasChanged(self2._value, self2._value = self2.effect.run())) { - triggerRefValue(self2, 4); - } - trackRefValue(self2); - if (self2.effect._dirtyLevel >= 2) { - if (this._warnRecursive) { - warn$2(COMPUTED_SIDE_EFFECT_WARN, ` - -getter: `, this.getter); - } - triggerRefValue(self2, 2); - } - return self2._value; - } - set value(newValue) { - this._setter(newValue); - } - // #region polyfill _dirty for backward compatibility third party code for Vue <= 3.3.x - get _dirty() { - return this.effect.dirty; - } - set _dirty(v2) { - this.effect.dirty = v2; - } - // #endregion -} -function computed$1(getterOrOptions, debugOptions, isSSR = false) { - let getter; - let setter; - const onlyGetter = isFunction(getterOrOptions); - if (onlyGetter) { - getter = getterOrOptions; - setter = () => { - warn$2("Write operation failed: computed value is readonly"); - }; - } else { - getter = getterOrOptions.get; - setter = getterOrOptions.set; - } - const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR); - if (debugOptions && !isSSR) { - cRef.effect.onTrack = debugOptions.onTrack; - cRef.effect.onTrigger = debugOptions.onTrigger; - } - return cRef; -} -function trackRefValue(ref2) { - var _a; - if (shouldTrack && activeEffect) { - ref2 = toRaw(ref2); - trackEffect( - activeEffect, - (_a = ref2.dep) != null ? _a : ref2.dep = createDep( - () => ref2.dep = void 0, - ref2 instanceof ComputedRefImpl ? ref2 : void 0 - ), - { - target: ref2, - type: "get", - key: "value" - } - ); - } -} -function triggerRefValue(ref2, dirtyLevel = 4, newVal) { - ref2 = toRaw(ref2); - const dep = ref2.dep; - if (dep) { - triggerEffects( - dep, - dirtyLevel, - { - target: ref2, - type: "set", - key: "value", - newValue: newVal - } - ); - } -} -function isRef(r2) { - return !!(r2 && r2.__v_isRef === true); -} -function ref(value) { - return createRef(value, false); -} -function createRef(rawValue, shallow) { - if (isRef(rawValue)) { - return rawValue; - } - return new RefImpl(rawValue, shallow); -} -class RefImpl { - constructor(value, __v_isShallow) { - this.__v_isShallow = __v_isShallow; - this.dep = void 0; - this.__v_isRef = true; - this._rawValue = __v_isShallow ? value : toRaw(value); - this._value = __v_isShallow ? value : toReactive(value); - } - get value() { - trackRefValue(this); - return this._value; - } - set value(newVal) { - const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal); - newVal = useDirectValue ? newVal : toRaw(newVal); - if (hasChanged(newVal, this._rawValue)) { - this._rawValue = newVal; - this._value = useDirectValue ? newVal : toReactive(newVal); - triggerRefValue(this, 4, newVal); - } - } -} -function unref(ref2) { - return isRef(ref2) ? ref2.value : ref2; -} -const shallowUnwrapHandlers = { - get: (target, key, receiver) => unref(Reflect.get(target, key, receiver)), - set: (target, key, value, receiver) => { - const oldValue = target[key]; - if (isRef(oldValue) && !isRef(value)) { - oldValue.value = value; - return true; - } else { - return Reflect.set(target, key, value, receiver); - } - } -}; -function proxyRefs(objectWithRefs) { - return isReactive(objectWithRefs) ? objectWithRefs : new Proxy(objectWithRefs, shallowUnwrapHandlers); -} -const stack = []; -function pushWarningContext(vnode) { - stack.push(vnode); -} -function popWarningContext() { - stack.pop(); -} -function warn$1(msg, ...args) { - pauseTracking(); - const instance = stack.length ? stack[stack.length - 1].component : null; - const appWarnHandler = instance && instance.appContext.config.warnHandler; - const trace = getComponentTrace(); - if (appWarnHandler) { - callWithErrorHandling( - appWarnHandler, - instance, - 11, - [ - msg + args.map((a2) => { - var _a, _b; - return (_b = (_a = a2.toString) == null ? void 0 : _a.call(a2)) != null ? _b : JSON.stringify(a2); - }).join(""), - instance && instance.proxy, - trace.map( - ({ vnode }) => `at <${formatComponentName(instance, vnode.type)}>` - ).join("\n"), - trace - ] - ); - } else { - const warnArgs = [`[Vue warn]: ${msg}`, ...args]; - if (trace.length && // avoid spamming console during tests - true) { - warnArgs.push(` -`, ...formatTrace(trace)); - } - console.warn(...warnArgs); - } - resetTracking(); -} -function getComponentTrace() { - let currentVNode = stack[stack.length - 1]; - if (!currentVNode) { - return []; - } - const normalizedStack = []; - while (currentVNode) { - const last = normalizedStack[0]; - if (last && last.vnode === currentVNode) { - last.recurseCount++; - } else { - normalizedStack.push({ - vnode: currentVNode, - recurseCount: 0 - }); - } - const parentInstance = currentVNode.component && currentVNode.component.parent; - currentVNode = parentInstance && parentInstance.vnode; - } - return normalizedStack; -} -function formatTrace(trace) { - const logs = []; - trace.forEach((entry, i2) => { - logs.push(...i2 === 0 ? [] : [` -`], ...formatTraceEntry(entry)); - }); - return logs; -} -function formatTraceEntry({ vnode, recurseCount }) { - const postfix = recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``; - const isRoot = vnode.component ? vnode.component.parent == null : false; - const open = ` at <${formatComponentName( - vnode.component, - vnode.type, - isRoot - )}`; - const close = `>` + postfix; - return vnode.props ? [open, ...formatProps(vnode.props), close] : [open + close]; -} -function formatProps(props) { - const res = []; - const keys = Object.keys(props); - keys.slice(0, 3).forEach((key) => { - res.push(...formatProp(key, props[key])); - }); - if (keys.length > 3) { - res.push(` ...`); - } - return res; -} -function formatProp(key, value, raw) { - if (isString(value)) { - value = JSON.stringify(value); - return raw ? value : [`${key}=${value}`]; - } else if (typeof value === "number" || typeof value === "boolean" || value == null) { - return raw ? value : [`${key}=${value}`]; - } else if (isRef(value)) { - value = formatProp(key, toRaw(value.value), true); - return raw ? value : [`${key}=Ref<`, value, `>`]; - } else if (isFunction(value)) { - return [`${key}=fn${value.name ? `<${value.name}>` : ``}`]; - } else { - value = toRaw(value); - return raw ? value : [`${key}=`, value]; - } -} -const ErrorTypeStrings = { - ["sp"]: "serverPrefetch hook", - ["bc"]: "beforeCreate hook", - ["c"]: "created hook", - ["bm"]: "beforeMount hook", - ["m"]: "mounted hook", - ["bu"]: "beforeUpdate hook", - ["u"]: "updated", - ["bum"]: "beforeUnmount hook", - ["um"]: "unmounted hook", - ["a"]: "activated hook", - ["da"]: "deactivated hook", - ["ec"]: "errorCaptured hook", - ["rtc"]: "renderTracked hook", - ["rtg"]: "renderTriggered hook", - [0]: "setup function", - [1]: "render function", - [2]: "watcher getter", - [3]: "watcher callback", - [4]: "watcher cleanup function", - [5]: "native event handler", - [6]: "component event handler", - [7]: "vnode hook", - [8]: "directive hook", - [9]: "transition hook", - [10]: "app errorHandler", - [11]: "app warnHandler", - [12]: "ref function", - [13]: "async component loader", - [14]: "scheduler flush. This is likely a Vue internals bug. Please open an issue at https://github.com/vuejs/core ." -}; -function callWithErrorHandling(fn, instance, type, args) { - try { - return args ? fn(...args) : fn(); - } catch (err) { - handleError(err, instance, type); - } -} -function callWithAsyncErrorHandling(fn, instance, type, args) { - if (isFunction(fn)) { - const res = callWithErrorHandling(fn, instance, type, args); - if (res && isPromise(res)) { - res.catch((err) => { - handleError(err, instance, type); - }); - } - return res; - } - const values = []; - for (let i2 = 0; i2 < fn.length; i2++) { - values.push(callWithAsyncErrorHandling(fn[i2], instance, type, args)); - } - return values; -} -function handleError(err, instance, type, throwInDev = true) { - const contextVNode = instance ? instance.vnode : null; - if (instance) { - let cur = instance.parent; - const exposedInstance = instance.proxy; - const errorInfo = ErrorTypeStrings[type] || type; - while (cur) { - const errorCapturedHooks = cur.ec; - if (errorCapturedHooks) { - for (let i2 = 0; i2 < errorCapturedHooks.length; i2++) { - if (errorCapturedHooks[i2](err, exposedInstance, errorInfo) === false) { - return; - } - } - } - cur = cur.parent; - } - const appErrorHandler = instance.appContext.config.errorHandler; - if (appErrorHandler) { - callWithErrorHandling( - appErrorHandler, - null, - 10, - [err, exposedInstance, errorInfo] - ); - return; - } - } - logError(err, type, contextVNode, throwInDev); -} -function logError(err, type, contextVNode, throwInDev = true) { - { - const info = ErrorTypeStrings[type] || type; - if (contextVNode) { - pushWarningContext(contextVNode); - } - warn$1(`Unhandled error${info ? ` during execution of ${info}` : ``}`); - if (contextVNode) { - popWarningContext(); - } - if (throwInDev) { - console.error(err); - } else { - console.error(err); - } - } -} -let isFlushing = false; -let isFlushPending = false; -const queue = []; -let flushIndex = 0; -const pendingPostFlushCbs = []; -let activePostFlushCbs = null; -let postFlushIndex = 0; -const resolvedPromise = /* @__PURE__ */ Promise.resolve(); -let currentFlushPromise = null; -const RECURSION_LIMIT = 100; -function nextTick$1(fn) { - const p2 = currentFlushPromise || resolvedPromise; - return fn ? p2.then(this ? fn.bind(this) : fn) : p2; -} -function findInsertionIndex(id) { - let start = flushIndex + 1; - let end = queue.length; - while (start < end) { - const middle = start + end >>> 1; - const middleJob = queue[middle]; - const middleJobId = getId(middleJob); - if (middleJobId < id || middleJobId === id && middleJob.pre) { - start = middle + 1; - } else { - end = middle; - } - } - return start; -} -function queueJob(job) { - if (!queue.length || !queue.includes( - job, - isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex - )) { - if (job.id == null) { - queue.push(job); - } else { - queue.splice(findInsertionIndex(job.id), 0, job); - } - queueFlush(); - } -} -function queueFlush() { - if (!isFlushing && !isFlushPending) { - isFlushPending = true; - currentFlushPromise = resolvedPromise.then(flushJobs); - } -} -function hasQueueJob(job) { - return queue.indexOf(job) > -1; -} -function invalidateJob(job) { - const i2 = queue.indexOf(job); - if (i2 > flushIndex) { - queue.splice(i2, 1); - } -} -function queuePostFlushCb(cb) { - if (!isArray(cb)) { - if (!activePostFlushCbs || !activePostFlushCbs.includes( - cb, - cb.allowRecurse ? postFlushIndex + 1 : postFlushIndex - )) { - pendingPostFlushCbs.push(cb); - } - } else { - pendingPostFlushCbs.push(...cb); - } - queueFlush(); -} -function flushPreFlushCbs(instance, seen, i2 = isFlushing ? flushIndex + 1 : 0) { - { - seen = seen || /* @__PURE__ */ new Map(); - } - for (; i2 < queue.length; i2++) { - const cb = queue[i2]; - if (cb && cb.pre) { - if (instance && cb.id !== instance.uid) { - continue; - } - if (checkRecursiveUpdates(seen, cb)) { - continue; - } - queue.splice(i2, 1); - i2--; - cb(); - } - } -} -function flushPostFlushCbs(seen) { - if (pendingPostFlushCbs.length) { - const deduped = [...new Set(pendingPostFlushCbs)].sort( - (a2, b2) => getId(a2) - getId(b2) - ); - pendingPostFlushCbs.length = 0; - if (activePostFlushCbs) { - activePostFlushCbs.push(...deduped); - return; - } - activePostFlushCbs = deduped; - { - seen = seen || /* @__PURE__ */ new Map(); - } - for (postFlushIndex = 0; postFlushIndex < activePostFlushCbs.length; postFlushIndex++) { - if (checkRecursiveUpdates(seen, activePostFlushCbs[postFlushIndex])) { - continue; - } - activePostFlushCbs[postFlushIndex](); - } - activePostFlushCbs = null; - postFlushIndex = 0; - } -} -const getId = (job) => job.id == null ? Infinity : job.id; -const comparator = (a2, b2) => { - const diff2 = getId(a2) - getId(b2); - if (diff2 === 0) { - if (a2.pre && !b2.pre) - return -1; - if (b2.pre && !a2.pre) - return 1; - } - return diff2; -}; -function flushJobs(seen) { - isFlushPending = false; - isFlushing = true; - { - seen = seen || /* @__PURE__ */ new Map(); - } - queue.sort(comparator); - const check = (job) => checkRecursiveUpdates(seen, job); - try { - for (flushIndex = 0; flushIndex < queue.length; flushIndex++) { - const job = queue[flushIndex]; - if (job && job.active !== false) { - if (check(job)) { - continue; - } - callWithErrorHandling(job, null, 14); - } - } - } finally { - flushIndex = 0; - queue.length = 0; - flushPostFlushCbs(seen); - isFlushing = false; - currentFlushPromise = null; - if (queue.length || pendingPostFlushCbs.length) { - flushJobs(seen); - } - } -} -function checkRecursiveUpdates(seen, fn) { - if (!seen.has(fn)) { - seen.set(fn, 1); - } else { - const count = seen.get(fn); - if (count > RECURSION_LIMIT) { - const instance = fn.ownerInstance; - const componentName = instance && getComponentName(instance.type); - handleError( - `Maximum recursive updates exceeded${componentName ? ` in component <${componentName}>` : ``}. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.`, - null, - 10 - ); - return true; - } else { - seen.set(fn, count + 1); - } - } -} -let devtools; -let buffer = []; -let devtoolsNotInstalled = false; -function emit$1(event, ...args) { - if (devtools) { - devtools.emit(event, ...args); - } else if (!devtoolsNotInstalled) { - buffer.push({ event, args }); - } -} -function setDevtoolsHook(hook, target) { - var _a, _b; - devtools = hook; - if (devtools) { - devtools.enabled = true; - buffer.forEach(({ event, args }) => devtools.emit(event, ...args)); - buffer = []; - } else if ( - // handle late devtools injection - only do this if we are in an actual - // browser environment to avoid the timer handle stalling test runner exit - // (#4815) - typeof window !== "undefined" && // some envs mock window but not fully - window.HTMLElement && // also exclude jsdom - !((_b = (_a = window.navigator) == null ? void 0 : _a.userAgent) == null ? void 0 : _b.includes("jsdom")) - ) { - const replay = target.__VUE_DEVTOOLS_HOOK_REPLAY__ = target.__VUE_DEVTOOLS_HOOK_REPLAY__ || []; - replay.push((newHook) => { - setDevtoolsHook(newHook, target); - }); - setTimeout(() => { - if (!devtools) { - target.__VUE_DEVTOOLS_HOOK_REPLAY__ = null; - devtoolsNotInstalled = true; - buffer = []; - } - }, 3e3); - } else { - devtoolsNotInstalled = true; - buffer = []; - } -} -function devtoolsInitApp(app, version2) { - emit$1("app:init", app, version2, { - Fragment, - Text, - Comment, - Static - }); -} -const devtoolsComponentAdded = /* @__PURE__ */ createDevtoolsComponentHook( - "component:added" - /* COMPONENT_ADDED */ -); -const devtoolsComponentUpdated = /* @__PURE__ */ createDevtoolsComponentHook( - "component:updated" - /* COMPONENT_UPDATED */ -); -const _devtoolsComponentRemoved = /* @__PURE__ */ createDevtoolsComponentHook( - "component:removed" - /* COMPONENT_REMOVED */ -); -const devtoolsComponentRemoved = (component) => { - if (devtools && typeof devtools.cleanupBuffer === "function" && // remove the component if it wasn't buffered - !devtools.cleanupBuffer(component)) { - _devtoolsComponentRemoved(component); - } -}; -/*! #__NO_SIDE_EFFECTS__ */ -// @__NO_SIDE_EFFECTS__ -function createDevtoolsComponentHook(hook) { - return (component) => { - emit$1( - hook, - component.appContext.app, - component.uid, - // fixed by xxxxxx - // 为 0 是 App,无 parent 是 Page 指向 App - component.uid === 0 ? void 0 : component.parent ? component.parent.uid : 0, - component - ); - }; -} -const devtoolsPerfStart = /* @__PURE__ */ createDevtoolsPerformanceHook( - "perf:start" - /* PERFORMANCE_START */ -); -const devtoolsPerfEnd = /* @__PURE__ */ createDevtoolsPerformanceHook( - "perf:end" - /* PERFORMANCE_END */ -); -function createDevtoolsPerformanceHook(hook) { - return (component, type, time) => { - emit$1(hook, component.appContext.app, component.uid, component, type, time); - }; -} -function devtoolsComponentEmit(component, event, params) { - emit$1( - "component:emit", - component.appContext.app, - component, - event, - params - ); -} -function emit(instance, event, ...rawArgs) { - if (instance.isUnmounted) - return; - const props = instance.vnode.props || EMPTY_OBJ; - { - const { - emitsOptions, - propsOptions: [propsOptions] - } = instance; - if (emitsOptions) { - if (!(event in emitsOptions) && true) { - if (!propsOptions || !(toHandlerKey(event) in propsOptions)) { - warn$1( - `Component emitted event "${event}" but it is neither declared in the emits option nor as an "${toHandlerKey(event)}" prop.` - ); - } - } else { - const validator = emitsOptions[event]; - if (isFunction(validator)) { - const isValid = validator(...rawArgs); - if (!isValid) { - warn$1( - `Invalid event arguments: event validation failed for event "${event}".` - ); - } - } - } - } - } - let args = rawArgs; - const isModelListener2 = event.startsWith("update:"); - const modelArg = isModelListener2 && event.slice(7); - if (modelArg && modelArg in props) { - const modifiersKey = `${modelArg === "modelValue" ? "model" : modelArg}Modifiers`; - const { number, trim } = props[modifiersKey] || EMPTY_OBJ; - if (trim) { - args = rawArgs.map((a2) => isString(a2) ? a2.trim() : a2); - } - if (number) { - args = rawArgs.map(looseToNumber); - } - } - { - devtoolsComponentEmit(instance, event, args); - } - { - const lowerCaseEvent = event.toLowerCase(); - if (lowerCaseEvent !== event && props[toHandlerKey(lowerCaseEvent)]) { - warn$1( - `Event "${lowerCaseEvent}" is emitted in component ${formatComponentName( - instance, - instance.type - )} but the handler is registered for "${event}". Note that HTML attributes are case-insensitive and you cannot use v-on to listen to camelCase events when using in-DOM templates. You should probably use "${hyphenate( - event - )}" instead of "${event}".` - ); - } - } - let handlerName; - let handler = props[handlerName = toHandlerKey(event)] || // also try camelCase event handler (#2249) - props[handlerName = toHandlerKey(camelize(event))]; - if (!handler && isModelListener2) { - handler = props[handlerName = toHandlerKey(hyphenate(event))]; - } - if (handler) { - callWithAsyncErrorHandling( - handler, - instance, - 6, - args - ); - } - const onceHandler = props[handlerName + `Once`]; - if (onceHandler) { - if (!instance.emitted) { - instance.emitted = {}; - } else if (instance.emitted[handlerName]) { - return; - } - instance.emitted[handlerName] = true; - callWithAsyncErrorHandling( - onceHandler, - instance, - 6, - args - ); - } -} -function normalizeEmitsOptions(comp, appContext, asMixin = false) { - const cache = appContext.emitsCache; - const cached = cache.get(comp); - if (cached !== void 0) { - return cached; - } - const raw = comp.emits; - let normalized = {}; - let hasExtends = false; - if (!isFunction(comp)) { - const extendEmits = (raw2) => { - const normalizedFromExtend = normalizeEmitsOptions(raw2, appContext, true); - if (normalizedFromExtend) { - hasExtends = true; - extend(normalized, normalizedFromExtend); - } - }; - if (!asMixin && appContext.mixins.length) { - appContext.mixins.forEach(extendEmits); - } - if (comp.extends) { - extendEmits(comp.extends); - } - if (comp.mixins) { - comp.mixins.forEach(extendEmits); - } - } - if (!raw && !hasExtends) { - if (isObject$1(comp)) { - cache.set(comp, null); - } - return null; - } - if (isArray(raw)) { - raw.forEach((key) => normalized[key] = null); - } else { - extend(normalized, raw); - } - if (isObject$1(comp)) { - cache.set(comp, normalized); - } - return normalized; -} -function isEmitListener(options, key) { - if (!options || !isOn(key)) { - return false; - } - key = key.slice(2).replace(/Once$/, ""); - return hasOwn$1(options, key[0].toLowerCase() + key.slice(1)) || hasOwn$1(options, hyphenate(key)) || hasOwn$1(options, key); -} -let currentRenderingInstance = null; -function setCurrentRenderingInstance(instance) { - const prev = currentRenderingInstance; - currentRenderingInstance = instance; - instance && instance.type.__scopeId || null; - return prev; -} -const COMPONENTS = "components"; -function resolveComponent(name, maybeSelfReference) { - return resolveAsset(COMPONENTS, name, true, maybeSelfReference) || name; -} -function resolveAsset(type, name, warnMissing = true, maybeSelfReference = false) { - const instance = currentRenderingInstance || currentInstance; - if (instance) { - const Component2 = instance.type; - if (type === COMPONENTS) { - const selfName = getComponentName( - Component2, - false - ); - if (selfName && (selfName === name || selfName === camelize(name) || selfName === capitalize(camelize(name)))) { - return Component2; - } - } - const res = ( - // local registration - // check instance[type] first which is resolved for options API - resolve(instance[type] || Component2[type], name) || // global registration - resolve(instance.appContext[type], name) - ); - if (!res && maybeSelfReference) { - return Component2; - } - if (warnMissing && !res) { - const extra = type === COMPONENTS ? ` -If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.` : ``; - warn$1(`Failed to resolve ${type.slice(0, -1)}: ${name}${extra}`); - } - return res; - } else { - warn$1( - `resolve${capitalize(type.slice(0, -1))} can only be used in render() or setup().` - ); - } -} -function resolve(registry, name) { - return registry && (registry[name] || registry[camelize(name)] || registry[capitalize(camelize(name))]); -} -const INITIAL_WATCHER_VALUE = {}; -function watch(source, cb, options) { - if (!isFunction(cb)) { - warn$1( - `\`watch(fn, options?)\` signature has been moved to a separate API. Use \`watchEffect(fn, options?)\` instead. \`watch\` now only supports \`watch(source, cb, options?) signature.` - ); - } - return doWatch(source, cb, options); -} -function doWatch(source, cb, { - immediate, - deep, - flush, - once: once2, - onTrack, - onTrigger -} = EMPTY_OBJ) { - if (cb && once2) { - const _cb = cb; - cb = (...args) => { - _cb(...args); - unwatch(); - }; - } - if (deep !== void 0 && typeof deep === "number") { - warn$1( - `watch() "deep" option with number value will be used as watch depth in future versions. Please use a boolean instead to avoid potential breakage.` - ); - } - if (!cb) { - if (immediate !== void 0) { - warn$1( - `watch() "immediate" option is only respected when using the watch(source, callback, options?) signature.` - ); - } - if (deep !== void 0) { - warn$1( - `watch() "deep" option is only respected when using the watch(source, callback, options?) signature.` - ); - } - if (once2 !== void 0) { - warn$1( - `watch() "once" option is only respected when using the watch(source, callback, options?) signature.` - ); - } - } - const warnInvalidSource = (s2) => { - warn$1( - `Invalid watch source: `, - s2, - `A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.` - ); - }; - const instance = currentInstance; - const reactiveGetter = (source2) => deep === true ? source2 : ( - // for deep: false, only traverse root-level properties - traverse(source2, deep === false ? 1 : void 0) - ); - let getter; - let forceTrigger = false; - let isMultiSource = false; - if (isRef(source)) { - getter = () => source.value; - forceTrigger = isShallow(source); - } else if (isReactive(source)) { - getter = () => reactiveGetter(source); - forceTrigger = true; - } else if (isArray(source)) { - isMultiSource = true; - forceTrigger = source.some((s2) => isReactive(s2) || isShallow(s2)); - getter = () => source.map((s2) => { - if (isRef(s2)) { - return s2.value; - } else if (isReactive(s2)) { - return reactiveGetter(s2); - } else if (isFunction(s2)) { - return callWithErrorHandling(s2, instance, 2); - } else { - warnInvalidSource(s2); - } - }); - } else if (isFunction(source)) { - if (cb) { - getter = () => callWithErrorHandling(source, instance, 2); - } else { - getter = () => { - if (cleanup) { - cleanup(); - } - return callWithAsyncErrorHandling( - source, - instance, - 3, - [onCleanup] - ); - }; - } - } else { - getter = NOOP; - warnInvalidSource(source); - } - if (cb && deep) { - const baseGetter = getter; - getter = () => traverse(baseGetter()); - } - let cleanup; - let onCleanup = (fn) => { - cleanup = effect2.onStop = () => { - callWithErrorHandling(fn, instance, 4); - cleanup = effect2.onStop = void 0; - }; - }; - let oldValue = isMultiSource ? new Array(source.length).fill(INITIAL_WATCHER_VALUE) : INITIAL_WATCHER_VALUE; - const job = () => { - if (!effect2.active || !effect2.dirty) { - return; - } - if (cb) { - const newValue = effect2.run(); - if (deep || forceTrigger || (isMultiSource ? newValue.some((v2, i2) => hasChanged(v2, oldValue[i2])) : hasChanged(newValue, oldValue)) || false) { - if (cleanup) { - cleanup(); - } - callWithAsyncErrorHandling(cb, instance, 3, [ - newValue, - // pass undefined as the old value when it's changed for the first time - oldValue === INITIAL_WATCHER_VALUE ? void 0 : isMultiSource && oldValue[0] === INITIAL_WATCHER_VALUE ? [] : oldValue, - onCleanup - ]); - oldValue = newValue; - } - } else { - effect2.run(); - } - }; - job.allowRecurse = !!cb; - let scheduler; - if (flush === "sync") { - scheduler = job; - } else if (flush === "post") { - scheduler = () => queuePostRenderEffect$1(job, instance && instance.suspense); - } else { - job.pre = true; - if (instance) - job.id = instance.uid; - scheduler = () => queueJob(job); - } - const effect2 = new ReactiveEffect2(getter, NOOP, scheduler); - const scope = getCurrentScope(); - const unwatch = () => { - effect2.stop(); - if (scope) { - remove(scope.effects, effect2); - } - }; - { - effect2.onTrack = onTrack; - effect2.onTrigger = onTrigger; - } - if (cb) { - if (immediate) { - job(); - } else { - oldValue = effect2.run(); - } - } else if (flush === "post") { - queuePostRenderEffect$1( - effect2.run.bind(effect2), - instance && instance.suspense - ); - } else { - effect2.run(); - } - return unwatch; -} -function instanceWatch(source, value, options) { - const publicThis = this.proxy; - const getter = isString(source) ? source.includes(".") ? createPathGetter(publicThis, source) : () => publicThis[source] : source.bind(publicThis, publicThis); - let cb; - if (isFunction(value)) { - cb = value; - } else { - cb = value.handler; - options = value; - } - const reset = setCurrentInstance(this); - const res = doWatch(getter, cb.bind(publicThis), options); - reset(); - return res; -} -function createPathGetter(ctx, path) { - const segments = path.split("."); - return () => { - let cur = ctx; - for (let i2 = 0; i2 < segments.length && cur; i2++) { - cur = cur[segments[i2]]; - } - return cur; - }; -} -function traverse(value, depth, currentDepth = 0, seen) { - if (!isObject$1(value) || value["__v_skip"]) { - return value; - } - if (depth && depth > 0) { - if (currentDepth >= depth) { - return value; - } - currentDepth++; - } - seen = seen || /* @__PURE__ */ new Set(); - if (seen.has(value)) { - return value; - } - seen.add(value); - if (isRef(value)) { - traverse(value.value, depth, currentDepth, seen); - } else if (isArray(value)) { - for (let i2 = 0; i2 < value.length; i2++) { - traverse(value[i2], depth, currentDepth, seen); - } - } else if (isSet(value) || isMap(value)) { - value.forEach((v2) => { - traverse(v2, depth, currentDepth, seen); - }); - } else if (isPlainObject(value)) { - for (const key in value) { - traverse(value[key], depth, currentDepth, seen); - } - } - return value; -} -function validateDirectiveName(name) { - if (isBuiltInDirective(name)) { - warn$1("Do not use built-in directive ids as custom directive id: " + name); - } -} -function createAppContext() { - return { - app: null, - config: { - isNativeTag: NO, - performance: false, - globalProperties: {}, - optionMergeStrategies: {}, - errorHandler: void 0, - warnHandler: void 0, - compilerOptions: {} - }, - mixins: [], - components: {}, - directives: {}, - provides: /* @__PURE__ */ Object.create(null), - optionsCache: /* @__PURE__ */ new WeakMap(), - propsCache: /* @__PURE__ */ new WeakMap(), - emitsCache: /* @__PURE__ */ new WeakMap() - }; -} -let uid$1 = 0; -function createAppAPI(render, hydrate) { - return function createApp2(rootComponent, rootProps = null) { - if (!isFunction(rootComponent)) { - rootComponent = extend({}, rootComponent); - } - if (rootProps != null && !isObject$1(rootProps)) { - warn$1(`root props passed to app.mount() must be an object.`); - rootProps = null; - } - const context = createAppContext(); - const installedPlugins = /* @__PURE__ */ new WeakSet(); - const app = context.app = { - _uid: uid$1++, - _component: rootComponent, - _props: rootProps, - _container: null, - _context: context, - _instance: null, - version, - get config() { - return context.config; - }, - set config(v2) { - { - warn$1( - `app.config cannot be replaced. Modify individual options instead.` - ); - } - }, - use(plugin2, ...options) { - if (installedPlugins.has(plugin2)) { - warn$1(`Plugin has already been applied to target app.`); - } else if (plugin2 && isFunction(plugin2.install)) { - installedPlugins.add(plugin2); - plugin2.install(app, ...options); - } else if (isFunction(plugin2)) { - installedPlugins.add(plugin2); - plugin2(app, ...options); - } else { - warn$1( - `A plugin must either be a function or an object with an "install" function.` - ); - } - return app; - }, - mixin(mixin) { - { - if (!context.mixins.includes(mixin)) { - context.mixins.push(mixin); - } else { - warn$1( - "Mixin has already been applied to target app" + (mixin.name ? `: ${mixin.name}` : "") - ); - } - } - return app; - }, - component(name, component) { - { - validateComponentName(name, context.config); - } - if (!component) { - return context.components[name]; - } - if (context.components[name]) { - warn$1(`Component "${name}" has already been registered in target app.`); - } - context.components[name] = component; - return app; - }, - directive(name, directive) { - { - validateDirectiveName(name); - } - if (!directive) { - return context.directives[name]; - } - if (context.directives[name]) { - warn$1(`Directive "${name}" has already been registered in target app.`); - } - context.directives[name] = directive; - return app; - }, - // fixed by xxxxxx - mount() { - }, - // fixed by xxxxxx - unmount() { - }, - provide(key, value) { - if (key in context.provides) { - warn$1( - `App already provides property with key "${String(key)}". It will be overwritten with the new value.` - ); - } - context.provides[key] = value; - return app; - }, - runWithContext(fn) { - const lastApp = currentApp; - currentApp = app; - try { - return fn(); - } finally { - currentApp = lastApp; - } - } - }; - return app; - }; -} -let currentApp = null; -function provide(key, value) { - if (!currentInstance) { - { - warn$1(`provide() can only be used inside setup().`); - } - } else { - let provides = currentInstance.provides; - const parentProvides = currentInstance.parent && currentInstance.parent.provides; - if (parentProvides === provides) { - provides = currentInstance.provides = Object.create(parentProvides); - } - provides[key] = value; - if (currentInstance.type.mpType === "app") { - currentInstance.appContext.app.provide(key, value); - } - } -} -function inject(key, defaultValue, treatDefaultAsFactory = false) { - const instance = currentInstance || currentRenderingInstance; - if (instance || currentApp) { - const provides = instance ? instance.parent == null ? instance.vnode.appContext && instance.vnode.appContext.provides : instance.parent.provides : currentApp._context.provides; - if (provides && key in provides) { - return provides[key]; - } else if (arguments.length > 1) { - return treatDefaultAsFactory && isFunction(defaultValue) ? defaultValue.call(instance && instance.proxy) : defaultValue; - } else { - warn$1(`injection "${String(key)}" not found.`); - } - } else { - warn$1(`inject() can only be used inside setup() or functional components.`); - } -} -/*! #__NO_SIDE_EFFECTS__ */ -// @__NO_SIDE_EFFECTS__ -function defineComponent(options, extraOptions) { - return isFunction(options) ? ( - // #8326: extend call and options.name access are considered side-effects - // by Rollup, so we have to wrap it in a pure-annotated IIFE. - /* @__PURE__ */ (() => extend({ name: options.name }, extraOptions, { setup: options }))() - ) : options; -} -const isKeepAlive = (vnode) => vnode.type.__isKeepAlive; -function onActivated(hook, target) { - registerKeepAliveHook(hook, "a", target); -} -function onDeactivated(hook, target) { - registerKeepAliveHook(hook, "da", target); -} -function registerKeepAliveHook(hook, type, target = currentInstance) { - const wrappedHook = hook.__wdc || (hook.__wdc = () => { - let current = target; - while (current) { - if (current.isDeactivated) { - return; - } - current = current.parent; - } - return hook(); - }); - injectHook(type, wrappedHook, target); - if (target) { - let current = target.parent; - while (current && current.parent) { - if (isKeepAlive(current.parent.vnode)) { - injectToKeepAliveRoot(wrappedHook, type, target, current); - } - current = current.parent; - } - } -} -function injectToKeepAliveRoot(hook, type, target, keepAliveRoot) { - const injected = injectHook( - type, - hook, - keepAliveRoot, - true - /* prepend */ - ); - onUnmounted(() => { - remove(keepAliveRoot[type], injected); - }, target); -} -function injectHook(type, hook, target = currentInstance, prepend = false) { - if (target) { - if (isRootHook(type)) { - target = target.root; - } - const hooks = target[type] || (target[type] = []); - const wrappedHook = hook.__weh || (hook.__weh = (...args) => { - if (target.isUnmounted) { - return; - } - pauseTracking(); - const reset = setCurrentInstance(target); - const res = callWithAsyncErrorHandling(hook, target, type, args); - reset(); - resetTracking(); - return res; - }); - if (prepend) { - hooks.unshift(wrappedHook); - } else { - hooks.push(wrappedHook); - } - return wrappedHook; - } else { - const apiName = toHandlerKey( - (ErrorTypeStrings[type] || type.replace(/^on/, "")).replace(/ hook$/, "") - ); - warn$1( - `${apiName} is called when there is no active component instance to be associated with. Lifecycle injection APIs can only be used during execution of setup().` - ); - } -} -const createHook$1 = (lifecycle) => (hook, target = currentInstance) => ( - // post-create lifecycle registrations are noops during SSR (except for serverPrefetch) - (!isInSSRComponentSetup || lifecycle === "sp") && injectHook(lifecycle, (...args) => hook(...args), target) -); -const onBeforeMount = createHook$1("bm"); -const onMounted = createHook$1("m"); -const onBeforeUpdate = createHook$1("bu"); -const onUpdated = createHook$1("u"); -const onBeforeUnmount = createHook$1("bum"); -const onUnmounted = createHook$1("um"); -const onServerPrefetch = createHook$1("sp"); -const onRenderTriggered = createHook$1( - "rtg" -); -const onRenderTracked = createHook$1( - "rtc" -); -function onErrorCaptured(hook, target = currentInstance) { - injectHook("ec", hook, target); -} -const getPublicInstance = (i2) => { - if (!i2) - return null; - if (isStatefulComponent(i2)) - return getExposeProxy(i2) || i2.proxy; - return getPublicInstance(i2.parent); -}; -const publicPropertiesMap = ( - // Move PURE marker to new line to workaround compiler discarding it - // due to type annotation - /* @__PURE__ */ extend(/* @__PURE__ */ Object.create(null), { - $: (i2) => i2, - // fixed by xxxxxx vue-i18n 在 dev 模式,访问了 $el,故模拟一个假的 - // $el: i => i.vnode.el, - $el: (i2) => i2.__$el || (i2.__$el = {}), - $data: (i2) => i2.data, - $props: (i2) => shallowReadonly(i2.props), - $attrs: (i2) => shallowReadonly(i2.attrs), - $slots: (i2) => shallowReadonly(i2.slots), - $refs: (i2) => shallowReadonly(i2.refs), - $parent: (i2) => getPublicInstance(i2.parent), - $root: (i2) => getPublicInstance(i2.root), - $emit: (i2) => i2.emit, - $options: (i2) => resolveMergedOptions(i2), - $forceUpdate: (i2) => i2.f || (i2.f = () => { - i2.effect.dirty = true; - queueJob(i2.update); - }), - // $nextTick: i => i.n || (i.n = nextTick.bind(i.proxy!)),// fixed by xxxxxx - $watch: (i2) => instanceWatch.bind(i2) - }) -); -const isReservedPrefix = (key) => key === "_" || key === "$"; -const hasSetupBinding = (state, key) => state !== EMPTY_OBJ && !state.__isScriptSetup && hasOwn$1(state, key); -const PublicInstanceProxyHandlers = { - get({ _: instance }, key) { - const { ctx, setupState, data, props, accessCache, type, appContext } = instance; - if (key === "__isVue") { - return true; - } - let normalizedProps; - if (key[0] !== "$") { - const n2 = accessCache[key]; - if (n2 !== void 0) { - switch (n2) { - case 1: - return setupState[key]; - case 2: - return data[key]; - case 4: - return ctx[key]; - case 3: - return props[key]; - } - } else if (hasSetupBinding(setupState, key)) { - accessCache[key] = 1; - return setupState[key]; - } else if (data !== EMPTY_OBJ && hasOwn$1(data, key)) { - accessCache[key] = 2; - return data[key]; - } else if ( - // only cache other properties when instance has declared (thus stable) - // props - (normalizedProps = instance.propsOptions[0]) && hasOwn$1(normalizedProps, key) - ) { - accessCache[key] = 3; - return props[key]; - } else if (ctx !== EMPTY_OBJ && hasOwn$1(ctx, key)) { - accessCache[key] = 4; - return ctx[key]; - } else if (shouldCacheAccess) { - accessCache[key] = 0; - } - } - const publicGetter = publicPropertiesMap[key]; - let cssModule, globalProperties; - if (publicGetter) { - if (key === "$attrs") { - track(instance, "get", key); - } else if (key === "$slots") { - track(instance, "get", key); - } - return publicGetter(instance); - } else if ( - // css module (injected by vue-loader) - (cssModule = type.__cssModules) && (cssModule = cssModule[key]) - ) { - return cssModule; - } else if (ctx !== EMPTY_OBJ && hasOwn$1(ctx, key)) { - accessCache[key] = 4; - return ctx[key]; - } else if ( - // global properties - globalProperties = appContext.config.globalProperties, hasOwn$1(globalProperties, key) - ) { - { - return globalProperties[key]; - } - } else if (currentRenderingInstance && (!isString(key) || // #1091 avoid internal isRef/isVNode checks on component instance leading - // to infinite warning loop - key.indexOf("__v") !== 0)) { - if (data !== EMPTY_OBJ && isReservedPrefix(key[0]) && hasOwn$1(data, key)) { - warn$1( - `Property ${JSON.stringify( - key - )} must be accessed via $data because it starts with a reserved character ("$" or "_") and is not proxied on the render context.` - ); - } else if (instance === currentRenderingInstance) { - warn$1( - `Property ${JSON.stringify(key)} was accessed during render but is not defined on instance.` - ); - } - } - }, - set({ _: instance }, key, value) { - const { data, setupState, ctx } = instance; - if (hasSetupBinding(setupState, key)) { - setupState[key] = value; - return true; - } else if (setupState.__isScriptSetup && hasOwn$1(setupState, key)) { - warn$1(`Cannot mutate <script setup> binding "${key}" from Options API.`); - return false; - } else if (data !== EMPTY_OBJ && hasOwn$1(data, key)) { - data[key] = value; - return true; - } else if (hasOwn$1(instance.props, key)) { - warn$1(`Attempting to mutate prop "${key}". Props are readonly.`); - return false; - } - if (key[0] === "$" && key.slice(1) in instance) { - warn$1( - `Attempting to mutate public property "${key}". Properties starting with $ are reserved and readonly.` - ); - return false; - } else { - if (key in instance.appContext.config.globalProperties) { - Object.defineProperty(ctx, key, { - enumerable: true, - configurable: true, - value - }); - } else { - ctx[key] = value; - } - } - return true; - }, - has({ - _: { data, setupState, accessCache, ctx, appContext, propsOptions } - }, key) { - let normalizedProps; - return !!accessCache[key] || data !== EMPTY_OBJ && hasOwn$1(data, key) || hasSetupBinding(setupState, key) || (normalizedProps = propsOptions[0]) && hasOwn$1(normalizedProps, key) || hasOwn$1(ctx, key) || hasOwn$1(publicPropertiesMap, key) || hasOwn$1(appContext.config.globalProperties, key); - }, - defineProperty(target, key, descriptor) { - if (descriptor.get != null) { - target._.accessCache[key] = 0; - } else if (hasOwn$1(descriptor, "value")) { - this.set(target, key, descriptor.value, null); - } - return Reflect.defineProperty(target, key, descriptor); - } -}; -{ - PublicInstanceProxyHandlers.ownKeys = (target) => { - warn$1( - `Avoid app logic that relies on enumerating keys on a component instance. The keys will be empty in production mode to avoid performance overhead.` - ); - return Reflect.ownKeys(target); - }; -} -function createDevRenderContext(instance) { - const target = {}; - Object.defineProperty(target, `_`, { - configurable: true, - enumerable: false, - get: () => instance - }); - Object.keys(publicPropertiesMap).forEach((key) => { - Object.defineProperty(target, key, { - configurable: true, - enumerable: false, - get: () => publicPropertiesMap[key](instance), - // intercepted by the proxy so no need for implementation, - // but needed to prevent set errors - set: NOOP - }); - }); - return target; -} -function exposePropsOnRenderContext(instance) { - const { - ctx, - propsOptions: [propsOptions] - } = instance; - if (propsOptions) { - Object.keys(propsOptions).forEach((key) => { - Object.defineProperty(ctx, key, { - enumerable: true, - configurable: true, - get: () => instance.props[key], - set: NOOP - }); - }); - } -} -function exposeSetupStateOnRenderContext(instance) { - const { ctx, setupState } = instance; - Object.keys(toRaw(setupState)).forEach((key) => { - if (!setupState.__isScriptSetup) { - if (isReservedPrefix(key[0])) { - warn$1( - `setup() return property ${JSON.stringify( - key - )} should not start with "$" or "_" which are reserved prefixes for Vue internals.` - ); - return; - } - Object.defineProperty(ctx, key, { - enumerable: true, - configurable: true, - get: () => setupState[key], - set: NOOP - }); - } - }); -} -function normalizePropsOrEmits(props) { - return isArray(props) ? props.reduce( - (normalized, p2) => (normalized[p2] = null, normalized), - {} - ) : props; -} -function createDuplicateChecker() { - const cache = /* @__PURE__ */ Object.create(null); - return (type, key) => { - if (cache[key]) { - warn$1(`${type} property "${key}" is already defined in ${cache[key]}.`); - } else { - cache[key] = type; - } - }; -} -let shouldCacheAccess = true; -function applyOptions$1(instance) { - const options = resolveMergedOptions(instance); - const publicThis = instance.proxy; - const ctx = instance.ctx; - shouldCacheAccess = false; - if (options.beforeCreate) { - callHook$1(options.beforeCreate, instance, "bc"); - } - const { - // state - data: dataOptions, - computed: computedOptions, - methods, - watch: watchOptions, - provide: provideOptions, - inject: injectOptions, - // lifecycle - created, - beforeMount, - mounted, - beforeUpdate, - updated, - activated, - deactivated, - beforeDestroy, - beforeUnmount, - destroyed, - unmounted, - render, - renderTracked, - renderTriggered, - errorCaptured, - serverPrefetch, - // public API - expose, - inheritAttrs, - // assets - components, - directives, - filters - } = options; - const checkDuplicateProperties = createDuplicateChecker(); - { - const [propsOptions] = instance.propsOptions; - if (propsOptions) { - for (const key in propsOptions) { - checkDuplicateProperties("Props", key); - } - } - } - if (injectOptions) { - resolveInjections(injectOptions, ctx, checkDuplicateProperties); - } - if (methods) { - for (const key in methods) { - const methodHandler = methods[key]; - if (isFunction(methodHandler)) { - { - Object.defineProperty(ctx, key, { - value: methodHandler.bind(publicThis), - configurable: true, - enumerable: true, - writable: true - }); - } - { - checkDuplicateProperties("Methods", key); - } - } else { - warn$1( - `Method "${key}" has type "${typeof methodHandler}" in the component definition. Did you reference the function correctly?` - ); - } - } - } - if (dataOptions) { - if (!isFunction(dataOptions)) { - warn$1( - `The data option must be a function. Plain object usage is no longer supported.` - ); - } - const data = dataOptions.call(publicThis, publicThis); - if (isPromise(data)) { - warn$1( - `data() returned a Promise - note data() cannot be async; If you intend to perform data fetching before component renders, use async setup() + <Suspense>.` - ); - } - if (!isObject$1(data)) { - warn$1(`data() should return an object.`); - } else { - instance.data = reactive(data); - { - for (const key in data) { - checkDuplicateProperties("Data", key); - if (!isReservedPrefix(key[0])) { - Object.defineProperty(ctx, key, { - configurable: true, - enumerable: true, - get: () => data[key], - set: NOOP - }); - } - } - } - } - } - shouldCacheAccess = true; - if (computedOptions) { - for (const key in computedOptions) { - const opt = computedOptions[key]; - const get2 = isFunction(opt) ? opt.bind(publicThis, publicThis) : isFunction(opt.get) ? opt.get.bind(publicThis, publicThis) : NOOP; - if (get2 === NOOP) { - warn$1(`Computed property "${key}" has no getter.`); - } - const set2 = !isFunction(opt) && isFunction(opt.set) ? opt.set.bind(publicThis) : () => { - warn$1( - `Write operation failed: computed property "${key}" is readonly.` - ); - }; - const c2 = computed({ - get: get2, - set: set2 - }); - Object.defineProperty(ctx, key, { - enumerable: true, - configurable: true, - get: () => c2.value, - set: (v2) => c2.value = v2 - }); - { - checkDuplicateProperties("Computed", key); - } - } - } - if (watchOptions) { - for (const key in watchOptions) { - createWatcher(watchOptions[key], ctx, publicThis, key); - } - } - { - if (provideOptions) { - const provides = isFunction(provideOptions) ? provideOptions.call(publicThis) : provideOptions; - Reflect.ownKeys(provides).forEach((key) => { - provide(key, provides[key]); - }); - } - } - { - if (created) { - callHook$1(created, instance, "c"); - } - } - function registerLifecycleHook(register, hook) { - if (isArray(hook)) { - hook.forEach((_hook) => register(_hook.bind(publicThis))); - } else if (hook) { - register(hook.bind(publicThis)); - } - } - registerLifecycleHook(onBeforeMount, beforeMount); - registerLifecycleHook(onMounted, mounted); - registerLifecycleHook(onBeforeUpdate, beforeUpdate); - registerLifecycleHook(onUpdated, updated); - registerLifecycleHook(onActivated, activated); - registerLifecycleHook(onDeactivated, deactivated); - registerLifecycleHook(onErrorCaptured, errorCaptured); - registerLifecycleHook(onRenderTracked, renderTracked); - registerLifecycleHook(onRenderTriggered, renderTriggered); - registerLifecycleHook(onBeforeUnmount, beforeUnmount); - registerLifecycleHook(onUnmounted, unmounted); - registerLifecycleHook(onServerPrefetch, serverPrefetch); - if (isArray(expose)) { - if (expose.length) { - const exposed = instance.exposed || (instance.exposed = {}); - expose.forEach((key) => { - Object.defineProperty(exposed, key, { - get: () => publicThis[key], - set: (val) => publicThis[key] = val - }); - }); - } else if (!instance.exposed) { - instance.exposed = {}; - } - } - if (render && instance.render === NOOP) { - instance.render = render; - } - if (inheritAttrs != null) { - instance.inheritAttrs = inheritAttrs; - } - if (components) - instance.components = components; - if (directives) - instance.directives = directives; - if (instance.ctx.$onApplyOptions) { - instance.ctx.$onApplyOptions(options, instance, publicThis); - } -} -function resolveInjections(injectOptions, ctx, checkDuplicateProperties = NOOP) { - if (isArray(injectOptions)) { - injectOptions = normalizeInject(injectOptions); - } - for (const key in injectOptions) { - const opt = injectOptions[key]; - let injected; - if (isObject$1(opt)) { - if ("default" in opt) { - injected = inject( - opt.from || key, - opt.default, - true - ); - } else { - injected = inject(opt.from || key); - } - } else { - injected = inject(opt); - } - if (isRef(injected)) { - Object.defineProperty(ctx, key, { - enumerable: true, - configurable: true, - get: () => injected.value, - set: (v2) => injected.value = v2 - }); - } else { - ctx[key] = injected; - } - { - checkDuplicateProperties("Inject", key); - } - } -} -function callHook$1(hook, instance, type) { - callWithAsyncErrorHandling( - isArray(hook) ? hook.map((h2) => h2.bind(instance.proxy)) : hook.bind(instance.proxy), - instance, - type - ); -} -function createWatcher(raw, ctx, publicThis, key) { - const getter = key.includes(".") ? createPathGetter(publicThis, key) : () => publicThis[key]; - if (isString(raw)) { - const handler = ctx[raw]; - if (isFunction(handler)) { - watch(getter, handler); - } else { - warn$1(`Invalid watch handler specified by key "${raw}"`, handler); - } - } else if (isFunction(raw)) { - watch(getter, raw.bind(publicThis)); - } else if (isObject$1(raw)) { - if (isArray(raw)) { - raw.forEach((r2) => createWatcher(r2, ctx, publicThis, key)); - } else { - const handler = isFunction(raw.handler) ? raw.handler.bind(publicThis) : ctx[raw.handler]; - if (isFunction(handler)) { - watch(getter, handler, raw); - } else { - warn$1(`Invalid watch handler specified by key "${raw.handler}"`, handler); - } - } - } else { - warn$1(`Invalid watch option: "${key}"`, raw); - } -} -function resolveMergedOptions(instance) { - const base = instance.type; - const { mixins, extends: extendsOptions } = base; - const { - mixins: globalMixins, - optionsCache: cache, - config: { optionMergeStrategies } - } = instance.appContext; - const cached = cache.get(base); - let resolved; - if (cached) { - resolved = cached; - } else if (!globalMixins.length && !mixins && !extendsOptions) { - { - resolved = base; - } - } else { - resolved = {}; - if (globalMixins.length) { - globalMixins.forEach( - (m2) => mergeOptions(resolved, m2, optionMergeStrategies, true) - ); - } - mergeOptions(resolved, base, optionMergeStrategies); - } - if (isObject$1(base)) { - cache.set(base, resolved); - } - return resolved; -} -function mergeOptions(to, from, strats, asMixin = false) { - const { mixins, extends: extendsOptions } = from; - if (extendsOptions) { - mergeOptions(to, extendsOptions, strats, true); - } - if (mixins) { - mixins.forEach( - (m2) => mergeOptions(to, m2, strats, true) - ); - } - for (const key in from) { - if (asMixin && key === "expose") { - warn$1( - `"expose" option is ignored when declared in mixins or extends. It should only be declared in the base component itself.` - ); - } else { - const strat = internalOptionMergeStrats[key] || strats && strats[key]; - to[key] = strat ? strat(to[key], from[key]) : from[key]; - } - } - return to; -} -const internalOptionMergeStrats = { - data: mergeDataFn, - props: mergeEmitsOrPropsOptions, - emits: mergeEmitsOrPropsOptions, - // objects - methods: mergeObjectOptions, - computed: mergeObjectOptions, - // lifecycle - beforeCreate: mergeAsArray$1, - created: mergeAsArray$1, - beforeMount: mergeAsArray$1, - mounted: mergeAsArray$1, - beforeUpdate: mergeAsArray$1, - updated: mergeAsArray$1, - beforeDestroy: mergeAsArray$1, - beforeUnmount: mergeAsArray$1, - destroyed: mergeAsArray$1, - unmounted: mergeAsArray$1, - activated: mergeAsArray$1, - deactivated: mergeAsArray$1, - errorCaptured: mergeAsArray$1, - serverPrefetch: mergeAsArray$1, - // assets - components: mergeObjectOptions, - directives: mergeObjectOptions, - // watch - watch: mergeWatchOptions, - // provide / inject - provide: mergeDataFn, - inject: mergeInject -}; -function mergeDataFn(to, from) { - if (!from) { - return to; - } - if (!to) { - return from; - } - return function mergedDataFn() { - return extend( - isFunction(to) ? to.call(this, this) : to, - isFunction(from) ? from.call(this, this) : from - ); - }; -} -function mergeInject(to, from) { - return mergeObjectOptions(normalizeInject(to), normalizeInject(from)); -} -function normalizeInject(raw) { - if (isArray(raw)) { - const res = {}; - for (let i2 = 0; i2 < raw.length; i2++) { - res[raw[i2]] = raw[i2]; - } - return res; - } - return raw; -} -function mergeAsArray$1(to, from) { - return to ? [...new Set([].concat(to, from))] : from; -} -function mergeObjectOptions(to, from) { - return to ? extend(/* @__PURE__ */ Object.create(null), to, from) : from; -} -function mergeEmitsOrPropsOptions(to, from) { - if (to) { - if (isArray(to) && isArray(from)) { - return [.../* @__PURE__ */ new Set([...to, ...from])]; - } - return extend( - /* @__PURE__ */ Object.create(null), - normalizePropsOrEmits(to), - normalizePropsOrEmits(from != null ? from : {}) - ); - } else { - return from; - } -} -function mergeWatchOptions(to, from) { - if (!to) - return from; - if (!from) - return to; - const merged = extend(/* @__PURE__ */ Object.create(null), to); - for (const key in from) { - merged[key] = mergeAsArray$1(to[key], from[key]); - } - return merged; -} -function initProps$1(instance, rawProps, isStateful, isSSR = false) { - const props = {}; - const attrs = {}; - instance.propsDefaults = /* @__PURE__ */ Object.create(null); - setFullProps(instance, rawProps, props, attrs); - for (const key in instance.propsOptions[0]) { - if (!(key in props)) { - props[key] = void 0; - } - } - { - validateProps(rawProps || {}, props, instance); - } - if (isStateful) { - instance.props = isSSR ? props : shallowReactive(props); - } else { - if (!instance.type.props) { - instance.props = attrs; - } else { - instance.props = props; - } - } - instance.attrs = attrs; -} -function isInHmrContext(instance) { - while (instance) { - if (instance.type.__hmrId) - return true; - instance = instance.parent; - } -} -function updateProps(instance, rawProps, rawPrevProps, optimized) { - const { - props, - attrs, - vnode: { patchFlag } - } = instance; - const rawCurrentProps = toRaw(props); - const [options] = instance.propsOptions; - let hasAttrsChanged = false; - if ( - // always force full diff in dev - // - #1942 if hmr is enabled with sfc component - // - vite#872 non-sfc component used by sfc component - !isInHmrContext(instance) && (optimized || patchFlag > 0) && !(patchFlag & 16) - ) { - if (patchFlag & 8) { - const propsToUpdate = instance.vnode.dynamicProps; - for (let i2 = 0; i2 < propsToUpdate.length; i2++) { - let key = propsToUpdate[i2]; - if (isEmitListener(instance.emitsOptions, key)) { - continue; - } - const value = rawProps[key]; - if (options) { - if (hasOwn$1(attrs, key)) { - if (value !== attrs[key]) { - attrs[key] = value; - hasAttrsChanged = true; - } - } else { - const camelizedKey = camelize(key); - props[camelizedKey] = resolvePropValue( - options, - rawCurrentProps, - camelizedKey, - value, - instance, - false - ); - } - } else { - if (value !== attrs[key]) { - attrs[key] = value; - hasAttrsChanged = true; - } - } - } - } - } else { - if (setFullProps(instance, rawProps, props, attrs)) { - hasAttrsChanged = true; - } - let kebabKey; - for (const key in rawCurrentProps) { - if (!rawProps || // for camelCase - !hasOwn$1(rawProps, key) && // it's possible the original props was passed in as kebab-case - // and converted to camelCase (#955) - ((kebabKey = hyphenate(key)) === key || !hasOwn$1(rawProps, kebabKey))) { - if (options) { - if (rawPrevProps && // for camelCase - (rawPrevProps[key] !== void 0 || // for kebab-case - rawPrevProps[kebabKey] !== void 0)) { - props[key] = resolvePropValue( - options, - rawCurrentProps, - key, - void 0, - instance, - true - ); - } - } else { - delete props[key]; - } - } - } - if (attrs !== rawCurrentProps) { - for (const key in attrs) { - if (!rawProps || !hasOwn$1(rawProps, key) && true) { - delete attrs[key]; - hasAttrsChanged = true; - } - } - } - } - if (hasAttrsChanged) { - trigger(instance, "set", "$attrs"); - } - { - validateProps(rawProps || {}, props, instance); - } -} -function setFullProps(instance, rawProps, props, attrs) { - const [options, needCastKeys] = instance.propsOptions; - let hasAttrsChanged = false; - let rawCastValues; - if (rawProps) { - for (let key in rawProps) { - if (isReservedProp(key)) { - continue; - } - const value = rawProps[key]; - let camelKey; - if (options && hasOwn$1(options, camelKey = camelize(key))) { - if (!needCastKeys || !needCastKeys.includes(camelKey)) { - props[camelKey] = value; - } else { - (rawCastValues || (rawCastValues = {}))[camelKey] = value; - } - } else if (!isEmitListener(instance.emitsOptions, key)) { - if (!(key in attrs) || value !== attrs[key]) { - attrs[key] = value; - hasAttrsChanged = true; - } - } - } - } - if (needCastKeys) { - const rawCurrentProps = toRaw(props); - const castValues = rawCastValues || EMPTY_OBJ; - for (let i2 = 0; i2 < needCastKeys.length; i2++) { - const key = needCastKeys[i2]; - props[key] = resolvePropValue( - options, - rawCurrentProps, - key, - castValues[key], - instance, - !hasOwn$1(castValues, key) - ); - } - } - return hasAttrsChanged; -} -function resolvePropValue(options, props, key, value, instance, isAbsent) { - const opt = options[key]; - if (opt != null) { - const hasDefault = hasOwn$1(opt, "default"); - if (hasDefault && value === void 0) { - const defaultValue = opt.default; - if (opt.type !== Function && !opt.skipFactory && isFunction(defaultValue)) { - const { propsDefaults } = instance; - if (key in propsDefaults) { - value = propsDefaults[key]; - } else { - const reset = setCurrentInstance(instance); - value = propsDefaults[key] = defaultValue.call( - null, - props - ); - reset(); - } - } else { - value = defaultValue; - } - } - if (opt[ - 0 - /* shouldCast */ - ]) { - if (isAbsent && !hasDefault) { - value = false; - } else if (opt[ - 1 - /* shouldCastTrue */ - ] && (value === "" || value === hyphenate(key))) { - value = true; - } - } - } - return value; -} -function normalizePropsOptions(comp, appContext, asMixin = false) { - const cache = appContext.propsCache; - const cached = cache.get(comp); - if (cached) { - return cached; - } - const raw = comp.props; - const normalized = {}; - const needCastKeys = []; - let hasExtends = false; - if (!isFunction(comp)) { - const extendProps = (raw2) => { - hasExtends = true; - const [props, keys] = normalizePropsOptions(raw2, appContext, true); - extend(normalized, props); - if (keys) - needCastKeys.push(...keys); - }; - if (!asMixin && appContext.mixins.length) { - appContext.mixins.forEach(extendProps); - } - if (comp.extends) { - extendProps(comp.extends); - } - if (comp.mixins) { - comp.mixins.forEach(extendProps); - } - } - if (!raw && !hasExtends) { - if (isObject$1(comp)) { - cache.set(comp, EMPTY_ARR); - } - return EMPTY_ARR; - } - if (isArray(raw)) { - for (let i2 = 0; i2 < raw.length; i2++) { - if (!isString(raw[i2])) { - warn$1(`props must be strings when using array syntax.`, raw[i2]); - } - const normalizedKey = camelize(raw[i2]); - if (validatePropName(normalizedKey)) { - normalized[normalizedKey] = EMPTY_OBJ; - } - } - } else if (raw) { - if (!isObject$1(raw)) { - warn$1(`invalid props options`, raw); - } - for (const key in raw) { - const normalizedKey = camelize(key); - if (validatePropName(normalizedKey)) { - const opt = raw[key]; - const prop = normalized[normalizedKey] = isArray(opt) || isFunction(opt) ? { type: opt } : extend({}, opt); - if (prop) { - const booleanIndex = getTypeIndex(Boolean, prop.type); - const stringIndex = getTypeIndex(String, prop.type); - prop[ - 0 - /* shouldCast */ - ] = booleanIndex > -1; - prop[ - 1 - /* shouldCastTrue */ - ] = stringIndex < 0 || booleanIndex < stringIndex; - if (booleanIndex > -1 || hasOwn$1(prop, "default")) { - needCastKeys.push(normalizedKey); - } - } - } - } - } - const res = [normalized, needCastKeys]; - if (isObject$1(comp)) { - cache.set(comp, res); - } - return res; -} -function validatePropName(key) { - if (key[0] !== "$" && !isReservedProp(key)) { - return true; - } else { - warn$1(`Invalid prop name: "${key}" is a reserved property.`); - } - return false; -} -function getType(ctor) { - if (ctor === null) { - return "null"; - } - if (typeof ctor === "function") { - return ctor.name || ""; - } else if (typeof ctor === "object") { - const name = ctor.constructor && ctor.constructor.name; - return name || ""; - } - return ""; -} -function isSameType(a2, b2) { - return getType(a2) === getType(b2); -} -function getTypeIndex(type, expectedTypes) { - if (isArray(expectedTypes)) { - return expectedTypes.findIndex((t2) => isSameType(t2, type)); - } else if (isFunction(expectedTypes)) { - return isSameType(expectedTypes, type) ? 0 : -1; - } - return -1; -} -function validateProps(rawProps, props, instance) { - const resolvedValues = toRaw(props); - const options = instance.propsOptions[0]; - for (const key in options) { - let opt = options[key]; - if (opt == null) - continue; - validateProp( - key, - resolvedValues[key], - opt, - shallowReadonly(resolvedValues), - !hasOwn$1(rawProps, key) && !hasOwn$1(rawProps, hyphenate(key)) - ); - } -} -function validateProp(name, value, prop, props, isAbsent) { - const { type, required, validator, skipCheck } = prop; - if (required && isAbsent) { - warn$1('Missing required prop: "' + name + '"'); - return; - } - if (value == null && !required) { - return; - } - if (type != null && type !== true && !skipCheck) { - let isValid = false; - const types = isArray(type) ? type : [type]; - const expectedTypes = []; - for (let i2 = 0; i2 < types.length && !isValid; i2++) { - const { valid, expectedType } = assertType(value, types[i2]); - expectedTypes.push(expectedType || ""); - isValid = valid; - } - if (!isValid) { - warn$1(getInvalidTypeMessage(name, value, expectedTypes)); - return; - } - } - if (validator && !validator(value, props)) { - warn$1('Invalid prop: custom validator check failed for prop "' + name + '".'); - } -} -const isSimpleType = /* @__PURE__ */ makeMap( - "String,Number,Boolean,Function,Symbol,BigInt" -); -function assertType(value, type) { - let valid; - const expectedType = getType(type); - if (isSimpleType(expectedType)) { - const t2 = typeof value; - valid = t2 === expectedType.toLowerCase(); - if (!valid && t2 === "object") { - valid = value instanceof type; - } - } else if (expectedType === "Object") { - valid = isObject$1(value); - } else if (expectedType === "Array") { - valid = isArray(value); - } else if (expectedType === "null") { - valid = value === null; - } else { - valid = value instanceof type; - } - return { - valid, - expectedType - }; -} -function getInvalidTypeMessage(name, value, expectedTypes) { - if (expectedTypes.length === 0) { - return `Prop type [] for prop "${name}" won't match anything. Did you mean to use type Array instead?`; - } - let message = `Invalid prop: type check failed for prop "${name}". Expected ${expectedTypes.map(capitalize).join(" | ")}`; - const expectedType = expectedTypes[0]; - const receivedType = toRawType(value); - const expectedValue = styleValue(value, expectedType); - const receivedValue = styleValue(value, receivedType); - if (expectedTypes.length === 1 && isExplicable(expectedType) && !isBoolean(expectedType, receivedType)) { - message += ` with value ${expectedValue}`; - } - message += `, got ${receivedType} `; - if (isExplicable(receivedType)) { - message += `with value ${receivedValue}.`; - } - return message; -} -function styleValue(value, type) { - if (type === "String") { - return `"${value}"`; - } else if (type === "Number") { - return `${Number(value)}`; - } else { - return `${value}`; - } -} -function isExplicable(type) { - const explicitTypes = ["string", "number", "boolean"]; - return explicitTypes.some((elem) => type.toLowerCase() === elem); -} -function isBoolean(...args) { - return args.some((elem) => elem.toLowerCase() === "boolean"); -} -let supported; -let perf; -function startMeasure(instance, type) { - if (instance.appContext.config.performance && isSupported()) { - perf.mark(`vue-${type}-${instance.uid}`); - } - { - devtoolsPerfStart(instance, type, isSupported() ? perf.now() : Date.now()); - } -} -function endMeasure(instance, type) { - if (instance.appContext.config.performance && isSupported()) { - const startTag = `vue-${type}-${instance.uid}`; - const endTag = startTag + `:end`; - perf.mark(endTag); - perf.measure( - `<${formatComponentName(instance, instance.type)}> ${type}`, - startTag, - endTag - ); - perf.clearMarks(startTag); - perf.clearMarks(endTag); - } - { - devtoolsPerfEnd(instance, type, isSupported() ? perf.now() : Date.now()); - } -} -function isSupported() { - if (supported !== void 0) { - return supported; - } - if (typeof window !== "undefined" && window.performance) { - supported = true; - perf = window.performance; - } else { - supported = false; - } - return supported; -} -const queuePostRenderEffect$1 = queuePostFlushCb; -const Fragment = Symbol.for("v-fgt"); -const Text = Symbol.for("v-txt"); -const Comment = Symbol.for("v-cmt"); -const Static = Symbol.for("v-stc"); -function isVNode(value) { - return value ? value.__v_isVNode === true : false; -} -const InternalObjectKey = `__vInternal`; -function guardReactiveProps(props) { - if (!props) - return null; - return isProxy(props) || InternalObjectKey in props ? extend({}, props) : props; -} -const emptyAppContext = createAppContext(); -let uid = 0; -function createComponentInstance(vnode, parent, suspense) { - const type = vnode.type; - const appContext = (parent ? parent.appContext : vnode.appContext) || emptyAppContext; - const instance = { - uid: uid++, - vnode, - type, - parent, - appContext, - root: null, - // to be immediately set - next: null, - subTree: null, - // will be set synchronously right after creation - effect: null, - update: null, - // will be set synchronously right after creation - scope: new EffectScope( - true - /* detached */ - ), - render: null, - proxy: null, - exposed: null, - exposeProxy: null, - withProxy: null, - provides: parent ? parent.provides : Object.create(appContext.provides), - accessCache: null, - renderCache: [], - // local resolved assets - components: null, - directives: null, - // resolved props and emits options - propsOptions: normalizePropsOptions(type, appContext), - emitsOptions: normalizeEmitsOptions(type, appContext), - // emit - emit: null, - // to be set immediately - emitted: null, - // props default value - propsDefaults: EMPTY_OBJ, - // inheritAttrs - inheritAttrs: type.inheritAttrs, - // state - ctx: EMPTY_OBJ, - data: EMPTY_OBJ, - props: EMPTY_OBJ, - attrs: EMPTY_OBJ, - slots: EMPTY_OBJ, - refs: EMPTY_OBJ, - setupState: EMPTY_OBJ, - setupContext: null, - attrsProxy: null, - slotsProxy: null, - // suspense related - suspense, - suspenseId: suspense ? suspense.pendingId : 0, - asyncDep: null, - asyncResolved: false, - // lifecycle hooks - // not using enums here because it results in computed properties - isMounted: false, - isUnmounted: false, - isDeactivated: false, - bc: null, - c: null, - bm: null, - m: null, - bu: null, - u: null, - um: null, - bum: null, - da: null, - a: null, - rtg: null, - rtc: null, - ec: null, - sp: null - }; - { - instance.ctx = createDevRenderContext(instance); - } - instance.root = parent ? parent.root : instance; - instance.emit = emit.bind(null, instance); - if (vnode.ce) { - vnode.ce(instance); - } - return instance; -} -let currentInstance = null; -const getCurrentInstance = () => currentInstance || currentRenderingInstance; -let internalSetCurrentInstance; -let setInSSRSetupState; -{ - internalSetCurrentInstance = (i2) => { - currentInstance = i2; - }; - setInSSRSetupState = (v2) => { - isInSSRComponentSetup = v2; - }; -} -const setCurrentInstance = (instance) => { - const prev = currentInstance; - internalSetCurrentInstance(instance); - instance.scope.on(); - return () => { - instance.scope.off(); - internalSetCurrentInstance(prev); - }; -}; -const unsetCurrentInstance = () => { - currentInstance && currentInstance.scope.off(); - internalSetCurrentInstance(null); -}; -const isBuiltInTag = /* @__PURE__ */ makeMap("slot,component"); -function validateComponentName(name, { isNativeTag }) { - if (isBuiltInTag(name) || isNativeTag(name)) { - warn$1( - "Do not use built-in or reserved HTML elements as component id: " + name - ); - } -} -function isStatefulComponent(instance) { - return instance.vnode.shapeFlag & 4; -} -let isInSSRComponentSetup = false; -function setupComponent(instance, isSSR = false) { - isSSR && setInSSRSetupState(isSSR); - const { - props - /*, children*/ - } = instance.vnode; - const isStateful = isStatefulComponent(instance); - initProps$1(instance, props, isStateful, isSSR); - const setupResult = isStateful ? setupStatefulComponent(instance, isSSR) : void 0; - isSSR && setInSSRSetupState(false); - return setupResult; -} -function setupStatefulComponent(instance, isSSR) { - const Component2 = instance.type; - { - if (Component2.name) { - validateComponentName(Component2.name, instance.appContext.config); - } - if (Component2.components) { - const names = Object.keys(Component2.components); - for (let i2 = 0; i2 < names.length; i2++) { - validateComponentName(names[i2], instance.appContext.config); - } - } - if (Component2.directives) { - const names = Object.keys(Component2.directives); - for (let i2 = 0; i2 < names.length; i2++) { - validateDirectiveName(names[i2]); - } - } - if (Component2.compilerOptions && isRuntimeOnly()) { - warn$1( - `"compilerOptions" is only supported when using a build of Vue that includes the runtime compiler. Since you are using a runtime-only build, the options should be passed via your build tool config instead.` - ); - } - } - instance.accessCache = /* @__PURE__ */ Object.create(null); - instance.proxy = markRaw(new Proxy(instance.ctx, PublicInstanceProxyHandlers)); - { - exposePropsOnRenderContext(instance); - } - const { setup } = Component2; - if (setup) { - const setupContext = instance.setupContext = setup.length > 1 ? createSetupContext(instance) : null; - const reset = setCurrentInstance(instance); - pauseTracking(); - const setupResult = callWithErrorHandling( - setup, - instance, - 0, - [ - shallowReadonly(instance.props), - setupContext - ] - ); - resetTracking(); - reset(); - if (isPromise(setupResult)) { - setupResult.then(unsetCurrentInstance, unsetCurrentInstance); - { - warn$1( - `setup() returned a Promise, but the version of Vue you are using does not support it yet.` - ); - } - } else { - handleSetupResult(instance, setupResult, isSSR); - } - } else { - finishComponentSetup(instance, isSSR); - } -} -function handleSetupResult(instance, setupResult, isSSR) { - if (isFunction(setupResult)) { - { - instance.render = setupResult; - } - } else if (isObject$1(setupResult)) { - if (isVNode(setupResult)) { - warn$1( - `setup() should not return VNodes directly - return a render function instead.` - ); - } - { - instance.devtoolsRawSetupState = setupResult; - } - instance.setupState = proxyRefs(setupResult); - { - exposeSetupStateOnRenderContext(instance); - } - } else if (setupResult !== void 0) { - warn$1( - `setup() should return an object. Received: ${setupResult === null ? "null" : typeof setupResult}` - ); - } - finishComponentSetup(instance, isSSR); -} -let compile; -const isRuntimeOnly = () => !compile; -function finishComponentSetup(instance, isSSR, skipOptions) { - const Component2 = instance.type; - if (!instance.render) { - instance.render = Component2.render || NOOP; - } - { - const reset = setCurrentInstance(instance); - pauseTracking(); - try { - applyOptions$1(instance); - } finally { - resetTracking(); - reset(); - } - } - if (!Component2.render && instance.render === NOOP && !isSSR) { - if (Component2.template) { - warn$1( - `Component provided template option but runtime compilation is not supported in this build of Vue. Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".` - ); - } else { - warn$1(`Component is missing template or render function.`); - } - } -} -function getAttrsProxy(instance) { - return instance.attrsProxy || (instance.attrsProxy = new Proxy( - instance.attrs, - { - get(target, key) { - track(instance, "get", "$attrs"); - return target[key]; - }, - set() { - warn$1(`setupContext.attrs is readonly.`); - return false; - }, - deleteProperty() { - warn$1(`setupContext.attrs is readonly.`); - return false; - } - } - )); -} -function getSlotsProxy(instance) { - return instance.slotsProxy || (instance.slotsProxy = new Proxy(instance.slots, { - get(target, key) { - track(instance, "get", "$slots"); - return target[key]; - } - })); -} -function createSetupContext(instance) { - const expose = (exposed) => { - { - if (instance.exposed) { - warn$1(`expose() should be called only once per setup().`); - } - if (exposed != null) { - let exposedType = typeof exposed; - if (exposedType === "object") { - if (isArray(exposed)) { - exposedType = "array"; - } else if (isRef(exposed)) { - exposedType = "ref"; - } - } - if (exposedType !== "object") { - warn$1( - `expose() should be passed a plain object, received ${exposedType}.` - ); - } - } - } - instance.exposed = exposed || {}; - }; - { - return Object.freeze({ - get attrs() { - return getAttrsProxy(instance); - }, - get slots() { - return getSlotsProxy(instance); - }, - get emit() { - return (event, ...args) => instance.emit(event, ...args); - }, - expose - }); - } -} -function getExposeProxy(instance) { - if (instance.exposed) { - return instance.exposeProxy || (instance.exposeProxy = new Proxy(proxyRefs(markRaw(instance.exposed)), { - get(target, key) { - if (key in target) { - return target[key]; - } - return instance.proxy[key]; - }, - has(target, key) { - return key in target || key in publicPropertiesMap; - } - })); - } -} -const classifyRE = /(?:^|[-_])(\w)/g; -const classify = (str) => str.replace(classifyRE, (c2) => c2.toUpperCase()).replace(/[-_]/g, ""); -function getComponentName(Component2, includeInferred = true) { - return isFunction(Component2) ? Component2.displayName || Component2.name : Component2.name || includeInferred && Component2.__name; -} -function formatComponentName(instance, Component2, isRoot = false) { - let name = getComponentName(Component2); - if (!name && Component2.__file) { - const match = Component2.__file.match(/([^/\\]+)\.\w+$/); - if (match) { - name = match[1]; - } - } - if (!name && instance && instance.parent) { - const inferFromRegistry = (registry) => { - for (const key in registry) { - if (registry[key] === Component2) { - return key; - } - } - }; - name = inferFromRegistry( - instance.components || instance.parent.type.components - ) || inferFromRegistry(instance.appContext.components); - } - return name ? classify(name) : isRoot ? `App` : `Anonymous`; -} -const computed = (getterOrOptions, debugOptions) => { - const c2 = computed$1(getterOrOptions, debugOptions, isInSSRComponentSetup); - { - const i2 = getCurrentInstance(); - if (i2 && i2.appContext.config.warnRecursiveComputed) { - c2._warnRecursive = true; - } - } - return c2; -}; -const version = "3.4.21"; -const warn = warn$1; -function unwrapper(target) { - return unref(target); -} -const ARRAYTYPE = "[object Array]"; -const OBJECTTYPE = "[object Object]"; -function diff(current, pre) { - const result = {}; - syncKeys(current, pre); - _diff(current, pre, "", result); - return result; -} -function syncKeys(current, pre) { - current = unwrapper(current); - if (current === pre) - return; - const rootCurrentType = toTypeString(current); - const rootPreType = toTypeString(pre); - if (rootCurrentType == OBJECTTYPE && rootPreType == OBJECTTYPE) { - for (let key in pre) { - const currentValue = current[key]; - if (currentValue === void 0) { - current[key] = null; - } else { - syncKeys(currentValue, pre[key]); - } - } - } else if (rootCurrentType == ARRAYTYPE && rootPreType == ARRAYTYPE) { - if (current.length >= pre.length) { - pre.forEach((item, index2) => { - syncKeys(current[index2], item); - }); - } - } -} -function _diff(current, pre, path, result) { - current = unwrapper(current); - if (current === pre) - return; - const rootCurrentType = toTypeString(current); - const rootPreType = toTypeString(pre); - if (rootCurrentType == OBJECTTYPE) { - if (rootPreType != OBJECTTYPE || Object.keys(current).length < Object.keys(pre).length) { - setResult(result, path, current); - } else { - for (let key in current) { - const currentValue = unwrapper(current[key]); - const preValue = pre[key]; - const currentType = toTypeString(currentValue); - const preType = toTypeString(preValue); - if (currentType != ARRAYTYPE && currentType != OBJECTTYPE) { - if (currentValue != preValue) { - setResult( - result, - (path == "" ? "" : path + ".") + key, - currentValue - ); - } - } else if (currentType == ARRAYTYPE) { - if (preType != ARRAYTYPE) { - setResult( - result, - (path == "" ? "" : path + ".") + key, - currentValue - ); - } else { - if (currentValue.length < preValue.length) { - setResult( - result, - (path == "" ? "" : path + ".") + key, - currentValue - ); - } else { - currentValue.forEach((item, index2) => { - _diff( - item, - preValue[index2], - (path == "" ? "" : path + ".") + key + "[" + index2 + "]", - result - ); - }); - } - } - } else if (currentType == OBJECTTYPE) { - if (preType != OBJECTTYPE || Object.keys(currentValue).length < Object.keys(preValue).length) { - setResult( - result, - (path == "" ? "" : path + ".") + key, - currentValue - ); - } else { - for (let subKey in currentValue) { - _diff( - currentValue[subKey], - preValue[subKey], - (path == "" ? "" : path + ".") + key + "." + subKey, - result - ); - } - } - } - } - } - } else if (rootCurrentType == ARRAYTYPE) { - if (rootPreType != ARRAYTYPE) { - setResult(result, path, current); - } else { - if (current.length < pre.length) { - setResult(result, path, current); - } else { - current.forEach((item, index2) => { - _diff(item, pre[index2], path + "[" + index2 + "]", result); - }); - } - } - } else { - setResult(result, path, current); - } -} -function setResult(result, k, v2) { - result[k] = v2; -} -function hasComponentEffect(instance) { - return queue.includes(instance.update); -} -function flushCallbacks(instance) { - const ctx = instance.ctx; - const callbacks = ctx.__next_tick_callbacks; - if (callbacks && callbacks.length) { - const copies = callbacks.slice(0); - callbacks.length = 0; - for (let i2 = 0; i2 < copies.length; i2++) { - copies[i2](); - } - } -} -function nextTick(instance, fn) { - const ctx = instance.ctx; - if (!ctx.__next_tick_pending && !hasComponentEffect(instance)) { - return nextTick$1(fn && fn.bind(instance.proxy)); - } - let _resolve; - if (!ctx.__next_tick_callbacks) { - ctx.__next_tick_callbacks = []; - } - ctx.__next_tick_callbacks.push(() => { - if (fn) { - callWithErrorHandling( - fn.bind(instance.proxy), - instance, - 14 - ); - } else if (_resolve) { - _resolve(instance.proxy); - } - }); - return new Promise((resolve2) => { - _resolve = resolve2; - }); -} -function clone(src, seen) { - src = unwrapper(src); - const type = typeof src; - if (type === "object" && src !== null) { - let copy = seen.get(src); - if (typeof copy !== "undefined") { - return copy; - } - if (isArray(src)) { - const len = src.length; - copy = new Array(len); - seen.set(src, copy); - for (let i2 = 0; i2 < len; i2++) { - copy[i2] = clone(src[i2], seen); - } - } else { - copy = {}; - seen.set(src, copy); - for (const name in src) { - if (hasOwn$1(src, name)) { - copy[name] = clone(src[name], seen); - } - } - } - return copy; - } - if (type !== "symbol") { - return src; - } -} -function deepCopy(src) { - return clone(src, typeof WeakMap !== "undefined" ? /* @__PURE__ */ new WeakMap() : /* @__PURE__ */ new Map()); -} -function getMPInstanceData(instance, keys) { - const data = instance.data; - const ret = /* @__PURE__ */ Object.create(null); - keys.forEach((key) => { - ret[key] = data[key]; - }); - return ret; -} -function patch(instance, data, oldData) { - if (!data) { - return; - } - data = deepCopy(data); - const ctx = instance.ctx; - const mpType = ctx.mpType; - if (mpType === "page" || mpType === "component") { - data.r0 = 1; - const mpInstance = ctx.$scope; - const keys = Object.keys(data); - const diffData = diff(data, oldData || getMPInstanceData(mpInstance, keys)); - if (Object.keys(diffData).length) { - ctx.__next_tick_pending = true; - mpInstance.setData(diffData, () => { - ctx.__next_tick_pending = false; - flushCallbacks(instance); - }); - flushPreFlushCbs(); - } else { - flushCallbacks(instance); - } - } -} -function initAppConfig(appConfig) { - appConfig.globalProperties.$nextTick = function $nextTick(fn) { - return nextTick(this.$, fn); - }; -} -function onApplyOptions(options, instance, publicThis) { - instance.appContext.config.globalProperties.$applyOptions( - options, - instance, - publicThis - ); - const computedOptions = options.computed; - if (computedOptions) { - const keys = Object.keys(computedOptions); - if (keys.length) { - const ctx = instance.ctx; - if (!ctx.$computedKeys) { - ctx.$computedKeys = []; - } - ctx.$computedKeys.push(...keys); - } - } - delete instance.ctx.$onApplyOptions; -} -function setRef$1(instance, isUnmount = false) { - const { - setupState, - $templateRefs, - ctx: { $scope, $mpPlatform } - } = instance; - if ($mpPlatform === "mp-alipay") { - return; - } - if (!$templateRefs || !$scope) { - return; - } - if (isUnmount) { - return $templateRefs.forEach( - (templateRef) => setTemplateRef(templateRef, null, setupState) - ); - } - const check = $mpPlatform === "mp-baidu" || $mpPlatform === "mp-toutiao"; - const doSetByRefs = (refs) => { - const mpComponents = ( - // 字节小程序 selectAllComponents 可能返回 null - // https://github.com/dcloudio/uni-app/issues/3954 - ($scope.selectAllComponents(".r") || []).concat( - $scope.selectAllComponents(".r-i-f") || [] - ) - ); - return refs.filter((templateRef) => { - const refValue = findComponentPublicInstance(mpComponents, templateRef.i); - if (check && refValue === null) { - return true; - } - setTemplateRef(templateRef, refValue, setupState); - return false; - }); - }; - const doSet = () => { - const refs = doSetByRefs($templateRefs); - if (refs.length && instance.proxy && instance.proxy.$scope) { - instance.proxy.$scope.setData({ r1: 1 }, () => { - doSetByRefs(refs); - }); - } - }; - if ($scope._$setRef) { - $scope._$setRef(doSet); - } else { - nextTick(instance, doSet); - } -} -function toSkip(value) { - if (isObject$1(value)) { - markRaw(value); - } - return value; -} -function findComponentPublicInstance(mpComponents, id) { - const mpInstance = mpComponents.find( - (com) => com && (com.properties || com.props).uI === id - ); - if (mpInstance) { - const vm = mpInstance.$vm; - if (vm) { - return getExposeProxy(vm.$) || vm; - } - return toSkip(mpInstance); - } - return null; -} -function setTemplateRef({ r: r2, f: f2 }, refValue, setupState) { - if (isFunction(r2)) { - r2(refValue, {}); - } else { - const _isString = isString(r2); - const _isRef = isRef(r2); - if (_isString || _isRef) { - if (f2) { - if (!_isRef) { - return; - } - if (!isArray(r2.value)) { - r2.value = []; - } - const existing = r2.value; - if (existing.indexOf(refValue) === -1) { - existing.push(refValue); - if (!refValue) { - return; - } - onBeforeUnmount(() => remove(existing, refValue), refValue.$); - } - } else if (_isString) { - if (hasOwn$1(setupState, r2)) { - setupState[r2] = refValue; - } - } else if (isRef(r2)) { - r2.value = refValue; - } else { - warnRef(r2); - } - } else { - warnRef(r2); - } - } -} -function warnRef(ref2) { - warn("Invalid template ref type:", ref2, `(${typeof ref2})`); -} -const queuePostRenderEffect = queuePostFlushCb; -function mountComponent(initialVNode, options) { - const instance = initialVNode.component = createComponentInstance(initialVNode, options.parentComponent, null); - { - instance.ctx.$onApplyOptions = onApplyOptions; - instance.ctx.$children = []; - } - if (options.mpType === "app") { - instance.render = NOOP; - } - if (options.onBeforeSetup) { - options.onBeforeSetup(instance, options); - } - { - pushWarningContext(initialVNode); - startMeasure(instance, `mount`); - } - { - startMeasure(instance, `init`); - } - setupComponent(instance); - { - endMeasure(instance, `init`); - } - { - if (options.parentComponent && instance.proxy) { - options.parentComponent.ctx.$children.push(getExposeProxy(instance) || instance.proxy); - } - } - setupRenderEffect(instance); - { - popWarningContext(); - endMeasure(instance, `mount`); - } - return instance.proxy; -} -const getFunctionalFallthrough = (attrs) => { - let res; - for (const key in attrs) { - if (key === "class" || key === "style" || isOn(key)) { - (res || (res = {}))[key] = attrs[key]; - } - } - return res; -}; -function renderComponentRoot(instance) { - const { - type: Component2, - vnode, - proxy, - withProxy, - props, - propsOptions: [propsOptions], - slots, - attrs, - emit: emit2, - render, - renderCache, - data, - setupState, - ctx, - uid: uid2, - appContext: { - app: { - config: { - globalProperties: { pruneComponentPropsCache: pruneComponentPropsCache2 } - } - } - }, - inheritAttrs - } = instance; - instance.$templateRefs = []; - instance.$ei = 0; - pruneComponentPropsCache2(uid2); - instance.__counter = instance.__counter === 0 ? 1 : 0; - let result; - const prev = setCurrentRenderingInstance(instance); - try { - if (vnode.shapeFlag & 4) { - fallthroughAttrs(inheritAttrs, props, propsOptions, attrs); - const proxyToUse = withProxy || proxy; - result = render.call( - proxyToUse, - proxyToUse, - renderCache, - props, - setupState, - data, - ctx - ); - } else { - fallthroughAttrs( - inheritAttrs, - props, - propsOptions, - Component2.props ? attrs : getFunctionalFallthrough(attrs) - ); - const render2 = Component2; - result = render2.length > 1 ? render2(props, { attrs, slots, emit: emit2 }) : render2( - props, - null - /* we know it doesn't need it */ - ); - } - } catch (err) { - handleError(err, instance, 1); - result = false; - } - setRef$1(instance); - setCurrentRenderingInstance(prev); - return result; -} -function fallthroughAttrs(inheritAttrs, props, propsOptions, fallthroughAttrs2) { - if (props && fallthroughAttrs2 && inheritAttrs !== false) { - const keys = Object.keys(fallthroughAttrs2).filter( - (key) => key !== "class" && key !== "style" - ); - if (!keys.length) { - return; - } - if (propsOptions && keys.some(isModelListener)) { - keys.forEach((key) => { - if (!isModelListener(key) || !(key.slice(9) in propsOptions)) { - props[key] = fallthroughAttrs2[key]; - } - }); - } else { - keys.forEach((key) => props[key] = fallthroughAttrs2[key]); - } - } -} -const updateComponentPreRender = (instance) => { - pauseTracking(); - flushPreFlushCbs(); - resetTracking(); -}; -function componentUpdateScopedSlotsFn() { - const scopedSlotsData = this.$scopedSlotsData; - if (!scopedSlotsData || scopedSlotsData.length === 0) { - return; - } - const mpInstance = this.ctx.$scope; - const oldData = mpInstance.data; - const diffData = /* @__PURE__ */ Object.create(null); - scopedSlotsData.forEach(({ path, index: index2, data }) => { - const oldScopedSlotData = getValueByDataPath(oldData, path); - const diffPath = isString(index2) ? `${path}.${index2}` : `${path}[${index2}]`; - if (typeof oldScopedSlotData === "undefined" || typeof oldScopedSlotData[index2] === "undefined") { - diffData[diffPath] = data; - } else { - const diffScopedSlotData = diff( - data, - oldScopedSlotData[index2] - ); - Object.keys(diffScopedSlotData).forEach((name) => { - diffData[diffPath + "." + name] = diffScopedSlotData[name]; - }); - } - }); - scopedSlotsData.length = 0; - if (Object.keys(diffData).length) { - mpInstance.setData(diffData); - } -} -function toggleRecurse({ effect: effect2, update }, allowed) { - effect2.allowRecurse = update.allowRecurse = allowed; -} -function setupRenderEffect(instance) { - const updateScopedSlots = componentUpdateScopedSlotsFn.bind( - instance - ); - instance.$updateScopedSlots = () => nextTick$1(() => queueJob(updateScopedSlots)); - const componentUpdateFn = () => { - if (!instance.isMounted) { - onBeforeUnmount(() => { - setRef$1(instance, true); - }, instance); - { - startMeasure(instance, `patch`); - } - patch(instance, renderComponentRoot(instance)); - { - endMeasure(instance, `patch`); - } - { - devtoolsComponentAdded(instance); - } - } else { - const { next, bu, u: u2 } = instance; - { - pushWarningContext(next || instance.vnode); - } - toggleRecurse(instance, false); - updateComponentPreRender(); - if (bu) { - invokeArrayFns$1(bu); - } - toggleRecurse(instance, true); - { - startMeasure(instance, `patch`); - } - patch(instance, renderComponentRoot(instance)); - { - endMeasure(instance, `patch`); - } - if (u2) { - queuePostRenderEffect(u2); - } - { - devtoolsComponentUpdated(instance); - } - { - popWarningContext(); - } - } - }; - const effect2 = instance.effect = new ReactiveEffect2( - componentUpdateFn, - NOOP, - () => queueJob(update), - instance.scope - // track it in component's effect scope - ); - const update = instance.update = () => { - if (effect2.dirty) { - effect2.run(); - } - }; - update.id = instance.uid; - toggleRecurse(instance, true); - { - effect2.onTrack = instance.rtc ? (e2) => invokeArrayFns$1(instance.rtc, e2) : void 0; - effect2.onTrigger = instance.rtg ? (e2) => invokeArrayFns$1(instance.rtg, e2) : void 0; - update.ownerInstance = instance; - } - update(); -} -function unmountComponent(instance) { - const { bum, scope, update, um } = instance; - if (bum) { - invokeArrayFns$1(bum); - } - scope.stop(); - if (update) { - update.active = false; - } - if (um) { - queuePostRenderEffect(um); - } - queuePostRenderEffect(() => { - instance.isUnmounted = true; - }); - { - devtoolsComponentRemoved(instance); - } -} -const oldCreateApp = createAppAPI(); -function getTarget() { - if (typeof window !== "undefined") { - return window; - } - if (typeof globalThis !== "undefined") { - return globalThis; - } - if (typeof global !== "undefined") { - return global; - } - if (typeof my !== "undefined") { - return my; - } -} -function createVueApp(rootComponent, rootProps = null) { - const target = getTarget(); - target.__VUE__ = true; - { - setDevtoolsHook(target.__VUE_DEVTOOLS_GLOBAL_HOOK__, target); - } - const app = oldCreateApp(rootComponent, rootProps); - const appContext = app._context; - initAppConfig(appContext.config); - const createVNode2 = (initialVNode) => { - initialVNode.appContext = appContext; - initialVNode.shapeFlag = 6; - return initialVNode; - }; - const createComponent2 = function createComponent22(initialVNode, options) { - return mountComponent(createVNode2(initialVNode), options); - }; - const destroyComponent = function destroyComponent2(component) { - return component && unmountComponent(component.$); - }; - app.mount = function mount() { - rootComponent.render = NOOP; - const instance = mountComponent( - createVNode2({ type: rootComponent }), - { - mpType: "app", - mpInstance: null, - parentComponent: null, - slots: [], - props: null - } - ); - app._instance = instance.$; - { - devtoolsInitApp(app, version); - } - instance.$app = app; - instance.$createComponent = createComponent2; - instance.$destroyComponent = destroyComponent; - appContext.$appInstance = instance; - return instance; - }; - app.unmount = function unmount() { - warn(`Cannot unmount an app.`); - }; - return app; -} -function injectLifecycleHook(name, hook, publicThis, instance) { - if (isFunction(hook)) { - injectHook(name, hook.bind(publicThis), instance); - } -} -function initHooks$1(options, instance, publicThis) { - const mpType = options.mpType || publicThis.$mpType; - if (!mpType || mpType === "component") { - return; - } - Object.keys(options).forEach((name) => { - if (isUniLifecycleHook(name, options[name], false)) { - const hooks = options[name]; - if (isArray(hooks)) { - hooks.forEach((hook) => injectLifecycleHook(name, hook, publicThis, instance)); - } else { - injectLifecycleHook(name, hooks, publicThis, instance); - } - } - }); -} -function applyOptions$2(options, instance, publicThis) { - initHooks$1(options, instance, publicThis); -} -function set(target, key, val) { - return target[key] = val; -} -function $callMethod(method, ...args) { - const fn = this[method]; - if (fn) { - return fn(...args); - } - console.error(`method ${method} not found`); - return null; -} -function createErrorHandler(app) { - return function errorHandler(err, instance, _info) { - if (!instance) { - throw err; - } - const appInstance = app._instance; - if (!appInstance || !appInstance.proxy) { - throw err; - } - { - appInstance.proxy.$callHook(ON_ERROR, err); - } - }; -} -function mergeAsArray(to, from) { - return to ? [...new Set([].concat(to, from))] : from; -} -function initOptionMergeStrategies(optionMergeStrategies) { - UniLifecycleHooks.forEach((name) => { - optionMergeStrategies[name] = mergeAsArray; - }); -} -let realAtob; -const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; -const b64re = /^(?:[A-Za-z\d+/]{4})*?(?:[A-Za-z\d+/]{2}(?:==)?|[A-Za-z\d+/]{3}=?)?$/; -if (typeof atob !== "function") { - realAtob = function(str) { - str = String(str).replace(/[\t\n\f\r ]+/g, ""); - if (!b64re.test(str)) { - throw new Error("Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded."); - } - str += "==".slice(2 - (str.length & 3)); - var bitmap; - var result = ""; - var r1; - var r2; - var i2 = 0; - for (; i2 < str.length; ) { - bitmap = b64.indexOf(str.charAt(i2++)) << 18 | b64.indexOf(str.charAt(i2++)) << 12 | (r1 = b64.indexOf(str.charAt(i2++))) << 6 | (r2 = b64.indexOf(str.charAt(i2++))); - result += r1 === 64 ? String.fromCharCode(bitmap >> 16 & 255) : r2 === 64 ? String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255) : String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255, bitmap & 255); - } - return result; - }; -} else { - realAtob = atob; -} -function b64DecodeUnicode(str) { - return decodeURIComponent(realAtob(str).split("").map(function(c2) { - return "%" + ("00" + c2.charCodeAt(0).toString(16)).slice(-2); - }).join("")); -} -function getCurrentUserInfo() { - const token = index.getStorageSync("uni_id_token") || ""; - const tokenArr = token.split("."); - if (!token || tokenArr.length !== 3) { - return { - uid: null, - role: [], - permission: [], - tokenExpired: 0 - }; - } - let userInfo; - try { - userInfo = JSON.parse(b64DecodeUnicode(tokenArr[1])); - } catch (error) { - throw new Error("获取当前用户信息出错,详细错误信息为:" + error.message); - } - userInfo.tokenExpired = userInfo.exp * 1e3; - delete userInfo.exp; - delete userInfo.iat; - return userInfo; -} -function uniIdMixin(globalProperties) { - globalProperties.uniIDHasRole = function(roleId) { - const { role } = getCurrentUserInfo(); - return role.indexOf(roleId) > -1; - }; - globalProperties.uniIDHasPermission = function(permissionId) { - const { permission } = getCurrentUserInfo(); - return this.uniIDHasRole("admin") || permission.indexOf(permissionId) > -1; - }; - globalProperties.uniIDTokenValid = function() { - const { tokenExpired } = getCurrentUserInfo(); - return tokenExpired > Date.now(); - }; -} -function initApp(app) { - const appConfig = app._context.config; - appConfig.errorHandler = invokeCreateErrorHandler(app, createErrorHandler); - initOptionMergeStrategies(appConfig.optionMergeStrategies); - const globalProperties = appConfig.globalProperties; - { - uniIdMixin(globalProperties); - } - { - globalProperties.$set = set; - globalProperties.$applyOptions = applyOptions$2; - globalProperties.$callMethod = $callMethod; - } - { - index.invokeCreateVueAppHook(app); - } -} -const propsCaches = /* @__PURE__ */ Object.create(null); -function renderProps(props) { - const { uid: uid2, __counter } = getCurrentInstance(); - const propsId = (propsCaches[uid2] || (propsCaches[uid2] = [])).push(guardReactiveProps(props)) - 1; - return uid2 + "," + propsId + "," + __counter; -} -function pruneComponentPropsCache(uid2) { - delete propsCaches[uid2]; -} -function findComponentPropsData(up) { - if (!up) { - return; - } - const [uid2, propsId] = up.split(","); - if (!propsCaches[uid2]) { - return; - } - return propsCaches[uid2][parseInt(propsId)]; -} -var plugin = { - install(app) { - initApp(app); - app.config.globalProperties.pruneComponentPropsCache = pruneComponentPropsCache; - const oldMount = app.mount; - app.mount = function mount(rootContainer) { - const instance = oldMount.call(app, rootContainer); - const createApp2 = getCreateApp(); - if (createApp2) { - createApp2(instance); - } else { - if (typeof createMiniProgramApp !== "undefined") { - createMiniProgramApp(instance); - } - } - return instance; - }; - } -}; -function getCreateApp() { - const method = "createApp"; - if (typeof global !== "undefined" && typeof global[method] !== "undefined") { - return global[method]; - } else if (typeof my !== "undefined") { - return my[method]; - } -} -function vOn(value, key) { - const instance = getCurrentInstance(); - const ctx = instance.ctx; - const extraKey = typeof key !== "undefined" && (ctx.$mpPlatform === "mp-weixin" || ctx.$mpPlatform === "mp-qq" || ctx.$mpPlatform === "mp-xhs") && (isString(key) || typeof key === "number") ? "_" + key : ""; - const name = "e" + instance.$ei++ + extraKey; - const mpInstance = ctx.$scope; - if (!value) { - delete mpInstance[name]; - return name; - } - const existingInvoker = mpInstance[name]; - if (existingInvoker) { - existingInvoker.value = value; - } else { - mpInstance[name] = createInvoker(value, instance); - } - return name; -} -function createInvoker(initialValue, instance) { - const invoker = (e2) => { - patchMPEvent(e2); - let args = [e2]; - if (e2.detail && e2.detail.__args__) { - args = e2.detail.__args__; - } - const eventValue = invoker.value; - const invoke = () => callWithAsyncErrorHandling(patchStopImmediatePropagation(e2, eventValue), instance, 5, args); - const eventTarget = e2.target; - const eventSync = eventTarget ? eventTarget.dataset ? String(eventTarget.dataset.eventsync) === "true" : false : false; - if (bubbles.includes(e2.type) && !eventSync) { - setTimeout(invoke); - } else { - const res = invoke(); - if (e2.type === "input" && (isArray(res) || isPromise(res))) { - return; - } - return res; - } - }; - invoker.value = initialValue; - return invoker; -} -const bubbles = [ - // touch事件暂不做延迟,否则在 Android 上会影响性能,比如一些拖拽跟手手势等 - // 'touchstart', - // 'touchmove', - // 'touchcancel', - // 'touchend', - "tap", - "longpress", - "longtap", - "transitionend", - "animationstart", - "animationiteration", - "animationend", - "touchforcechange" -]; -function patchMPEvent(event) { - if (event.type && event.target) { - event.preventDefault = NOOP; - event.stopPropagation = NOOP; - event.stopImmediatePropagation = NOOP; - if (!hasOwn$1(event, "detail")) { - event.detail = {}; - } - if (hasOwn$1(event, "markerId")) { - event.detail = typeof event.detail === "object" ? event.detail : {}; - event.detail.markerId = event.markerId; - } - if (isPlainObject(event.detail) && hasOwn$1(event.detail, "checked") && !hasOwn$1(event.detail, "value")) { - event.detail.value = event.detail.checked; - } - if (isPlainObject(event.detail)) { - event.target = extend({}, event.target, event.detail); - } - } -} -function patchStopImmediatePropagation(e2, value) { - if (isArray(value)) { - const originalStop = e2.stopImmediatePropagation; - e2.stopImmediatePropagation = () => { - originalStop && originalStop.call(e2); - e2._stopped = true; - }; - return value.map((fn) => (e3) => !e3._stopped && fn(e3)); - } else { - return value; - } -} -function vFor(source, renderItem) { - let ret; - if (isArray(source) || isString(source)) { - ret = new Array(source.length); - for (let i2 = 0, l2 = source.length; i2 < l2; i2++) { - ret[i2] = renderItem(source[i2], i2, i2); - } - } else if (typeof source === "number") { - if (!Number.isInteger(source)) { - warn(`The v-for range expect an integer value but got ${source}.`); - return []; - } - ret = new Array(source); - for (let i2 = 0; i2 < source; i2++) { - ret[i2] = renderItem(i2 + 1, i2, i2); - } - } else if (isObject$1(source)) { - if (source[Symbol.iterator]) { - ret = Array.from(source, (item, i2) => renderItem(item, i2, i2)); - } else { - const keys = Object.keys(source); - ret = new Array(keys.length); - for (let i2 = 0, l2 = keys.length; i2 < l2; i2++) { - const key = keys[i2]; - ret[i2] = renderItem(source[key], key, i2); - } - } - } else { - ret = []; - } - return ret; -} -function stringifyStyle(value) { - if (isString(value)) { - return value; - } - return stringify(normalizeStyle(value)); -} -function stringify(styles) { - let ret = ""; - if (!styles || isString(styles)) { - return ret; - } - for (const key in styles) { - ret += `${key.startsWith(`--`) ? key : hyphenate(key)}:${styles[key]};`; - } - return ret; -} -function setRef(ref2, id, opts = {}) { - const { $templateRefs } = getCurrentInstance(); - $templateRefs.push({ i: id, r: ref2, k: opts.k, f: opts.f }); -} -const o$1 = (value, key) => vOn(value, key); -const f$1 = (source, renderItem) => vFor(source, renderItem); -const s$1 = (value) => stringifyStyle(value); -const e$1 = (target, ...sources) => extend(target, ...sources); -const n$1 = (value) => normalizeClass(value); -const t$1 = (val) => toDisplayString(val); -const p$1 = (props) => renderProps(props); -const sr = (ref2, id, opts) => setRef(ref2, id, opts); -function createApp$1(rootComponent, rootProps = null) { - rootComponent && (rootComponent.mpType = "app"); - return createVueApp(rootComponent, rootProps).use(plugin); -} -const createSSRApp = createApp$1; -const MP_METHODS = [ - "createSelectorQuery", - "createIntersectionObserver", - "selectAllComponents", - "selectComponent" -]; -function createEmitFn(oldEmit, ctx) { - return function emit2(event, ...args) { - const scope = ctx.$scope; - if (scope && event) { - const detail = { __args__: args }; - { - scope.triggerEvent(event, detail); - } - } - return oldEmit.apply(this, [event, ...args]); - }; -} -function initBaseInstance(instance, options) { - const ctx = instance.ctx; - ctx.mpType = options.mpType; - ctx.$mpType = options.mpType; - ctx.$mpPlatform = "mp-weixin"; - ctx.$scope = options.mpInstance; - ctx.$mp = {}; - { - ctx._self = {}; - } - instance.slots = {}; - if (isArray(options.slots) && options.slots.length) { - options.slots.forEach((name) => { - instance.slots[name] = true; - }); - if (instance.slots[SLOT_DEFAULT_NAME]) { - instance.slots.default = true; - } - } - ctx.getOpenerEventChannel = function() { - { - return options.mpInstance.getOpenerEventChannel(); - } - }; - ctx.$hasHook = hasHook; - ctx.$callHook = callHook; - instance.emit = createEmitFn(instance.emit, ctx); -} -function initComponentInstance(instance, options) { - initBaseInstance(instance, options); - const ctx = instance.ctx; - MP_METHODS.forEach((method) => { - ctx[method] = function(...args) { - const mpInstance = ctx.$scope; - if (mpInstance && mpInstance[method]) { - return mpInstance[method].apply(mpInstance, args); - } - }; - }); -} -function initMocks(instance, mpInstance, mocks2) { - const ctx = instance.ctx; - mocks2.forEach((mock) => { - if (hasOwn$1(mpInstance, mock)) { - instance[mock] = ctx[mock] = mpInstance[mock]; - } - }); -} -function hasHook(name) { - const hooks = this.$[name]; - if (hooks && hooks.length) { - return true; - } - return false; -} -function callHook(name, args) { - if (name === "mounted") { - callHook.call(this, "bm"); - this.$.isMounted = true; - name = "m"; - } - const hooks = this.$[name]; - return hooks && invokeArrayFns(hooks, args); -} -const PAGE_INIT_HOOKS = [ - ON_LOAD, - ON_SHOW, - ON_HIDE, - ON_UNLOAD, - ON_RESIZE, - ON_TAB_ITEM_TAP, - ON_REACH_BOTTOM, - ON_PULL_DOWN_REFRESH, - ON_ADD_TO_FAVORITES - // 'onReady', // lifetimes.ready - // 'onPageScroll', // 影响性能,开发者手动注册 - // 'onShareTimeline', // 右上角菜单,开发者手动注册 - // 'onShareAppMessage' // 右上角菜单,开发者手动注册 -]; -function findHooks(vueOptions, hooks = /* @__PURE__ */ new Set()) { - if (vueOptions) { - Object.keys(vueOptions).forEach((name) => { - if (isUniLifecycleHook(name, vueOptions[name])) { - hooks.add(name); - } - }); - { - const { extends: extendsOptions, mixins } = vueOptions; - if (mixins) { - mixins.forEach((mixin) => findHooks(mixin, hooks)); - } - if (extendsOptions) { - findHooks(extendsOptions, hooks); - } - } - } - return hooks; -} -function initHook(mpOptions, hook, excludes) { - if (excludes.indexOf(hook) === -1 && !hasOwn$1(mpOptions, hook)) { - mpOptions[hook] = function(args) { - return this.$vm && this.$vm.$callHook(hook, args); - }; - } -} -const EXCLUDE_HOOKS = [ON_READY]; -function initHooks(mpOptions, hooks, excludes = EXCLUDE_HOOKS) { - hooks.forEach((hook) => initHook(mpOptions, hook, excludes)); -} -function initUnknownHooks(mpOptions, vueOptions, excludes = EXCLUDE_HOOKS) { - findHooks(vueOptions).forEach((hook) => initHook(mpOptions, hook, excludes)); -} -function initRuntimeHooks(mpOptions, runtimeHooks) { - if (!runtimeHooks) { - return; - } - const hooks = Object.keys(MINI_PROGRAM_PAGE_RUNTIME_HOOKS); - hooks.forEach((hook) => { - if (runtimeHooks & MINI_PROGRAM_PAGE_RUNTIME_HOOKS[hook]) { - initHook(mpOptions, hook, []); - } - }); -} -const findMixinRuntimeHooks = /* @__PURE__ */ once(() => { - const runtimeHooks = []; - const app = isFunction(getApp) && getApp({ allowDefault: true }); - if (app && app.$vm && app.$vm.$) { - const mixins = app.$vm.$.appContext.mixins; - if (isArray(mixins)) { - const hooks = Object.keys(MINI_PROGRAM_PAGE_RUNTIME_HOOKS); - mixins.forEach((mixin) => { - hooks.forEach((hook) => { - if (hasOwn$1(mixin, hook) && !runtimeHooks.includes(hook)) { - runtimeHooks.push(hook); - } - }); - }); - } - } - return runtimeHooks; -}); -function initMixinRuntimeHooks(mpOptions) { - initHooks(mpOptions, findMixinRuntimeHooks()); -} -const HOOKS = [ - ON_SHOW, - ON_HIDE, - ON_ERROR, - ON_THEME_CHANGE, - ON_PAGE_NOT_FOUND, - ON_UNHANDLE_REJECTION -]; -function parseApp(instance, parseAppOptions) { - const internalInstance = instance.$; - const appOptions = { - globalData: instance.$options && instance.$options.globalData || {}, - $vm: instance, - // mp-alipay 组件 data 初始化比 onLaunch 早,提前挂载 - onLaunch(options) { - this.$vm = instance; - const ctx = internalInstance.ctx; - if (this.$vm && ctx.$scope) { - return; - } - initBaseInstance(internalInstance, { - mpType: "app", - mpInstance: this, - slots: [] - }); - ctx.globalData = this.globalData; - instance.$callHook(ON_LAUNCH, options); - } - }; - const { onError } = internalInstance; - if (onError) { - internalInstance.appContext.config.errorHandler = (err) => { - instance.$callHook(ON_ERROR, err); - }; - } - initLocale(instance); - const vueOptions = instance.$.type; - initHooks(appOptions, HOOKS); - initUnknownHooks(appOptions, vueOptions); - { - const methods = vueOptions.methods; - methods && extend(appOptions, methods); - } - if (parseAppOptions) { - parseAppOptions.parse(appOptions); - } - return appOptions; -} -function initCreateApp(parseAppOptions) { - return function createApp2(vm) { - return App(parseApp(vm, parseAppOptions)); - }; -} -function initCreateSubpackageApp(parseAppOptions) { - return function createApp2(vm) { - const appOptions = parseApp(vm, parseAppOptions); - const app = isFunction(getApp) && getApp({ - allowDefault: true - }); - if (!app) - return; - vm.$.ctx.$scope = app; - const globalData = app.globalData; - if (globalData) { - Object.keys(appOptions.globalData).forEach((name) => { - if (!hasOwn$1(globalData, name)) { - globalData[name] = appOptions.globalData[name]; - } - }); - } - Object.keys(appOptions).forEach((name) => { - if (!hasOwn$1(app, name)) { - app[name] = appOptions[name]; - } - }); - initAppLifecycle(appOptions, vm); - }; -} -function initAppLifecycle(appOptions, vm) { - if (isFunction(appOptions.onLaunch)) { - const args = wx.getLaunchOptionsSync && wx.getLaunchOptionsSync(); - appOptions.onLaunch(args); - } - if (isFunction(appOptions.onShow) && wx.onAppShow) { - wx.onAppShow((args) => { - vm.$callHook("onShow", args); - }); - } - if (isFunction(appOptions.onHide) && wx.onAppHide) { - wx.onAppHide((args) => { - vm.$callHook("onHide", args); - }); - } -} -function initLocale(appVm) { - const locale = ref(normalizeLocale(wx.getSystemInfoSync().language) || LOCALE_EN); - Object.defineProperty(appVm, "$locale", { - get() { - return locale.value; - }, - set(v2) { - locale.value = v2; - } - }); -} -function initVueIds(vueIds, mpInstance) { - if (!vueIds) { - return; - } - const ids = vueIds.split(","); - const len = ids.length; - if (len === 1) { - mpInstance._$vueId = ids[0]; - } else if (len === 2) { - mpInstance._$vueId = ids[0]; - mpInstance._$vuePid = ids[1]; - } -} -const EXTRAS = ["externalClasses"]; -function initExtraOptions(miniProgramComponentOptions, vueOptions) { - EXTRAS.forEach((name) => { - if (hasOwn$1(vueOptions, name)) { - miniProgramComponentOptions[name] = vueOptions[name]; - } - }); -} -const WORKLET_RE = /_(.*)_worklet_factory_/; -function initWorkletMethods(mpMethods, vueMethods) { - if (vueMethods) { - Object.keys(vueMethods).forEach((name) => { - const matches = name.match(WORKLET_RE); - if (matches) { - const workletName = matches[1]; - mpMethods[name] = vueMethods[name]; - mpMethods[workletName] = vueMethods[workletName]; - } - }); - } -} -function initWxsCallMethods(methods, wxsCallMethods) { - if (!isArray(wxsCallMethods)) { - return; - } - wxsCallMethods.forEach((callMethod) => { - methods[callMethod] = function(args) { - return this.$vm[callMethod](args); - }; - }); -} -function selectAllComponents(mpInstance, selector, $refs) { - const components = mpInstance.selectAllComponents(selector); - components.forEach((component) => { - const ref2 = component.properties.uR; - $refs[ref2] = component.$vm || component; - }); -} -function initRefs(instance, mpInstance) { - Object.defineProperty(instance, "refs", { - get() { - const $refs = {}; - selectAllComponents(mpInstance, ".r", $refs); - const forComponents = mpInstance.selectAllComponents(".r-i-f"); - forComponents.forEach((component) => { - const ref2 = component.properties.uR; - if (!ref2) { - return; - } - if (!$refs[ref2]) { - $refs[ref2] = []; - } - $refs[ref2].push(component.$vm || component); - }); - return $refs; - } - }); -} -function findVmByVueId(instance, vuePid) { - const $children = instance.$children; - for (let i2 = $children.length - 1; i2 >= 0; i2--) { - const childVm = $children[i2]; - if (childVm.$scope._$vueId === vuePid) { - return childVm; - } - } - let parentVm; - for (let i2 = $children.length - 1; i2 >= 0; i2--) { - parentVm = findVmByVueId($children[i2], vuePid); - if (parentVm) { - return parentVm; - } - } -} -const builtInProps = [ - // 百度小程序,快手小程序自定义组件不支持绑定动态事件,动态dataset,故通过props传递事件信息 - // event-opts - "eO", - // 组件 ref - "uR", - // 组件 ref-in-for - "uRIF", - // 组件 id - "uI", - // 组件类型 m: 小程序组件 - "uT", - // 组件 props - "uP", - // 小程序不能直接定义 $slots 的 props,所以通过 vueSlots 转换到 $slots - "uS" -]; -function initDefaultProps(options, isBehavior = false) { - const properties = {}; - if (!isBehavior) { - builtInProps.forEach((name) => { - properties[name] = { - type: null, - value: "" - }; - }); - properties.uS = { - type: null, - value: [], - observer: function(newVal) { - const $slots = /* @__PURE__ */ Object.create(null); - newVal && newVal.forEach((slotName) => { - $slots[slotName] = true; - }); - this.setData({ - $slots - }); - } - }; - } - if (options.behaviors) { - if (options.behaviors.includes("wx://form-field")) { - if (!options.properties || !options.properties.name) { - properties.name = { - type: null, - value: "" - }; - } - if (!options.properties || !options.properties.value) { - properties.value = { - type: null, - value: "" - }; - } - } - } - return properties; -} -function initVirtualHostProps(options) { - const properties = {}; - { - if (options && options.virtualHost) { - properties.virtualHostStyle = { - type: null, - value: "" - }; - properties.virtualHostClass = { - type: null, - value: "" - }; - } - } - return properties; -} -function initProps(mpComponentOptions) { - if (!mpComponentOptions.properties) { - mpComponentOptions.properties = {}; - } - extend(mpComponentOptions.properties, initDefaultProps(mpComponentOptions), initVirtualHostProps(mpComponentOptions.options)); -} -const PROP_TYPES = [String, Number, Boolean, Object, Array, null]; -function parsePropType(type, defaultValue) { - if (isArray(type) && type.length === 1) { - return type[0]; - } - return type; -} -function normalizePropType(type, defaultValue) { - const res = parsePropType(type); - return PROP_TYPES.indexOf(res) !== -1 ? res : null; -} -function initPageProps({ properties }, rawProps) { - if (isArray(rawProps)) { - rawProps.forEach((key) => { - properties[key] = { - type: String, - value: "" - }; - }); - } else if (isPlainObject(rawProps)) { - Object.keys(rawProps).forEach((key) => { - const opts = rawProps[key]; - if (isPlainObject(opts)) { - let value = opts.default; - if (isFunction(value)) { - value = value(); - } - const type = opts.type; - opts.type = normalizePropType(type); - properties[key] = { - type: opts.type, - value - }; - } else { - properties[key] = { - type: normalizePropType(opts) - }; - } - }); - } -} -function findPropsData(properties, isPage2) { - return (isPage2 ? findPagePropsData(properties) : findComponentPropsData(properties.uP)) || {}; -} -function findPagePropsData(properties) { - const propsData = {}; - if (isPlainObject(properties)) { - Object.keys(properties).forEach((name) => { - if (builtInProps.indexOf(name) === -1) { - propsData[name] = properties[name]; - } - }); - } - return propsData; -} -function initFormField(vm) { - const vueOptions = vm.$options; - if (isArray(vueOptions.behaviors) && vueOptions.behaviors.includes("uni://form-field")) { - vm.$watch("modelValue", () => { - vm.$scope && vm.$scope.setData({ - name: vm.name, - value: vm.modelValue - }); - }, { - immediate: true - }); - } -} -function initData(_2) { - return {}; -} -function initPropsObserver(componentOptions) { - const observe = function observe2() { - const up = this.properties.uP; - if (!up) { - return; - } - if (this.$vm) { - updateComponentProps(up, this.$vm.$); - } else if (this.properties.uT === "m") { - updateMiniProgramComponentProperties(up, this); - } - }; - { - if (!componentOptions.observers) { - componentOptions.observers = {}; - } - componentOptions.observers.uP = observe; - } -} -function updateMiniProgramComponentProperties(up, mpInstance) { - const prevProps = mpInstance.properties; - const nextProps = findComponentPropsData(up) || {}; - if (hasPropsChanged(prevProps, nextProps, false)) { - mpInstance.setData(nextProps); - } -} -function updateComponentProps(up, instance) { - const prevProps = toRaw(instance.props); - const nextProps = findComponentPropsData(up) || {}; - if (hasPropsChanged(prevProps, nextProps)) { - updateProps(instance, nextProps, prevProps, false); - if (hasQueueJob(instance.update)) { - invalidateJob(instance.update); - } - { - instance.update(); - } - } -} -function hasPropsChanged(prevProps, nextProps, checkLen = true) { - const nextKeys = Object.keys(nextProps); - if (checkLen && nextKeys.length !== Object.keys(prevProps).length) { - return true; - } - for (let i2 = 0; i2 < nextKeys.length; i2++) { - const key = nextKeys[i2]; - if (nextProps[key] !== prevProps[key]) { - return true; - } - } - return false; -} -function initBehaviors(vueOptions) { - const vueBehaviors = vueOptions.behaviors; - let vueProps = vueOptions.props; - if (!vueProps) { - vueOptions.props = vueProps = []; - } - const behaviors = []; - if (isArray(vueBehaviors)) { - vueBehaviors.forEach((behavior) => { - behaviors.push(behavior.replace("uni://", "wx://")); - if (behavior === "uni://form-field") { - if (isArray(vueProps)) { - vueProps.push("name"); - vueProps.push("modelValue"); - } else { - vueProps.name = { - type: String, - default: "" - }; - vueProps.modelValue = { - type: [String, Number, Boolean, Array, Object, Date], - default: "" - }; - } - } - }); - } - return behaviors; -} -function applyOptions(componentOptions, vueOptions) { - componentOptions.data = initData(); - componentOptions.behaviors = initBehaviors(vueOptions); -} -function parseComponent(vueOptions, { parse: parse2, mocks: mocks2, isPage: isPage2, initRelation: initRelation2, handleLink: handleLink2, initLifetimes: initLifetimes2 }) { - vueOptions = vueOptions.default || vueOptions; - const options = { - multipleSlots: true, - // styleIsolation: 'apply-shared', - addGlobalClass: true, - pureDataPattern: /^uP$/ - }; - if (isArray(vueOptions.mixins)) { - vueOptions.mixins.forEach((item) => { - if (isObject$1(item.options)) { - extend(options, item.options); - } - }); - } - if (vueOptions.options) { - extend(options, vueOptions.options); - } - const mpComponentOptions = { - options, - lifetimes: initLifetimes2({ mocks: mocks2, isPage: isPage2, initRelation: initRelation2, vueOptions }), - pageLifetimes: { - show() { - this.$vm && this.$vm.$callHook("onPageShow"); - }, - hide() { - this.$vm && this.$vm.$callHook("onPageHide"); - }, - resize(size2) { - this.$vm && this.$vm.$callHook("onPageResize", size2); - } - }, - methods: { - __l: handleLink2 - } - }; - { - applyOptions(mpComponentOptions, vueOptions); - } - initProps(mpComponentOptions); - initPropsObserver(mpComponentOptions); - initExtraOptions(mpComponentOptions, vueOptions); - initWxsCallMethods(mpComponentOptions.methods, vueOptions.wxsCallMethods); - { - initWorkletMethods(mpComponentOptions.methods, vueOptions.methods); - } - if (parse2) { - parse2(mpComponentOptions, { handleLink: handleLink2 }); - } - return mpComponentOptions; -} -function initCreateComponent(parseOptions2) { - return function createComponent2(vueComponentOptions) { - return Component(parseComponent(vueComponentOptions, parseOptions2)); - }; -} -let $createComponentFn; -let $destroyComponentFn; -function getAppVm() { - return getApp().$vm; -} -function $createComponent(initialVNode, options) { - if (!$createComponentFn) { - $createComponentFn = getAppVm().$createComponent; - } - const proxy = $createComponentFn(initialVNode, options); - return getExposeProxy(proxy.$) || proxy; -} -function $destroyComponent(instance) { - if (!$destroyComponentFn) { - $destroyComponentFn = getAppVm().$destroyComponent; - } - return $destroyComponentFn(instance); -} -function parsePage(vueOptions, parseOptions2) { - const { parse: parse2, mocks: mocks2, isPage: isPage2, initRelation: initRelation2, handleLink: handleLink2, initLifetimes: initLifetimes2 } = parseOptions2; - const miniProgramPageOptions = parseComponent(vueOptions, { - mocks: mocks2, - isPage: isPage2, - initRelation: initRelation2, - handleLink: handleLink2, - initLifetimes: initLifetimes2 - }); - initPageProps(miniProgramPageOptions, (vueOptions.default || vueOptions).props); - const methods = miniProgramPageOptions.methods; - methods.onLoad = function(query) { - this.options = query; - this.$page = { - fullPath: addLeadingSlash(this.route + stringifyQuery(query)) - }; - return this.$vm && this.$vm.$callHook(ON_LOAD, query); - }; - initHooks(methods, PAGE_INIT_HOOKS); - { - initUnknownHooks(methods, vueOptions); - } - initRuntimeHooks(methods, vueOptions.__runtimeHooks); - initMixinRuntimeHooks(methods); - parse2 && parse2(miniProgramPageOptions, { handleLink: handleLink2 }); - return miniProgramPageOptions; -} -function initCreatePage(parseOptions2) { - return function createPage2(vuePageOptions) { - return Component(parsePage(vuePageOptions, parseOptions2)); - }; -} -function initCreatePluginApp(parseAppOptions) { - return function createApp2(vm) { - initAppLifecycle(parseApp(vm, parseAppOptions), vm); - }; -} -const MPPage = Page; -const MPComponent = Component; -function initTriggerEvent(mpInstance) { - const oldTriggerEvent = mpInstance.triggerEvent; - const newTriggerEvent = function(event, ...args) { - return oldTriggerEvent.apply(mpInstance, [customizeEvent(event), ...args]); - }; - try { - mpInstance.triggerEvent = newTriggerEvent; - } catch (error) { - mpInstance._triggerEvent = newTriggerEvent; - } -} -function initMiniProgramHook(name, options, isComponent) { - const oldHook = options[name]; - if (!oldHook) { - options[name] = function() { - initTriggerEvent(this); - }; - } else { - options[name] = function(...args) { - initTriggerEvent(this); - return oldHook.apply(this, args); - }; - } -} -Page = function(options) { - initMiniProgramHook(ON_LOAD, options); - return MPPage(options); -}; -Component = function(options) { - initMiniProgramHook("created", options); - const isVueComponent = options.properties && options.properties.uP; - if (!isVueComponent) { - initProps(options); - initPropsObserver(options); - } - return MPComponent(options); -}; -function initLifetimes({ mocks: mocks2, isPage: isPage2, initRelation: initRelation2, vueOptions }) { - return { - attached() { - let properties = this.properties; - initVueIds(properties.uI, this); - const relationOptions = { - vuePid: this._$vuePid - }; - initRelation2(this, relationOptions); - const mpInstance = this; - const isMiniProgramPage = isPage2(mpInstance); - let propsData = properties; - this.$vm = $createComponent({ - type: vueOptions, - props: findPropsData(propsData, isMiniProgramPage) - }, { - mpType: isMiniProgramPage ? "page" : "component", - mpInstance, - slots: properties.uS || {}, - // vueSlots - parentComponent: relationOptions.parent && relationOptions.parent.$, - onBeforeSetup(instance, options) { - initRefs(instance, mpInstance); - initMocks(instance, mpInstance, mocks2); - initComponentInstance(instance, options); - } - }); - if (!isMiniProgramPage) { - initFormField(this.$vm); - } - }, - ready() { - if (this.$vm) { - { - this.$vm.$callHook("mounted"); - this.$vm.$callHook(ON_READY); - } - } - }, - detached() { - if (this.$vm) { - pruneComponentPropsCache(this.$vm.$.uid); - $destroyComponent(this.$vm); - } - } - }; -} -const mocks = ["__route__", "__wxExparserNodeId__", "__wxWebviewId__"]; -function isPage(mpInstance) { - return !!mpInstance.route; -} -function initRelation(mpInstance, detail) { - mpInstance.triggerEvent("__l", detail); -} -function handleLink(event) { - const detail = event.detail || event.value; - const vuePid = detail.vuePid; - let parentVm; - if (vuePid) { - parentVm = findVmByVueId(this.$vm, vuePid); - } - if (!parentVm) { - parentVm = this.$vm; - } - detail.parent = parentVm; -} -var parseOptions = /* @__PURE__ */ Object.freeze({ - __proto__: null, - handleLink, - initLifetimes, - initRelation, - isPage, - mocks -}); -const createApp = initCreateApp(); -const createPage = initCreatePage(parseOptions); -const createComponent = initCreateComponent(parseOptions); -const createPluginApp = initCreatePluginApp(); -const createSubpackageApp = initCreateSubpackageApp(); -{ - wx.createApp = global.createApp = createApp; - wx.createPage = createPage; - wx.createComponent = createComponent; - wx.createPluginApp = global.createPluginApp = createPluginApp; - wx.createSubpackageApp = global.createSubpackageApp = createSubpackageApp; -} -const createHook = (lifecycle) => (hook, target = getCurrentInstance()) => { - !isInSSRComponentSetup && injectHook(lifecycle, hook, target); -}; -const onShow = /* @__PURE__ */ createHook(ON_SHOW); -const onLaunch = /* @__PURE__ */ createHook(ON_LAUNCH); -const onLoad = /* @__PURE__ */ createHook(ON_LOAD); -const pages = [ - { - path: "pages/login/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/index/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/addCard/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/userInfo/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/functionList/balanceRecord/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/functionList/paymentRecord/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/functionList/rechargeRecord/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/functionList/share/cardShare/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/functionList/share/shareConfirm/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/functionList/share/shareList/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/scanWater/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/recharge/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/sendWater/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/preSendWater/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/address/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/addressAdd/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/addressLocate/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/success/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/facilityList/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/station/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/infoBreakdown/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/scanRecharge/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/scanSuccess/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/counterRecharge/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pages/wxScanWater/index", - style: { - navigationStyle: "custom" - } - } -]; -const subPackages = [ - { - root: "pagesAdmin", - pages: [ - { - path: "adminPlatform/home", - style: { - navigationStyle: "custom" - } - }, - { - path: "adminPlatform/facility", - style: { - navigationStyle: "custom" - } - }, - { - path: "adminPlatform/facilityDetail", - style: { - navigationStyle: "custom" - } - }, - { - path: "adminPlatform/inspect", - style: { - navigationStyle: "custom" - } - }, - { - path: "adminPlatform/params", - style: { - navigationStyle: "custom" - } - }, - { - path: "adminPlatform/params1", - style: { - navigationStyle: "custom" - } - }, - { - path: "adminPlatform/breakdown", - style: { - navigationStyle: "custom" - } - }, - { - path: "adminPlatform/maintain", - style: { - navigationStyle: "custom" - } - } - ] - }, - { - root: "pagesPoints", - pages: [ - { - path: "pointsMall/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pointsMall/pointsGetRecord/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pointsMall/pointsExchangeRecord/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pointsMall/pointsGoodsDetail/index", - style: { - navigationStyle: "custom" - } - }, - { - path: "pointsMall/pointsrule/index", - style: { - navigationStyle: "custom" - } - } - ] - } -]; -const globalStyle = { - navigationStyle: "custom" -}; -const easycom = { - autoscan: true, - custom: { - "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue" - } -}; -const e = { - pages, - subPackages, - globalStyle, - easycom -}; -var define_process_env_UNI_SECURE_NETWORK_CONFIG_default = []; -function t(e2) { - return e2 && e2.__esModule && Object.prototype.hasOwnProperty.call(e2, "default") ? e2.default : e2; -} -function n(e2, t2, n2) { - return e2(n2 = { path: t2, exports: {}, require: function(e3, t3) { - return function() { - throw new Error("Dynamic requires are not currently supported by @rollup/plugin-commonjs"); - }(null == t3 && n2.path); - } }, n2.exports), n2.exports; -} -var s = n(function(e2, t2) { - var n2; - e2.exports = (n2 = n2 || function(e3, t3) { - var n3 = Object.create || /* @__PURE__ */ function() { - function e4() { - } - return function(t4) { - var n4; - return e4.prototype = t4, n4 = new e4(), e4.prototype = null, n4; - }; - }(), s2 = {}, r2 = s2.lib = {}, i2 = r2.Base = { extend: function(e4) { - var t4 = n3(this); - return e4 && t4.mixIn(e4), t4.hasOwnProperty("init") && this.init !== t4.init || (t4.init = function() { - t4.$super.init.apply(this, arguments); - }), t4.init.prototype = t4, t4.$super = this, t4; - }, create: function() { - var e4 = this.extend(); - return e4.init.apply(e4, arguments), e4; - }, init: function() { - }, mixIn: function(e4) { - for (var t4 in e4) - e4.hasOwnProperty(t4) && (this[t4] = e4[t4]); - e4.hasOwnProperty("toString") && (this.toString = e4.toString); - }, clone: function() { - return this.init.prototype.extend(this); - } }, o2 = r2.WordArray = i2.extend({ init: function(e4, n4) { - e4 = this.words = e4 || [], this.sigBytes = n4 != t3 ? n4 : 4 * e4.length; - }, toString: function(e4) { - return (e4 || c2).stringify(this); - }, concat: function(e4) { - var t4 = this.words, n4 = e4.words, s3 = this.sigBytes, r3 = e4.sigBytes; - if (this.clamp(), s3 % 4) - for (var i3 = 0; i3 < r3; i3++) { - var o3 = n4[i3 >>> 2] >>> 24 - i3 % 4 * 8 & 255; - t4[s3 + i3 >>> 2] |= o3 << 24 - (s3 + i3) % 4 * 8; - } - else - for (i3 = 0; i3 < r3; i3 += 4) - t4[s3 + i3 >>> 2] = n4[i3 >>> 2]; - return this.sigBytes += r3, this; - }, clamp: function() { - var t4 = this.words, n4 = this.sigBytes; - t4[n4 >>> 2] &= 4294967295 << 32 - n4 % 4 * 8, t4.length = e3.ceil(n4 / 4); - }, clone: function() { - var e4 = i2.clone.call(this); - return e4.words = this.words.slice(0), e4; - }, random: function(t4) { - for (var n4, s3 = [], r3 = function(t5) { - t5 = t5; - var n5 = 987654321, s4 = 4294967295; - return function() { - var r4 = ((n5 = 36969 * (65535 & n5) + (n5 >> 16) & s4) << 16) + (t5 = 18e3 * (65535 & t5) + (t5 >> 16) & s4) & s4; - return r4 /= 4294967296, (r4 += 0.5) * (e3.random() > 0.5 ? 1 : -1); - }; - }, i3 = 0; i3 < t4; i3 += 4) { - var a3 = r3(4294967296 * (n4 || e3.random())); - n4 = 987654071 * a3(), s3.push(4294967296 * a3() | 0); - } - return new o2.init(s3, t4); - } }), a2 = s2.enc = {}, c2 = a2.Hex = { stringify: function(e4) { - for (var t4 = e4.words, n4 = e4.sigBytes, s3 = [], r3 = 0; r3 < n4; r3++) { - var i3 = t4[r3 >>> 2] >>> 24 - r3 % 4 * 8 & 255; - s3.push((i3 >>> 4).toString(16)), s3.push((15 & i3).toString(16)); - } - return s3.join(""); - }, parse: function(e4) { - for (var t4 = e4.length, n4 = [], s3 = 0; s3 < t4; s3 += 2) - n4[s3 >>> 3] |= parseInt(e4.substr(s3, 2), 16) << 24 - s3 % 8 * 4; - return new o2.init(n4, t4 / 2); - } }, u2 = a2.Latin1 = { stringify: function(e4) { - for (var t4 = e4.words, n4 = e4.sigBytes, s3 = [], r3 = 0; r3 < n4; r3++) { - var i3 = t4[r3 >>> 2] >>> 24 - r3 % 4 * 8 & 255; - s3.push(String.fromCharCode(i3)); - } - return s3.join(""); - }, parse: function(e4) { - for (var t4 = e4.length, n4 = [], s3 = 0; s3 < t4; s3++) - n4[s3 >>> 2] |= (255 & e4.charCodeAt(s3)) << 24 - s3 % 4 * 8; - return new o2.init(n4, t4); - } }, l2 = a2.Utf8 = { stringify: function(e4) { - try { - return decodeURIComponent(escape(u2.stringify(e4))); - } catch (e5) { - throw new Error("Malformed UTF-8 data"); - } - }, parse: function(e4) { - return u2.parse(unescape(encodeURIComponent(e4))); - } }, h2 = r2.BufferedBlockAlgorithm = i2.extend({ reset: function() { - this._data = new o2.init(), this._nDataBytes = 0; - }, _append: function(e4) { - "string" == typeof e4 && (e4 = l2.parse(e4)), this._data.concat(e4), this._nDataBytes += e4.sigBytes; - }, _process: function(t4) { - var n4 = this._data, s3 = n4.words, r3 = n4.sigBytes, i3 = this.blockSize, a3 = r3 / (4 * i3), c3 = (a3 = t4 ? e3.ceil(a3) : e3.max((0 | a3) - this._minBufferSize, 0)) * i3, u3 = e3.min(4 * c3, r3); - if (c3) { - for (var l3 = 0; l3 < c3; l3 += i3) - this._doProcessBlock(s3, l3); - var h3 = s3.splice(0, c3); - n4.sigBytes -= u3; - } - return new o2.init(h3, u3); - }, clone: function() { - var e4 = i2.clone.call(this); - return e4._data = this._data.clone(), e4; - }, _minBufferSize: 0 }); - r2.Hasher = h2.extend({ cfg: i2.extend(), init: function(e4) { - this.cfg = this.cfg.extend(e4), this.reset(); - }, reset: function() { - h2.reset.call(this), this._doReset(); - }, update: function(e4) { - return this._append(e4), this._process(), this; - }, finalize: function(e4) { - return e4 && this._append(e4), this._doFinalize(); - }, blockSize: 16, _createHelper: function(e4) { - return function(t4, n4) { - return new e4.init(n4).finalize(t4); - }; - }, _createHmacHelper: function(e4) { - return function(t4, n4) { - return new d2.HMAC.init(e4, n4).finalize(t4); - }; - } }); - var d2 = s2.algo = {}; - return s2; - }(Math), n2); -}), r = s, i = (n(function(e2, t2) { - var n2; - e2.exports = (n2 = r, function(e3) { - var t3 = n2, s2 = t3.lib, r2 = s2.WordArray, i2 = s2.Hasher, o2 = t3.algo, a2 = []; - !function() { - for (var t4 = 0; t4 < 64; t4++) - a2[t4] = 4294967296 * e3.abs(e3.sin(t4 + 1)) | 0; - }(); - var c2 = o2.MD5 = i2.extend({ _doReset: function() { - this._hash = new r2.init([1732584193, 4023233417, 2562383102, 271733878]); - }, _doProcessBlock: function(e4, t4) { - for (var n3 = 0; n3 < 16; n3++) { - var s3 = t4 + n3, r3 = e4[s3]; - e4[s3] = 16711935 & (r3 << 8 | r3 >>> 24) | 4278255360 & (r3 << 24 | r3 >>> 8); - } - var i3 = this._hash.words, o3 = e4[t4 + 0], c3 = e4[t4 + 1], p2 = e4[t4 + 2], f2 = e4[t4 + 3], g2 = e4[t4 + 4], m2 = e4[t4 + 5], y2 = e4[t4 + 6], _2 = e4[t4 + 7], w2 = e4[t4 + 8], v2 = e4[t4 + 9], I2 = e4[t4 + 10], S2 = e4[t4 + 11], b2 = e4[t4 + 12], k2 = e4[t4 + 13], A2 = e4[t4 + 14], C2 = e4[t4 + 15], P2 = i3[0], T2 = i3[1], x2 = i3[2], O2 = i3[3]; - P2 = u2(P2, T2, x2, O2, o3, 7, a2[0]), O2 = u2(O2, P2, T2, x2, c3, 12, a2[1]), x2 = u2(x2, O2, P2, T2, p2, 17, a2[2]), T2 = u2(T2, x2, O2, P2, f2, 22, a2[3]), P2 = u2(P2, T2, x2, O2, g2, 7, a2[4]), O2 = u2(O2, P2, T2, x2, m2, 12, a2[5]), x2 = u2(x2, O2, P2, T2, y2, 17, a2[6]), T2 = u2(T2, x2, O2, P2, _2, 22, a2[7]), P2 = u2(P2, T2, x2, O2, w2, 7, a2[8]), O2 = u2(O2, P2, T2, x2, v2, 12, a2[9]), x2 = u2(x2, O2, P2, T2, I2, 17, a2[10]), T2 = u2(T2, x2, O2, P2, S2, 22, a2[11]), P2 = u2(P2, T2, x2, O2, b2, 7, a2[12]), O2 = u2(O2, P2, T2, x2, k2, 12, a2[13]), x2 = u2(x2, O2, P2, T2, A2, 17, a2[14]), P2 = l2(P2, T2 = u2(T2, x2, O2, P2, C2, 22, a2[15]), x2, O2, c3, 5, a2[16]), O2 = l2(O2, P2, T2, x2, y2, 9, a2[17]), x2 = l2(x2, O2, P2, T2, S2, 14, a2[18]), T2 = l2(T2, x2, O2, P2, o3, 20, a2[19]), P2 = l2(P2, T2, x2, O2, m2, 5, a2[20]), O2 = l2(O2, P2, T2, x2, I2, 9, a2[21]), x2 = l2(x2, O2, P2, T2, C2, 14, a2[22]), T2 = l2(T2, x2, O2, P2, g2, 20, a2[23]), P2 = l2(P2, T2, x2, O2, v2, 5, a2[24]), O2 = l2(O2, P2, T2, x2, A2, 9, a2[25]), x2 = l2(x2, O2, P2, T2, f2, 14, a2[26]), T2 = l2(T2, x2, O2, P2, w2, 20, a2[27]), P2 = l2(P2, T2, x2, O2, k2, 5, a2[28]), O2 = l2(O2, P2, T2, x2, p2, 9, a2[29]), x2 = l2(x2, O2, P2, T2, _2, 14, a2[30]), P2 = h2(P2, T2 = l2(T2, x2, O2, P2, b2, 20, a2[31]), x2, O2, m2, 4, a2[32]), O2 = h2(O2, P2, T2, x2, w2, 11, a2[33]), x2 = h2(x2, O2, P2, T2, S2, 16, a2[34]), T2 = h2(T2, x2, O2, P2, A2, 23, a2[35]), P2 = h2(P2, T2, x2, O2, c3, 4, a2[36]), O2 = h2(O2, P2, T2, x2, g2, 11, a2[37]), x2 = h2(x2, O2, P2, T2, _2, 16, a2[38]), T2 = h2(T2, x2, O2, P2, I2, 23, a2[39]), P2 = h2(P2, T2, x2, O2, k2, 4, a2[40]), O2 = h2(O2, P2, T2, x2, o3, 11, a2[41]), x2 = h2(x2, O2, P2, T2, f2, 16, a2[42]), T2 = h2(T2, x2, O2, P2, y2, 23, a2[43]), P2 = h2(P2, T2, x2, O2, v2, 4, a2[44]), O2 = h2(O2, P2, T2, x2, b2, 11, a2[45]), x2 = h2(x2, O2, P2, T2, C2, 16, a2[46]), P2 = d2(P2, T2 = h2(T2, x2, O2, P2, p2, 23, a2[47]), x2, O2, o3, 6, a2[48]), O2 = d2(O2, P2, T2, x2, _2, 10, a2[49]), x2 = d2(x2, O2, P2, T2, A2, 15, a2[50]), T2 = d2(T2, x2, O2, P2, m2, 21, a2[51]), P2 = d2(P2, T2, x2, O2, b2, 6, a2[52]), O2 = d2(O2, P2, T2, x2, f2, 10, a2[53]), x2 = d2(x2, O2, P2, T2, I2, 15, a2[54]), T2 = d2(T2, x2, O2, P2, c3, 21, a2[55]), P2 = d2(P2, T2, x2, O2, w2, 6, a2[56]), O2 = d2(O2, P2, T2, x2, C2, 10, a2[57]), x2 = d2(x2, O2, P2, T2, y2, 15, a2[58]), T2 = d2(T2, x2, O2, P2, k2, 21, a2[59]), P2 = d2(P2, T2, x2, O2, g2, 6, a2[60]), O2 = d2(O2, P2, T2, x2, S2, 10, a2[61]), x2 = d2(x2, O2, P2, T2, p2, 15, a2[62]), T2 = d2(T2, x2, O2, P2, v2, 21, a2[63]), i3[0] = i3[0] + P2 | 0, i3[1] = i3[1] + T2 | 0, i3[2] = i3[2] + x2 | 0, i3[3] = i3[3] + O2 | 0; - }, _doFinalize: function() { - var t4 = this._data, n3 = t4.words, s3 = 8 * this._nDataBytes, r3 = 8 * t4.sigBytes; - n3[r3 >>> 5] |= 128 << 24 - r3 % 32; - var i3 = e3.floor(s3 / 4294967296), o3 = s3; - n3[15 + (r3 + 64 >>> 9 << 4)] = 16711935 & (i3 << 8 | i3 >>> 24) | 4278255360 & (i3 << 24 | i3 >>> 8), n3[14 + (r3 + 64 >>> 9 << 4)] = 16711935 & (o3 << 8 | o3 >>> 24) | 4278255360 & (o3 << 24 | o3 >>> 8), t4.sigBytes = 4 * (n3.length + 1), this._process(); - for (var a3 = this._hash, c3 = a3.words, u3 = 0; u3 < 4; u3++) { - var l3 = c3[u3]; - c3[u3] = 16711935 & (l3 << 8 | l3 >>> 24) | 4278255360 & (l3 << 24 | l3 >>> 8); - } - return a3; - }, clone: function() { - var e4 = i2.clone.call(this); - return e4._hash = this._hash.clone(), e4; - } }); - function u2(e4, t4, n3, s3, r3, i3, o3) { - var a3 = e4 + (t4 & n3 | ~t4 & s3) + r3 + o3; - return (a3 << i3 | a3 >>> 32 - i3) + t4; - } - function l2(e4, t4, n3, s3, r3, i3, o3) { - var a3 = e4 + (t4 & s3 | n3 & ~s3) + r3 + o3; - return (a3 << i3 | a3 >>> 32 - i3) + t4; - } - function h2(e4, t4, n3, s3, r3, i3, o3) { - var a3 = e4 + (t4 ^ n3 ^ s3) + r3 + o3; - return (a3 << i3 | a3 >>> 32 - i3) + t4; - } - function d2(e4, t4, n3, s3, r3, i3, o3) { - var a3 = e4 + (n3 ^ (t4 | ~s3)) + r3 + o3; - return (a3 << i3 | a3 >>> 32 - i3) + t4; - } - t3.MD5 = i2._createHelper(c2), t3.HmacMD5 = i2._createHmacHelper(c2); - }(Math), n2.MD5); -}), n(function(e2, t2) { - var n2; - e2.exports = (n2 = r, void function() { - var e3 = n2, t3 = e3.lib.Base, s2 = e3.enc.Utf8; - e3.algo.HMAC = t3.extend({ init: function(e4, t4) { - e4 = this._hasher = new e4.init(), "string" == typeof t4 && (t4 = s2.parse(t4)); - var n3 = e4.blockSize, r2 = 4 * n3; - t4.sigBytes > r2 && (t4 = e4.finalize(t4)), t4.clamp(); - for (var i2 = this._oKey = t4.clone(), o2 = this._iKey = t4.clone(), a2 = i2.words, c2 = o2.words, u2 = 0; u2 < n3; u2++) - a2[u2] ^= 1549556828, c2[u2] ^= 909522486; - i2.sigBytes = o2.sigBytes = r2, this.reset(); - }, reset: function() { - var e4 = this._hasher; - e4.reset(), e4.update(this._iKey); - }, update: function(e4) { - return this._hasher.update(e4), this; - }, finalize: function(e4) { - var t4 = this._hasher, n3 = t4.finalize(e4); - return t4.reset(), t4.finalize(this._oKey.clone().concat(n3)); - } }); - }()); -}), n(function(e2, t2) { - e2.exports = r.HmacMD5; -})), o = n(function(e2, t2) { - e2.exports = r.enc.Utf8; -}), a = n(function(e2, t2) { - var n2; - e2.exports = (n2 = r, function() { - var e3 = n2, t3 = e3.lib.WordArray; - function s2(e4, n3, s3) { - for (var r2 = [], i2 = 0, o2 = 0; o2 < n3; o2++) - if (o2 % 4) { - var a2 = s3[e4.charCodeAt(o2 - 1)] << o2 % 4 * 2, c2 = s3[e4.charCodeAt(o2)] >>> 6 - o2 % 4 * 2; - r2[i2 >>> 2] |= (a2 | c2) << 24 - i2 % 4 * 8, i2++; - } - return t3.create(r2, i2); - } - e3.enc.Base64 = { stringify: function(e4) { - var t4 = e4.words, n3 = e4.sigBytes, s3 = this._map; - e4.clamp(); - for (var r2 = [], i2 = 0; i2 < n3; i2 += 3) - for (var o2 = (t4[i2 >>> 2] >>> 24 - i2 % 4 * 8 & 255) << 16 | (t4[i2 + 1 >>> 2] >>> 24 - (i2 + 1) % 4 * 8 & 255) << 8 | t4[i2 + 2 >>> 2] >>> 24 - (i2 + 2) % 4 * 8 & 255, a2 = 0; a2 < 4 && i2 + 0.75 * a2 < n3; a2++) - r2.push(s3.charAt(o2 >>> 6 * (3 - a2) & 63)); - var c2 = s3.charAt(64); - if (c2) - for (; r2.length % 4; ) - r2.push(c2); - return r2.join(""); - }, parse: function(e4) { - var t4 = e4.length, n3 = this._map, r2 = this._reverseMap; - if (!r2) { - r2 = this._reverseMap = []; - for (var i2 = 0; i2 < n3.length; i2++) - r2[n3.charCodeAt(i2)] = i2; - } - var o2 = n3.charAt(64); - if (o2) { - var a2 = e4.indexOf(o2); - -1 !== a2 && (t4 = a2); - } - return s2(e4, t4, r2); - }, _map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" }; - }(), n2.enc.Base64); -}); -const c = "FUNCTION", u = "OBJECT", l = "CLIENT_DB", h = "pending", d = "fulfilled", p = "rejected"; -function f(e2) { - return Object.prototype.toString.call(e2).slice(8, -1).toLowerCase(); -} -function g(e2) { - return "object" === f(e2); -} -function m(e2) { - return "function" == typeof e2; -} -function y(e2) { - return function() { - try { - return e2.apply(e2, arguments); - } catch (e3) { - console.error(e3); - } - }; -} -const _ = "REJECTED", w = "NOT_PENDING"; -class v { - constructor({ createPromise: e2, retryRule: t2 = _ } = {}) { - this.createPromise = e2, this.status = null, this.promise = null, this.retryRule = t2; - } - get needRetry() { - if (!this.status) - return true; - switch (this.retryRule) { - case _: - return this.status === p; - case w: - return this.status !== h; - } - } - exec() { - return this.needRetry ? (this.status = h, this.promise = this.createPromise().then((e2) => (this.status = d, Promise.resolve(e2)), (e2) => (this.status = p, Promise.reject(e2))), this.promise) : this.promise; - } -} -function I(e2) { - return e2 && "string" == typeof e2 ? JSON.parse(e2) : e2; -} -const S = true, b = "mp-weixin", A = I(define_process_env_UNI_SECURE_NETWORK_CONFIG_default), C = b, P = I(""), T = I("[]") || []; -let O = ""; -try { - O = "__UNI__97986D6"; -} catch (e2) { -} -let E = {}; -function L(e2, t2 = {}) { - var n2, s2; - return n2 = E, s2 = e2, Object.prototype.hasOwnProperty.call(n2, s2) || (E[e2] = t2), E[e2]; -} -const R = ["invoke", "success", "fail", "complete"], U = L("_globalUniCloudInterceptor"); -function N(e2, t2) { - U[e2] || (U[e2] = {}), g(t2) && Object.keys(t2).forEach((n2) => { - R.indexOf(n2) > -1 && function(e3, t3, n3) { - let s2 = U[e3][t3]; - s2 || (s2 = U[e3][t3] = []), -1 === s2.indexOf(n3) && m(n3) && s2.push(n3); - }(e2, n2, t2[n2]); - }); -} -function D(e2, t2) { - U[e2] || (U[e2] = {}), g(t2) ? Object.keys(t2).forEach((n2) => { - R.indexOf(n2) > -1 && function(e3, t3, n3) { - const s2 = U[e3][t3]; - if (!s2) - return; - const r2 = s2.indexOf(n3); - r2 > -1 && s2.splice(r2, 1); - }(e2, n2, t2[n2]); - }) : delete U[e2]; -} -function M(e2, t2) { - return e2 && 0 !== e2.length ? e2.reduce((e3, n2) => e3.then(() => n2(t2)), Promise.resolve()) : Promise.resolve(); -} -function q(e2, t2) { - return U[e2] && U[e2][t2] || []; -} -function F(e2) { - N("callObject", e2); -} -const K = L("_globalUniCloudListener"), j = "response", $ = "needLogin", B = "refreshToken", W = "clientdb", H = "cloudfunction", J = "cloudobject"; -function z(e2) { - return K[e2] || (K[e2] = []), K[e2]; -} -function V(e2, t2) { - const n2 = z(e2); - n2.includes(t2) || n2.push(t2); -} -function G(e2, t2) { - const n2 = z(e2), s2 = n2.indexOf(t2); - -1 !== s2 && n2.splice(s2, 1); -} -function Y(e2, t2) { - const n2 = z(e2); - for (let e3 = 0; e3 < n2.length; e3++) { - (0, n2[e3])(t2); - } -} -let Q, X = false; -function Z() { - return Q || (Q = new Promise((e2) => { - X && e2(), function t2() { - if ("function" == typeof getCurrentPages) { - const t3 = getCurrentPages(); - t3 && t3[0] && (X = true, e2()); - } - X || setTimeout(() => { - t2(); - }, 30); - }(); - }), Q); -} -function ee(e2) { - const t2 = {}; - for (const n2 in e2) { - const s2 = e2[n2]; - m(s2) && (t2[n2] = y(s2)); - } - return t2; -} -class te extends Error { - constructor(e2) { - super(e2.message), this.errMsg = e2.message || e2.errMsg || "unknown system error", this.code = this.errCode = e2.code || e2.errCode || "SYSTEM_ERROR", this.errSubject = this.subject = e2.subject || e2.errSubject, this.cause = e2.cause, this.requestId = e2.requestId; - } - toJson(e2 = 0) { - if (!(e2 >= 10)) - return e2++, { errCode: this.errCode, errMsg: this.errMsg, errSubject: this.errSubject, cause: this.cause && this.cause.toJson ? this.cause.toJson(e2) : this.cause }; - } -} -var ne = { request: (e2) => index.request(e2), uploadFile: (e2) => index.uploadFile(e2), setStorageSync: (e2, t2) => index.setStorageSync(e2, t2), getStorageSync: (e2) => index.getStorageSync(e2), removeStorageSync: (e2) => index.removeStorageSync(e2), clearStorageSync: () => index.clearStorageSync(), connectSocket: (e2) => index.connectSocket(e2) }; -function se(e2) { - return e2 && se(e2.__v_raw) || e2; -} -function re() { - return { token: ne.getStorageSync("uni_id_token") || ne.getStorageSync("uniIdToken"), tokenExpired: ne.getStorageSync("uni_id_token_expired") }; -} -function ie({ token: e2, tokenExpired: t2 } = {}) { - e2 && ne.setStorageSync("uni_id_token", e2), t2 && ne.setStorageSync("uni_id_token_expired", t2); -} -let oe, ae; -function ce() { - return oe || (oe = index.getSystemInfoSync()), oe; -} -function ue() { - let e2, t2; - try { - if (index.getLaunchOptionsSync) { - if (index.getLaunchOptionsSync.toString().indexOf("not yet implemented") > -1) - return; - const { scene: n2, channel: s2 } = index.getLaunchOptionsSync(); - e2 = s2, t2 = n2; - } - } catch (e3) { - } - return { channel: e2, scene: t2 }; -} -let le = {}; -function he() { - const e2 = index.getLocale && index.getLocale() || "en"; - if (ae) - return { ...le, ...ae, locale: e2, LOCALE: e2 }; - const t2 = ce(), { deviceId: n2, osName: s2, uniPlatform: r2, appId: i2 } = t2, o2 = ["appId", "appLanguage", "appName", "appVersion", "appVersionCode", "appWgtVersion", "browserName", "browserVersion", "deviceBrand", "deviceId", "deviceModel", "deviceType", "osName", "osVersion", "romName", "romVersion", "ua", "hostName", "hostVersion", "uniPlatform", "uniRuntimeVersion", "uniRuntimeVersionCode", "uniCompilerVersion", "uniCompilerVersionCode"]; - for (const e3 in t2) - Object.hasOwnProperty.call(t2, e3) && -1 === o2.indexOf(e3) && delete t2[e3]; - return ae = { PLATFORM: r2, OS: s2, APPID: i2, DEVICEID: n2, ...ue(), ...t2 }, { ...le, ...ae, locale: e2, LOCALE: e2 }; -} -var de = { sign: function(e2, t2) { - let n2 = ""; - return Object.keys(e2).sort().forEach(function(t3) { - e2[t3] && (n2 = n2 + "&" + t3 + "=" + e2[t3]); - }), n2 = n2.slice(1), i(n2, t2).toString(); -}, wrappedRequest: function(e2, t2) { - return new Promise((n2, s2) => { - t2(Object.assign(e2, { complete(e3) { - e3 || (e3 = {}); - const t3 = e3.data && e3.data.header && e3.data.header["x-serverless-request-id"] || e3.header && e3.header["request-id"]; - if (!e3.statusCode || e3.statusCode >= 400) { - const n3 = e3.data && e3.data.error && e3.data.error.code || "SYS_ERR", r3 = e3.data && e3.data.error && e3.data.error.message || e3.errMsg || "request:fail"; - return s2(new te({ code: n3, message: r3, requestId: t3 })); - } - const r2 = e3.data; - if (r2.error) - return s2(new te({ code: r2.error.code, message: r2.error.message, requestId: t3 })); - r2.result = r2.data, r2.requestId = t3, delete r2.data, n2(r2); - } })); - }); -}, toBase64: function(e2) { - return a.stringify(o.parse(e2)); -} }; -var pe = class { - constructor(e2) { - ["spaceId", "clientSecret"].forEach((t2) => { - if (!Object.prototype.hasOwnProperty.call(e2, t2)) - throw new Error(`${t2} required`); - }), this.config = Object.assign({}, { endpoint: 0 === e2.spaceId.indexOf("mp-") ? "https://api.next.bspapp.com" : "https://api.bspapp.com" }, e2), this.config.provider = "aliyun", this.config.requestUrl = this.config.endpoint + "/client", this.config.envType = this.config.envType || "public", this.config.accessTokenKey = "access_token_" + this.config.spaceId, this.adapter = ne, this._getAccessTokenPromiseHub = new v({ createPromise: () => this.requestAuth(this.setupRequest({ method: "serverless.auth.user.anonymousAuthorize", params: "{}" }, "auth")).then((e3) => { - if (!e3.result || !e3.result.accessToken) - throw new te({ code: "AUTH_FAILED", message: "获取accessToken失败" }); - this.setAccessToken(e3.result.accessToken); - }), retryRule: w }); - } - get hasAccessToken() { - return !!this.accessToken; - } - setAccessToken(e2) { - this.accessToken = e2; - } - requestWrapped(e2) { - return de.wrappedRequest(e2, this.adapter.request); - } - requestAuth(e2) { - return this.requestWrapped(e2); - } - request(e2, t2) { - return Promise.resolve().then(() => this.hasAccessToken ? t2 ? this.requestWrapped(e2) : this.requestWrapped(e2).catch((t3) => new Promise((e3, n2) => { - !t3 || "GATEWAY_INVALID_TOKEN" !== t3.code && "InvalidParameter.InvalidToken" !== t3.code ? n2(t3) : e3(); - }).then(() => this.getAccessToken()).then(() => { - const t4 = this.rebuildRequest(e2); - return this.request(t4, true); - })) : this.getAccessToken().then(() => { - const t3 = this.rebuildRequest(e2); - return this.request(t3, true); - })); - } - rebuildRequest(e2) { - const t2 = Object.assign({}, e2); - return t2.data.token = this.accessToken, t2.header["x-basement-token"] = this.accessToken, t2.header["x-serverless-sign"] = de.sign(t2.data, this.config.clientSecret), t2; - } - setupRequest(e2, t2) { - const n2 = Object.assign({}, e2, { spaceId: this.config.spaceId, timestamp: Date.now() }), s2 = { "Content-Type": "application/json" }; - return "auth" !== t2 && (n2.token = this.accessToken, s2["x-basement-token"] = this.accessToken), s2["x-serverless-sign"] = de.sign(n2, this.config.clientSecret), { url: this.config.requestUrl, method: "POST", data: n2, dataType: "json", header: s2 }; - } - getAccessToken() { - return this._getAccessTokenPromiseHub.exec(); - } - async authorize() { - await this.getAccessToken(); - } - callFunction(e2) { - const t2 = { method: "serverless.function.runtime.invoke", params: JSON.stringify({ functionTarget: e2.name, functionArgs: e2.data || {} }) }; - return this.request({ ...this.setupRequest(t2), timeout: e2.timeout }); - } - getOSSUploadOptionsFromPath(e2) { - const t2 = { method: "serverless.file.resource.generateProximalSign", params: JSON.stringify(e2) }; - return this.request(this.setupRequest(t2)); - } - uploadFileToOSS({ url: e2, formData: t2, name: n2, filePath: s2, fileType: r2, onUploadProgress: i2 }) { - return new Promise((o2, a2) => { - const c2 = this.adapter.uploadFile({ url: e2, formData: t2, name: n2, filePath: s2, fileType: r2, header: { "X-OSS-server-side-encrpytion": "AES256" }, success(e3) { - e3 && e3.statusCode < 400 ? o2(e3) : a2(new te({ code: "UPLOAD_FAILED", message: "文件上传失败" })); - }, fail(e3) { - a2(new te({ code: e3.code || "UPLOAD_FAILED", message: e3.message || e3.errMsg || "文件上传失败" })); - } }); - "function" == typeof i2 && c2 && "function" == typeof c2.onProgressUpdate && c2.onProgressUpdate((e3) => { - i2({ loaded: e3.totalBytesSent, total: e3.totalBytesExpectedToSend }); - }); - }); - } - reportOSSUpload(e2) { - const t2 = { method: "serverless.file.resource.report", params: JSON.stringify(e2) }; - return this.request(this.setupRequest(t2)); - } - async uploadFile({ filePath: e2, cloudPath: t2, fileType: n2 = "image", cloudPathAsRealPath: s2 = false, onUploadProgress: r2, config: i2 }) { - if ("string" !== f(t2)) - throw new te({ code: "INVALID_PARAM", message: "cloudPath必须为字符串类型" }); - if (!(t2 = t2.trim())) - throw new te({ code: "INVALID_PARAM", message: "cloudPath不可为空" }); - if (/:\/\//.test(t2)) - throw new te({ code: "INVALID_PARAM", message: "cloudPath不合法" }); - const o2 = i2 && i2.envType || this.config.envType; - if (s2 && ("/" !== t2[0] && (t2 = "/" + t2), t2.indexOf("\\") > -1)) - throw new te({ code: "INVALID_PARAM", message: "使用cloudPath作为路径时,cloudPath不可包含“\\”" }); - const a2 = (await this.getOSSUploadOptionsFromPath({ env: o2, filename: s2 ? t2.split("/").pop() : t2, fileId: s2 ? t2 : void 0 })).result, c2 = "https://" + a2.cdnDomain + "/" + a2.ossPath, { securityToken: u2, accessKeyId: l2, signature: h2, host: d2, ossPath: p2, id: g2, policy: m2, ossCallbackUrl: y2 } = a2, _2 = { "Cache-Control": "max-age=2592000", "Content-Disposition": "attachment", OSSAccessKeyId: l2, Signature: h2, host: d2, id: g2, key: p2, policy: m2, success_action_status: 200 }; - if (u2 && (_2["x-oss-security-token"] = u2), y2) { - const e3 = JSON.stringify({ callbackUrl: y2, callbackBody: JSON.stringify({ fileId: g2, spaceId: this.config.spaceId }), callbackBodyType: "application/json" }); - _2.callback = de.toBase64(e3); - } - const w2 = { url: "https://" + a2.host, formData: _2, fileName: "file", name: "file", filePath: e2, fileType: n2 }; - if (await this.uploadFileToOSS(Object.assign({}, w2, { onUploadProgress: r2 })), y2) - return { success: true, filePath: e2, fileID: c2 }; - if ((await this.reportOSSUpload({ id: g2 })).success) - return { success: true, filePath: e2, fileID: c2 }; - throw new te({ code: "UPLOAD_FAILED", message: "文件上传失败" }); - } - getTempFileURL({ fileList: e2 } = {}) { - return new Promise((t2, n2) => { - Array.isArray(e2) && 0 !== e2.length || n2(new te({ code: "INVALID_PARAM", message: "fileList的元素必须是非空的字符串" })), t2({ fileList: e2.map((e3) => ({ fileID: e3, tempFileURL: e3 })) }); - }); - } - async getFileInfo({ fileList: e2 } = {}) { - if (!Array.isArray(e2) || 0 === e2.length) - throw new te({ code: "INVALID_PARAM", message: "fileList的元素必须是非空的字符串" }); - const t2 = { method: "serverless.file.resource.info", params: JSON.stringify({ id: e2.map((e3) => e3.split("?")[0]).join(",") }) }; - return { fileList: (await this.request(this.setupRequest(t2))).result }; - } -}; -var fe = { init(e2) { - const t2 = new pe(e2), n2 = { signInAnonymously: function() { - return t2.authorize(); - }, getLoginState: function() { - return Promise.resolve(false); - } }; - return t2.auth = function() { - return n2; - }, t2.customAuth = t2.auth, t2; -} }; -const ge = "undefined" != typeof location && "http:" === location.protocol ? "http:" : "https:"; -var me; -!function(e2) { - e2.local = "local", e2.none = "none", e2.session = "session"; -}(me || (me = {})); -var ye = function() { -}, _e = n(function(e2, t2) { - var n2; - e2.exports = (n2 = r, function(e3) { - var t3 = n2, s2 = t3.lib, r2 = s2.WordArray, i2 = s2.Hasher, o2 = t3.algo, a2 = [], c2 = []; - !function() { - function t4(t5) { - for (var n4 = e3.sqrt(t5), s4 = 2; s4 <= n4; s4++) - if (!(t5 % s4)) - return false; - return true; - } - function n3(e4) { - return 4294967296 * (e4 - (0 | e4)) | 0; - } - for (var s3 = 2, r3 = 0; r3 < 64; ) - t4(s3) && (r3 < 8 && (a2[r3] = n3(e3.pow(s3, 0.5))), c2[r3] = n3(e3.pow(s3, 1 / 3)), r3++), s3++; - }(); - var u2 = [], l2 = o2.SHA256 = i2.extend({ _doReset: function() { - this._hash = new r2.init(a2.slice(0)); - }, _doProcessBlock: function(e4, t4) { - for (var n3 = this._hash.words, s3 = n3[0], r3 = n3[1], i3 = n3[2], o3 = n3[3], a3 = n3[4], l3 = n3[5], h2 = n3[6], d2 = n3[7], p2 = 0; p2 < 64; p2++) { - if (p2 < 16) - u2[p2] = 0 | e4[t4 + p2]; - else { - var f2 = u2[p2 - 15], g2 = (f2 << 25 | f2 >>> 7) ^ (f2 << 14 | f2 >>> 18) ^ f2 >>> 3, m2 = u2[p2 - 2], y2 = (m2 << 15 | m2 >>> 17) ^ (m2 << 13 | m2 >>> 19) ^ m2 >>> 10; - u2[p2] = g2 + u2[p2 - 7] + y2 + u2[p2 - 16]; - } - var _2 = s3 & r3 ^ s3 & i3 ^ r3 & i3, w2 = (s3 << 30 | s3 >>> 2) ^ (s3 << 19 | s3 >>> 13) ^ (s3 << 10 | s3 >>> 22), v2 = d2 + ((a3 << 26 | a3 >>> 6) ^ (a3 << 21 | a3 >>> 11) ^ (a3 << 7 | a3 >>> 25)) + (a3 & l3 ^ ~a3 & h2) + c2[p2] + u2[p2]; - d2 = h2, h2 = l3, l3 = a3, a3 = o3 + v2 | 0, o3 = i3, i3 = r3, r3 = s3, s3 = v2 + (w2 + _2) | 0; - } - n3[0] = n3[0] + s3 | 0, n3[1] = n3[1] + r3 | 0, n3[2] = n3[2] + i3 | 0, n3[3] = n3[3] + o3 | 0, n3[4] = n3[4] + a3 | 0, n3[5] = n3[5] + l3 | 0, n3[6] = n3[6] + h2 | 0, n3[7] = n3[7] + d2 | 0; - }, _doFinalize: function() { - var t4 = this._data, n3 = t4.words, s3 = 8 * this._nDataBytes, r3 = 8 * t4.sigBytes; - return n3[r3 >>> 5] |= 128 << 24 - r3 % 32, n3[14 + (r3 + 64 >>> 9 << 4)] = e3.floor(s3 / 4294967296), n3[15 + (r3 + 64 >>> 9 << 4)] = s3, t4.sigBytes = 4 * n3.length, this._process(), this._hash; - }, clone: function() { - var e4 = i2.clone.call(this); - return e4._hash = this._hash.clone(), e4; - } }); - t3.SHA256 = i2._createHelper(l2), t3.HmacSHA256 = i2._createHmacHelper(l2); - }(Math), n2.SHA256); -}), we = _e, ve = n(function(e2, t2) { - e2.exports = r.HmacSHA256; -}); -const Ie = () => { - let e2; - if (!Promise) { - e2 = () => { - }, e2.promise = {}; - const t3 = () => { - throw new te({ message: 'Your Node runtime does support ES6 Promises. Set "global.Promise" to your preferred implementation of promises.' }); - }; - return Object.defineProperty(e2.promise, "then", { get: t3 }), Object.defineProperty(e2.promise, "catch", { get: t3 }), e2; - } - const t2 = new Promise((t3, n2) => { - e2 = (e3, s2) => e3 ? n2(e3) : t3(s2); - }); - return e2.promise = t2, e2; -}; -function Se(e2) { - return void 0 === e2; -} -function be(e2) { - return "[object Null]" === Object.prototype.toString.call(e2); -} -var ke; -function Ae(e2) { - const t2 = (n2 = e2, "[object Array]" === Object.prototype.toString.call(n2) ? e2 : [e2]); - var n2; - for (const e3 of t2) { - const { isMatch: t3, genAdapter: n3, runtime: s2 } = e3; - if (t3()) - return { adapter: n3(), runtime: s2 }; - } -} -!function(e2) { - e2.WEB = "web", e2.WX_MP = "wx_mp"; -}(ke || (ke = {})); -const Ce = { adapter: null, runtime: void 0 }, Pe = ["anonymousUuidKey"]; -class Te extends ye { - constructor() { - super(), Ce.adapter.root.tcbObject || (Ce.adapter.root.tcbObject = {}); - } - setItem(e2, t2) { - Ce.adapter.root.tcbObject[e2] = t2; - } - getItem(e2) { - return Ce.adapter.root.tcbObject[e2]; - } - removeItem(e2) { - delete Ce.adapter.root.tcbObject[e2]; - } - clear() { - delete Ce.adapter.root.tcbObject; - } -} -function xe(e2, t2) { - switch (e2) { - case "local": - return t2.localStorage || new Te(); - case "none": - return new Te(); - default: - return t2.sessionStorage || new Te(); - } -} -class Oe { - constructor(e2) { - if (!this._storage) { - this._persistence = Ce.adapter.primaryStorage || e2.persistence, this._storage = xe(this._persistence, Ce.adapter); - const t2 = `access_token_${e2.env}`, n2 = `access_token_expire_${e2.env}`, s2 = `refresh_token_${e2.env}`, r2 = `anonymous_uuid_${e2.env}`, i2 = `login_type_${e2.env}`, o2 = `user_info_${e2.env}`; - this.keys = { accessTokenKey: t2, accessTokenExpireKey: n2, refreshTokenKey: s2, anonymousUuidKey: r2, loginTypeKey: i2, userInfoKey: o2 }; - } - } - updatePersistence(e2) { - if (e2 === this._persistence) - return; - const t2 = "local" === this._persistence; - this._persistence = e2; - const n2 = xe(e2, Ce.adapter); - for (const e3 in this.keys) { - const s2 = this.keys[e3]; - if (t2 && Pe.includes(e3)) - continue; - const r2 = this._storage.getItem(s2); - Se(r2) || be(r2) || (n2.setItem(s2, r2), this._storage.removeItem(s2)); - } - this._storage = n2; - } - setStore(e2, t2, n2) { - if (!this._storage) - return; - const s2 = { version: n2 || "localCachev1", content: t2 }, r2 = JSON.stringify(s2); - try { - this._storage.setItem(e2, r2); - } catch (e3) { - throw e3; - } - } - getStore(e2, t2) { - try { - if (!this._storage) - return; - } catch (e3) { - return ""; - } - t2 = t2 || "localCachev1"; - const n2 = this._storage.getItem(e2); - if (!n2) - return ""; - if (n2.indexOf(t2) >= 0) { - return JSON.parse(n2).content; - } - return ""; - } - removeStore(e2) { - this._storage.removeItem(e2); - } -} -const Ee = {}, Le = {}; -function Re(e2) { - return Ee[e2]; -} -class Ue { - constructor(e2, t2) { - this.data = t2 || null, this.name = e2; - } -} -class Ne extends Ue { - constructor(e2, t2) { - super("error", { error: e2, data: t2 }), this.error = e2; - } -} -const De = new class { - constructor() { - this._listeners = {}; - } - on(e2, t2) { - return function(e3, t3, n2) { - n2[e3] = n2[e3] || [], n2[e3].push(t3); - }(e2, t2, this._listeners), this; - } - off(e2, t2) { - return function(e3, t3, n2) { - if (n2 && n2[e3]) { - const s2 = n2[e3].indexOf(t3); - -1 !== s2 && n2[e3].splice(s2, 1); - } - }(e2, t2, this._listeners), this; - } - fire(e2, t2) { - if (e2 instanceof Ne) - return console.error(e2.error), this; - const n2 = "string" == typeof e2 ? new Ue(e2, t2 || {}) : e2; - const s2 = n2.name; - if (this._listens(s2)) { - n2.target = this; - const e3 = this._listeners[s2] ? [...this._listeners[s2]] : []; - for (const t3 of e3) - t3.call(this, n2); - } - return this; - } - _listens(e2) { - return this._listeners[e2] && this._listeners[e2].length > 0; - } -}(); -function Me(e2, t2) { - De.on(e2, t2); -} -function qe(e2, t2 = {}) { - De.fire(e2, t2); -} -function Fe(e2, t2) { - De.off(e2, t2); -} -const Ke = "loginStateChanged", je = "loginStateExpire", $e = "loginTypeChanged", Be = "anonymousConverted", We = "refreshAccessToken"; -var He; -!function(e2) { - e2.ANONYMOUS = "ANONYMOUS", e2.WECHAT = "WECHAT", e2.WECHAT_PUBLIC = "WECHAT-PUBLIC", e2.WECHAT_OPEN = "WECHAT-OPEN", e2.CUSTOM = "CUSTOM", e2.EMAIL = "EMAIL", e2.USERNAME = "USERNAME", e2.NULL = "NULL"; -}(He || (He = {})); -const Je = ["auth.getJwt", "auth.logout", "auth.signInWithTicket", "auth.signInAnonymously", "auth.signIn", "auth.fetchAccessTokenWithRefreshToken", "auth.signUpWithEmailAndPassword", "auth.activateEndUserMail", "auth.sendPasswordResetEmail", "auth.resetPasswordWithToken", "auth.isUsernameRegistered"], ze = { "X-SDK-Version": "1.3.5" }; -function Ve(e2, t2, n2) { - const s2 = e2[t2]; - e2[t2] = function(t3) { - const r2 = {}, i2 = {}; - n2.forEach((n3) => { - const { data: s3, headers: o3 } = n3.call(e2, t3); - Object.assign(r2, s3), Object.assign(i2, o3); - }); - const o2 = t3.data; - return o2 && (() => { - var e3; - if (e3 = o2, "[object FormData]" !== Object.prototype.toString.call(e3)) - t3.data = { ...o2, ...r2 }; - else - for (const e4 in r2) - o2.append(e4, r2[e4]); - })(), t3.headers = { ...t3.headers || {}, ...i2 }, s2.call(e2, t3); - }; -} -function Ge() { - const e2 = Math.random().toString(16).slice(2); - return { data: { seqId: e2 }, headers: { ...ze, "x-seqid": e2 } }; -} -class Ye { - constructor(e2 = {}) { - var t2; - this.config = e2, this._reqClass = new Ce.adapter.reqClass({ timeout: this.config.timeout, timeoutMsg: `请求在${this.config.timeout / 1e3}s内未完成,已中断`, restrictedMethods: ["post"] }), this._cache = Re(this.config.env), this._localCache = (t2 = this.config.env, Le[t2]), Ve(this._reqClass, "post", [Ge]), Ve(this._reqClass, "upload", [Ge]), Ve(this._reqClass, "download", [Ge]); - } - async post(e2) { - return await this._reqClass.post(e2); - } - async upload(e2) { - return await this._reqClass.upload(e2); - } - async download(e2) { - return await this._reqClass.download(e2); - } - async refreshAccessToken() { - let e2, t2; - this._refreshAccessTokenPromise || (this._refreshAccessTokenPromise = this._refreshAccessToken()); - try { - e2 = await this._refreshAccessTokenPromise; - } catch (e3) { - t2 = e3; - } - if (this._refreshAccessTokenPromise = null, this._shouldRefreshAccessTokenHook = null, t2) - throw t2; - return e2; - } - async _refreshAccessToken() { - const { accessTokenKey: e2, accessTokenExpireKey: t2, refreshTokenKey: n2, loginTypeKey: s2, anonymousUuidKey: r2 } = this._cache.keys; - this._cache.removeStore(e2), this._cache.removeStore(t2); - let i2 = this._cache.getStore(n2); - if (!i2) - throw new te({ message: "未登录CloudBase" }); - const o2 = { refresh_token: i2 }, a2 = await this.request("auth.fetchAccessTokenWithRefreshToken", o2); - if (a2.data.code) { - const { code: e3 } = a2.data; - if ("SIGN_PARAM_INVALID" === e3 || "REFRESH_TOKEN_EXPIRED" === e3 || "INVALID_REFRESH_TOKEN" === e3) { - if (this._cache.getStore(s2) === He.ANONYMOUS && "INVALID_REFRESH_TOKEN" === e3) { - const e4 = this._cache.getStore(r2), t3 = this._cache.getStore(n2), s3 = await this.send("auth.signInAnonymously", { anonymous_uuid: e4, refresh_token: t3 }); - return this.setRefreshToken(s3.refresh_token), this._refreshAccessToken(); - } - qe(je), this._cache.removeStore(n2); - } - throw new te({ code: a2.data.code, message: `刷新access token失败:${a2.data.code}` }); - } - if (a2.data.access_token) - return qe(We), this._cache.setStore(e2, a2.data.access_token), this._cache.setStore(t2, a2.data.access_token_expire + Date.now()), { accessToken: a2.data.access_token, accessTokenExpire: a2.data.access_token_expire }; - a2.data.refresh_token && (this._cache.removeStore(n2), this._cache.setStore(n2, a2.data.refresh_token), this._refreshAccessToken()); - } - async getAccessToken() { - const { accessTokenKey: e2, accessTokenExpireKey: t2, refreshTokenKey: n2 } = this._cache.keys; - if (!this._cache.getStore(n2)) - throw new te({ message: "refresh token不存在,登录状态异常" }); - let s2 = this._cache.getStore(e2), r2 = this._cache.getStore(t2), i2 = true; - return this._shouldRefreshAccessTokenHook && !await this._shouldRefreshAccessTokenHook(s2, r2) && (i2 = false), (!s2 || !r2 || r2 < Date.now()) && i2 ? this.refreshAccessToken() : { accessToken: s2, accessTokenExpire: r2 }; - } - async request(e2, t2, n2) { - const s2 = `x-tcb-trace_${this.config.env}`; - let r2 = "application/x-www-form-urlencoded"; - const i2 = { action: e2, env: this.config.env, dataVersion: "2019-08-16", ...t2 }; - if (-1 === Je.indexOf(e2)) { - const { refreshTokenKey: e3 } = this._cache.keys; - this._cache.getStore(e3) && (i2.access_token = (await this.getAccessToken()).accessToken); - } - let o2; - if ("storage.uploadFile" === e2) { - o2 = new FormData(); - for (let e3 in o2) - o2.hasOwnProperty(e3) && void 0 !== o2[e3] && o2.append(e3, i2[e3]); - r2 = "multipart/form-data"; - } else { - r2 = "application/json", o2 = {}; - for (let e3 in i2) - void 0 !== i2[e3] && (o2[e3] = i2[e3]); - } - let a2 = { headers: { "content-type": r2 } }; - n2 && n2.timeout && (a2.timeout = n2.timeout), n2 && n2.onUploadProgress && (a2.onUploadProgress = n2.onUploadProgress); - const c2 = this._localCache.getStore(s2); - c2 && (a2.headers["X-TCB-Trace"] = c2); - const { parse: u2, inQuery: l2, search: h2 } = t2; - let d2 = { env: this.config.env }; - u2 && (d2.parse = true), l2 && (d2 = { ...l2, ...d2 }); - let p2 = function(e3, t3, n3 = {}) { - const s3 = /\?/.test(t3); - let r3 = ""; - for (let e4 in n3) - "" === r3 ? !s3 && (t3 += "?") : r3 += "&", r3 += `${e4}=${encodeURIComponent(n3[e4])}`; - return /^http(s)?\:\/\//.test(t3 += r3) ? t3 : `${e3}${t3}`; - }(ge, "//tcb-api.tencentcloudapi.com/web", d2); - h2 && (p2 += h2); - const f2 = await this.post({ url: p2, data: o2, ...a2 }), g2 = f2.header && f2.header["x-tcb-trace"]; - if (g2 && this._localCache.setStore(s2, g2), 200 !== Number(f2.status) && 200 !== Number(f2.statusCode) || !f2.data) - throw new te({ code: "NETWORK_ERROR", message: "network request error" }); - return f2; - } - async send(e2, t2 = {}, n2 = {}) { - const s2 = await this.request(e2, t2, { ...n2, onUploadProgress: t2.onUploadProgress }); - if ("ACCESS_TOKEN_EXPIRED" === s2.data.code && -1 === Je.indexOf(e2)) { - await this.refreshAccessToken(); - const s3 = await this.request(e2, t2, { ...n2, onUploadProgress: t2.onUploadProgress }); - if (s3.data.code) - throw new te({ code: s3.data.code, message: s3.data.message }); - return s3.data; - } - if (s2.data.code) - throw new te({ code: s2.data.code, message: s2.data.message }); - return s2.data; - } - setRefreshToken(e2) { - const { accessTokenKey: t2, accessTokenExpireKey: n2, refreshTokenKey: s2 } = this._cache.keys; - this._cache.removeStore(t2), this._cache.removeStore(n2), this._cache.setStore(s2, e2); - } -} -const Qe = {}; -function Xe(e2) { - return Qe[e2]; -} -class Ze { - constructor(e2) { - this.config = e2, this._cache = Re(e2.env), this._request = Xe(e2.env); - } - setRefreshToken(e2) { - const { accessTokenKey: t2, accessTokenExpireKey: n2, refreshTokenKey: s2 } = this._cache.keys; - this._cache.removeStore(t2), this._cache.removeStore(n2), this._cache.setStore(s2, e2); - } - setAccessToken(e2, t2) { - const { accessTokenKey: n2, accessTokenExpireKey: s2 } = this._cache.keys; - this._cache.setStore(n2, e2), this._cache.setStore(s2, t2); - } - async refreshUserInfo() { - const { data: e2 } = await this._request.send("auth.getUserInfo", {}); - return this.setLocalUserInfo(e2), e2; - } - setLocalUserInfo(e2) { - const { userInfoKey: t2 } = this._cache.keys; - this._cache.setStore(t2, e2); - } -} -class et { - constructor(e2) { - if (!e2) - throw new te({ code: "PARAM_ERROR", message: "envId is not defined" }); - this._envId = e2, this._cache = Re(this._envId), this._request = Xe(this._envId), this.setUserInfo(); - } - linkWithTicket(e2) { - if ("string" != typeof e2) - throw new te({ code: "PARAM_ERROR", message: "ticket must be string" }); - return this._request.send("auth.linkWithTicket", { ticket: e2 }); - } - linkWithRedirect(e2) { - e2.signInWithRedirect(); - } - updatePassword(e2, t2) { - return this._request.send("auth.updatePassword", { oldPassword: t2, newPassword: e2 }); - } - updateEmail(e2) { - return this._request.send("auth.updateEmail", { newEmail: e2 }); - } - updateUsername(e2) { - if ("string" != typeof e2) - throw new te({ code: "PARAM_ERROR", message: "username must be a string" }); - return this._request.send("auth.updateUsername", { username: e2 }); - } - async getLinkedUidList() { - const { data: e2 } = await this._request.send("auth.getLinkedUidList", {}); - let t2 = false; - const { users: n2 } = e2; - return n2.forEach((e3) => { - e3.wxOpenId && e3.wxPublicId && (t2 = true); - }), { users: n2, hasPrimaryUid: t2 }; - } - setPrimaryUid(e2) { - return this._request.send("auth.setPrimaryUid", { uid: e2 }); - } - unlink(e2) { - return this._request.send("auth.unlink", { platform: e2 }); - } - async update(e2) { - const { nickName: t2, gender: n2, avatarUrl: s2, province: r2, country: i2, city: o2 } = e2, { data: a2 } = await this._request.send("auth.updateUserInfo", { nickName: t2, gender: n2, avatarUrl: s2, province: r2, country: i2, city: o2 }); - this.setLocalUserInfo(a2); - } - async refresh() { - const { data: e2 } = await this._request.send("auth.getUserInfo", {}); - return this.setLocalUserInfo(e2), e2; - } - setUserInfo() { - const { userInfoKey: e2 } = this._cache.keys, t2 = this._cache.getStore(e2); - ["uid", "loginType", "openid", "wxOpenId", "wxPublicId", "unionId", "qqMiniOpenId", "email", "hasPassword", "customUserId", "nickName", "gender", "avatarUrl"].forEach((e3) => { - this[e3] = t2[e3]; - }), this.location = { country: t2.country, province: t2.province, city: t2.city }; - } - setLocalUserInfo(e2) { - const { userInfoKey: t2 } = this._cache.keys; - this._cache.setStore(t2, e2), this.setUserInfo(); - } -} -class tt { - constructor(e2) { - if (!e2) - throw new te({ code: "PARAM_ERROR", message: "envId is not defined" }); - this._cache = Re(e2); - const { refreshTokenKey: t2, accessTokenKey: n2, accessTokenExpireKey: s2 } = this._cache.keys, r2 = this._cache.getStore(t2), i2 = this._cache.getStore(n2), o2 = this._cache.getStore(s2); - this.credential = { refreshToken: r2, accessToken: i2, accessTokenExpire: o2 }, this.user = new et(e2); - } - get isAnonymousAuth() { - return this.loginType === He.ANONYMOUS; - } - get isCustomAuth() { - return this.loginType === He.CUSTOM; - } - get isWeixinAuth() { - return this.loginType === He.WECHAT || this.loginType === He.WECHAT_OPEN || this.loginType === He.WECHAT_PUBLIC; - } - get loginType() { - return this._cache.getStore(this._cache.keys.loginTypeKey); - } -} -class nt extends Ze { - async signIn() { - this._cache.updatePersistence("local"); - const { anonymousUuidKey: e2, refreshTokenKey: t2 } = this._cache.keys, n2 = this._cache.getStore(e2) || void 0, s2 = this._cache.getStore(t2) || void 0, r2 = await this._request.send("auth.signInAnonymously", { anonymous_uuid: n2, refresh_token: s2 }); - if (r2.uuid && r2.refresh_token) { - this._setAnonymousUUID(r2.uuid), this.setRefreshToken(r2.refresh_token), await this._request.refreshAccessToken(), qe(Ke), qe($e, { env: this.config.env, loginType: He.ANONYMOUS, persistence: "local" }); - const e3 = new tt(this.config.env); - return await e3.user.refresh(), e3; - } - throw new te({ message: "匿名登录失败" }); - } - async linkAndRetrieveDataWithTicket(e2) { - const { anonymousUuidKey: t2, refreshTokenKey: n2 } = this._cache.keys, s2 = this._cache.getStore(t2), r2 = this._cache.getStore(n2), i2 = await this._request.send("auth.linkAndRetrieveDataWithTicket", { anonymous_uuid: s2, refresh_token: r2, ticket: e2 }); - if (i2.refresh_token) - return this._clearAnonymousUUID(), this.setRefreshToken(i2.refresh_token), await this._request.refreshAccessToken(), qe(Be, { env: this.config.env }), qe($e, { loginType: He.CUSTOM, persistence: "local" }), { credential: { refreshToken: i2.refresh_token } }; - throw new te({ message: "匿名转化失败" }); - } - _setAnonymousUUID(e2) { - const { anonymousUuidKey: t2, loginTypeKey: n2 } = this._cache.keys; - this._cache.removeStore(t2), this._cache.setStore(t2, e2), this._cache.setStore(n2, He.ANONYMOUS); - } - _clearAnonymousUUID() { - this._cache.removeStore(this._cache.keys.anonymousUuidKey); - } -} -class st extends Ze { - async signIn(e2) { - if ("string" != typeof e2) - throw new te({ code: "PARAM_ERROR", message: "ticket must be a string" }); - const { refreshTokenKey: t2 } = this._cache.keys, n2 = await this._request.send("auth.signInWithTicket", { ticket: e2, refresh_token: this._cache.getStore(t2) || "" }); - if (n2.refresh_token) - return this.setRefreshToken(n2.refresh_token), await this._request.refreshAccessToken(), qe(Ke), qe($e, { env: this.config.env, loginType: He.CUSTOM, persistence: this.config.persistence }), await this.refreshUserInfo(), new tt(this.config.env); - throw new te({ message: "自定义登录失败" }); - } -} -class rt extends Ze { - async signIn(e2, t2) { - if ("string" != typeof e2) - throw new te({ code: "PARAM_ERROR", message: "email must be a string" }); - const { refreshTokenKey: n2 } = this._cache.keys, s2 = await this._request.send("auth.signIn", { loginType: "EMAIL", email: e2, password: t2, refresh_token: this._cache.getStore(n2) || "" }), { refresh_token: r2, access_token: i2, access_token_expire: o2 } = s2; - if (r2) - return this.setRefreshToken(r2), i2 && o2 ? this.setAccessToken(i2, o2) : await this._request.refreshAccessToken(), await this.refreshUserInfo(), qe(Ke), qe($e, { env: this.config.env, loginType: He.EMAIL, persistence: this.config.persistence }), new tt(this.config.env); - throw s2.code ? new te({ code: s2.code, message: `邮箱登录失败: ${s2.message}` }) : new te({ message: "邮箱登录失败" }); - } - async activate(e2) { - return this._request.send("auth.activateEndUserMail", { token: e2 }); - } - async resetPasswordWithToken(e2, t2) { - return this._request.send("auth.resetPasswordWithToken", { token: e2, newPassword: t2 }); - } -} -class it extends Ze { - async signIn(e2, t2) { - if ("string" != typeof e2) - throw new te({ code: "PARAM_ERROR", message: "username must be a string" }); - "string" != typeof t2 && (t2 = "", console.warn("password is empty")); - const { refreshTokenKey: n2 } = this._cache.keys, s2 = await this._request.send("auth.signIn", { loginType: He.USERNAME, username: e2, password: t2, refresh_token: this._cache.getStore(n2) || "" }), { refresh_token: r2, access_token_expire: i2, access_token: o2 } = s2; - if (r2) - return this.setRefreshToken(r2), o2 && i2 ? this.setAccessToken(o2, i2) : await this._request.refreshAccessToken(), await this.refreshUserInfo(), qe(Ke), qe($e, { env: this.config.env, loginType: He.USERNAME, persistence: this.config.persistence }), new tt(this.config.env); - throw s2.code ? new te({ code: s2.code, message: `用户名密码登录失败: ${s2.message}` }) : new te({ message: "用户名密码登录失败" }); - } -} -class ot { - constructor(e2) { - this.config = e2, this._cache = Re(e2.env), this._request = Xe(e2.env), this._onAnonymousConverted = this._onAnonymousConverted.bind(this), this._onLoginTypeChanged = this._onLoginTypeChanged.bind(this), Me($e, this._onLoginTypeChanged); - } - get currentUser() { - const e2 = this.hasLoginState(); - return e2 && e2.user || null; - } - get loginType() { - return this._cache.getStore(this._cache.keys.loginTypeKey); - } - anonymousAuthProvider() { - return new nt(this.config); - } - customAuthProvider() { - return new st(this.config); - } - emailAuthProvider() { - return new rt(this.config); - } - usernameAuthProvider() { - return new it(this.config); - } - async signInAnonymously() { - return new nt(this.config).signIn(); - } - async signInWithEmailAndPassword(e2, t2) { - return new rt(this.config).signIn(e2, t2); - } - signInWithUsernameAndPassword(e2, t2) { - return new it(this.config).signIn(e2, t2); - } - async linkAndRetrieveDataWithTicket(e2) { - this._anonymousAuthProvider || (this._anonymousAuthProvider = new nt(this.config)), Me(Be, this._onAnonymousConverted); - return await this._anonymousAuthProvider.linkAndRetrieveDataWithTicket(e2); - } - async signOut() { - if (this.loginType === He.ANONYMOUS) - throw new te({ message: "匿名用户不支持登出操作" }); - const { refreshTokenKey: e2, accessTokenKey: t2, accessTokenExpireKey: n2 } = this._cache.keys, s2 = this._cache.getStore(e2); - if (!s2) - return; - const r2 = await this._request.send("auth.logout", { refresh_token: s2 }); - return this._cache.removeStore(e2), this._cache.removeStore(t2), this._cache.removeStore(n2), qe(Ke), qe($e, { env: this.config.env, loginType: He.NULL, persistence: this.config.persistence }), r2; - } - async signUpWithEmailAndPassword(e2, t2) { - return this._request.send("auth.signUpWithEmailAndPassword", { email: e2, password: t2 }); - } - async sendPasswordResetEmail(e2) { - return this._request.send("auth.sendPasswordResetEmail", { email: e2 }); - } - onLoginStateChanged(e2) { - Me(Ke, () => { - const t3 = this.hasLoginState(); - e2.call(this, t3); - }); - const t2 = this.hasLoginState(); - e2.call(this, t2); - } - onLoginStateExpired(e2) { - Me(je, e2.bind(this)); - } - onAccessTokenRefreshed(e2) { - Me(We, e2.bind(this)); - } - onAnonymousConverted(e2) { - Me(Be, e2.bind(this)); - } - onLoginTypeChanged(e2) { - Me($e, () => { - const t2 = this.hasLoginState(); - e2.call(this, t2); - }); - } - async getAccessToken() { - return { accessToken: (await this._request.getAccessToken()).accessToken, env: this.config.env }; - } - hasLoginState() { - const { refreshTokenKey: e2 } = this._cache.keys; - return this._cache.getStore(e2) ? new tt(this.config.env) : null; - } - async isUsernameRegistered(e2) { - if ("string" != typeof e2) - throw new te({ code: "PARAM_ERROR", message: "username must be a string" }); - const { data: t2 } = await this._request.send("auth.isUsernameRegistered", { username: e2 }); - return t2 && t2.isRegistered; - } - getLoginState() { - return Promise.resolve(this.hasLoginState()); - } - async signInWithTicket(e2) { - return new st(this.config).signIn(e2); - } - shouldRefreshAccessToken(e2) { - this._request._shouldRefreshAccessTokenHook = e2.bind(this); - } - getUserInfo() { - return this._request.send("auth.getUserInfo", {}).then((e2) => e2.code ? e2 : { ...e2.data, requestId: e2.seqId }); - } - getAuthHeader() { - const { refreshTokenKey: e2, accessTokenKey: t2 } = this._cache.keys, n2 = this._cache.getStore(e2); - return { "x-cloudbase-credentials": this._cache.getStore(t2) + "/@@/" + n2 }; - } - _onAnonymousConverted(e2) { - const { env: t2 } = e2.data; - t2 === this.config.env && this._cache.updatePersistence(this.config.persistence); - } - _onLoginTypeChanged(e2) { - const { loginType: t2, persistence: n2, env: s2 } = e2.data; - s2 === this.config.env && (this._cache.updatePersistence(n2), this._cache.setStore(this._cache.keys.loginTypeKey, t2)); - } -} -const at = function(e2, t2) { - t2 = t2 || Ie(); - const n2 = Xe(this.config.env), { cloudPath: s2, filePath: r2, onUploadProgress: i2, fileType: o2 = "image" } = e2; - return n2.send("storage.getUploadMetadata", { path: s2 }).then((e3) => { - const { data: { url: a2, authorization: c2, token: u2, fileId: l2, cosFileId: h2 }, requestId: d2 } = e3, p2 = { key: s2, signature: c2, "x-cos-meta-fileid": h2, success_action_status: "201", "x-cos-security-token": u2 }; - n2.upload({ url: a2, data: p2, file: r2, name: s2, fileType: o2, onUploadProgress: i2 }).then((e4) => { - 201 === e4.statusCode ? t2(null, { fileID: l2, requestId: d2 }) : t2(new te({ code: "STORAGE_REQUEST_FAIL", message: `STORAGE_REQUEST_FAIL: ${e4.data}` })); - }).catch((e4) => { - t2(e4); - }); - }).catch((e3) => { - t2(e3); - }), t2.promise; -}, ct = function(e2, t2) { - t2 = t2 || Ie(); - const n2 = Xe(this.config.env), { cloudPath: s2 } = e2; - return n2.send("storage.getUploadMetadata", { path: s2 }).then((e3) => { - t2(null, e3); - }).catch((e3) => { - t2(e3); - }), t2.promise; -}, ut = function({ fileList: e2 }, t2) { - if (t2 = t2 || Ie(), !e2 || !Array.isArray(e2)) - return { code: "INVALID_PARAM", message: "fileList必须是非空的数组" }; - for (let t3 of e2) - if (!t3 || "string" != typeof t3) - return { code: "INVALID_PARAM", message: "fileList的元素必须是非空的字符串" }; - const n2 = { fileid_list: e2 }; - return Xe(this.config.env).send("storage.batchDeleteFile", n2).then((e3) => { - e3.code ? t2(null, e3) : t2(null, { fileList: e3.data.delete_list, requestId: e3.requestId }); - }).catch((e3) => { - t2(e3); - }), t2.promise; -}, lt = function({ fileList: e2 }, t2) { - t2 = t2 || Ie(), e2 && Array.isArray(e2) || t2(null, { code: "INVALID_PARAM", message: "fileList必须是非空的数组" }); - let n2 = []; - for (let s3 of e2) - "object" == typeof s3 ? (s3.hasOwnProperty("fileID") && s3.hasOwnProperty("maxAge") || t2(null, { code: "INVALID_PARAM", message: "fileList的元素必须是包含fileID和maxAge的对象" }), n2.push({ fileid: s3.fileID, max_age: s3.maxAge })) : "string" == typeof s3 ? n2.push({ fileid: s3 }) : t2(null, { code: "INVALID_PARAM", message: "fileList的元素必须是字符串" }); - const s2 = { file_list: n2 }; - return Xe(this.config.env).send("storage.batchGetDownloadUrl", s2).then((e3) => { - e3.code ? t2(null, e3) : t2(null, { fileList: e3.data.download_list, requestId: e3.requestId }); - }).catch((e3) => { - t2(e3); - }), t2.promise; -}, ht = async function({ fileID: e2 }, t2) { - const n2 = (await lt.call(this, { fileList: [{ fileID: e2, maxAge: 600 }] })).fileList[0]; - if ("SUCCESS" !== n2.code) - return t2 ? t2(n2) : new Promise((e3) => { - e3(n2); - }); - const s2 = Xe(this.config.env); - let r2 = n2.download_url; - if (r2 = encodeURI(r2), !t2) - return s2.download({ url: r2 }); - t2(await s2.download({ url: r2 })); -}, dt = function({ name: e2, data: t2, query: n2, parse: s2, search: r2, timeout: i2 }, o2) { - const a2 = o2 || Ie(); - let c2; - try { - c2 = t2 ? JSON.stringify(t2) : ""; - } catch (e3) { - return Promise.reject(e3); - } - if (!e2) - return Promise.reject(new te({ code: "PARAM_ERROR", message: "函数名不能为空" })); - const u2 = { inQuery: n2, parse: s2, search: r2, function_name: e2, request_data: c2 }; - return Xe(this.config.env).send("functions.invokeFunction", u2, { timeout: i2 }).then((e3) => { - if (e3.code) - a2(null, e3); - else { - let t3 = e3.data.response_data; - if (s2) - a2(null, { result: t3, requestId: e3.requestId }); - else - try { - t3 = JSON.parse(e3.data.response_data), a2(null, { result: t3, requestId: e3.requestId }); - } catch (e4) { - a2(new te({ message: "response data must be json" })); - } - } - return a2.promise; - }).catch((e3) => { - a2(e3); - }), a2.promise; -}, pt = { timeout: 15e3, persistence: "session" }, ft = {}; -class gt { - constructor(e2) { - this.config = e2 || this.config, this.authObj = void 0; - } - init(e2) { - switch (Ce.adapter || (this.requestClient = new Ce.adapter.reqClass({ timeout: e2.timeout || 5e3, timeoutMsg: `请求在${(e2.timeout || 5e3) / 1e3}s内未完成,已中断` })), this.config = { ...pt, ...e2 }, true) { - case this.config.timeout > 6e5: - console.warn("timeout大于可配置上限[10分钟],已重置为上限数值"), this.config.timeout = 6e5; - break; - case this.config.timeout < 100: - console.warn("timeout小于可配置下限[100ms],已重置为下限数值"), this.config.timeout = 100; - } - return new gt(this.config); - } - auth({ persistence: e2 } = {}) { - if (this.authObj) - return this.authObj; - const t2 = e2 || Ce.adapter.primaryStorage || pt.persistence; - var n2; - return t2 !== this.config.persistence && (this.config.persistence = t2), function(e3) { - const { env: t3 } = e3; - Ee[t3] = new Oe(e3), Le[t3] = new Oe({ ...e3, persistence: "local" }); - }(this.config), n2 = this.config, Qe[n2.env] = new Ye(n2), this.authObj = new ot(this.config), this.authObj; - } - on(e2, t2) { - return Me.apply(this, [e2, t2]); - } - off(e2, t2) { - return Fe.apply(this, [e2, t2]); - } - callFunction(e2, t2) { - return dt.apply(this, [e2, t2]); - } - deleteFile(e2, t2) { - return ut.apply(this, [e2, t2]); - } - getTempFileURL(e2, t2) { - return lt.apply(this, [e2, t2]); - } - downloadFile(e2, t2) { - return ht.apply(this, [e2, t2]); - } - uploadFile(e2, t2) { - return at.apply(this, [e2, t2]); - } - getUploadMetadata(e2, t2) { - return ct.apply(this, [e2, t2]); - } - registerExtension(e2) { - ft[e2.name] = e2; - } - async invokeExtension(e2, t2) { - const n2 = ft[e2]; - if (!n2) - throw new te({ message: `扩展${e2} 必须先注册` }); - return await n2.invoke(t2, this); - } - useAdapters(e2) { - const { adapter: t2, runtime: n2 } = Ae(e2) || {}; - t2 && (Ce.adapter = t2), n2 && (Ce.runtime = n2); - } -} -var mt = new gt(); -function yt(e2, t2, n2) { - void 0 === n2 && (n2 = {}); - var s2 = /\?/.test(t2), r2 = ""; - for (var i2 in n2) - "" === r2 ? !s2 && (t2 += "?") : r2 += "&", r2 += i2 + "=" + encodeURIComponent(n2[i2]); - return /^http(s)?:\/\//.test(t2 += r2) ? t2 : "" + e2 + t2; -} -class _t { - post(e2) { - const { url: t2, data: n2, headers: s2, timeout: r2 } = e2; - return new Promise((e3, i2) => { - ne.request({ url: yt("https:", t2), data: n2, method: "POST", header: s2, timeout: r2, success(t3) { - e3(t3); - }, fail(e4) { - i2(e4); - } }); - }); - } - upload(e2) { - return new Promise((t2, n2) => { - const { url: s2, file: r2, data: i2, headers: o2, fileType: a2 } = e2, c2 = ne.uploadFile({ url: yt("https:", s2), name: "file", formData: Object.assign({}, i2), filePath: r2, fileType: a2, header: o2, success(e3) { - const n3 = { statusCode: e3.statusCode, data: e3.data || {} }; - 200 === e3.statusCode && i2.success_action_status && (n3.statusCode = parseInt(i2.success_action_status, 10)), t2(n3); - }, fail(e3) { - n2(new Error(e3.errMsg || "uploadFile:fail")); - } }); - "function" == typeof e2.onUploadProgress && c2 && "function" == typeof c2.onProgressUpdate && c2.onProgressUpdate((t3) => { - e2.onUploadProgress({ loaded: t3.totalBytesSent, total: t3.totalBytesExpectedToSend }); - }); - }); - } -} -const wt = { setItem(e2, t2) { - ne.setStorageSync(e2, t2); -}, getItem: (e2) => ne.getStorageSync(e2), removeItem(e2) { - ne.removeStorageSync(e2); -}, clear() { - ne.clearStorageSync(); -} }; -var vt = { genAdapter: function() { - return { root: {}, reqClass: _t, localStorage: wt, primaryStorage: "local" }; -}, isMatch: function() { - return true; -}, runtime: "uni_app" }; -mt.useAdapters(vt); -const It = mt, St = It.init; -It.init = function(e2) { - e2.env = e2.spaceId; - const t2 = St.call(this, e2); - t2.config.provider = "tencent", t2.config.spaceId = e2.spaceId; - const n2 = t2.auth; - return t2.auth = function(e3) { - const t3 = n2.call(this, e3); - return ["linkAndRetrieveDataWithTicket", "signInAnonymously", "signOut", "getAccessToken", "getLoginState", "signInWithTicket", "getUserInfo"].forEach((e4) => { - var n3; - t3[e4] = (n3 = t3[e4], function(e5) { - e5 = e5 || {}; - const { success: t4, fail: s2, complete: r2 } = ee(e5); - if (!(t4 || s2 || r2)) - return n3.call(this, e5); - n3.call(this, e5).then((e6) => { - t4 && t4(e6), r2 && r2(e6); - }, (e6) => { - s2 && s2(e6), r2 && r2(e6); - }); - }).bind(t3); - }), t3; - }, t2.customAuth = t2.auth, t2; -}; -var bt = It; -async function kt(e2, t2) { - const n2 = `http://${e2}:${t2}/system/ping`; - try { - const e3 = await (s2 = { url: n2, timeout: 500 }, new Promise((e4, t3) => { - ne.request({ ...s2, success(t4) { - e4(t4); - }, fail(e5) { - t3(e5); - } }); - })); - return !(!e3.data || 0 !== e3.data.code); - } catch (e3) { - return false; - } - var s2; -} -async function At(e2, t2) { - let n2; - for (let s2 = 0; s2 < e2.length; s2++) { - const r2 = e2[s2]; - if (await kt(r2, t2)) { - n2 = r2; - break; - } - } - return { address: n2, port: t2 }; -} -const Ct = { "serverless.file.resource.generateProximalSign": "storage/generate-proximal-sign", "serverless.file.resource.report": "storage/report", "serverless.file.resource.delete": "storage/delete", "serverless.file.resource.getTempFileURL": "storage/get-temp-file-url" }; -var Pt = class { - constructor(e2) { - if (["spaceId", "clientSecret"].forEach((t2) => { - if (!Object.prototype.hasOwnProperty.call(e2, t2)) - throw new Error(`${t2} required`); - }), !e2.endpoint) - throw new Error("集群空间未配置ApiEndpoint,配置后需要重新关联服务空间后生效"); - this.config = Object.assign({}, e2), this.config.provider = "dcloud", this.config.requestUrl = this.config.endpoint + "/client", this.config.envType = this.config.envType || "public", this.adapter = ne; - } - async request(e2, t2 = true) { - const n2 = t2; - return e2 = n2 ? await this.setupLocalRequest(e2) : this.setupRequest(e2), Promise.resolve().then(() => n2 ? this.requestLocal(e2) : de.wrappedRequest(e2, this.adapter.request)); - } - requestLocal(e2) { - return new Promise((t2, n2) => { - this.adapter.request(Object.assign(e2, { complete(e3) { - if (e3 || (e3 = {}), !e3.statusCode || e3.statusCode >= 400) { - const t3 = e3.data && e3.data.code || "SYS_ERR", s2 = e3.data && e3.data.message || "request:fail"; - return n2(new te({ code: t3, message: s2 })); - } - t2({ success: true, result: e3.data }); - } })); - }); - } - setupRequest(e2) { - const t2 = Object.assign({}, e2, { spaceId: this.config.spaceId, timestamp: Date.now() }), n2 = { "Content-Type": "application/json" }; - n2["x-serverless-sign"] = de.sign(t2, this.config.clientSecret); - const s2 = he(); - n2["x-client-info"] = encodeURIComponent(JSON.stringify(s2)); - const { token: r2 } = re(); - return n2["x-client-token"] = r2, { url: this.config.requestUrl, method: "POST", data: t2, dataType: "json", header: JSON.parse(JSON.stringify(n2)) }; - } - async setupLocalRequest(e2) { - const t2 = he(), { token: n2 } = re(), s2 = Object.assign({}, e2, { spaceId: this.config.spaceId, timestamp: Date.now(), clientInfo: t2, token: n2 }), { address: r2, servePort: i2 } = Ys.__dev__.debugInfo, { address: o2 } = await At(r2, i2); - return { url: `http://${o2}:${i2}/${Ct[e2.method]}`, method: "POST", data: s2, dataType: "json", header: JSON.parse(JSON.stringify({ "Content-Type": "application/json" })) }; - } - callFunction(e2) { - const t2 = { method: "serverless.function.runtime.invoke", params: JSON.stringify({ functionTarget: e2.name, functionArgs: e2.data || {} }) }; - return this.request(t2, false); - } - getUploadFileOptions(e2) { - const t2 = { method: "serverless.file.resource.generateProximalSign", params: JSON.stringify(e2) }; - return this.request(t2); - } - reportUploadFile(e2) { - const t2 = { method: "serverless.file.resource.report", params: JSON.stringify(e2) }; - return this.request(t2); - } - uploadFile({ filePath: e2, cloudPath: t2, fileType: n2 = "image", onUploadProgress: s2 }) { - if (!t2) - throw new te({ code: "CLOUDPATH_REQUIRED", message: "cloudPath不可为空" }); - let r2; - return this.getUploadFileOptions({ cloudPath: t2 }).then((t3) => { - const { url: i2, formData: o2, name: a2 } = t3.result; - return r2 = t3.result.fileUrl, new Promise((t4, r3) => { - const c2 = this.adapter.uploadFile({ url: i2, formData: o2, name: a2, filePath: e2, fileType: n2, success(e3) { - e3 && e3.statusCode < 400 ? t4(e3) : r3(new te({ code: "UPLOAD_FAILED", message: "文件上传失败" })); - }, fail(e3) { - r3(new te({ code: e3.code || "UPLOAD_FAILED", message: e3.message || e3.errMsg || "文件上传失败" })); - } }); - "function" == typeof s2 && c2 && "function" == typeof c2.onProgressUpdate && c2.onProgressUpdate((e3) => { - s2({ loaded: e3.totalBytesSent, total: e3.totalBytesExpectedToSend }); - }); - }); - }).then(() => this.reportUploadFile({ cloudPath: t2 })).then((t3) => new Promise((n3, s3) => { - t3.success ? n3({ success: true, filePath: e2, fileID: r2 }) : s3(new te({ code: "UPLOAD_FAILED", message: "文件上传失败" })); - })); - } - deleteFile({ fileList: e2 }) { - const t2 = { method: "serverless.file.resource.delete", params: JSON.stringify({ fileList: e2 }) }; - return this.request(t2).then((e3) => { - if (e3.success) - return e3.result; - throw new te({ code: "DELETE_FILE_FAILED", message: "删除文件失败" }); - }); - } - getTempFileURL({ fileList: e2, maxAge: t2 } = {}) { - if (!Array.isArray(e2) || 0 === e2.length) - throw new te({ code: "INVALID_PARAM", message: "fileList的元素必须是非空的字符串" }); - const n2 = { method: "serverless.file.resource.getTempFileURL", params: JSON.stringify({ fileList: e2, maxAge: t2 }) }; - return this.request(n2).then((e3) => { - if (e3.success) - return { fileList: e3.result.fileList.map((e4) => ({ fileID: e4.fileID, tempFileURL: e4.tempFileURL })) }; - throw new te({ code: "GET_TEMP_FILE_URL_FAILED", message: "获取临时文件链接失败" }); - }); - } -}; -var Tt = { init(e2) { - const t2 = new Pt(e2), n2 = { signInAnonymously: function() { - return Promise.resolve(); - }, getLoginState: function() { - return Promise.resolve(false); - } }; - return t2.auth = function() { - return n2; - }, t2.customAuth = t2.auth, t2; -} }, xt = n(function(e2, t2) { - e2.exports = r.enc.Hex; -}); -function Ot() { - return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(e2) { - var t2 = 16 * Math.random() | 0; - return ("x" === e2 ? t2 : 3 & t2 | 8).toString(16); - }); -} -function Et(e2 = "", t2 = {}) { - const { data: n2, functionName: s2, method: r2, headers: i2, signHeaderKeys: o2 = [], config: a2 } = t2, c2 = Date.now(), u2 = Ot(), l2 = Object.assign({}, i2, { "x-from-app-id": a2.spaceAppId, "x-from-env-id": a2.spaceId, "x-to-env-id": a2.spaceId, "x-from-instance-id": c2, "x-from-function-name": s2, "x-client-timestamp": c2, "x-alipay-source": "client", "x-request-id": u2, "x-alipay-callid": u2, "x-trace-id": u2 }), h2 = ["x-from-app-id", "x-from-env-id", "x-to-env-id", "x-from-instance-id", "x-from-function-name", "x-client-timestamp"].concat(o2), [d2 = "", p2 = ""] = e2.split("?") || [], f2 = function(e3) { - const t3 = e3.signedHeaders.join(";"), n3 = e3.signedHeaders.map((t4) => `${t4.toLowerCase()}:${e3.headers[t4]} -`).join(""), s3 = we(e3.body).toString(xt), r3 = `${e3.method.toUpperCase()} -${e3.path} -${e3.query} -${n3} -${t3} -${s3} -`, i3 = we(r3).toString(xt), o3 = `HMAC-SHA256 -${e3.timestamp} -${i3} -`, a3 = ve(o3, e3.secretKey).toString(xt); - return `HMAC-SHA256 Credential=${e3.secretId}, SignedHeaders=${t3}, Signature=${a3}`; - }({ path: d2, query: p2, method: r2, headers: l2, timestamp: c2, body: JSON.stringify(n2), secretId: a2.accessKey, secretKey: a2.secretKey, signedHeaders: h2.sort() }); - return { url: `${a2.endpoint}${e2}`, headers: Object.assign({}, l2, { Authorization: f2 }) }; -} -function Lt({ url: e2, data: t2, method: n2 = "POST", headers: s2 = {}, timeout: r2 }) { - return new Promise((i2, o2) => { - ne.request({ url: e2, method: n2, data: "object" == typeof t2 ? JSON.stringify(t2) : t2, header: s2, dataType: "json", timeout: r2, complete: (e3 = {}) => { - const t3 = s2["x-trace-id"] || ""; - if (!e3.statusCode || e3.statusCode >= 400) { - const { message: n3, errMsg: s3, trace_id: r3 } = e3.data || {}; - return o2(new te({ code: "SYS_ERR", message: n3 || s3 || "request:fail", requestId: r3 || t3 })); - } - i2({ status: e3.statusCode, data: e3.data, headers: e3.header, requestId: t3 }); - } }); - }); -} -function Rt(e2, t2) { - const { path: n2, data: s2, method: r2 = "GET" } = e2, { url: i2, headers: o2 } = Et(n2, { functionName: "", data: s2, method: r2, headers: { "x-alipay-cloud-mode": "oss", "x-data-api-type": "oss", "x-expire-timestamp": Date.now() + 6e4 }, signHeaderKeys: ["x-data-api-type", "x-expire-timestamp"], config: t2 }); - return Lt({ url: i2, data: s2, method: r2, headers: o2 }).then((e3) => { - const t3 = e3.data || {}; - if (!t3.success) - throw new te({ code: e3.errCode, message: e3.errMsg, requestId: e3.requestId }); - return t3.data || {}; - }).catch((e3) => { - throw new te({ code: e3.errCode, message: e3.errMsg, requestId: e3.requestId }); - }); -} -function Ut(e2 = "") { - const t2 = e2.trim().replace(/^cloud:\/\//, ""), n2 = t2.indexOf("/"); - if (n2 <= 0) - throw new te({ code: "INVALID_PARAM", message: "fileID不合法" }); - const s2 = t2.substring(0, n2), r2 = t2.substring(n2 + 1); - return s2 !== this.config.spaceId && console.warn("file ".concat(e2, " does not belong to env ").concat(this.config.spaceId)), r2; -} -function Nt(e2 = "") { - return "cloud://".concat(this.config.spaceId, "/").concat(e2.replace(/^\/+/, "")); -} -class Dt { - constructor(e2) { - this.config = e2; - } - signedURL(e2, t2 = {}) { - const n2 = `/ws/function/${e2}`, s2 = this.config.wsEndpoint.replace(/^ws(s)?:\/\//, ""), r2 = Object.assign({}, t2, { accessKeyId: this.config.accessKey, signatureNonce: Ot(), timestamp: "" + Date.now() }), i2 = [n2, ["accessKeyId", "authorization", "signatureNonce", "timestamp"].sort().map(function(e3) { - return r2[e3] ? "".concat(e3, "=").concat(r2[e3]) : null; - }).filter(Boolean).join("&"), `host:${s2}`].join("\n"), o2 = ["HMAC-SHA256", we(i2).toString(xt)].join("\n"), a2 = ve(o2, this.config.secretKey).toString(xt), c2 = Object.keys(r2).map((e3) => `${e3}=${encodeURIComponent(r2[e3])}`).join("&"); - return `${this.config.wsEndpoint}${n2}?${c2}&signature=${a2}`; - } -} -var Mt = class { - constructor(e2) { - if (["spaceId", "spaceAppId", "accessKey", "secretKey"].forEach((t2) => { - if (!Object.prototype.hasOwnProperty.call(e2, t2)) - throw new Error(`${t2} required`); - }), e2.endpoint) { - if ("string" != typeof e2.endpoint) - throw new Error("endpoint must be string"); - if (!/^https:\/\//.test(e2.endpoint)) - throw new Error("endpoint must start with https://"); - e2.endpoint = e2.endpoint.replace(/\/$/, ""); - } - this.config = Object.assign({}, e2, { endpoint: e2.endpoint || `https://${e2.spaceId}.api-hz.cloudbasefunction.cn`, wsEndpoint: e2.wsEndpoint || `wss://${e2.spaceId}.api-hz.cloudbasefunction.cn` }), this._websocket = new Dt(this.config); - } - callFunction(e2) { - return function(e3, t2) { - const { name: n2, data: s2, async: r2 = false, timeout: i2 } = e3, o2 = "POST", a2 = { "x-to-function-name": n2 }; - r2 && (a2["x-function-invoke-type"] = "async"); - const { url: c2, headers: u2 } = Et("/functions/invokeFunction", { functionName: n2, data: s2, method: o2, headers: a2, signHeaderKeys: ["x-to-function-name"], config: t2 }); - return Lt({ url: c2, data: s2, method: o2, headers: u2, timeout: i2 }).then((e4) => { - let t3 = 0; - if (r2) { - const n3 = e4.data || {}; - t3 = "200" === n3.errCode ? 0 : n3.errCode, e4.data = n3.data || {}, e4.errMsg = n3.errMsg; - } - if (0 !== t3) - throw new te({ code: t3, message: e4.errMsg, requestId: e4.requestId }); - return { errCode: t3, success: 0 === t3, requestId: e4.requestId, result: e4.data }; - }).catch((e4) => { - throw new te({ code: e4.errCode, message: e4.errMsg, requestId: e4.requestId }); - }); - }(e2, this.config); - } - uploadFileToOSS({ url: e2, filePath: t2, fileType: n2, formData: s2, onUploadProgress: r2 }) { - return new Promise((i2, o2) => { - const a2 = ne.uploadFile({ url: e2, filePath: t2, fileType: n2, formData: s2, name: "file", success(e3) { - e3 && e3.statusCode < 400 ? i2(e3) : o2(new te({ code: "UPLOAD_FAILED", message: "文件上传失败" })); - }, fail(e3) { - o2(new te({ code: e3.code || "UPLOAD_FAILED", message: e3.message || e3.errMsg || "文件上传失败" })); - } }); - "function" == typeof r2 && a2 && "function" == typeof a2.onProgressUpdate && a2.onProgressUpdate((e3) => { - r2({ loaded: e3.totalBytesSent, total: e3.totalBytesExpectedToSend }); - }); - }); - } - async uploadFile({ filePath: e2, cloudPath: t2 = "", fileType: n2 = "image", onUploadProgress: s2 }) { - if ("string" !== f(t2)) - throw new te({ code: "INVALID_PARAM", message: "cloudPath必须为字符串类型" }); - if (!(t2 = t2.trim())) - throw new te({ code: "INVALID_PARAM", message: "cloudPath不可为空" }); - if (/:\/\//.test(t2)) - throw new te({ code: "INVALID_PARAM", message: "cloudPath不合法" }); - const r2 = await Rt({ path: "/".concat(t2.replace(/^\//, ""), "?post_url") }, this.config), { file_id: i2, upload_url: o2, form_data: a2 } = r2, c2 = a2 && a2.reduce((e3, t3) => (e3[t3.key] = t3.value, e3), {}); - return this.uploadFileToOSS({ url: o2, filePath: e2, fileType: n2, formData: c2, onUploadProgress: s2 }).then(() => ({ fileID: i2 })); - } - async getTempFileURL({ fileList: e2 }) { - return new Promise((t2, n2) => { - (!e2 || e2.length < 0) && n2(new te({ errCode: "INVALID_PARAM", errMsg: "fileList不能为空数组" })), e2.length > 50 && n2(new te({ errCode: "INVALID_PARAM", errMsg: "fileList数组长度不能超过50" })); - const s2 = []; - for (const t3 of e2) { - "string" !== f(t3) && n2(new te({ errCode: "INVALID_PARAM", errMsg: "fileList的元素必须是非空的字符串" })); - const e3 = Ut.call(this, t3); - s2.push({ file_id: e3, expire: 600 }); - } - Rt({ path: "/?download_url", data: { file_list: s2 }, method: "POST" }, this.config).then((e3) => { - const { file_list: n3 = [] } = e3; - t2({ fileList: n3.map((e4) => ({ fileID: Nt.call(this, e4.file_id), tempFileURL: e4.download_url })) }); - }).catch((e3) => n2(e3)); - }); - } - async connectWebSocket(e2) { - const { name: t2, query: n2 } = e2; - return ne.connectSocket({ url: this._websocket.signedURL(t2, n2), complete: () => { - } }); - } -}; -var qt = { init: (e2) => { - e2.provider = "alipay"; - const t2 = new Mt(e2); - return t2.auth = function() { - return { signInAnonymously: function() { - return Promise.resolve(); - }, getLoginState: function() { - return Promise.resolve(true); - } }; - }, t2; -} }; -function Ft({ data: e2 }) { - let t2; - t2 = he(); - const n2 = JSON.parse(JSON.stringify(e2 || {})); - if (Object.assign(n2, { clientInfo: t2 }), !n2.uniIdToken) { - const { token: e3 } = re(); - e3 && (n2.uniIdToken = e3); - } - return n2; -} -async function Kt(e2 = {}) { - await this.__dev__.initLocalNetwork(); - const { localAddress: t2, localPort: n2 } = this.__dev__, s2 = { aliyun: "aliyun", tencent: "tcb", alipay: "alipay", dcloud: "dcloud" }[this.config.provider], r2 = this.config.spaceId, i2 = `http://${t2}:${n2}/system/check-function`, o2 = `http://${t2}:${n2}/cloudfunctions/${e2.name}`; - return new Promise((t3, n3) => { - ne.request({ method: "POST", url: i2, data: { name: e2.name, platform: C, provider: s2, spaceId: r2 }, timeout: 3e3, success(e3) { - t3(e3); - }, fail() { - t3({ data: { code: "NETWORK_ERROR", message: "连接本地调试服务失败,请检查客户端是否和主机在同一局域网下,自动切换为已部署的云函数。" } }); - } }); - }).then(({ data: e3 } = {}) => { - const { code: t3, message: n3 } = e3 || {}; - return { code: 0 === t3 ? 0 : t3 || "SYS_ERR", message: n3 || "SYS_ERR" }; - }).then(({ code: t3, message: n3 }) => { - if (0 !== t3) { - switch (t3) { - case "MODULE_ENCRYPTED": - console.error(`此云函数(${e2.name})依赖加密公共模块不可本地调试,自动切换为云端已部署的云函数`); - break; - case "FUNCTION_ENCRYPTED": - console.error(`此云函数(${e2.name})已加密不可本地调试,自动切换为云端已部署的云函数`); - break; - case "ACTION_ENCRYPTED": - console.error(n3 || "需要访问加密的uni-clientDB-action,自动切换为云端环境"); - break; - case "NETWORK_ERROR": - console.error(n3 || "连接本地调试服务失败,请检查客户端是否和主机在同一局域网下"); - break; - case "SWITCH_TO_CLOUD": - break; - default: { - const e3 = `检测本地调试服务出现错误:${n3},请检查网络环境或重启客户端再试`; - throw console.error(e3), new Error(e3); - } - } - return this._callCloudFunction(e2); - } - return new Promise((t4, n4) => { - const r3 = Ft.call(this, { data: e2.data }); - ne.request({ method: "POST", url: o2, data: { provider: s2, platform: C, param: r3 }, timeout: e2.timeout, success: ({ statusCode: e3, data: s3 } = {}) => !e3 || e3 >= 400 ? n4(new te({ code: s3.code || "SYS_ERR", message: s3.message || "request:fail" })) : t4({ result: s3 }), fail(e3) { - n4(new te({ code: e3.code || e3.errCode || "SYS_ERR", message: e3.message || e3.errMsg || "request:fail" })); - } }); - }); - }); -} -const jt = [{ rule: /fc_function_not_found|FUNCTION_NOT_FOUND/, content: ",云函数[{functionName}]在云端不存在,请检查此云函数名称是否正确以及该云函数是否已上传到服务空间", mode: "append" }]; -var $t = /[\\^$.*+?()[\]{}|]/g, Bt = RegExp($t.source); -function Wt(e2, t2, n2) { - return e2.replace(new RegExp((s2 = t2) && Bt.test(s2) ? s2.replace($t, "\\$&") : s2, "g"), n2); - var s2; -} -const Jt = "request", zt = "response", Vt = "both"; -const En = { code: 2e4, message: "System error" }, Ln = { code: 20101, message: "Invalid client" }; -function Nn(e2) { - const { errSubject: t2, subject: n2, errCode: s2, errMsg: r2, code: i2, message: o2, cause: a2 } = e2 || {}; - return new te({ subject: t2 || n2 || "uni-secure-network", code: s2 || i2 || En.code, message: r2 || o2, cause: a2 }); -} -let Mn; -function $n({ secretType: e2 } = {}) { - return e2 === Jt || e2 === zt || e2 === Vt; -} -function Bn({ name: e2, data: t2 = {} } = {}) { - return "app" === C; -} -function Wn({ provider: e2, spaceId: t2, functionName: n2 } = {}) { - const { appId: s2, uniPlatform: r2, osName: i2 } = ce(); - let o2 = r2; - "app" === r2 && (o2 = i2); - const a2 = function({ provider: e3, spaceId: t3 } = {}) { - const n3 = A; - if (!n3) - return {}; - e3 = /* @__PURE__ */ function(e4) { - return "tencent" === e4 ? "tcb" : e4; - }(e3); - const s3 = n3.find((n4) => n4.provider === e3 && n4.spaceId === t3); - return s3 && s3.config; - }({ provider: e2, spaceId: t2 }); - if (!a2 || !a2.accessControl || !a2.accessControl.enable) - return false; - const c2 = a2.accessControl.function || {}, u2 = Object.keys(c2); - if (0 === u2.length) - return true; - const l2 = function(e3, t3) { - let n3, s3, r3; - for (let i3 = 0; i3 < e3.length; i3++) { - const o3 = e3[i3]; - o3 !== t3 ? "*" !== o3 ? o3.split(",").map((e4) => e4.trim()).indexOf(t3) > -1 && (s3 = o3) : r3 = o3 : n3 = o3; - } - return n3 || s3 || r3; - }(u2, n2); - if (!l2) - return false; - if ((c2[l2] || []).find((e3 = {}) => e3.appId === s2 && (e3.platform || "").toLowerCase() === o2.toLowerCase())) - return true; - throw console.error(`此应用[appId: ${s2}, platform: ${o2}]不在云端配置的允许访问的应用列表内,参考:https://uniapp.dcloud.net.cn/uniCloud/secure-network.html#verify-client`), Nn(Ln); -} -function Hn({ functionName: e2, result: t2, logPvd: n2 }) { - if (this.__dev__.debugLog && t2 && t2.requestId) { - const s2 = JSON.stringify({ spaceId: this.config.spaceId, functionName: e2, requestId: t2.requestId }); - console.log(`[${n2}-request]${s2}[/${n2}-request]`); - } -} -function Jn(e2) { - const t2 = e2.callFunction, n2 = function(n3) { - const s2 = n3.name; - n3.data = Ft.call(e2, { data: n3.data }); - const r2 = { aliyun: "aliyun", tencent: "tcb", tcb: "tcb", alipay: "alipay", dcloud: "dcloud" }[this.config.provider], i2 = $n(n3), o2 = Bn(n3), a2 = i2 || o2; - return t2.call(this, n3).then((e3) => (e3.errCode = 0, !a2 && Hn.call(this, { functionName: s2, result: e3, logPvd: r2 }), Promise.resolve(e3)), (e3) => (!a2 && Hn.call(this, { functionName: s2, result: e3, logPvd: r2 }), e3 && e3.message && (e3.message = function({ message: e4 = "", extraInfo: t3 = {}, formatter: n4 = [] } = {}) { - for (let s3 = 0; s3 < n4.length; s3++) { - const { rule: r3, content: i3, mode: o3 } = n4[s3], a3 = e4.match(r3); - if (!a3) - continue; - let c2 = i3; - for (let e5 = 1; e5 < a3.length; e5++) - c2 = Wt(c2, `{$${e5}}`, a3[e5]); - for (const e5 in t3) - c2 = Wt(c2, `{${e5}}`, t3[e5]); - return "replace" === o3 ? c2 : e4 + c2; - } - return e4; - }({ message: `[${n3.name}]: ${e3.message}`, formatter: jt, extraInfo: { functionName: s2 } })), Promise.reject(e3))); - }; - e2.callFunction = function(t3) { - const { provider: s2, spaceId: r2 } = e2.config, i2 = t3.name; - let o2, a2; - if (t3.data = t3.data || {}, e2.__dev__.debugInfo && !e2.__dev__.debugInfo.forceRemote && T ? (e2._callCloudFunction || (e2._callCloudFunction = n2, e2._callLocalFunction = Kt), o2 = Kt) : o2 = n2, o2 = o2.bind(e2), Bn(t3)) - ; - else if (function({ name: e3, data: t4 = {} }) { - return "uni-id-co" === e3 && "secureNetworkHandshakeByWeixin" === t4.method; - }(t3)) - a2 = o2.call(e2, t3); - else if ($n(t3)) { - a2 = new Mn({ secretType: t3.secretType, uniCloudIns: e2 }).wrapEncryptDataCallFunction(n2.bind(e2))(t3); - } else if (Wn({ provider: s2, spaceId: r2, functionName: i2 })) { - a2 = new Mn({ secretType: t3.secretType, uniCloudIns: e2 }).wrapVerifyClientCallFunction(n2.bind(e2))(t3); - } else - a2 = o2(t3); - return Object.defineProperty(a2, "result", { get: () => (console.warn("当前返回结果为Promise类型,不可直接访问其result属性,详情请参考:https://uniapp.dcloud.net.cn/uniCloud/faq?id=promise"), {}) }), a2.then((e3) => ("undefined" != typeof UTSJSONObject && (e3.result = new UTSJSONObject(e3.result)), e3)); - }; -} -Mn = class { - constructor() { - throw Nn({ message: `Platform ${C} is not enabled, please check whether secure network module is enabled in your manifest.json` }); - } -}; -const zn = Symbol("CLIENT_DB_INTERNAL"); -function Vn(e2, t2) { - return e2.then = "DoNotReturnProxyWithAFunctionNamedThen", e2._internalType = zn, e2.inspect = null, e2.__v_raw = void 0, new Proxy(e2, { get(e3, n2, s2) { - if ("_uniClient" === n2) - return null; - if ("symbol" == typeof n2) - return e3[n2]; - if (n2 in e3 || "string" != typeof n2) { - const t3 = e3[n2]; - return "function" == typeof t3 ? t3.bind(e3) : t3; - } - return t2.get(e3, n2, s2); - } }); -} -function Gn(e2) { - return { on: (t2, n2) => { - e2[t2] = e2[t2] || [], e2[t2].indexOf(n2) > -1 || e2[t2].push(n2); - }, off: (t2, n2) => { - e2[t2] = e2[t2] || []; - const s2 = e2[t2].indexOf(n2); - -1 !== s2 && e2[t2].splice(s2, 1); - } }; -} -const Yn = ["db.Geo", "db.command", "command.aggregate"]; -function Qn(e2, t2) { - return Yn.indexOf(`${e2}.${t2}`) > -1; -} -function Xn(e2) { - switch (f(e2 = se(e2))) { - case "array": - return e2.map((e3) => Xn(e3)); - case "object": - return e2._internalType === zn || Object.keys(e2).forEach((t2) => { - e2[t2] = Xn(e2[t2]); - }), e2; - case "regexp": - return { $regexp: { source: e2.source, flags: e2.flags } }; - case "date": - return { $date: e2.toISOString() }; - default: - return e2; - } -} -function Zn(e2) { - return e2 && e2.content && e2.content.$method; -} -class es { - constructor(e2, t2, n2) { - this.content = e2, this.prevStage = t2 || null, this.udb = null, this._database = n2; - } - toJSON() { - let e2 = this; - const t2 = [e2.content]; - for (; e2.prevStage; ) - e2 = e2.prevStage, t2.push(e2.content); - return { $db: t2.reverse().map((e3) => ({ $method: e3.$method, $param: Xn(e3.$param) })) }; - } - toString() { - return JSON.stringify(this.toJSON()); - } - getAction() { - const e2 = this.toJSON().$db.find((e3) => "action" === e3.$method); - return e2 && e2.$param && e2.$param[0]; - } - getCommand() { - return { $db: this.toJSON().$db.filter((e2) => "action" !== e2.$method) }; - } - get isAggregate() { - let e2 = this; - for (; e2; ) { - const t2 = Zn(e2), n2 = Zn(e2.prevStage); - if ("aggregate" === t2 && "collection" === n2 || "pipeline" === t2) - return true; - e2 = e2.prevStage; - } - return false; - } - get isCommand() { - let e2 = this; - for (; e2; ) { - if ("command" === Zn(e2)) - return true; - e2 = e2.prevStage; - } - return false; - } - get isAggregateCommand() { - let e2 = this; - for (; e2; ) { - const t2 = Zn(e2), n2 = Zn(e2.prevStage); - if ("aggregate" === t2 && "command" === n2) - return true; - e2 = e2.prevStage; - } - return false; - } - getNextStageFn(e2) { - const t2 = this; - return function() { - return ts({ $method: e2, $param: Xn(Array.from(arguments)) }, t2, t2._database); - }; - } - get count() { - return this.isAggregate ? this.getNextStageFn("count") : function() { - return this._send("count", Array.from(arguments)); - }; - } - get remove() { - return this.isCommand ? this.getNextStageFn("remove") : function() { - return this._send("remove", Array.from(arguments)); - }; - } - get() { - return this._send("get", Array.from(arguments)); - } - get add() { - return this.isCommand ? this.getNextStageFn("add") : function() { - return this._send("add", Array.from(arguments)); - }; - } - update() { - return this._send("update", Array.from(arguments)); - } - end() { - return this._send("end", Array.from(arguments)); - } - get set() { - return this.isCommand ? this.getNextStageFn("set") : function() { - throw new Error("JQL禁止使用set方法"); - }; - } - _send(e2, t2) { - const n2 = this.getAction(), s2 = this.getCommand(); - if (s2.$db.push({ $method: e2, $param: Xn(t2) }), S) { - const e3 = s2.$db.find((e4) => "collection" === e4.$method), t3 = e3 && e3.$param; - t3 && 1 === t3.length && "string" == typeof e3.$param[0] && e3.$param[0].indexOf(",") > -1 && console.warn("检测到使用JQL语法联表查询时,未使用getTemp先过滤主表数据,在主表数据量大的情况下可能会查询缓慢。\n- 如何优化请参考此文档:https://uniapp.dcloud.net.cn/uniCloud/jql?id=lookup-with-temp \n- 如果主表数据量很小请忽略此信息,项目发行时不会出现此提示。"); - } - return this._database._callCloudFunction({ action: n2, command: s2 }); - } -} -function ts(e2, t2, n2) { - return Vn(new es(e2, t2, n2), { get(e3, t3) { - let s2 = "db"; - return e3 && e3.content && (s2 = e3.content.$method), Qn(s2, t3) ? ts({ $method: t3 }, e3, n2) : function() { - return ts({ $method: t3, $param: Xn(Array.from(arguments)) }, e3, n2); - }; - } }); -} -function ns({ path: e2, method: t2 }) { - return class { - constructor() { - this.param = Array.from(arguments); - } - toJSON() { - return { $newDb: [...e2.map((e3) => ({ $method: e3 })), { $method: t2, $param: this.param }] }; - } - toString() { - return JSON.stringify(this.toJSON()); - } - }; -} -function ss(e2, t2 = {}) { - return Vn(new e2(t2), { get: (e3, t3) => Qn("db", t3) ? ts({ $method: t3 }, null, e3) : function() { - return ts({ $method: t3, $param: Xn(Array.from(arguments)) }, null, e3); - } }); -} -class rs extends class { - constructor({ uniClient: e2 = {}, isJQL: t2 = false } = {}) { - this._uniClient = e2, this._authCallBacks = {}, this._dbCallBacks = {}, e2._isDefault && (this._dbCallBacks = L("_globalUniCloudDatabaseCallback")), t2 || (this.auth = Gn(this._authCallBacks)), this._isJQL = t2, Object.assign(this, Gn(this._dbCallBacks)), this.env = Vn({}, { get: (e3, t3) => ({ $env: t3 }) }), this.Geo = Vn({}, { get: (e3, t3) => ns({ path: ["Geo"], method: t3 }) }), this.serverDate = ns({ path: [], method: "serverDate" }), this.RegExp = ns({ path: [], method: "RegExp" }); - } - getCloudEnv(e2) { - if ("string" != typeof e2 || !e2.trim()) - throw new Error("getCloudEnv参数错误"); - return { $env: e2.replace("$cloudEnv_", "") }; - } - _callback(e2, t2) { - const n2 = this._dbCallBacks; - n2[e2] && n2[e2].forEach((e3) => { - e3(...t2); - }); - } - _callbackAuth(e2, t2) { - const n2 = this._authCallBacks; - n2[e2] && n2[e2].forEach((e3) => { - e3(...t2); - }); - } - multiSend() { - const e2 = Array.from(arguments), t2 = e2.map((e3) => { - const t3 = e3.getAction(), n2 = e3.getCommand(); - if ("getTemp" !== n2.$db[n2.$db.length - 1].$method) - throw new Error("multiSend只支持子命令内使用getTemp"); - return { action: t3, command: n2 }; - }); - return this._callCloudFunction({ multiCommand: t2, queryList: e2 }); - } -} { - _parseResult(e2) { - return this._isJQL ? e2.result : e2; - } - _callCloudFunction({ action: e2, command: t2, multiCommand: n2, queryList: s2 }) { - function r2(e3, t3) { - if (n2 && s2) - for (let n3 = 0; n3 < s2.length; n3++) { - const r3 = s2[n3]; - r3.udb && "function" == typeof r3.udb.setResult && (t3 ? r3.udb.setResult(t3) : r3.udb.setResult(e3.result.dataList[n3])); - } - } - const i2 = this, o2 = this._isJQL ? "databaseForJQL" : "database"; - function a2(e3) { - return i2._callback("error", [e3]), M(q(o2, "fail"), e3).then(() => M(q(o2, "complete"), e3)).then(() => (r2(null, e3), Y(j, { type: W, content: e3 }), Promise.reject(e3))); - } - const c2 = M(q(o2, "invoke")), u2 = this._uniClient; - return c2.then(() => u2.callFunction({ name: "DCloud-clientDB", type: l, data: { action: e2, command: t2, multiCommand: n2 } })).then((e3) => { - const { code: t3, message: n3, token: s3, tokenExpired: c3, systemInfo: u3 = [] } = e3.result; - if (u3) - for (let e4 = 0; e4 < u3.length; e4++) { - const { level: t4, message: n4, detail: s4 } = u3[e4], r3 = console[t4] || console.log; - let i3 = "[System Info]" + n4; - s4 && (i3 = `${i3} -详细信息:${s4}`), r3(i3); - } - if (t3) { - return a2(new te({ code: t3, message: n3, requestId: e3.requestId })); - } - e3.result.errCode = e3.result.errCode || e3.result.code, e3.result.errMsg = e3.result.errMsg || e3.result.message, s3 && c3 && (ie({ token: s3, tokenExpired: c3 }), this._callbackAuth("refreshToken", [{ token: s3, tokenExpired: c3 }]), this._callback("refreshToken", [{ token: s3, tokenExpired: c3 }]), Y(B, { token: s3, tokenExpired: c3 })); - const l2 = [{ prop: "affectedDocs", tips: "affectedDocs不再推荐使用,请使用inserted/deleted/updated/data.length替代" }, { prop: "code", tips: "code不再推荐使用,请使用errCode替代" }, { prop: "message", tips: "message不再推荐使用,请使用errMsg替代" }]; - for (let t4 = 0; t4 < l2.length; t4++) { - const { prop: n4, tips: s4 } = l2[t4]; - if (n4 in e3.result) { - const t5 = e3.result[n4]; - Object.defineProperty(e3.result, n4, { get: () => (console.warn(s4), t5) }); - } - } - return function(e4) { - return M(q(o2, "success"), e4).then(() => M(q(o2, "complete"), e4)).then(() => { - r2(e4, null); - const t4 = i2._parseResult(e4); - return Y(j, { type: W, content: t4 }), Promise.resolve(t4); - }); - }(e3); - }, (e3) => { - /fc_function_not_found|FUNCTION_NOT_FOUND/g.test(e3.message) && console.warn("clientDB未初始化,请在web控制台保存一次schema以开启clientDB"); - return a2(new te({ code: e3.code || "SYSTEM_ERROR", message: e3.message, requestId: e3.requestId })); - }); - } -} -const is = "token无效,跳转登录页面", os = "token过期,跳转登录页面", as = { TOKEN_INVALID_TOKEN_EXPIRED: os, TOKEN_INVALID_INVALID_CLIENTID: is, TOKEN_INVALID: is, TOKEN_INVALID_WRONG_TOKEN: is, TOKEN_INVALID_ANONYMOUS_USER: is }, cs = { "uni-id-token-expired": os, "uni-id-check-token-failed": is, "uni-id-token-not-exist": is, "uni-id-check-device-feature-failed": is }; -function us(e2, t2) { - let n2 = ""; - return n2 = e2 ? `${e2}/${t2}` : t2, n2.replace(/^\//, ""); -} -function ls(e2 = [], t2 = "") { - const n2 = [], s2 = []; - return e2.forEach((e3) => { - true === e3.needLogin ? n2.push(us(t2, e3.path)) : false === e3.needLogin && s2.push(us(t2, e3.path)); - }), { needLoginPage: n2, notNeedLoginPage: s2 }; -} -function hs(e2) { - return e2.split("?")[0].replace(/^\//, ""); -} -function ds() { - return function(e2) { - let t2 = e2 && e2.$page && e2.$page.fullPath || ""; - return t2 ? ("/" !== t2.charAt(0) && (t2 = "/" + t2), t2) : t2; - }(function() { - const e2 = getCurrentPages(); - return e2[e2.length - 1]; - }()); -} -function ps() { - return hs(ds()); -} -function fs(e2 = "", t2 = {}) { - if (!e2) - return false; - if (!(t2 && t2.list && t2.list.length)) - return false; - const n2 = t2.list, s2 = hs(e2); - return n2.some((e3) => e3.pagePath === s2); -} -const gs = !!e.uniIdRouter; -const { loginPage: ms, routerNeedLogin: ys, resToLogin: _s, needLoginPage: ws, notNeedLoginPage: vs, loginPageInTabBar: Is } = function({ pages: t2 = [], subPackages: n2 = [], uniIdRouter: s2 = {}, tabBar: r2 = {} } = e) { - const { loginPage: i2, needLogin: o2 = [], resToLogin: a2 = true } = s2, { needLoginPage: c2, notNeedLoginPage: u2 } = ls(t2), { needLoginPage: l2, notNeedLoginPage: h2 } = function(e2 = []) { - const t3 = [], n3 = []; - return e2.forEach((e3) => { - const { root: s3, pages: r3 = [] } = e3, { needLoginPage: i3, notNeedLoginPage: o3 } = ls(r3, s3); - t3.push(...i3), n3.push(...o3); - }), { needLoginPage: t3, notNeedLoginPage: n3 }; - }(n2); - return { loginPage: i2, routerNeedLogin: o2, resToLogin: a2, needLoginPage: [...c2, ...l2], notNeedLoginPage: [...u2, ...h2], loginPageInTabBar: fs(i2, r2) }; -}(); -if (ws.indexOf(ms) > -1) - throw new Error(`Login page [${ms}] should not be "needLogin", please check your pages.json`); -function Ss(e2) { - const t2 = ps(); - if ("/" === e2.charAt(0)) - return e2; - const [n2, s2] = e2.split("?"), r2 = n2.replace(/^\//, "").split("/"), i2 = t2.split("/"); - i2.pop(); - for (let e3 = 0; e3 < r2.length; e3++) { - const t3 = r2[e3]; - ".." === t3 ? i2.pop() : "." !== t3 && i2.push(t3); - } - return "" === i2[0] && i2.shift(), "/" + i2.join("/") + (s2 ? "?" + s2 : ""); -} -function bs(e2) { - const t2 = hs(Ss(e2)); - return !(vs.indexOf(t2) > -1) && (ws.indexOf(t2) > -1 || ys.some((t3) => function(e3, t4) { - return new RegExp(t4).test(e3); - }(e2, t3))); -} -function ks({ redirect: e2 }) { - const t2 = hs(e2), n2 = hs(ms); - return ps() !== n2 && t2 !== n2; -} -function As({ api: e2, redirect: t2 } = {}) { - if (!t2 || !ks({ redirect: t2 })) - return; - const n2 = function(e3, t3) { - return "/" !== e3.charAt(0) && (e3 = "/" + e3), t3 ? e3.indexOf("?") > -1 ? e3 + `&uniIdRedirectUrl=${encodeURIComponent(t3)}` : e3 + `?uniIdRedirectUrl=${encodeURIComponent(t3)}` : e3; - }(ms, t2); - Is ? "navigateTo" !== e2 && "redirectTo" !== e2 || (e2 = "switchTab") : "switchTab" === e2 && (e2 = "navigateTo"); - const s2 = { navigateTo: index.navigateTo, redirectTo: index.redirectTo, switchTab: index.switchTab, reLaunch: index.reLaunch }; - setTimeout(() => { - s2[e2]({ url: n2 }); - }, 0); -} -function Cs({ url: e2 } = {}) { - const t2 = { abortLoginPageJump: false, autoToLoginPage: false }, n2 = function() { - const { token: e3, tokenExpired: t3 } = re(); - let n3; - if (e3) { - if (t3 < Date.now()) { - const e4 = "uni-id-token-expired"; - n3 = { errCode: e4, errMsg: cs[e4] }; - } - } else { - const e4 = "uni-id-check-token-failed"; - n3 = { errCode: e4, errMsg: cs[e4] }; - } - return n3; - }(); - if (bs(e2) && n2) { - n2.uniIdRedirectUrl = e2; - if (z($).length > 0) - return setTimeout(() => { - Y($, n2); - }, 0), t2.abortLoginPageJump = true, t2; - t2.autoToLoginPage = true; - } - return t2; -} -function Ps() { - !function() { - const e3 = ds(), { abortLoginPageJump: t2, autoToLoginPage: n2 } = Cs({ url: e3 }); - t2 || n2 && As({ api: "redirectTo", redirect: e3 }); - }(); - const e2 = ["navigateTo", "redirectTo", "reLaunch", "switchTab"]; - for (let t2 = 0; t2 < e2.length; t2++) { - const n2 = e2[t2]; - index.addInterceptor(n2, { invoke(e3) { - const { abortLoginPageJump: t3, autoToLoginPage: s2 } = Cs({ url: e3.url }); - return t3 ? e3 : s2 ? (As({ api: n2, redirect: Ss(e3.url) }), false) : e3; - } }); - } -} -function Ts() { - this.onResponse((e2) => { - const { type: t2, content: n2 } = e2; - let s2 = false; - switch (t2) { - case "cloudobject": - s2 = function(e3) { - if ("object" != typeof e3) - return false; - const { errCode: t3 } = e3 || {}; - return t3 in cs; - }(n2); - break; - case "clientdb": - s2 = function(e3) { - if ("object" != typeof e3) - return false; - const { errCode: t3 } = e3 || {}; - return t3 in as; - }(n2); - } - s2 && function(e3 = {}) { - const t3 = z($); - Z().then(() => { - const n3 = ds(); - if (n3 && ks({ redirect: n3 })) - return t3.length > 0 ? Y($, Object.assign({ uniIdRedirectUrl: n3 }, e3)) : void (ms && As({ api: "navigateTo", redirect: n3 })); - }); - }(n2); - }); -} -function xs(e2) { - !function(e3) { - e3.onResponse = function(e4) { - V(j, e4); - }, e3.offResponse = function(e4) { - G(j, e4); - }; - }(e2), function(e3) { - e3.onNeedLogin = function(e4) { - V($, e4); - }, e3.offNeedLogin = function(e4) { - G($, e4); - }, gs && (L("_globalUniCloudStatus").needLoginInit || (L("_globalUniCloudStatus").needLoginInit = true, Z().then(() => { - Ps.call(e3); - }), _s && Ts.call(e3))); - }(e2), function(e3) { - e3.onRefreshToken = function(e4) { - V(B, e4); - }, e3.offRefreshToken = function(e4) { - G(B, e4); - }; - }(e2); -} -let Os; -const Es = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", Ls = /^(?:[A-Za-z\d+/]{4})*?(?:[A-Za-z\d+/]{2}(?:==)?|[A-Za-z\d+/]{3}=?)?$/; -function Rs() { - const e2 = re().token || "", t2 = e2.split("."); - if (!e2 || 3 !== t2.length) - return { uid: null, role: [], permission: [], tokenExpired: 0 }; - let n2; - try { - n2 = JSON.parse((s2 = t2[1], decodeURIComponent(Os(s2).split("").map(function(e3) { - return "%" + ("00" + e3.charCodeAt(0).toString(16)).slice(-2); - }).join("")))); - } catch (e3) { - throw new Error("获取当前用户信息出错,详细错误信息为:" + e3.message); - } - var s2; - return n2.tokenExpired = 1e3 * n2.exp, delete n2.exp, delete n2.iat, n2; -} -Os = "function" != typeof atob ? function(e2) { - if (e2 = String(e2).replace(/[\t\n\f\r ]+/g, ""), !Ls.test(e2)) - throw new Error("Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded."); - var t2; - e2 += "==".slice(2 - (3 & e2.length)); - for (var n2, s2, r2 = "", i2 = 0; i2 < e2.length; ) - t2 = Es.indexOf(e2.charAt(i2++)) << 18 | Es.indexOf(e2.charAt(i2++)) << 12 | (n2 = Es.indexOf(e2.charAt(i2++))) << 6 | (s2 = Es.indexOf(e2.charAt(i2++))), r2 += 64 === n2 ? String.fromCharCode(t2 >> 16 & 255) : 64 === s2 ? String.fromCharCode(t2 >> 16 & 255, t2 >> 8 & 255) : String.fromCharCode(t2 >> 16 & 255, t2 >> 8 & 255, 255 & t2); - return r2; -} : atob; -var Us = n(function(e2, t2) { - Object.defineProperty(t2, "__esModule", { value: true }); - const n2 = "chooseAndUploadFile:ok", s2 = "chooseAndUploadFile:fail"; - function r2(e3, t3) { - return e3.tempFiles.forEach((e4, n3) => { - e4.name || (e4.name = e4.path.substring(e4.path.lastIndexOf("/") + 1)), t3 && (e4.fileType = t3), e4.cloudPath = Date.now() + "_" + n3 + e4.name.substring(e4.name.lastIndexOf(".")); - }), e3.tempFilePaths || (e3.tempFilePaths = e3.tempFiles.map((e4) => e4.path)), e3; - } - function i2(e3, t3, { onChooseFile: s3, onUploadProgress: r3 }) { - return t3.then((e4) => { - if (s3) { - const t4 = s3(e4); - if (void 0 !== t4) - return Promise.resolve(t4).then((t5) => void 0 === t5 ? e4 : t5); - } - return e4; - }).then((t4) => false === t4 ? { errMsg: n2, tempFilePaths: [], tempFiles: [] } : function(e4, t5, s4 = 5, r4) { - (t5 = Object.assign({}, t5)).errMsg = n2; - const i3 = t5.tempFiles, o2 = i3.length; - let a2 = 0; - return new Promise((n3) => { - for (; a2 < s4; ) - c2(); - function c2() { - const s5 = a2++; - if (s5 >= o2) - return void (!i3.find((e5) => !e5.url && !e5.errMsg) && n3(t5)); - const u2 = i3[s5]; - e4.uploadFile({ provider: u2.provider, filePath: u2.path, cloudPath: u2.cloudPath, fileType: u2.fileType, cloudPathAsRealPath: u2.cloudPathAsRealPath, onUploadProgress(e5) { - e5.index = s5, e5.tempFile = u2, e5.tempFilePath = u2.path, r4 && r4(e5); - } }).then((e5) => { - u2.url = e5.fileID, s5 < o2 && c2(); - }).catch((e5) => { - u2.errMsg = e5.errMsg || e5.message, s5 < o2 && c2(); - }); - } - }); - }(e3, t4, 5, r3)); - } - t2.initChooseAndUploadFile = function(e3) { - return function(t3 = { type: "all" }) { - return "image" === t3.type ? i2(e3, function(e4) { - const { count: t4, sizeType: n3, sourceType: i3 = ["album", "camera"], extension: o2 } = e4; - return new Promise((e5, a2) => { - index.chooseImage({ count: t4, sizeType: n3, sourceType: i3, extension: o2, success(t5) { - e5(r2(t5, "image")); - }, fail(e6) { - a2({ errMsg: e6.errMsg.replace("chooseImage:fail", s2) }); - } }); - }); - }(t3), t3) : "video" === t3.type ? i2(e3, function(e4) { - const { camera: t4, compressed: n3, maxDuration: i3, sourceType: o2 = ["album", "camera"], extension: a2 } = e4; - return new Promise((e5, c2) => { - index.chooseVideo({ camera: t4, compressed: n3, maxDuration: i3, sourceType: o2, extension: a2, success(t5) { - const { tempFilePath: n4, duration: s3, size: i4, height: o3, width: a3 } = t5; - e5(r2({ errMsg: "chooseVideo:ok", tempFilePaths: [n4], tempFiles: [{ name: t5.tempFile && t5.tempFile.name || "", path: n4, size: i4, type: t5.tempFile && t5.tempFile.type || "", width: a3, height: o3, duration: s3, fileType: "video", cloudPath: "" }] }, "video")); - }, fail(e6) { - c2({ errMsg: e6.errMsg.replace("chooseVideo:fail", s2) }); - } }); - }); - }(t3), t3) : i2(e3, function(e4) { - const { count: t4, extension: n3 } = e4; - return new Promise((e5, i3) => { - let o2 = index.chooseFile; - if ("undefined" != typeof wx$1 && "function" == typeof wx$1.chooseMessageFile && (o2 = wx$1.chooseMessageFile), "function" != typeof o2) - return i3({ errMsg: s2 + " 请指定 type 类型,该平台仅支持选择 image 或 video。" }); - o2({ type: "all", count: t4, extension: n3, success(t5) { - e5(r2(t5)); - }, fail(e6) { - i3({ errMsg: e6.errMsg.replace("chooseFile:fail", s2) }); - } }); - }); - }(t3), t3); - }; - }; -}), Ns = t(Us); -const Ds = "manual"; -function Ms(e2) { - return { props: { localdata: { type: Array, default: () => [] }, options: { type: [Object, Array], default: () => ({}) }, spaceInfo: { type: Object, default: () => ({}) }, collection: { type: [String, Array], default: "" }, action: { type: String, default: "" }, field: { type: String, default: "" }, orderby: { type: String, default: "" }, where: { type: [String, Object], default: "" }, pageData: { type: String, default: "add" }, pageCurrent: { type: Number, default: 1 }, pageSize: { type: Number, default: 20 }, getcount: { type: [Boolean, String], default: false }, gettree: { type: [Boolean, String], default: false }, gettreepath: { type: [Boolean, String], default: false }, startwith: { type: String, default: "" }, limitlevel: { type: Number, default: 10 }, groupby: { type: String, default: "" }, groupField: { type: String, default: "" }, distinct: { type: [Boolean, String], default: false }, foreignKey: { type: String, default: "" }, loadtime: { type: String, default: "auto" }, manual: { type: Boolean, default: false } }, data: () => ({ mixinDatacomLoading: false, mixinDatacomHasMore: false, mixinDatacomResData: [], mixinDatacomErrorMessage: "", mixinDatacomPage: {}, mixinDatacomError: null }), created() { - this.mixinDatacomPage = { current: this.pageCurrent, size: this.pageSize, count: 0 }, this.$watch(() => { - var e3 = []; - return ["pageCurrent", "pageSize", "localdata", "collection", "action", "field", "orderby", "where", "getont", "getcount", "gettree", "groupby", "groupField", "distinct"].forEach((t2) => { - e3.push(this[t2]); - }), e3; - }, (e3, t2) => { - if (this.loadtime === Ds) - return; - let n2 = false; - const s2 = []; - for (let r2 = 2; r2 < e3.length; r2++) - e3[r2] !== t2[r2] && (s2.push(e3[r2]), n2 = true); - e3[0] !== t2[0] && (this.mixinDatacomPage.current = this.pageCurrent), this.mixinDatacomPage.size = this.pageSize, this.onMixinDatacomPropsChange(n2, s2); - }); - }, methods: { onMixinDatacomPropsChange(e3, t2) { - }, mixinDatacomEasyGet({ getone: e3 = false, success: t2, fail: n2 } = {}) { - this.mixinDatacomLoading || (this.mixinDatacomLoading = true, this.mixinDatacomErrorMessage = "", this.mixinDatacomError = null, this.mixinDatacomGet().then((n3) => { - this.mixinDatacomLoading = false; - const { data: s2, count: r2 } = n3.result; - this.getcount && (this.mixinDatacomPage.count = r2), this.mixinDatacomHasMore = s2.length < this.pageSize; - const i2 = e3 ? s2.length ? s2[0] : void 0 : s2; - this.mixinDatacomResData = i2, t2 && t2(i2); - }).catch((e4) => { - this.mixinDatacomLoading = false, this.mixinDatacomErrorMessage = e4, this.mixinDatacomError = e4, n2 && n2(e4); - })); - }, mixinDatacomGet(t2 = {}) { - let n2; - t2 = t2 || {}, n2 = "undefined" != typeof __uniX && __uniX ? e2.databaseForJQL(this.spaceInfo) : e2.database(this.spaceInfo); - const s2 = t2.action || this.action; - s2 && (n2 = n2.action(s2)); - const r2 = t2.collection || this.collection; - n2 = Array.isArray(r2) ? n2.collection(...r2) : n2.collection(r2); - const i2 = t2.where || this.where; - i2 && Object.keys(i2).length && (n2 = n2.where(i2)); - const o2 = t2.field || this.field; - o2 && (n2 = n2.field(o2)); - const a2 = t2.foreignKey || this.foreignKey; - a2 && (n2 = n2.foreignKey(a2)); - const c2 = t2.groupby || this.groupby; - c2 && (n2 = n2.groupBy(c2)); - const u2 = t2.groupField || this.groupField; - u2 && (n2 = n2.groupField(u2)); - true === (void 0 !== t2.distinct ? t2.distinct : this.distinct) && (n2 = n2.distinct()); - const l2 = t2.orderby || this.orderby; - l2 && (n2 = n2.orderBy(l2)); - const h2 = void 0 !== t2.pageCurrent ? t2.pageCurrent : this.mixinDatacomPage.current, d2 = void 0 !== t2.pageSize ? t2.pageSize : this.mixinDatacomPage.size, p2 = void 0 !== t2.getcount ? t2.getcount : this.getcount, f2 = void 0 !== t2.gettree ? t2.gettree : this.gettree, g2 = void 0 !== t2.gettreepath ? t2.gettreepath : this.gettreepath, m2 = { getCount: p2 }, y2 = { limitLevel: void 0 !== t2.limitlevel ? t2.limitlevel : this.limitlevel, startWith: void 0 !== t2.startwith ? t2.startwith : this.startwith }; - return f2 && (m2.getTree = y2), g2 && (m2.getTreePath = y2), n2 = n2.skip(d2 * (h2 - 1)).limit(d2).get(m2), n2; - } } }; -} -function qs(e2) { - return function(t2, n2 = {}) { - n2 = function(e3, t3 = {}) { - return e3.customUI = t3.customUI || e3.customUI, e3.parseSystemError = t3.parseSystemError || e3.parseSystemError, Object.assign(e3.loadingOptions, t3.loadingOptions), Object.assign(e3.errorOptions, t3.errorOptions), "object" == typeof t3.secretMethods && (e3.secretMethods = t3.secretMethods), e3; - }({ customUI: false, loadingOptions: { title: "加载中...", mask: true }, errorOptions: { type: "modal", retry: false } }, n2); - const { customUI: s2, loadingOptions: r2, errorOptions: i2, parseSystemError: o2 } = n2, a2 = !s2; - return new Proxy({}, { get(s3, c2) { - switch (c2) { - case "toString": - return "[object UniCloudObject]"; - case "toJSON": - return {}; - } - return function({ fn: e3, interceptorName: t3, getCallbackArgs: n3 } = {}) { - return async function(...s4) { - const r3 = n3 ? n3({ params: s4 }) : {}; - let i3, o3; - try { - return await M(q(t3, "invoke"), { ...r3 }), i3 = await e3(...s4), await M(q(t3, "success"), { ...r3, result: i3 }), i3; - } catch (e4) { - throw o3 = e4, await M(q(t3, "fail"), { ...r3, error: o3 }), o3; - } finally { - await M(q(t3, "complete"), o3 ? { ...r3, error: o3 } : { ...r3, result: i3 }); - } - }; - }({ fn: async function s4(...l2) { - let h2; - a2 && index.showLoading({ title: r2.title, mask: r2.mask }); - const d2 = { name: t2, type: u, data: { method: c2, params: l2 } }; - "object" == typeof n2.secretMethods && function(e3, t3) { - const n3 = t3.data.method, s5 = e3.secretMethods || {}, r3 = s5[n3] || s5["*"]; - r3 && (t3.secretType = r3); - }(n2, d2); - let p2 = false; - try { - h2 = await e2.callFunction(d2); - } catch (e3) { - p2 = true, h2 = { result: new te(e3) }; - } - const { errSubject: f2, errCode: g2, errMsg: m2, newToken: y2 } = h2.result || {}; - if (a2 && index.hideLoading(), y2 && y2.token && y2.tokenExpired && (ie(y2), Y(B, { ...y2 })), g2) { - let e3 = m2; - if (p2 && o2) { - e3 = (await o2({ objectName: t2, methodName: c2, params: l2, errSubject: f2, errCode: g2, errMsg: m2 })).errMsg || m2; - } - if (a2) - if ("toast" === i2.type) - index.showToast({ title: e3, icon: "none" }); - else { - if ("modal" !== i2.type) - throw new Error(`Invalid errorOptions.type: ${i2.type}`); - { - const { confirm: t3 } = await async function({ title: e4, content: t4, showCancel: n4, cancelText: s5, confirmText: r3 } = {}) { - return new Promise((i3, o3) => { - index.showModal({ title: e4, content: t4, showCancel: n4, cancelText: s5, confirmText: r3, success(e5) { - i3(e5); - }, fail() { - i3({ confirm: false, cancel: true }); - } }); - }); - }({ title: "提示", content: e3, showCancel: i2.retry, cancelText: "取消", confirmText: i2.retry ? "重试" : "确定" }); - if (i2.retry && t3) - return s4(...l2); - } - } - const n3 = new te({ subject: f2, code: g2, message: m2, requestId: h2.requestId }); - throw n3.detail = h2.result, Y(j, { type: J, content: n3 }), n3; - } - return Y(j, { type: J, content: h2.result }), h2.result; - }, interceptorName: "callObject", getCallbackArgs: function({ params: e3 } = {}) { - return { objectName: t2, methodName: c2, params: e3 }; - } }); - } }); - }; -} -function Fs(e2) { - return L("_globalUniCloudSecureNetworkCache__{spaceId}".replace("{spaceId}", e2.config.spaceId)); -} -async function Ks({ openid: e2, callLoginByWeixin: t2 = false } = {}) { - const n2 = Fs(this); - if (e2 && t2) - throw new Error("[SecureNetwork] openid and callLoginByWeixin cannot be passed at the same time"); - if (e2) - return n2.mpWeixinOpenid = e2, {}; - const s2 = await new Promise((e3, t3) => { - index.login({ success(t4) { - e3(t4.code); - }, fail(e4) { - t3(new Error(e4.errMsg)); - } }); - }), r2 = this.importObject("uni-id-co", { customUI: true }); - return await r2.secureNetworkHandshakeByWeixin({ code: s2, callLoginByWeixin: t2 }), n2.mpWeixinCode = s2, { code: s2 }; -} -async function js(e2) { - const t2 = Fs(this); - return t2.initPromise || (t2.initPromise = Ks.call(this, e2).then((e3) => e3).catch((e3) => { - throw delete t2.initPromise, e3; - })), t2.initPromise; -} -function $s(e2) { - return function({ openid: t2, callLoginByWeixin: n2 = false } = {}) { - return js.call(e2, { openid: t2, callLoginByWeixin: n2 }); - }; -} -function Bs(e2) { - !function(e3) { - le = e3; - }(e2); -} -function Ws(e2) { - const t2 = { getSystemInfo: index.getSystemInfo, getPushClientId: index.getPushClientId }; - return function(n2) { - return new Promise((s2, r2) => { - t2[e2]({ ...n2, success(e3) { - s2(e3); - }, fail(e3) { - r2(e3); - } }); - }); - }; -} -class Hs extends class { - constructor() { - this._callback = {}; - } - addListener(e2, t2) { - this._callback[e2] || (this._callback[e2] = []), this._callback[e2].push(t2); - } - on(e2, t2) { - return this.addListener(e2, t2); - } - removeListener(e2, t2) { - if (!t2) - throw new Error('The "listener" argument must be of type function. Received undefined'); - const n2 = this._callback[e2]; - if (!n2) - return; - const s2 = function(e3, t3) { - for (let n3 = e3.length - 1; n3 >= 0; n3--) - if (e3[n3] === t3) - return n3; - return -1; - }(n2, t2); - n2.splice(s2, 1); - } - off(e2, t2) { - return this.removeListener(e2, t2); - } - removeAllListener(e2) { - delete this._callback[e2]; - } - emit(e2, ...t2) { - const n2 = this._callback[e2]; - if (n2) - for (let e3 = 0; e3 < n2.length; e3++) - n2[e3](...t2); - } -} { - constructor() { - super(), this._uniPushMessageCallback = this._receivePushMessage.bind(this), this._currentMessageId = -1, this._payloadQueue = []; - } - init() { - return Promise.all([Ws("getSystemInfo")(), Ws("getPushClientId")()]).then(([{ appId: e2 } = {}, { cid: t2 } = {}] = []) => { - if (!e2) - throw new Error("Invalid appId, please check the manifest.json file"); - if (!t2) - throw new Error("Invalid push client id"); - this._appId = e2, this._pushClientId = t2, this._seqId = Date.now() + "-" + Math.floor(9e5 * Math.random() + 1e5), this.emit("open"), this._initMessageListener(); - }, (e2) => { - throw this.emit("error", e2), this.close(), e2; - }); - } - async open() { - return this.init(); - } - _isUniCloudSSE(e2) { - if ("receive" !== e2.type) - return false; - const t2 = e2 && e2.data && e2.data.payload; - return !(!t2 || "UNI_CLOUD_SSE" !== t2.channel || t2.seqId !== this._seqId); - } - _receivePushMessage(e2) { - if (!this._isUniCloudSSE(e2)) - return; - const t2 = e2 && e2.data && e2.data.payload, { action: n2, messageId: s2, message: r2 } = t2; - this._payloadQueue.push({ action: n2, messageId: s2, message: r2 }), this._consumMessage(); - } - _consumMessage() { - for (; ; ) { - const e2 = this._payloadQueue.find((e3) => e3.messageId === this._currentMessageId + 1); - if (!e2) - break; - this._currentMessageId++, this._parseMessagePayload(e2); - } - } - _parseMessagePayload(e2) { - const { action: t2, messageId: n2, message: s2 } = e2; - "end" === t2 ? this._end({ messageId: n2, message: s2 }) : "message" === t2 && this._appendMessage({ messageId: n2, message: s2 }); - } - _appendMessage({ messageId: e2, message: t2 } = {}) { - this.emit("message", t2); - } - _end({ messageId: e2, message: t2 } = {}) { - this.emit("end", t2), this.close(); - } - _initMessageListener() { - index.onPushMessage(this._uniPushMessageCallback); - } - _destroy() { - index.offPushMessage(this._uniPushMessageCallback); - } - toJSON() { - return { appId: this._appId, pushClientId: this._pushClientId, seqId: this._seqId }; - } - close() { - this._destroy(), this.emit("close"); - } -} -async function Js(e2) { - const t2 = e2.__dev__; - if (!t2.debugInfo) - return; - const { address: n2, servePort: s2 } = t2.debugInfo, { address: r2 } = await At(n2, s2); - if (r2) - return t2.localAddress = r2, void (t2.localPort = s2); - const i2 = console["warn"]; - let o2 = ""; - if ("remote" === t2.debugInfo.initialLaunchType ? (t2.debugInfo.forceRemote = true, o2 = "当前客户端和HBuilderX不在同一局域网下(或其他网络原因无法连接HBuilderX),uniCloud本地调试服务不对当前客户端生效。\n- 如果不使用uniCloud本地调试服务,请直接忽略此信息。\n- 如需使用uniCloud本地调试服务,请将客户端与主机连接到同一局域网下并重新运行到客户端。") : o2 = "无法连接uniCloud本地调试服务,请检查当前客户端是否与主机在同一局域网下。\n- 如需使用uniCloud本地调试服务,请将客户端与主机连接到同一局域网下并重新运行到客户端。", o2 += "\n- 如果在HBuilderX开启的状态下切换过网络环境,请重启HBuilderX后再试\n- 检查系统防火墙是否拦截了HBuilderX自带的nodejs\n- 检查是否错误的使用拦截器修改uni.request方法的参数", 0 === C.indexOf("mp-") && (o2 += "\n- 小程序中如何使用uniCloud,请参考:https://uniapp.dcloud.net.cn/uniCloud/publish.html#useinmp"), !t2.debugInfo.forceRemote) - throw new Error(o2); - i2(o2); -} -function zs(e2) { - e2._initPromiseHub || (e2._initPromiseHub = new v({ createPromise: function() { - let t2 = Promise.resolve(); - var n2; - n2 = 1, t2 = new Promise((e3) => { - setTimeout(() => { - e3(); - }, n2); - }); - const s2 = e2.auth(); - return t2.then(() => s2.getLoginState()).then((e3) => e3 ? Promise.resolve() : s2.signInAnonymously()); - } })); -} -const Vs = { tcb: bt, tencent: bt, aliyun: fe, private: Tt, dcloud: Tt, alipay: qt }; -let Gs = new class { - init(e2) { - let t2 = {}; - const n2 = Vs[e2.provider]; - if (!n2) - throw new Error("未提供正确的provider参数"); - t2 = n2.init(e2), function(e3) { - const t3 = {}; - e3.__dev__ = t3, t3.debugLog = "app" === C; - const n3 = P; - n3 && !n3.code && (t3.debugInfo = n3); - const s2 = new v({ createPromise: function() { - return Js(e3); - } }); - t3.initLocalNetwork = function() { - return s2.exec(); - }; - }(t2), zs(t2), Jn(t2), function(e3) { - const t3 = e3.uploadFile; - e3.uploadFile = function(e4) { - return t3.call(this, e4); - }; - }(t2), function(e3) { - e3.database = function(t3) { - if (t3 && Object.keys(t3).length > 0) - return e3.init(t3).database(); - if (this._database) - return this._database; - const n3 = ss(rs, { uniClient: e3 }); - return this._database = n3, n3; - }, e3.databaseForJQL = function(t3) { - if (t3 && Object.keys(t3).length > 0) - return e3.init(t3).databaseForJQL(); - if (this._databaseForJQL) - return this._databaseForJQL; - const n3 = ss(rs, { uniClient: e3, isJQL: true }); - return this._databaseForJQL = n3, n3; - }; - }(t2), function(e3) { - e3.getCurrentUserInfo = Rs, e3.chooseAndUploadFile = Ns.initChooseAndUploadFile(e3), Object.assign(e3, { get mixinDatacom() { - return Ms(e3); - } }), e3.SSEChannel = Hs, e3.initSecureNetworkByWeixin = $s(e3), e3.setCustomClientInfo = Bs, e3.importObject = qs(e3); - }(t2); - return ["callFunction", "uploadFile", "deleteFile", "getTempFileURL", "downloadFile", "chooseAndUploadFile"].forEach((e3) => { - if (!t2[e3]) - return; - const n3 = t2[e3]; - t2[e3] = function() { - return n3.apply(t2, Array.from(arguments)); - }, t2[e3] = (/* @__PURE__ */ function(e4, t3) { - return function(n4) { - let s2 = false; - if ("callFunction" === t3) { - const e5 = n4 && n4.type || c; - s2 = e5 !== c; - } - const r2 = "callFunction" === t3 && !s2, i2 = this._initPromiseHub.exec(); - n4 = n4 || {}; - const { success: o2, fail: a2, complete: u2 } = ee(n4), l2 = i2.then(() => s2 ? Promise.resolve() : M(q(t3, "invoke"), n4)).then(() => e4.call(this, n4)).then((e5) => s2 ? Promise.resolve(e5) : M(q(t3, "success"), e5).then(() => M(q(t3, "complete"), e5)).then(() => (r2 && Y(j, { type: H, content: e5 }), Promise.resolve(e5))), (e5) => s2 ? Promise.reject(e5) : M(q(t3, "fail"), e5).then(() => M(q(t3, "complete"), e5)).then(() => (Y(j, { type: H, content: e5 }), Promise.reject(e5)))); - if (!(o2 || a2 || u2)) - return l2; - l2.then((e5) => { - o2 && o2(e5), u2 && u2(e5), r2 && Y(j, { type: H, content: e5 }); - }, (e5) => { - a2 && a2(e5), u2 && u2(e5), r2 && Y(j, { type: H, content: e5 }); - }); - }; - }(t2[e3], e3)).bind(t2); - }), t2.init = this.init, t2; - } -}(); -(() => { - const e2 = T; - let t2 = {}; - if (e2 && 1 === e2.length) - t2 = e2[0], Gs = Gs.init(t2), Gs._isDefault = true; - else { - const t3 = ["auth", "callFunction", "uploadFile", "deleteFile", "getTempFileURL", "downloadFile", "database", "getCurrentUSerInfo", "importObject"]; - let n2; - n2 = e2 && e2.length > 0 ? "应用有多个服务空间,请通过uniCloud.init方法指定要使用的服务空间" : "应用未关联服务空间,请在uniCloud目录右键关联服务空间", t3.forEach((e3) => { - Gs[e3] = function() { - return console.error(n2), Promise.reject(new te({ code: "SYS_ERR", message: n2 })); - }; - }); - } - Object.assign(Gs, { get mixinDatacom() { - return Ms(Gs); - } }), xs(Gs), Gs.addInterceptor = N, Gs.removeInterceptor = D, Gs.interceptObject = F; -})(); -var Ys = Gs; -exports.Ys = Ys; -exports._export_sfc = _export_sfc; -exports.computed = computed; -exports.createSSRApp = createSSRApp; -exports.defineComponent = defineComponent; -exports.e = e$1; -exports.f = f$1; -exports.getCurrentInstance = getCurrentInstance; -exports.index = index; -exports.initVueI18n = initVueI18n; -exports.n = n$1; -exports.o = o$1; -exports.onLaunch = onLaunch; -exports.onLoad = onLoad; -exports.onMounted = onMounted; -exports.onShow = onShow; -exports.onUnmounted = onUnmounted; -exports.p = p$1; -exports.ref = ref; -exports.resolveComponent = resolveComponent; -exports.s = s$1; -exports.sr = sr; -exports.t = t$1; -exports.unref = unref; -exports.watch = watch; -exports.wx$1 = wx$1; diff --git a/unpackage/dist/dev/mp-weixin/components/navbar/navbar.js b/unpackage/dist/dev/mp-weixin/components/navbar/navbar.js deleted file mode 100644 index 4ccaa99..0000000 --- a/unpackage/dist/dev/mp-weixin/components/navbar/navbar.js +++ /dev/null @@ -1,34 +0,0 @@ -"use strict"; -const common_vendor = require("../../common/vendor.js"); -const common_assets = require("../../common/assets.js"); -const _sfc_main = { - __name: "navbar", - props: { - title: { - type: String, - default: "" - } - }, - setup(__props) { - function navBackTo() { - common_vendor.index.navigateBack(); - } - const props = __props; - const pageTitle = common_vendor.ref(props.title); - common_vendor.watch( - props.title, - (New, Old) => { - pageTitle.value = New; - }, - { immediate: true } - ); - return (_ctx, _cache) => { - return { - a: common_assets._imports_0, - b: common_vendor.o(($event) => navBackTo()), - c: common_vendor.t(__props.title) - }; - }; - } -}; -wx.createComponent(_sfc_main); diff --git a/unpackage/dist/dev/mp-weixin/components/navbar/navbar.json b/unpackage/dist/dev/mp-weixin/components/navbar/navbar.json deleted file mode 100644 index e8cfaaf..0000000 --- a/unpackage/dist/dev/mp-weixin/components/navbar/navbar.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "component": true, - "usingComponents": {} -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/components/navbar/navbar.wxml b/unpackage/dist/dev/mp-weixin/components/navbar/navbar.wxml deleted file mode 100644 index b49cde2..0000000 --- a/unpackage/dist/dev/mp-weixin/components/navbar/navbar.wxml +++ /dev/null @@ -1 +0,0 @@ -<view class="navbar"><image src="{{a}}" alt="" bindtap="{{b}}"></image><text>{{c}}</text></view> \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/components/navbar/navbar.wxss b/unpackage/dist/dev/mp-weixin/components/navbar/navbar.wxss deleted file mode 100644 index 2fc8dfb..0000000 --- a/unpackage/dist/dev/mp-weixin/components/navbar/navbar.wxss +++ /dev/null @@ -1,51 +0,0 @@ -/** - * 这里是uni-app内置的常用样式变量 - * - * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 - * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App - * - */ -/** - * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 - * - * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 - */ -/* 颜色变量 */ -/* 行为相关颜色 */ -/* 文字基本颜色 */ -/* 背景颜色 */ -/* 边框颜色 */ -/* 尺寸变量 */ -/* 文字尺寸 */ -/* 图片尺寸 */ -/* Border Radius */ -/* 水平间距 */ -/* 垂直间距 */ -/* 透明度 */ -/* 文章场景相关 */ -.navbar { - width: 100%; - height: 176rpx; - background: linear-gradient(to bottom, #5EA1FA, #8BC1FC); - display: flex; - align-items: flex-end; - box-sizing: border-box; - position: relative; - padding: 0 32rpx 24rpx; -} -.navbar image { - width: 40rpx; - height: 40rpx; - transform: rotate(180deg); -} -.navbar text { - font-weight: 400; - font-size: 36rpx; - color: #000000; - position: absolute; - width: 200rpx; - left: calc(50% - 100rpx); - text-align: center; - color: #e4ecf9; - letter-spacing: 1rpx; -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/config/baseUrl.js b/unpackage/dist/dev/mp-weixin/config/baseUrl.js deleted file mode 100644 index f5ac894..0000000 --- a/unpackage/dist/dev/mp-weixin/config/baseUrl.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -const BASE_URL = "https://www.huiwuyuntong.com/water-drinking-beta"; -exports.BASE_URL = BASE_URL; diff --git a/unpackage/dist/dev/mp-weixin/pages/addCard/index.js b/unpackage/dist/dev/mp-weixin/pages/addCard/index.js deleted file mode 100644 index afa7afe..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/addCard/index.js +++ /dev/null @@ -1,132 +0,0 @@ -"use strict"; -const common_vendor = require("../../common/vendor.js"); -const common_assets = require("../../common/assets.js"); -const api_index = require("../../api/index.js"); -if (!Array) { - const _easycom_navbar2 = common_vendor.resolveComponent("navbar"); - const _easycom_uni_forms_item2 = common_vendor.resolveComponent("uni-forms-item"); - const _easycom_uni_forms2 = common_vendor.resolveComponent("uni-forms"); - (_easycom_navbar2 + _easycom_uni_forms_item2 + _easycom_uni_forms2)(); -} -const _easycom_navbar = () => "../../components/navbar/navbar.js"; -const _easycom_uni_forms_item = () => "../../uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.js"; -const _easycom_uni_forms = () => "../../uni_modules/uni-forms/components/uni-forms/uni-forms.js"; -if (!Math) { - (_easycom_navbar + _easycom_uni_forms_item + _easycom_uni_forms)(); -} -const _sfc_main = { - __name: "index", - setup(__props) { - const form = common_vendor.ref({ - id: JSON.parse(common_vendor.index.getStorageSync("userInfo")).id, - waterCardNumber: "", - userName: "", - userPhone: "", - remark: "" - }); - function toScan() { - common_vendor.index.scanCode({ - scanType: ["qrCode"], - success: function(res) { - console.log("条码内容:" + res.result); - let data = res.result.split("&id="); - form.value.waterCardNumber = data[1]; - console.log("form.value.waterCardNumber", form.value.waterCardNumber); - } - }); - } - const formRef = common_vendor.ref(); - const rules = common_vendor.ref({ - waterCardNumber: { - rules: [ - { required: true, errorMessage: "请输入卡号" } - ] - }, - userName: { - rules: [ - { required: true, errorMessage: "请输入姓名" }, - { pattern: /^[\u4e00-\u9fa5\\.]+$/, errorMessage: '请输入中文或" . "符号' } - ] - }, - userPhone: { - rules: [ - { required: true, errorMessage: "请输入您的手机号" }, - { pattern: /^1[3-9]\d{9}$/, errorMessage: "手机号格式不正确" } - ] - } - }); - async function submit() { - formRef.value.validate().then(async () => { - await api_index.creatUserArchive(form.value).then((res) => { - if (res.code == 200) { - common_vendor.index.showToast({ - title: "添加成功!", - image: "../../static/images/other/success.svg", - duration: 2e3 - }).then(() => { - common_vendor.index.navigateBack(); - }); - } else if (res.code == 300) { - common_vendor.index.showToast({ - title: res.msg, - duration: 2e3, - icon: "none" - }); - } else { - common_vendor.index.showToast({ - title: "绑卡失败", - duration: 2e3, - icon: "none" - }); - } - }); - }); - } - common_vendor.onMounted(() => { - }); - return (_ctx, _cache) => { - return { - a: common_vendor.p({ - title: "绑定会员卡" - }), - b: form.value.waterCardNumber, - c: common_vendor.o(($event) => form.value.waterCardNumber = $event.detail.value), - d: common_vendor.o(($event) => toScan()), - e: common_assets._imports_0$5, - f: common_vendor.p({ - label: "卡号:", - name: "waterCardNumber" - }), - g: form.value.userName, - h: common_vendor.o(($event) => form.value.userName = $event.detail.value), - i: common_vendor.p({ - label: "姓名:", - name: "userName" - }), - j: form.value.userPhone, - k: common_vendor.o(($event) => form.value.userPhone = $event.detail.value), - l: common_vendor.p({ - label: "联系方式:", - name: "userPhone" - }), - m: form.value.remark, - n: common_vendor.o(($event) => form.value.remark = $event.detail.value), - o: common_vendor.p({ - label: "备注:", - name: "remark" - }), - p: common_vendor.sr(formRef, "4c5e7c86-1", { - "k": "formRef" - }), - q: common_vendor.p({ - model: form.value, - rules: rules.value, - ["label-width"]: "100" - }), - r: common_vendor.o(($event) => submit()) - }; - }; - } -}; -const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-4c5e7c86"]]); -wx.createPage(MiniProgramPage); diff --git a/unpackage/dist/dev/mp-weixin/pages/addCard/index.json b/unpackage/dist/dev/mp-weixin/pages/addCard/index.json deleted file mode 100644 index a7ecc1a..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/addCard/index.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "navigationStyle": "custom", - "usingComponents": { - "navbar": "../../components/navbar/navbar", - "uni-forms-item": "../../uni_modules/uni-forms/components/uni-forms-item/uni-forms-item", - "uni-forms": "../../uni_modules/uni-forms/components/uni-forms/uni-forms" - } -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/addCard/index.wxml b/unpackage/dist/dev/mp-weixin/pages/addCard/index.wxml deleted file mode 100644 index 830e7fc..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/addCard/index.wxml +++ /dev/null @@ -1 +0,0 @@ -<view class="container data-v-4c5e7c86"><view class="content data-v-4c5e7c86"><navbar wx:if="{{a}}" class="data-v-4c5e7c86" u-i="4c5e7c86-0" bind:__l="__l" u-p="{{a}}"></navbar><view class="main data-v-4c5e7c86"><view class="title data-v-4c5e7c86">请填写卡及个人信息</view><view class="form data-v-4c5e7c86"><uni-forms wx:if="{{q}}" class="r data-v-4c5e7c86" u-s="{{['d']}}" u-r="formRef" u-i="4c5e7c86-1" bind:__l="__l" u-p="{{q}}"><uni-forms-item wx:if="{{f}}" u-s="{{['d']}}" class="form-item data-v-4c5e7c86" u-i="4c5e7c86-2,4c5e7c86-1" bind:__l="__l" u-p="{{f}}"><view class="card-item data-v-4c5e7c86"><input class="scan-input data-v-4c5e7c86" placeholder="请输入卡号或扫描二维码" value="{{b}}" bindinput="{{c}}"/><image class="scan-img data-v-4c5e7c86" bindtap="{{d}}" src="{{e}}" alt=""></image></view></uni-forms-item><uni-forms-item wx:if="{{i}}" class="data-v-4c5e7c86" u-s="{{['d']}}" u-i="4c5e7c86-3,4c5e7c86-1" bind:__l="__l" u-p="{{i}}"><input class="input-item data-v-4c5e7c86" placeholder="请输入姓名" value="{{g}}" bindinput="{{h}}"/></uni-forms-item><uni-forms-item wx:if="{{l}}" class="data-v-4c5e7c86" u-s="{{['d']}}" u-i="4c5e7c86-4,4c5e7c86-1" bind:__l="__l" u-p="{{l}}"><input class="input-item data-v-4c5e7c86" type="textarea" placeholder="请输入手机号" value="{{j}}" bindinput="{{k}}"/></uni-forms-item><uni-forms-item wx:if="{{o}}" class="data-v-4c5e7c86" u-s="{{['d']}}" u-i="4c5e7c86-5,4c5e7c86-1" bind:__l="__l" u-p="{{o}}"><input class="input-item data-v-4c5e7c86" type="textarea" placeholder="请输入内容" value="{{m}}" bindinput="{{n}}"/></uni-forms-item></uni-forms></view><view class="tips data-v-4c5e7c86"><text class="tips-text data-v-4c5e7c86">1、完成实名认证,方便遗失挂失及补卡等。</text><text class="data-v-4c5e7c86">2、绑卡后,运营商可以看到您实名时填写的姓名和电话。</text></view><view class="subBtn data-v-4c5e7c86" bindtap="{{r}}">提交信息</view></view></view></view> \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/addCard/index.wxss b/unpackage/dist/dev/mp-weixin/pages/addCard/index.wxss deleted file mode 100644 index 31f0be0..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/addCard/index.wxss +++ /dev/null @@ -1,103 +0,0 @@ -/** - * 这里是uni-app内置的常用样式变量 - * - * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 - * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App - * - */ -/** - * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 - * - * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 - */ -/* 颜色变量 */ -/* 行为相关颜色 */ -/* 文字基本颜色 */ -/* 背景颜色 */ -/* 边框颜色 */ -/* 尺寸变量 */ -/* 文字尺寸 */ -/* 图片尺寸 */ -/* Border Radius */ -/* 水平间距 */ -/* 垂直间距 */ -/* 透明度 */ -/* 文章场景相关 */ -.container.data-v-4c5e7c86 { - width: 100%; - height: 100vh; -} -.container .content.data-v-4c5e7c86 { - width: 100%; - height: 100vh; -} -.container .content .main.data-v-4c5e7c86 { - width: 100%; - height: calc(100vh - 176rpx); - background: linear-gradient(to top, #FFFFFF, #E8EFFF); - padding: 0 50rpx 38rpx; - box-sizing: border-box; -} -.container .content .main .title.data-v-4c5e7c86 { - width: 100%; - height: 130rpx; - text-align: left; - line-height: 130rpx; -} -.container .content .main .form.data-v-4c5e7c86 { - height: 600rpx; - width: 100%; - background: #FFFFFF; - border-radius: 24rpx; - padding: 30rpx 50rpx 0; - box-sizing: border-box; -} -.container .content .main .form .input-item.data-v-4c5e7c86 { - line-height: 100%; - height: 100%; -} -.container .content .main .form .card-item.data-v-4c5e7c86 { - display: flex; - justify-content: space-around; - height: 100%; - align-items: center; -} -.container .content .main .form .card-item .scan-input.data-v-4c5e7c86 { - width: 375rpx; - margin-right: 20rpx; - line-height: 100%; - height: 100%; -} -.container .content .main .form .card-item .scan-img.data-v-4c5e7c86 { - width: 56rpx; - height: 56rpx; -} -.container .content .main .tips.data-v-4c5e7c86 { - width: 100%; - padding: 0 20rpx; - box-sizing: border-box; - display: flex; - justify-content: space-between; - align-items: flex-start; - flex-direction: column; - margin-top: 56rpx; - font-weight: 300; - font-size: 28rpx; - color: #484848; -} -.container .content .main .tips .tips-text.data-v-4c5e7c86 { - margin-bottom: 28rpx; -} -.container .content .main .subBtn.data-v-4c5e7c86 { - width: 100%; - height: 98rpx; - background-color: #5EA1FA; - border-radius: 50rpx; - font-weight: 300; - font-size: 36rpx; - color: #FFFFFF; - text-align: center; - line-height: 98rpx; - margin-top: 236rpx; - letter-spacing: 2px; -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/address/index.js b/unpackage/dist/dev/mp-weixin/pages/address/index.js deleted file mode 100644 index bcf1344..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/address/index.js +++ /dev/null @@ -1,98 +0,0 @@ -"use strict"; -const common_vendor = require("../../common/vendor.js"); -const common_assets = require("../../common/assets.js"); -const api_index = require("../../api/index.js"); -if (!Array) { - const _easycom_navbar2 = common_vendor.resolveComponent("navbar"); - _easycom_navbar2(); -} -const _easycom_navbar = () => "../../components/navbar/navbar.js"; -if (!Math) { - _easycom_navbar(); -} -const _sfc_main = { - __name: "index", - setup(__props) { - const addressList = common_vendor.ref([]); - async function getAddressList() { - await api_index.searchAddress({ limit: 100, page: 1 }).then((res) => { - if (res.code == 200) { - res.data.list.forEach((item) => { - res.data.list.forEach((item2) => { - item2.isDefault = item2.isDefault == 1 ? true : false; - item2.addressWhole = item2.regionName.replace(/[,#]/g, " ") + item2.address; - }); - }); - addressList.value = res.data.list; - } - }); - } - async function setDefault(isDefault, id, index1) { - if (isDefault == true) { - console.log("true"); - } else { - console.log("false"); - await api_index.setAddressDefaultApi(id).then((res) => { - if (res.code == 200) { - getAddressList(); - } else { - common_vendor.index.showToast({ - title: "设置失败", - image: "../../static/images/other/success.svg", - duration: 500 - }); - } - }); - } - } - async function deleteAddress(id) { - await api_index.deleteAddressApi(id).then((res) => { - if (res.code == 200) { - common_vendor.index.showToast({ - title: "删除成功", - image: "../../static/images/other/success.svg", - duration: 500 - }); - getAddressList(); - } - }); - } - function navToModify(id) { - common_vendor.index.navigateTo({ - url: `/pages/addressAdd/index?id=${id}` - }); - } - function navToAdd() { - let id = ""; - common_vendor.index.navigateTo({ - url: `/pages/addressAdd/index?id=${id}` - }); - } - common_vendor.onShow(async () => { - getAddressList(); - }); - return (_ctx, _cache) => { - return { - a: common_vendor.p({ - title: "地址管理" - }), - b: common_vendor.f(addressList.value, (item, index, i0) => { - return { - a: common_vendor.t(item.userName), - b: common_vendor.t(item.userPhone), - c: common_vendor.t(item.addressWhole), - d: item.id, - e: item.isDefault, - f: common_vendor.o(($event) => setDefault(item.isDefault, item.id)), - g: common_vendor.o(($event) => navToModify(item.id)), - h: common_vendor.o(($event) => deleteAddress(item.id)) - }; - }), - c: common_assets._imports_0$9, - d: common_assets._imports_1$4, - e: common_vendor.o(($event) => navToAdd()) - }; - }; - } -}; -wx.createPage(_sfc_main); diff --git a/unpackage/dist/dev/mp-weixin/pages/address/index.json b/unpackage/dist/dev/mp-weixin/pages/address/index.json deleted file mode 100644 index ff63ccc..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/address/index.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "navigationStyle": "custom", - "usingComponents": { - "navbar": "../../components/navbar/navbar" - } -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/address/index.wxml b/unpackage/dist/dev/mp-weixin/pages/address/index.wxml deleted file mode 100644 index 5155251..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/address/index.wxml +++ /dev/null @@ -1 +0,0 @@ -<view class="container"><navbar wx:if="{{a}}" u-i="696da406-0" bind:__l="__l" u-p="{{a}}"></navbar><view class="content"><view class="main"><block wx:for="{{b}}" wx:for-item="item"><view class="address-item"><view class="user-info"><text>{{item.a}}</text><text>{{item.b}}</text></view><view class="address-info">{{item.c}}</view><view class="address-divide"></view><view class="handel"><view class="handel-left"><radio color="#4996E3" value="{{item.d}}" checked="{{item.e}}" bindtap="{{item.f}}"></radio><text>设为默认</text></view><view class="handel-right"><view bindtap="{{item.g}}"><image src="{{c}}" alt=""></image><text>编辑</text></view><view bindtap="{{item.h}}"><image src="{{d}}" alt=""></image><text>删除</text></view></view></view></view></block></view><view class="subBtn" bindtap="{{e}}">新增收货地址</view></view></view> \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/address/index.wxss b/unpackage/dist/dev/mp-weixin/pages/address/index.wxss deleted file mode 100644 index 28d3283..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/address/index.wxss +++ /dev/null @@ -1,128 +0,0 @@ -/** - * 这里是uni-app内置的常用样式变量 - * - * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 - * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App - * - */ -/** - * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 - * - * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 - */ -/* 颜色变量 */ -/* 行为相关颜色 */ -/* 文字基本颜色 */ -/* 背景颜色 */ -/* 边框颜色 */ -/* 尺寸变量 */ -/* 文字尺寸 */ -/* 图片尺寸 */ -/* Border Radius */ -/* 水平间距 */ -/* 垂直间距 */ -/* 透明度 */ -/* 文章场景相关 */ -.container { - width: 100%; - height: 100vh; -} -.container .content { - width: 100%; - height: calc(100vh - 176rpx - 20rpx); - background: linear-gradient(to top, #FFFFFF, #E8EFFF); - padding-top: 20rpx; -} -.container .content .main { - width: 686rpx; - height: 1162rpx; - margin: 0 auto; - overflow-y: scroll; -} -.container .content .main .address-item { - width: 100%; - height: 254rpx; - padding: 20rpx 0 26rpx; - box-sizing: border-box; - margin-bottom: 20rpx; - background: #FFFFFF; - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: flex-start; -} -.container .content .main .address-item .user-info { - width: 100%; - padding: 0 65rpx; - box-sizing: border-box; - display: flex; - justify-content: space-between; - font-weight: 300; - font-size: 32rpx; - color: #000000; -} -.container .content .main .address-item .address-info { - padding: 0 65rpx; - box-sizing: border-box; - font-weight: 300; - font-size: 28rpx; - color: #646464; -} -.container .content .main .address-item .address-divide { - width: 100%; - height: 1rpx; - border-bottom: 2rpx dashed #D5DDE0; -} -.container .content .main .address-item .handel { - width: 100%; - padding: 0 65rpx; - box-sizing: border-box; - display: flex; - justify-content: space-between; - align-items: flex-start; -} -.container .content .main .address-item .handel .handel-left radio { - color: #000000; -} -.container .content .main .address-item .handel .handel-left text { - font-weight: 300; - font-size: 32rpx; - color: #0088FF; - line-height: 28rpx; -} -.container .content .main .address-item .handel .handel-right { - width: 35%; - height: 48rpx; - display: flex; - justify-content: space-between; - align-items: center; -} -.container .content .main .address-item .handel .handel-right view { - display: flex; - align-items: center; -} -.container .content .main .address-item .handel .handel-right view image { - width: 28rpx; - height: 28rpx; - margin-right: 10rpx; -} -.container .content .main .address-item .handel .handel-right view text { - font-weight: 300; - font-size: 24rpx; - color: #373737; -} -.container .content .subBtn { - width: 686rpx; - height: 98rpx; - padding: 0 26rpx; - box-sizing: border-box; - background-color: #5EA1FA; - border-radius: 50rpx; - font-weight: 300; - font-size: 36rpx; - color: #FFFFFF; - line-height: 98rpx; - text-align: center; - letter-spacing: 2rpx; - margin: 100rpx auto 0; -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/index/index.js b/unpackage/dist/dev/mp-weixin/pages/index/index.js deleted file mode 100644 index 2376f8f..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/index/index.js +++ /dev/null @@ -1,344 +0,0 @@ -"use strict"; -const common_vendor = require("../../common/vendor.js"); -const common_assets = require("../../common/assets.js"); -const config_baseUrl = require("../../config/baseUrl.js"); -const api_index = require("../../api/index.js"); -const _sfc_main = { - __name: "index", - setup(__props) { - const topHeight = common_vendor.ref(0); - function getTopHeight() { - if (common_vendor.index.getMenuButtonBoundingClientRect) { - topHeight.value = common_vendor.index.getMenuButtonBoundingClientRect().top * 2; - } - } - const cardInfo = common_vendor.ref({ - //用户档案信息 - id: "", - userName: "微信用户", - headImg: "", - userPhone: "", - integral: "", - waterCardNumber: "", - balance: "", - count: "", - state: "", - //state:true:挂失,false:正常 - cardType: "", - //卡类型01卡消费 02计次消费 03游客消费 04电子卡 - userCardType: "" - //userCardType:true:共享卡,false:正常卡 - }); - const isLoss = common_vendor.ref(false); - const isVip = common_vendor.ref(false); - const isShareCard = common_vendor.ref(false); - async function getVipInfo() { - await api_index.getVipInfoApi().then((res) => { - console.log("会员卡信息响应", res); - if (res.code == 200 && res.data.id) { - cardInfo.value.waterCardNumber = res.data.cardNumber; - cardInfo.value.cardType = res.data.cardType; - cardInfo.value.state = res.data.state == 2 ? true : false; - cardInfo.value.userCardType = res.data.userCardType == 2 ? true : false; - isLoss.value = cardInfo.value.state; - isShareCard.value = cardInfo.value.userCardType; - if (res.data.cardType == 1) { - cardInfo.value.balance = res.data.balance; - } else if (res.data.cardType == 2) { - cardInfo.value.count = res.data.count; - } - isVip.value = true; - console.log("会员卡及用户信息", cardInfo.value); - } - }); - } - async function getInfo() { - await api_index.getUserInfo().then(async (res) => { - console.log("用户信息响应", res); - if (res.code == 200) { - cardInfo.value.userName = res.data.userInfo.userName ? res.data.userInfo.userName : cardInfo.value.userName; - cardInfo.value.headImg = res.data.userInfo.headImg; - cardInfo.value.integral = res.data.userInfo.integral; - cardInfo.value.userPhone = res.data.userInfo.userPhone; - cardInfo.value.id = res.data.userInfo.id; - await getVipInfo(); - console.log("用户信息", cardInfo.value); - common_vendor.index.setStorageSync("userInfo", JSON.stringify(cardInfo.value)); - } - }); - } - function toScan() { - common_vendor.index.scanCode({ - scanType: ["qrCode"], - success: async function(res) { - let code = res.result.split("=")[1]; - console.log("条码内容:" + res.result, "code", code); - common_vendor.index.navigateTo({ - url: `/pages/scanWater/index?facilityCode=${code}` - }); - } - }); - } - const functionList = common_vendor.ref([ - //isUrl:1:页面跳转,2:弹窗功能,3:弹窗功能,4:未开发 - { text: "余额记录", icon: "../../static/images/index/icon31.png", url: "/pages/functionList/balanceRecord/index", isUrl: 1 }, - { text: "消费记录", icon: "../../static/images/index/icon32.png", url: "/pages/functionList/paymentRecord/index", isUrl: 1 }, - { text: "充值记录", icon: "../../static/images/index/icon33.png", url: "/pages/functionList/rechargeRecord/index", isUrl: 1 }, - { text: "水卡共享", icon: "../../static/images/index/icon34.png", url: "/pages/functionList/share/cardShare/index", isUrl: 1 }, - { text: "故障上报", icon: "../../static/images/index/icon35.png", url: "/pages/infoBreakdown/index", isUrl: 1 }, - { text: "优惠卷", icon: "../../static/images/index/icon36.png", url: "", isUrl: 4 }, - { text: "卡挂失", icon: "../../static/images/index/icon37.png", url: "", isUrl: 2 }, - { text: "卡解绑", icon: "../../static/images/index/icon38.png", url: "", isUrl: 3 } - ]); - function navTo(itemUrl) { - if (itemUrl) { - common_vendor.index.navigateTo({ - url: itemUrl - }); - } - } - async function setEvent(isUrl, url) { - if (isUrl == 1) { - navTo(url); - } else if (isUrl == 2) { - if (cardInfo.value.waterCardNumber) { - if (!isShareCard.value && !cardInfo.value.state) { - common_vendor.index.showModal({ - title: "会员卡挂失", - content: "确认挂失会员卡?", - success: async function(res) { - if (res.confirm) { - await api_index.reportLossApi(cardInfo.value.waterCardNumber).then(async (res2) => { - if (res2.code == 200) { - common_vendor.index.showToast({ - title: "已挂失", - duration: 2e3 - }); - await api_index.getVipInfoApi().then((res3) => { - if (res3.code == 200) { - if (res3.data.state == 2) { - isLoss.value = true; - } - } - }); - } - }); - } - } - }); - } else if (isShareCard.value) { - common_vendor.index.showToast({ - title: "共享卡无权限", - icon: "none" - }); - } else if (cardInfo.value.state) { - common_vendor.index.showToast({ - title: "已挂失", - icon: "none" - }); - } - } else { - common_vendor.index.showToast({ - title: "未绑定会员卡!", - icon: "none" - }); - } - } else if (isUrl == 3) { - if (cardInfo.value.waterCardNumber) { - if (!isShareCard.value) { - common_vendor.index.showModal({ - title: "会员卡注销", - content: "确认注销会员卡?", - success: async function(res) { - if (res.confirm) { - await api_index.removeBindingApi(cardInfo.value.waterCardNumber).then(async (res2) => { - if (res2.code == 200) { - common_vendor.index.showToast({ - title: "已注销", - duration: 2e3, - icon: "none" - }); - await api_index.getVipInfoApi().then((res3) => { - if (res3.code == 200) { - if (!res3.data.id) { - isVip.value = false; - cardInfo.value.balance = ""; - cardInfo.value.count = ""; - cardInfo.value.waterCardNumber = ""; - common_vendor.index.setStorageSync("userInfo", JSON.stringify(cardInfo.value)); - } - } - }); - } - }); - } - } - }); - } else { - common_vendor.index.showToast({ - title: "共享卡无权限", - icon: "none" - }); - } - } else { - common_vendor.index.showToast({ - title: "未绑定会员卡!", - icon: "none" - }); - } - } else { - upGrade(); - } - } - function upGrade() { - common_vendor.index.showToast({ - title: "该功能正在升级中!", - duration: 1e3, - icon: "none" - }); - } - let userLocation = { - lat: "", - lon: "" - }; - function getUserLocation() { - common_vendor.index.authorize({ - scope: "scope.userLocation", - success() { - console.log("成功授权位置信息1"); - storageLocation(); - }, - fail() { - console.log("用户拒绝授权位置信息,再次提示用户授权"); - showToast(); - } - }); - function storageLocation() { - common_vendor.index.getLocation({ - type: "gcj02", - isHighAccuracy: true, - success: (res) => { - userLocation.lat = res.latitude; - userLocation.lon = res.longitude; - common_vendor.index.setStorageSync("userLocation", JSON.stringify(userLocation)); - }, - fail: () => { - showToast(); - } - }); - } - function showToast() { - common_vendor.index.showModal({ - title: "请求授权当前位置", - content: "请求获取您的位置,加载附近饮水设备信息!", - confirmText: "前往设置", - success: (res) => { - if (res.confirm) { - common_vendor.index.openSetting({ - success: (res1) => { - if (res1.authSetting["scope.userLocation"]) { - console.log("用户二次授权成功"); - setTimeout(() => { - storageLocation(); - }, 1e3); - } else { - showToast(); - console.log("用户拒绝授权"); - } - } - }); - } else { - common_vendor.index.showToast({ - title: "请先授权!", - duration: 2e3, - icon: "none" - }); - showToast(); - } - } - }); - } - } - common_vendor.onMounted(async () => { - console.log("空用户信息", cardInfo.value); - common_vendor.index.setStorageSync("userInfo", JSON.stringify(cardInfo.value)); - getTopHeight(); - await getInfo(); - await getUserLocation(); - console.log("onMounted", "用户存储本地数据", JSON.parse(common_vendor.index.getStorageSync("userInfo"))); - }); - common_vendor.onShow(async () => { - getTopHeight(); - await getInfo(); - console.log("onShow", "用户存储本地数据", JSON.parse(common_vendor.index.getStorageSync("userInfo"))); - }); - common_vendor.index.showShareMenu({ - withShareTicket: true, - menus: ["shareAppMessage", "shareTimeline"], - success: function() { - }, - fail: function(err) { - } - }); - return (_ctx, _cache) => { - return common_vendor.e({ - a: cardInfo.value.headImg - }, cardInfo.value.headImg ? { - b: common_vendor.unref(config_baseUrl.BASE_URL) + "/upload" + cardInfo.value.headImg - } : { - c: common_assets._imports_0$4 - }, { - d: common_vendor.t(cardInfo.value.userName), - e: common_vendor.o(($event) => navTo("/pages/userInfo/index")), - f: common_vendor.o(($event) => navTo("/pages/userInfo/index")), - g: common_assets._imports_1$2, - h: common_assets._imports_0$3, - i: isVip.value - }, isVip.value ? common_vendor.e({ - j: isShareCard.value - }, isShareCard.value ? common_vendor.e({ - k: isLoss.value - }, isLoss.value ? {} : {}) : {}, { - l: !isShareCard.value - }, !isShareCard.value ? common_vendor.e({ - m: isLoss.value - }, isLoss.value ? {} : {}) : {}, { - n: common_vendor.t(cardInfo.value.waterCardNumber), - o: common_vendor.t(cardInfo.value.balance), - p: !isShareCard.value - }, !isShareCard.value ? { - q: common_vendor.o(($event) => navTo("/pages/recharge/index")) - } : {}) : {}, { - r: !isVip.value - }, !isVip.value ? { - s: common_vendor.o(($event) => navTo("/pages/addCard/index")) - } : {}, { - t: topHeight.value + "rpx", - v: common_assets._imports_3$1, - w: common_vendor.o(upGrade), - x: common_assets._imports_1$1, - y: common_vendor.o(($event) => navTo("/pages/facilityList/index")), - z: common_assets._imports_5, - A: common_vendor.o(($event) => toScan()), - B: common_vendor.f(functionList.value, (item, index, i0) => { - return { - a: item.icon, - b: common_vendor.t(item.text), - c: common_vendor.o(($event) => setEvent(item.isUrl, item.url), index), - d: index - }; - }), - C: common_assets._imports_6, - D: common_vendor.o(($event) => navTo("/pagesPoints/pointsMall/index")), - E: common_assets._imports_7, - F: common_assets._imports_8, - G: common_vendor.o(($event) => navTo("/pages/station/index")), - H: common_assets._imports_9, - I: common_assets._imports_8, - J: common_vendor.o(upGrade) - }); - }; - } -}; -const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-1cf27b2a"]]); -wx.createPage(MiniProgramPage); diff --git a/unpackage/dist/dev/mp-weixin/pages/index/index.json b/unpackage/dist/dev/mp-weixin/pages/index/index.json deleted file mode 100644 index ced5b27..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/index/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "navigationStyle": "custom", - "usingComponents": {} -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/index/index.wxml b/unpackage/dist/dev/mp-weixin/pages/index/index.wxml deleted file mode 100644 index 4282251..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/index/index.wxml +++ /dev/null @@ -1 +0,0 @@ -<view class="container data-v-1cf27b2a"><view class="content data-v-1cf27b2a"><view class="card-box data-v-1cf27b2a" style="{{'padding-top:' + t}}"><view class="user-info data-v-1cf27b2a"><image wx:if="{{a}}" class="user-img data-v-1cf27b2a" src="{{b}}" alt=""></image><image wx:else class="user-img data-v-1cf27b2a" src="{{c}}" alt=""></image><text class="data-v-1cf27b2a" bindtap="{{e}}">{{d}}</text><image bindtap="{{f}}" class="user-more data-v-1cf27b2a" src="{{g}}"></image></view><view class="card data-v-1cf27b2a"><image class="card-bg data-v-1cf27b2a" src="{{h}}" alt=""></image><view wx:if="{{i}}" class="card-detail data-v-1cf27b2a"><view class="card-top data-v-1cf27b2a"><view class="vip data-v-1cf27b2a">VIP</view><view class="top-right data-v-1cf27b2a"><view wx:if="{{j}}" class="card-name data-v-1cf27b2a"><text class="data-v-1cf27b2a">共享卡</text><text wx:if="{{k}}" class="data-v-1cf27b2a">[已挂失]</text></view><view wx:if="{{l}}" class="card-name data-v-1cf27b2a"><text class="data-v-1cf27b2a">会员卡</text><text wx:if="{{m}}" class="card-loss data-v-1cf27b2a">[已挂失]</text></view><view class="card-num data-v-1cf27b2a"> 卡号:{{n}}</view></view></view><view class="card-bottom data-v-1cf27b2a"><view class="bottom-left data-v-1cf27b2a"><text class="bottom-left-text data-v-1cf27b2a">余额:</text><text class="bottom-left-money data-v-1cf27b2a">{{o}}</text></view><view wx:if="{{p}}" bindtap="{{q}}" class="card-btn data-v-1cf27b2a">充值</view></view></view><view wx:if="{{r}}" class="card-none data-v-1cf27b2a"><view class="card-none-top data-v-1cf27b2a"><text class="data-v-1cf27b2a">未绑定会员卡</text></view><view class="card-none-bottom data-v-1cf27b2a"><view bindtap="{{s}}" class="card-btn data-v-1cf27b2a">去绑定</view></view></view></view></view><view class="function-box1 data-v-1cf27b2a"><view class="box1-bg box1-one data-v-1cf27b2a" bindtap="{{w}}"><image class="box-one-image1 data-v-1cf27b2a" src="{{v}}" alt=""></image><view class="data-v-1cf27b2a">送水到家</view></view><view bindtap="{{y}}" class="box1-bg box1-two data-v-1cf27b2a"><image class="box-one-image2 data-v-1cf27b2a" src="{{x}}" alt=""></image><view class="data-v-1cf27b2a">附近设备</view></view></view><view class="scan-box data-v-1cf27b2a"><view class="scan data-v-1cf27b2a" bindtap="{{A}}"><image class="scan-img data-v-1cf27b2a" src="{{z}}"></image><view class="data-v-1cf27b2a">扫码取水</view></view></view><view class="function-box2 data-v-1cf27b2a"><view class="box2-title data-v-1cf27b2a"><text class="data-v-1cf27b2a">功能列表</text></view><view class="box2-content data-v-1cf27b2a"><block wx:for="{{B}}" wx:for-item="item" wx:key="d"><view class="box2-item data-v-1cf27b2a" bindtap="{{item.c}}"><view class="box2-item-img data-v-1cf27b2a"><image class="box2-image data-v-1cf27b2a" src="{{item.a}}" alt=""></image></view><view class="box2-item-text data-v-1cf27b2a">{{item.b}}</view></view></block></view></view><view class="advertisement-box data-v-1cf27b2a" bindtap="{{D}}"><image class="banner-img data-v-1cf27b2a" src="{{C}}"></image></view><view class="service-box data-v-1cf27b2a"><view class="service-title data-v-1cf27b2a"><text class="data-v-1cf27b2a">服务指南</text></view><view class="service-content data-v-1cf27b2a"><view class="service-bg data-v-1cf27b2a" bindtap="{{G}}"><view class="data-v-1cf27b2a"><image class="data-v-1cf27b2a" src="{{E}}" alt=""></image><text class="data-v-1cf27b2a">附近站点</text></view><view class="service-info data-v-1cf27b2a">服务中心正在营业中……</view><view class="data-v-1cf27b2a"><text class="data-v-1cf27b2a">查看更多</text><image class="data-v-1cf27b2a" src="{{F}}" alt=""></image></view></view><view class="service-bg data-v-1cf27b2a" bindtap="{{J}}"><view class="data-v-1cf27b2a"><image class="data-v-1cf27b2a" src="{{H}}" alt=""></image><text class="data-v-1cf27b2a">公示公告</text></view><view class="service-info data-v-1cf27b2a">停水公告的部分摘要的内容展示……</view><view class="data-v-1cf27b2a"><text class="data-v-1cf27b2a">查看更多</text><image class="data-v-1cf27b2a" src="{{I}}" alt=""></image></view></view></view></view></view></view> \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/index/index.wxss b/unpackage/dist/dev/mp-weixin/pages/index/index.wxss deleted file mode 100644 index f993546..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/index/index.wxss +++ /dev/null @@ -1,467 +0,0 @@ -/** - * 这里是uni-app内置的常用样式变量 - * - * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 - * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App - * - */ -/** - * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 - * - * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 - */ -/* 颜色变量 */ -/* 行为相关颜色 */ -/* 文字基本颜色 */ -/* 背景颜色 */ -/* 边框颜色 */ -/* 尺寸变量 */ -/* 文字尺寸 */ -/* 图片尺寸 */ -/* Border Radius */ -/* 水平间距 */ -/* 垂直间距 */ -/* 透明度 */ -/* 文章场景相关 */ -.container.data-v-1cf27b2a { - width: 100%; -} -.container .content.data-v-1cf27b2a { - width: 100%; - background: #F6F6F6; - overflow: scroll; -} -.container .content .card-box.data-v-1cf27b2a { - width: 100%; - height: auto; - box-sizing: border-box; - padding: 0 32rpx; - background: linear-gradient(to bottom, #5EA1FA, #D2F2FE); -} -.container .content .card-box .user-info.data-v-1cf27b2a { - width: 100%; - height: 100rpx; - align-items: center; - padding-left: 15rpx; - box-sizing: border-box; - margin-bottom: 14rpx; - display: flex; -} -.container .content .card-box .user-info .user-img.data-v-1cf27b2a { - width: 90rpx; - height: 90rpx; - border-radius: 50%; - margin-right: 20rpx; -} -.container .content .card-box .user-info text.data-v-1cf27b2a { - margin-right: 20rpx; - color: #fff; -} -.container .content .card-box .user-info .user-more.data-v-1cf27b2a { - width: 12rpx; - height: 28rpx; -} -.container .content .card-box .card.data-v-1cf27b2a { - width: 100%; - height: 256rpx; - position: relative; -} -.container .content .card-box .card .card-bg.data-v-1cf27b2a { - height: 256rpx; - width: 100%; - top: 0; - position: absolute; - z-index: 1; -} -.container .content .card-box .card .card-none.data-v-1cf27b2a { - width: 100%; - height: 100%; - padding: 38rpx; - box-sizing: border-box; - display: flex; - flex-direction: column; - justify-content: space-around; - color: #fff; - z-index: 2; -} -.container .content .card-box .card .card-none .card-none-top.data-v-1cf27b2a { - font-size: 40rpx; - font-weight: 600; - z-index: 2; - letter-spacing: 2rpx; -} -.container .content .card-box .card .card-none .card-none-bottom.data-v-1cf27b2a { - display: flex; - justify-content: flex-end; - z-index: 2; -} -.container .content .card-box .card .card-none .card-none-bottom .card-btn.data-v-1cf27b2a { - width: 196rpx; - height: 80rpx; - background: linear-gradient(to right, #338AFD 0%, #65A7FD 100%); - border-radius: 40rpx; - text-align: center; - line-height: 80rpx; - font-size: 44rpx; - letter-spacing: 3rpx; - font-weight: 500; -} -.container .content .card-box .card .card-detail.data-v-1cf27b2a { - width: 100%; - height: 100%; - padding: 38rpx; - box-sizing: border-box; - display: flex; - flex-direction: column; - justify-content: center; - color: #fff; - z-index: 2; -} -.container .content .card-box .card .card-detail .card-top.data-v-1cf27b2a { - width: 100%; - display: flex; - align-items: center; - z-index: 2; -} -.container .content .card-box .card .card-detail .card-top .vip.data-v-1cf27b2a { - font-size: 114rpx; - font-weight: 600; -} -.container .content .card-box .card .card-detail .card-top .top-right.data-v-1cf27b2a { - display: flex; - flex-direction: column; - justify-content: center; - margin-left: 20rpx; -} -.container .content .card-box .card .card-detail .card-top .top-right .card-name.data-v-1cf27b2a { - font-size: 40rpx; - font-weight: 400; -} -.container .content .card-box .card .card-detail .card-top .top-right .card-name .card-loss.data-v-1cf27b2a { - font-size: 34rpx; - font-weight: 600; - margin-left: 10rpx; - letter-spacing: 2rpx; - color: #333434; -} -.container .content .card-box .card .card-detail .card-top .top-right .card-num.data-v-1cf27b2a { - font-size: 34rpx; - font-weight: 400; -} -.container .content .card-box .card .card-detail .card-bottom.data-v-1cf27b2a { - width: 100%; - display: flex; - align-items: center; - justify-content: space-between; - z-index: 2; -} -.container .content .card-box .card .card-detail .card-bottom .bottom-left.data-v-1cf27b2a { - display: flex; - align-items: center; -} -.container .content .card-box .card .card-detail .card-bottom .bottom-left .bottom-left-text.data-v-1cf27b2a { - font-size: 40rpx; - font-weight: 400; - letter-spacing: 2rpx; -} -.container .content .card-box .card .card-detail .card-bottom .bottom-left .bottom-left-money.data-v-1cf27b2a { - font-size: 60rpx; - font-weight: 600; -} -.container .content .card-box .card .card-detail .card-bottom .card-btn.data-v-1cf27b2a { - width: 196rpx; - height: 80rpx; - background: linear-gradient(270deg, #65B5FD 0%, #338AFD 100%, #65A7FD 100%); - border-radius: 40rpx; - text-align: center; - line-height: 80rpx; - font-size: 48rpx; - letter-spacing: 6rpx; -} -.container .content .function-box1.data-v-1cf27b2a { - width: 100%; - box-sizing: border-box; - height: 172rpx; - padding: 20rpx 32rpx 22rpx; - display: flex; - justify-content: space-between; - align-items: center; - font-size: 36rpx; - font-weight: 500; - color: #4d4d4d; - letter-spacing: 2rpx; -} -.container .content .function-box1 .box1-bg.data-v-1cf27b2a { - width: 326rpx; - height: 130rpx; - border-radius: 24rpx; - background: #fff; - box-shadow: 0 0 12rpx 3rpx rgba(13, 118, 255, 0.16); -} -.container .content .function-box1 .box1-one.data-v-1cf27b2a { - text-align: center; - display: flex; - align-items: center; - justify-content: center; - justify-content: space-evenly; -} -.container .content .function-box1 .box1-one .box-one-image1.data-v-1cf27b2a { - width: 86rpx; - height: 86rpx; -} -.container .content .function-box1 .box1-two.data-v-1cf27b2a { - display: flex; - align-items: center; - justify-content: space-evenly; -} -.container .content .function-box1 .box1-two .box-one-image2.data-v-1cf27b2a { - width: 96rpx; - height: 96rpx; -} -.container .content .function-box1 .box1-three.data-v-1cf27b2a { - font-weight: 300; - font-size: 32rpx; - color: #000000; - text-align: center; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} -.container .content .function-box1 .box1-three image.data-v-1cf27b2a { - width: 83rpx; - height: 83rpx; - margin-bottom: 20rpx; -} -.container .content .scan-box.data-v-1cf27b2a { - width: 100%; - height: 116rpx; - padding: 0 42rpx 16rpx; - box-sizing: border-box; -} -.container .content .scan-box .scan.data-v-1cf27b2a { - width: 100%; - height: 100rpx; - background: #1678FF; - border-radius: 178rpx; - display: flex; - justify-content: center; - align-items: center; - color: #fff; - font-size: 44rpx; - font-weight: 500; - letter-spacing: 1rpx; -} -.container .content .scan-box .scan .scan-img.data-v-1cf27b2a { - width: 44rpx; - height: 44rpx; - margin-right: 20rpx; -} -.container .content .function-box2.data-v-1cf27b2a { - width: 100%; - box-sizing: border-box; - height: 420rpx; - padding: 0 32rpx 0; -} -.container .content .function-box2 .box2-title.data-v-1cf27b2a { - width: 100%; - font-weight: 300; - font-size: 32rpx; - color: #000000; - text-align: left; - margin-bottom: 10rpx; - margin-left: 30rpx; -} -.container .content .function-box2 .box2-content.data-v-1cf27b2a { - box-sizing: border-box; - padding: 22rpx 35rpx; - width: 686rpx; - height: 360rpx; - background: #fff; - border-radius: 24rpx; - box-shadow: 0 0 12rpx 2rpx rgba(0, 0, 0, 0.1); - display: flex; - flex-wrap: wrap; - justify-content: space-between; - align-items: center; -} -.container .content .function-box2 .box2-content .box2-item.data-v-1cf27b2a { - width: 146rpx; - height: 143rpx; - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; -} -.container .content .function-box2 .box2-content .box2-item .box2-item-img.data-v-1cf27b2a { - width: 100rpx; - height: 100rpx; - display: flex; - justify-content: center; - align-items: center; - background: #5EA1FA; - border-radius: 50%; -} -.container .content .function-box2 .box2-content .box2-item .box2-item-img .box2-image.data-v-1cf27b2a { - width: 48rpx; - height: 48rpx; -} -.container .content .function-box2 .box2-content .box2-item .box2-item-text.data-v-1cf27b2a { - width: 100%; - height: 40rpx; - font-weight: 300; - font-size: 30rpx; - color: #3e3e3e; - text-align: center; - font-weight: 600; -} -.container .content .advertisement-box.data-v-1cf27b2a { - width: 100%; - height: 188rpx; - padding: 0 32rpx 10rpx; - box-sizing: border-box; - display: flex; - justify-content: center; -} -.container .content .advertisement-box .banner-img.data-v-1cf27b2a { - width: 686rpx; - height: 178rpx; -} -.container .content .service-box.data-v-1cf27b2a { - width: 100%; - height: 255rpx; - padding: 0 32rpx; - box-sizing: border-box; -} -.container .content .service-box .service-title.data-v-1cf27b2a { - height: 44rpx; - font-weight: 300; - font-size: 32rpx; - color: #000000; - text-align: left; - margin-bottom: 5rpx; - margin-left: 30rpx; -} -.container .content .service-box .service-content.data-v-1cf27b2a { - width: 100%; - height: 206rpx; - display: flex; - justify-content: space-between; - align-items: center; -} -.container .content .service-box .service-content .service-bg.data-v-1cf27b2a { - width: 330rpx; - height: 206rpx; - background: linear-gradient(to bottom, #AFD5FF, #FFFFFF); - border-radius: 24rpx; - display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; -} -.container .content .service-box .service-content .service-bg view.data-v-1cf27b2a:first-child { - display: flex; - align-items: center; - margin-left: 40rpx; -} -.container .content .service-box .service-content .service-bg view:first-child image.data-v-1cf27b2a { - width: 48rpx; - height: 48rpx; - margin-right: 10rpx; -} -.container .content .service-box .service-content .service-bg view:first-child text.data-v-1cf27b2a { - font-weight: 300; - font-size: 28rpx; - color: #000000; -} -.container .content .service-box .service-content .service-bg .service-info.data-v-1cf27b2a { - width: 214rpx; - font-weight: 300; - font-size: 24rpx; - color: #4D4B4B; - margin-left: 96rpx; -} -.container .content .service-box .service-content .service-bg view.data-v-1cf27b2a:last-child { - margin-left: 174rpx; - margin-top: 18rpx; - display: flex; - align-items: center; -} -.container .content .service-box .service-content .service-bg view:last-child image.data-v-1cf27b2a { - width: 32rpx; - height: 32rpx; -} -.container .content .service-box .service-content .service-bg view:last-child text.data-v-1cf27b2a { - font-weight: 300; - font-size: 24rpx; - color: #4D4B4B; - margin-right: 10rpx; -} -.container .popup-content.data-v-1cf27b2a { - height: 560rpx; - position: relative; - z-index: 1000; -} -.container .popup-content .popup-title.data-v-1cf27b2a { - width: 100%; - display: block; - text-align: center; - margin: 15rpx 0; - font-weight: 600; - font-size: 36rpx; -} -.container .popup-content .hr.data-v-1cf27b2a { - width: 100%; - height: 2rpx; - background: #e2e2e2; -} -.container .popup-content .img-box.data-v-1cf27b2a { - width: 65rpx; - height: 65rpx; - margin: 40rpx auto 30rpx; - border: 1rpx solid #e2e2e2; - border-radius: 50%; - padding: 15rpx; -} -.container .popup-content .img-box .img.data-v-1cf27b2a { - width: 100%; - height: 100%; -} -.container .popup-content .info-box.data-v-1cf27b2a { - width: 100%; - padding: 0 80rpx; - box-sizing: border-box; -} -.container .popup-content .info-box .hr2.data-v-1cf27b2a { - width: 100%; - height: 2rpx; - background: #e2e2e2; - margin: 20rpx 0; -} -.container .popup-content .info-box .info-text2.data-v-1cf27b2a { - font-size: 26rpx; - color: #767676; -} -.container .popup-content .button-box.data-v-1cf27b2a { - display: flex; - margin-top: 20rpx; -} -.container .popup-content .button-box button.data-v-1cf27b2a { - width: 230rpx; - height: 80rpx; - line-height: 80rpx; - border: none; - box-shadow: none; - margin-top: 30rpx; -} -.container .popup-content .button-box button.data-v-1cf27b2a::after { - border: none !important; -} -.container .popup-content .button-box .button1.data-v-1cf27b2a { - background-color: #F0F0F0; - color: #333333; -} -.container .popup-content .button-box .button2.data-v-1cf27b2a { - background-color: #06BF5E; - color: #ffffff; -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/preSendWater/index.js b/unpackage/dist/dev/mp-weixin/pages/preSendWater/index.js deleted file mode 100644 index 8681c24..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/preSendWater/index.js +++ /dev/null @@ -1,29 +0,0 @@ -"use strict"; -const common_vendor = require("../../common/vendor.js"); -if (!Array) { - const _easycom_navbar2 = common_vendor.resolveComponent("navbar"); - _easycom_navbar2(); -} -const _easycom_navbar = () => "../../components/navbar/navbar.js"; -if (!Math) { - _easycom_navbar(); -} -const _sfc_main = { - __name: "index", - setup(__props) { - function navTo() { - common_vendor.index.navigateTo({ - url: "/pages/address/index" - }); - } - return (_ctx, _cache) => { - return { - a: common_vendor.p({ - title: "预约下单" - }), - b: common_vendor.o(($event) => navTo()) - }; - }; - } -}; -wx.createPage(_sfc_main); diff --git a/unpackage/dist/dev/mp-weixin/pages/preSendWater/index.json b/unpackage/dist/dev/mp-weixin/pages/preSendWater/index.json deleted file mode 100644 index ff63ccc..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/preSendWater/index.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "navigationStyle": "custom", - "usingComponents": { - "navbar": "../../components/navbar/navbar" - } -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/preSendWater/index.wxml b/unpackage/dist/dev/mp-weixin/pages/preSendWater/index.wxml deleted file mode 100644 index be6bd2f..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/preSendWater/index.wxml +++ /dev/null @@ -1 +0,0 @@ -<view class="container"><navbar wx:if="{{a}}" u-i="0c68eff9-0" bind:__l="__l" u-p="{{a}}"></navbar><view class="content"><view class="add-address" bindtap="{{b}}">地址管理</view></view></view> \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/preSendWater/index.wxss b/unpackage/dist/dev/mp-weixin/pages/preSendWater/index.wxss deleted file mode 100644 index cd077c9..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/preSendWater/index.wxss +++ /dev/null @@ -1,46 +0,0 @@ -/** - * 这里是uni-app内置的常用样式变量 - * - * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 - * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App - * - */ -/** - * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 - * - * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 - */ -/* 颜色变量 */ -/* 行为相关颜色 */ -/* 文字基本颜色 */ -/* 背景颜色 */ -/* 边框颜色 */ -/* 尺寸变量 */ -/* 文字尺寸 */ -/* 图片尺寸 */ -/* Border Radius */ -/* 水平间距 */ -/* 垂直间距 */ -/* 透明度 */ -/* 文章场景相关 */ -.container { - width: 100%; - height: 100vh; -} -.container .content { - width: 100%; - height: calc(100vh - 176rpx); - background: linear-gradient(to top, #FFFFFF, #E8EFFF); -} -.container .content .add-address { - width: 100%; - padding-right: 32rpx; - box-sizing: border-box; - height: 94rpx; - line-height: 60rpx; - font-size: 26rpx; - font-weight: 600; - color: #5EA1FA; - display: flex; - justify-content: flex-end; -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/recharge/index.js b/unpackage/dist/dev/mp-weixin/pages/recharge/index.js deleted file mode 100644 index 7b00dc2..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/recharge/index.js +++ /dev/null @@ -1,167 +0,0 @@ -"use strict"; -const common_vendor = require("../../common/vendor.js"); -const api_index = require("../../api/index.js"); -if (!Array) { - const _easycom_navbar2 = common_vendor.resolveComponent("navbar"); - _easycom_navbar2(); -} -const _easycom_navbar = () => "../../components/navbar/navbar.js"; -if (!Math) { - _easycom_navbar(); -} -const _sfc_main = { - __name: "index", - setup(__props) { - const choosedMoney = common_vendor.ref(50); - const disabled = common_vendor.ref(true); - const userInfo = JSON.parse(common_vendor.index.getStorageSync("userInfo")); - const moneyList = common_vendor.ref([ - { value: 50, active: true }, - { value: 100, active: false }, - { value: 200, active: false }, - { value: 500, active: false }, - { value: 1e3, active: false }, - { value: "其他金额", active: false } - ]); - const activeStyle = common_vendor.ref({ - backgroundColor: "#5EADFF", - color: "#fff" - }); - function chooseMoney(index1) { - moneyList.value.forEach((item, index) => { - if (item.active == true) { - item.active = false; - } - }); - moneyList.value[index1].active = true; - if (index1 == 5) { - disabled.value = false; - choosedMoney.value = ""; - } else { - choosedMoney.value = moneyList.value[index1].value; - otherMoney.value = ""; - disabled.value = true; - } - } - const otherMoney = common_vendor.ref(); - common_vendor.ref(); - const payList = common_vendor.ref([ - // {text:"支付宝支付",value:1,checked:true,img:'../../static/images/recharge/pay.png'}, - { text: "微信支付", value: 2, checked: true, img: "../../static/images/recharge/wx.png" } - // {text:"余额",value:3,checked:false,img:'../../static/images/recharge/balance.png'}, - ]); - let timerId; - let payStatus = common_vendor.ref(true); - async function submit() { - if (payStatus.value) { - payStatus.value = false; - timerId = setTimeout(() => { - payStatus.value = true; - }, 2e4); - let money = ""; - if (disabled.value) { - money = choosedMoney.value; - } else { - money = Number(otherMoney.value); - } - let data = { - cardNumber: userInfo.waterCardNumber, - paymentMethod: 1, - rechargeAmount: money - }; - await api_index.rechargeApi(data).then(async (res) => { - if (res.code == 200) { - let data2 = { - businessType: 1, - businessNo: res.data - }; - await wxPay(data2); - otherMoney.value = ""; - } - }); - } else { - common_vendor.index.showToast({ - title: "充值间隔20秒,请稍后!", - icon: "none" - }); - } - } - async function wxPay(data) { - await api_index.wxPayApi(data).then((res) => { - if (res.code == 200) { - common_vendor.wx$1.requestPayment({ - //预支付订单信息 - // appId: res.data.appId, - timeStamp: res.data.timeStamp, - //时间戳 - nonceStr: res.data.nonceStr, - //随机串 - package: res.data.package, - //prepay_id - signType: res.data.signType, - //签名算法MD5 - paySign: res.data.paySign, - //微信签名 - success(res2) { - if (res2.errMsg == "requestPayment:ok") { - console.log("支付成功", res2); - common_vendor.index.showToast({ - title: "支付成功", - duration: 2e3 - }); - common_vendor.index.navigateBack(); - } else { - common_vendor.index.showToast({ - title: "支付失败", - duration: 2e3, - icon: "none" - }); - } - }, - fail(res2) { - common_vendor.index.showToast({ - title: "微信支付失败", - duration: 2e3, - icon: "none" - }); - } - }); - } - }); - } - common_vendor.onUnmounted(() => { - clearTimeout(timerId); - }); - return (_ctx, _cache) => { - return { - a: common_vendor.p({ - title: "账户充值" - }), - b: common_vendor.f(moneyList.value, (item, index, i0) => { - return { - a: common_vendor.t(item.value), - b: common_vendor.s(item.active ? activeStyle.value : ""), - c: common_vendor.o(($event) => chooseMoney(index), index), - d: index - }; - }), - c: disabled.value, - d: !disabled.value, - e: otherMoney.value, - f: common_vendor.o(($event) => otherMoney.value = $event.detail.value), - g: common_vendor.f(payList.value, (item, index, i0) => { - return { - a: item.img, - b: common_vendor.t(item.text), - c: item.value, - d: item.checked - }; - }), - h: common_vendor.unref(payStatus) ? 1 : 0.7, - i: common_vendor.o(($event) => submit()) - }; - }; - } -}; -const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-f06186df"]]); -wx.createPage(MiniProgramPage); diff --git a/unpackage/dist/dev/mp-weixin/pages/recharge/index.json b/unpackage/dist/dev/mp-weixin/pages/recharge/index.json deleted file mode 100644 index ff63ccc..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/recharge/index.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "navigationStyle": "custom", - "usingComponents": { - "navbar": "../../components/navbar/navbar" - } -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/recharge/index.wxml b/unpackage/dist/dev/mp-weixin/pages/recharge/index.wxml deleted file mode 100644 index 9584a67..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/recharge/index.wxml +++ /dev/null @@ -1 +0,0 @@ -<view class="container data-v-f06186df"><navbar wx:if="{{a}}" class="data-v-f06186df" u-i="f06186df-0" bind:__l="__l" u-p="{{a}}"></navbar><view class="content data-v-f06186df"><view class="main data-v-f06186df"><view class="money data-v-f06186df"><view class="money-title data-v-f06186df"><view class="title-icon data-v-f06186df"></view><view class="title-text data-v-f06186df">充值金额</view></view><view class="money-list data-v-f06186df"><block wx:for="{{b}}" wx:for-item="item" wx:key="d"><view class="money-box data-v-f06186df" style="{{item.b}}" bindtap="{{item.c}}">¥{{item.a}}</view></block></view></view><view class="other-money data-v-f06186df"><view class="money-title data-v-f06186df"><view class="title-icon data-v-f06186df"></view><view class="title-text data-v-f06186df">其他金额</view></view><view class="money-input data-v-f06186df"><input class="data-v-f06186df" disabled="{{c}}" focus="{{d}}" placeholder="请输入其他金额" value="{{e}}" bindinput="{{f}}"/></view></view><view class="pay-methods data-v-f06186df"><view class="methods-list data-v-f06186df"><block wx:for="{{g}}" wx:for-item="item"><view class="pay-item data-v-f06186df"><view class="pay-item-right data-v-f06186df"><image class="data-v-f06186df" src="{{item.a}}" alt=""></image><view class="data-v-f06186df">{{item.b}}</view></view><radio class="data-v-f06186df" value="{{item.c}}" checked="{{item.d}}"/></view></block></view></view></view><view style="{{'opacity:' + h}}" class="submit-btn data-v-f06186df" bindtap="{{i}}">立即充值</view></view></view> \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/recharge/index.wxss b/unpackage/dist/dev/mp-weixin/pages/recharge/index.wxss deleted file mode 100644 index b64853b..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/recharge/index.wxss +++ /dev/null @@ -1,153 +0,0 @@ -/** - * 这里是uni-app内置的常用样式变量 - * - * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 - * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App - * - */ -/** - * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 - * - * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 - */ -/* 颜色变量 */ -/* 行为相关颜色 */ -/* 文字基本颜色 */ -/* 背景颜色 */ -/* 边框颜色 */ -/* 尺寸变量 */ -/* 文字尺寸 */ -/* 图片尺寸 */ -/* Border Radius */ -/* 水平间距 */ -/* 垂直间距 */ -/* 透明度 */ -/* 文章场景相关 */ -.container.data-v-f06186df { - width: 100%; - height: 100vh; -} -.container .content.data-v-f06186df { - width: 100%; - height: calc(100vh - 176rpx); - background: linear-gradient(to top, #FFFFFF, #E8EFFF); - overflow-y: scroll; -} -.container .content .main.data-v-f06186df { - width: 686rpx; - height: 1262rpx; - background: #FFFFFF; - border-top-right-radius: 24rpx; - border-top-left-radius: 24rpx; - margin: 20rpx auto 0; - padding: 0 25rpx; - box-sizing: border-box; -} -.container .content .main .money.data-v-f06186df { - width: 100%; - height: 378rpx; - padding-top: 58rpx; - box-sizing: border-box; -} -.container .content .main .money .money-list.data-v-f06186df { - margin-top: 40rpx; - width: 100%; - height: 240rpx; - padding: 0 20rpx; - box-sizing: border-box; - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; -} -.container .content .main .money .money-list .money-box.data-v-f06186df { - width: 160rpx; - height: 100rpx; - border: 2rpx solid #75C8EB; - border-radius: 8rpx; - font-weight: 300; - font-size: 28rpx; - color: #4EB6E3; - text-align: center; - line-height: 100rpx; -} -.container .content .main .other-money.data-v-f06186df { - width: 100%; - height: 164rpx; - margin-top: 58rpx; -} -.container .content .main .other-money .money-input.data-v-f06186df { - margin: 40rpx auto 0; - width: calc(100% - 40rpx); - height: 80rpx; - box-sizing: border-box; - border-radius: 8rpx; - border: 2rpx solid #75C8EB; -} -.container .content .main .other-money .money-input input.data-v-f06186df { - width: 94%; - height: 80rpx; - margin-left: 6%; -} -.container .content .main .pay-methods.data-v-f06186df { - width: 100%; - margin-top: 58rpx; -} -.container .content .main .pay-methods .methods-list.data-v-f06186df { - margin-top: 40rpx; - width: 100%; - padding: 0 20rpx; - box-sizing: border-box; - height: 130rpx; - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: flex-start; -} -.container .content .main .pay-methods .methods-list .pay-item.data-v-f06186df { - width: 100%; - display: flex; - justify-content: space-between; - align-items: center; -} -.container .content .main .pay-methods .methods-list .pay-item .pay-item-right.data-v-f06186df { - display: flex; -} -.container .content .main .pay-methods .methods-list .pay-item .pay-item-right image.data-v-f06186df { - width: 42rpx; - height: 42rpx; - margin-right: 34rpx; -} -.container .content .main .pay-methods .methods-list .pay-item .pay-item-right view.data-v-f06186df { - font-weight: 300; - font-size: 28rpx; - color: #111111; -} -.container .submit-btn.data-v-f06186df { - width: 686rpx; - height: 98rpx; - background: #5EA1FA; - border-radius: 50rpx; - text-align: center; - line-height: 98rpx; - letter-spacing: 3rpx; - margin: 0 auto; - font-weight: 300; - font-size: 36rpx; - color: #FFFFFF; -} -.money-title.data-v-f06186df { - display: flex; - align-items: center; -} -.money-title .title-icon.data-v-f06186df { - width: 8rpx; - height: 36rpx; - background: #49B4E3; - margin-right: 12rpx; -} -.money-title .title-text.data-v-f06186df { - font-weight: 300; - font-size: 28rpx; - color: #000000; -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/sendWater/index.js b/unpackage/dist/dev/mp-weixin/pages/sendWater/index.js deleted file mode 100644 index 9e57bd7..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/sendWater/index.js +++ /dev/null @@ -1,68 +0,0 @@ -"use strict"; -const common_vendor = require("../../common/vendor.js"); -if (!Array) { - const _easycom_navbar2 = common_vendor.resolveComponent("navbar"); - _easycom_navbar2(); -} -const _easycom_navbar = () => "../../components/navbar/navbar.js"; -if (!Math) { - _easycom_navbar(); -} -const _sfc_main = { - __name: "index", - setup(__props) { - function navTo() { - common_vendor.index.navigateTo({ - url: "/pages/preSendWater/index" - }); - } - const isComplete = common_vendor.ref(false); - const isActive = common_vendor.ref({ borderBottom: `3px solid #226AC9` }); - function swatch(val) { - if (val == 1 && !isComplete.value || val == 2 && isComplete.value) - ; - else if (val == 1) { - isComplete.value = false; - } else if (val == 2) { - isComplete.value = true; - } - } - const deliverList = common_vendor.ref([ - { count: "20", num: "132123123", addre1: "测试1", addre2: "测试2", time: "16:00-18:00" }, - { count: "20", num: "132123123", addre1: "测试1", addre2: "测试2", time: "16:00-18:00" }, - { count: "20", num: "132123123", addre1: "测试1", addre2: "测试2", time: "16:00-18:00" } - ]); - const completeList = common_vendor.ref([1, 2, 3]); - return (_ctx, _cache) => { - return common_vendor.e({ - a: common_vendor.p({ - title: "送水到家" - }), - b: common_vendor.o(($event) => navTo()), - c: common_vendor.o(($event) => swatch(1)), - d: common_vendor.s(!isComplete.value ? isActive.value : {}), - e: common_vendor.o(($event) => swatch(2)), - f: common_vendor.s(isComplete.value ? isActive.value : {}), - g: !isComplete.value - }, !isComplete.value ? { - h: common_vendor.f(deliverList.value, (item, index, i0) => { - return { - a: common_vendor.t(item.count), - b: common_vendor.t(item.num), - c: common_vendor.t(item.addre1), - d: common_vendor.t(item.addre2), - e: common_vendor.t(item.time), - f: index - }; - }) - } : { - i: common_vendor.f(completeList.value, (item, index, i0) => { - return { - a: index - }; - }) - }); - }; - } -}; -wx.createPage(_sfc_main); diff --git a/unpackage/dist/dev/mp-weixin/pages/sendWater/index.json b/unpackage/dist/dev/mp-weixin/pages/sendWater/index.json deleted file mode 100644 index ff63ccc..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/sendWater/index.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "navigationStyle": "custom", - "usingComponents": { - "navbar": "../../components/navbar/navbar" - } -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/sendWater/index.wxml b/unpackage/dist/dev/mp-weixin/pages/sendWater/index.wxml deleted file mode 100644 index b477186..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/sendWater/index.wxml +++ /dev/null @@ -1 +0,0 @@ -<view class="container"><navbar wx:if="{{a}}" u-i="5d3da990-0" bind:__l="__l" u-p="{{a}}"></navbar><view class="content"><view class="nav"><view class="nav-title"><view class="title-one" bindtap="{{b}}">立即预约</view><view class="title-text" bindtap="{{c}}" style="{{d}}">待配送</view><view class="title-text" bindtap="{{e}}" style="{{f}}">已完成</view></view></view><view wx:if="{{g}}" class="main-deliving"><block wx:for="{{h}}" wx:for-item="item" wx:key="f"><view class="item"><view class="item-top"><view class="top-left"><text>{{item.a}}</text><text>桶</text></view><text class="top-right">订单编号:{{item.b}}</text></view><view class="item-content"><view class="item-one"><view class="item-color1">取</view><text>{{item.c}}</text></view><view class="line"></view><view class="item-one"><view class="item-color2">送</view><text>{{item.d}}</text></view><view class="line"></view><view class="item-one"><view class="item-color3">达</view><text>预计今天{{item.e}}送达</text></view></view></view></block></view><view wx:else class="main-complete"><block wx:for="{{i}}" wx:for-item="item" wx:key="a"><view class="item"></view></block></view></view></view> \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/pages/sendWater/index.wxss b/unpackage/dist/dev/mp-weixin/pages/sendWater/index.wxss deleted file mode 100644 index 8b3b710..0000000 --- a/unpackage/dist/dev/mp-weixin/pages/sendWater/index.wxss +++ /dev/null @@ -1,162 +0,0 @@ -/** - * 这里是uni-app内置的常用样式变量 - * - * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 - * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App - * - */ -/** - * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 - * - * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 - */ -/* 颜色变量 */ -/* 行为相关颜色 */ -/* 文字基本颜色 */ -/* 背景颜色 */ -/* 边框颜色 */ -/* 尺寸变量 */ -/* 文字尺寸 */ -/* 图片尺寸 */ -/* Border Radius */ -/* 水平间距 */ -/* 垂直间距 */ -/* 透明度 */ -/* 文章场景相关 */ -.container { - width: 100%; - height: 100vh; -} -.container .content { - width: 100%; - height: calc(100vh - 176rpx); - background: linear-gradient(to top, #FFFFFF, #E8EFFF); -} -.container .content .nav { - height: 160rpx; - width: 100%; - padding: 20rpx 0; - box-sizing: border-box; -} -.container .content .nav .nav-title { - width: 100%; - background: #FFF; - padding: 0 32rpx; - box-sizing: border-box; - height: 120rpx; - display: flex; - justify-content: space-between; - align-items: center; -} -.container .content .nav .nav-title .title-one { - height: 60rpx; - width: 160rpx; - background: #DEEDFF; - border: 2rpx solid #4AA3FF; - text-align: center; - line-height: 60rpx; - border-radius: 24rpx; - font-weight: 300; - font-size: 28rpx; - color: #007EFF; -} -.container .content .nav .nav-title .title-text { - font-weight: 300; - font-size: 28rpx; - color: #000000; - width: 120rpx; - height: 88rpx; - text-align: center; - line-height: 88rpx; -} -.container .content .main-deliving { - height: calc(100% - 320rpx); - width: 100%; - padding: 32rpx; - overflow: scroll; - box-sizing: border-box; -} -.container .content .main-deliving .item { - width: 100%; - height: 420rpx; - background-color: #FFF; - margin-bottom: 20rpx; - border-radius: 24rpx; -} -.container .content .main-deliving .item .item-top { - width: 100%; - height: 88rpx; - border-bottom: 2rpx solid #DFDFDF; - display: flex; - justify-content: space-between; - align-items: center; -} -.container .content .main-deliving .item .item-top .top-left { - margin-left: 52rpx; - font-weight: 300; - font-size: 28rpx; - color: #000000; -} -.container .content .main-deliving .item .item-top .top-left text:first-child { - color: #FF3400; -} -.container .content .main-deliving .item .item-top .top-right { - margin-right: 22rpx; - font-weight: 300; - font-size: 24rpx; - color: #4D4D4D; -} -.container .content .main-deliving .item .item-content { - width: 100%; - height: 330rpx; - padding: 50rpx 46rpx; - box-sizing: border-box; - display: flex; - flex-direction: column; -} -.container .content .main-deliving .item .item-content .item-one { - display: flex; - align-items: center; -} -.container .content .main-deliving .item .item-content .item-one view { - width: 52rpx; - height: 52rpx; - text-align: center; - line-height: 52rpx; - border-radius: 50%; - color: #fff; - font-size: 24rpx; - margin-right: 32rpx; -} -.container .content .main-deliving .item .item-content .item-one text { - font-size: 28rpx; -} -.container .content .main-deliving .item .item-content .item-one .item-color1 { - background-color: #189Df5; -} -.container .content .main-deliving .item .item-content .item-one .item-color2 { - background-color: #32CB65; -} -.container .content .main-deliving .item .item-content .item-one .item-color3 { - background-color: #DCDCDC; -} -.container .content .main-deliving .item .item-content .line { - height: 40rpx; - width: 2rpx; - background-color: #707070; - margin-left: 25rpx; -} -.container .content .main-complete { - height: calc(100% - 320rpx); - width: 100%; - padding: 32rpx; - overflow: scroll; - box-sizing: border-box; -} -.container .content .main-complete .item { - width: 100%; - height: 420rpx; - background-color: #FFF; - margin-bottom: 20rpx; - border-radius: 24rpx; -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/project.config.json b/unpackage/dist/dev/mp-weixin/project.config.json deleted file mode 100644 index e1facc3..0000000 --- a/unpackage/dist/dev/mp-weixin/project.config.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "description": "项目配置文件。", - "packOptions": { - "ignore": [], - "include": [] - }, - "setting": { - "urlCheck": false, - "es6": true, - "postcss": false, - "minified": false, - "newFeature": true, - "bigPackageSizeSupport": true, - "babelSetting": { - "ignore": [], - "disablePlugins": [], - "outputPath": "" - } - }, - "compileType": "miniprogram", - "libVersion": "", - "appid": "wxe86c40810c2c0e98", - "projectname": "water-drinking-uniapp", - "condition": {}, - "editorSetting": { - "tabIndent": "insertSpaces", - "tabSize": 2 - } -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/project.private.config.json b/unpackage/dist/dev/mp-weixin/project.private.config.json deleted file mode 100644 index 1c71e23..0000000 --- a/unpackage/dist/dev/mp-weixin/project.private.config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", - "libVersion": "3.6.6", - "projectname": "water-drinking-uniapp", - "setting": { - "compileHotReLoad": true - } -} \ No newline at end of file diff --git a/unpackage/dist/dev/mp-weixin/static/images/addCard/back.png b/unpackage/dist/dev/mp-weixin/static/images/addCard/back.png deleted file mode 100644 index dabb93b..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/addCard/back.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/index/back.png b/unpackage/dist/dev/mp-weixin/static/images/index/back.png deleted file mode 100644 index 8a85bec..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/index/back.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/index/bottom-line.png b/unpackage/dist/dev/mp-weixin/static/images/index/bottom-line.png deleted file mode 100644 index 4c60bfa..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/index/bottom-line.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/index/code1.png b/unpackage/dist/dev/mp-weixin/static/images/index/code1.png deleted file mode 100644 index e917b08..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/index/code1.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/index/icon23.png b/unpackage/dist/dev/mp-weixin/static/images/index/icon23.png deleted file mode 100644 index 40dd4df..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/index/icon23.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/index/icon31.png b/unpackage/dist/dev/mp-weixin/static/images/index/icon31.png deleted file mode 100644 index 9413f85..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/index/icon31.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/index/icon32.png b/unpackage/dist/dev/mp-weixin/static/images/index/icon32.png deleted file mode 100644 index 2119427..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/index/icon32.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/index/icon33.png b/unpackage/dist/dev/mp-weixin/static/images/index/icon33.png deleted file mode 100644 index 767168c..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/index/icon33.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/index/icon34.png b/unpackage/dist/dev/mp-weixin/static/images/index/icon34.png deleted file mode 100644 index 97c9d23..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/index/icon34.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/index/icon35.png b/unpackage/dist/dev/mp-weixin/static/images/index/icon35.png deleted file mode 100644 index 0f3e8f8..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/index/icon35.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/index/icon36.png b/unpackage/dist/dev/mp-weixin/static/images/index/icon36.png deleted file mode 100644 index c7eed78..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/index/icon36.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/index/icon37.png b/unpackage/dist/dev/mp-weixin/static/images/index/icon37.png deleted file mode 100644 index 86f01b2..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/index/icon37.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/index/icon38.png b/unpackage/dist/dev/mp-weixin/static/images/index/icon38.png deleted file mode 100644 index 78010d9..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/index/icon38.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/index/more-info5.png b/unpackage/dist/dev/mp-weixin/static/images/index/more-info5.png deleted file mode 100644 index 3dc2aed..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/index/more-info5.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/index/vip-add.png b/unpackage/dist/dev/mp-weixin/static/images/index/vip-add.png deleted file mode 100644 index 2e43e8b..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/index/vip-add.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/index/vip1.png b/unpackage/dist/dev/mp-weixin/static/images/index/vip1.png deleted file mode 100644 index e66d06f..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/index/vip1.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/login/logo.png b/unpackage/dist/dev/mp-weixin/static/images/login/logo.png deleted file mode 100644 index e5a3b4e..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/login/logo.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/login/wechat.png b/unpackage/dist/dev/mp-weixin/static/images/login/wechat.png deleted file mode 100644 index 3d5d6c1..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/login/wechat.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/recharge/balance.png b/unpackage/dist/dev/mp-weixin/static/images/recharge/balance.png deleted file mode 100644 index 79a7903..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/recharge/balance.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/recharge/pay.png b/unpackage/dist/dev/mp-weixin/static/images/recharge/pay.png deleted file mode 100644 index 18c80b5..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/recharge/pay.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/images/recharge/wx.png b/unpackage/dist/dev/mp-weixin/static/images/recharge/wx.png deleted file mode 100644 index 86c2530..0000000 --- a/unpackage/dist/dev/mp-weixin/static/images/recharge/wx.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/static/logo.png b/unpackage/dist/dev/mp-weixin/static/logo.png deleted file mode 100644 index b5771e2..0000000 --- a/unpackage/dist/dev/mp-weixin/static/logo.png +++ /dev/null Binary files differ diff --git a/unpackage/dist/dev/mp-weixin/util/request.js b/unpackage/dist/dev/mp-weixin/util/request.js deleted file mode 100644 index 161dcb4..0000000 --- a/unpackage/dist/dev/mp-weixin/util/request.js +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; -const common_vendor = require("../common/vendor.js"); -const config_baseUrl = require("../config/baseUrl.js"); -const request = (url, data, method) => { - return new Promise((resolve, reject) => { - common_vendor.index.request({ - url: config_baseUrl.BASE_URL + url, - method: method || "POST", - header: { - token: common_vendor.index.getStorageSync("token") || "" - }, - data: data || {}, - success: (res) => { - const data2 = res.data; - resolve(data2); - }, - fail: (error) => { - common_vendor.index.showToast({ - icon: "error", - title: "请求错误" - }); - reject(error); - } - }); - }).catch((e) => { - }); -}; -exports.request = request; -- Gitblit v1.9.3