gaoluyang
2 天以前 fe75cffbf3bae6777aa2794fd89fa5dc37f5df8d
项目初始化
已添加272个文件
59878 ■■■■■ 文件已修改
.gitignore 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LICENSE 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
index.html 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json 13030 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pnpm-lock.yaml 9075 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-countdown/components/uni-countdown/i18n/en.json 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-countdown/components/uni-countdown/i18n/index.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hans.json 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hant.json 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-data-checkbox/changelog.md 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue 821 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-data-checkbox/package.json 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-data-checkbox/readme.md 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-data-picker/changelog.md 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-data-picker/components/uni-data-picker/keypress.js 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue 547 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js 622 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue 321 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-data-picker/package.json 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-data-picker/readme.md 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-data-select/changelog.md 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue 440 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-data-select/package.json 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-data-select/readme.md 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-dateformat/changelog.md 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-dateformat/components/uni-dateformat/date-format.js 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-dateformat/components/uni-dateformat/uni-dateformat.vue 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-dateformat/package.json 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-dateformat/readme.md 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-datetime-picker/changelog.md 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue 924 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/keypress.js 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue 946 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue 1015 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js 410 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-datetime-picker/package.json 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-datetime-picker/readme.md 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-drawer/changelog.md 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-drawer/components/uni-drawer/keypress.js 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-drawer/package.json 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-drawer/readme.md 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-easyinput/changelog.md 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-easyinput/components/uni-easyinput/common.js 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue 679 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-easyinput/package.json 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-easyinput/readme.md 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-fab/changelog.md 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-fab/components/uni-fab/uni-fab.vue 490 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-fab/package.json 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-fab/readme.md 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-fav/changelog.md 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-fav/components/uni-fav/i18n/en.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-fav/components/uni-fav/i18n/index.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-fav/components/uni-fav/uni-fav.vue 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-fav/package.json 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-fav/readme.md 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-file-picker/changelog.md 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js 224 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue 656 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue 325 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue 292 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-file-picker/components/uni-file-picker/utils.js 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-file-picker/package.json 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-file-picker/readme.md 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-forms/changelog.md 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue 631 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-forms/components/uni-forms/uni-forms.vue 397 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-forms/components/uni-forms/utils.js 293 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-forms/components/uni-forms/validate.js 486 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-forms/package.json 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-forms/readme.md 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-goods-nav/changelog.md 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/en.json 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/index.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hans.json 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hant.json 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-goods-nav/components/uni-goods-nav/uni-goods-nav.vue 229 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-goods-nav/package.json 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-goods-nav/readme.md 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-grid/changelog.md 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-grid/components/uni-grid-item/uni-grid-item.vue 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-grid/components/uni-grid/uni-grid.vue 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-grid/package.json 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-grid/readme.md 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-group/changelog.md 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-group/components/uni-group/uni-group.vue 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-group/package.json 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-group/readme.md 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-icons/changelog.md 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-icons/components/uni-icons/icons.js 1169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-icons/components/uni-icons/uni-icons.vue 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-icons/components/uni-icons/uniicons.css 663 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-icons/components/uni-icons/uniicons.ttf 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-icons/package.json 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-icons/readme.md 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-indexed-list/changelog.md 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list-item.vue 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list.vue 367 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-indexed-list/package.json 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-indexed-list/readme.md 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-link/changelog.md 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-link/components/uni-link/uni-link.vue 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-link/package.json 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-link/readme.md 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-list/changelog.md 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-list/components/uni-list-ad/uni-list-ad.vue 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.scss 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.vue 538 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-list/components/uni-list-item/uni-list-item.vue 454 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-list/components/uni-list/uni-list.vue 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-list/components/uni-list/uni-refresh.vue 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-list/components/uni-list/uni-refresh.wxs 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-list/package.json 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-list/readme.md 346 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-load-more/changelog.md 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-load-more/components/uni-load-more/i18n/en.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-load-more/components/uni-load-more/i18n/index.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue 399 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-load-more/package.json 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-load-more/readme.md 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-nav-bar/changelog.md 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue 357 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-nav-bar/package.json 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-nav-bar/readme.md 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-notice-bar/changelog.md 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue 426 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-notice-bar/package.json 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-notice-bar/readme.md 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-number-box/changelog.md 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue 221 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-number-box/package.json 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-number-box/readme.md 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-pagination/changelog.md 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-pagination/components/uni-pagination/i18n/en.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-pagination/components/uni-pagination/i18n/es.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-pagination/components/uni-pagination/i18n/fr.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-pagination/components/uni-pagination/i18n/index.js 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hans.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hant.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue 465 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-pagination/package.json 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-pagination/readme.md 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-popup/changelog.md 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-popup/components/uni-popup-dialog/keypress.js 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue 275 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-popup/components/uni-popup/i18n/en.json 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-popup/components/uni-popup/i18n/index.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-popup/components/uni-popup/keypress.js 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-popup/components/uni-popup/popup.js 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-popup/components/uni-popup/uni-popup.vue 474 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-popup/package.json 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-popup/readme.md 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-rate/changelog.md 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-rate/components/uni-rate/uni-rate.vue 361 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-rate/package.json 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-rate/readme.md 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-row/changelog.md 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-row/components/uni-col/uni-col.vue 317 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-row/components/uni-row/uni-row.vue 190 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-row/package.json 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-row/readme.md 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-scss/changelog.md 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-scss/index.scss 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-scss/package.json 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-scss/readme.md 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-scss/styles/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-scss/styles/setting/_border.scss 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-scss/styles/setting/_color.scss 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-scss/styles/setting/_radius.scss 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-scss/styles/setting/_space.scss 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-scss/styles/setting/_styles.scss 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-scss/styles/setting/_text.scss 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-scss/styles/setting/_variables.scss 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-scss/styles/tools/functions.scss 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-scss/theme.scss 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-scss/variables.scss 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-search-bar/changelog.md 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-search-bar/components/uni-search-bar/i18n/en.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-search-bar/components/uni-search-bar/i18n/index.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hans.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hant.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue 298 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-search-bar/package.json 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-search-bar/readme.md 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-segmented-control/changelog.md 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-segmented-control/components/uni-segmented-control/uni-segmented-control.vue 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-segmented-control/package.json 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-segmented-control/readme.md 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-steps/changelog.md 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-steps/components/uni-steps/uni-steps.vue 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-steps/package.json 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-steps/readme.md 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-swipe-action/changelog.md 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/bindingx.js 302 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/isPC.js 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpalipay.js 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpother.js 259 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpwxs.js 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/render.js 270 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/uni-swipe-action-item.vue 347 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/wx.wxs 341 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-swipe-action/components/uni-swipe-action/uni-swipe-action.vue 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-swipe-action/package.json 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-swipe-action/readme.md 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-swiper-dot/changelog.md 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-swiper-dot/components/uni-swiper-dot/uni-swiper-dot.vue 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-swiper-dot/package.json 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-swiper-dot/readme.md 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/changelog.md 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/components/uni-table/uni-table.vue 455 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/components/uni-tbody/uni-tbody.vue 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/components/uni-td/uni-td.vue 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/components/uni-th/filter-dropdown.vue 503 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/components/uni-th/uni-th.vue 278 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/components/uni-thead/uni-thead.vue 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/components/uni-tr/table-checkbox.vue 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/components/uni-tr/uni-tr.vue 171 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/i18n/en.json 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/i18n/es.json 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/i18n/fr.json 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/i18n/index.js 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/i18n/zh-Hans.json 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/i18n/zh-Hant.json 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/package.json 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-table/readme.md 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-tag/changelog.md 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-tag/components/uni-tag/uni-tag.vue 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-tag/package.json 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-tag/readme.md 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-title/changelog.md 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-title/components/uni-title/uni-title.vue 171 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-title/package.json 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-title/readme.md 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-tooltip/changelog.md 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-tooltip/package.json 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-tooltip/readme.md 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-transition/changelog.md 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-transition/components/uni-transition/createAnimation.js 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-transition/components/uni-transition/uni-transition.vue 277 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-transition/package.json 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/uni_modules/uni-transition/readme.md 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/auth.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/common.ts 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/constant.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/dict.ts 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/errorCode.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/geek.ts 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/permission.ts 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/request.ts 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/ruoyi.js 226 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/storage.ts 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/upload.ts 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tsconfig.json 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yarn.lock 5080 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
# å¿½ç•¥ç”Ÿæˆçš„æ–‡ä»¶
build/
dist/
unpackage/
*.class
*.jar
*.war
*.ear
# å¿½ç•¥ç¼–辑器/IDE生成的文件和目录
.idea/
.hbuilderx
*.swp
*.swo
*~
# å¿½ç•¥ä¾èµ–管理工具生成的目录
node_modules/
bower_components/
vendor/
# å¿½ç•¥æ“ä½œç³»ç»Ÿæ–‡ä»¶
.DS_Store
Thumbs.db
# å¿½ç•¥æ—¥å¿—文件
*.log
# å¿½ç•¥æ•æ„Ÿæˆ–包含个人信息的文件(根据需要添加更多)
credentials.json
config.ini
secrets.txt
# å¿½ç•¥å…¶ä»–自定义的文件或目录
/custom_directory/
# æŽ’除特定扩展名的文件(根据需要添加更多)
*.bak
*.tmp
# æŽ’除特定文件名(根据需要添加更多)
debug.log
# ä¸æŽ’除下列扩展名的文件
!*.allowed_extension
# ä¸æŽ’除下列文件名
!important_file.txt
LICENSE
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 å“¦NO
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
index.html
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Ruoyi-App-Geek</title>
    <meta charset="UTF-8" name="viewport" content="width=device-width, initial-scale=1">
    <script>
      var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
        CSS.supports('top: constant(a)'))
      document.write(
        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
        (coverSupport ? ', viewport-fit=cover' : '') + '" />')
    </script>
    <title></title>
    <!--preload-links-->
    <!--app-context-->
  </head>
  <body>
    <div id="app"><!--app-html--></div>
    <script type="module" src="./src/main.js"></script>
  </body>
</html>
package-lock.json
¶Ô±ÈÐÂÎļþ
ÎļþÌ«´ó
package.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,99 @@
{
  "name": "ruoyi-geek-app",
  "version": "1.0.0",
  "description": "若依Geek管理系统",
  "author": "Geek-XD",
  "license": "MIT",
  "homepage": "https://gitee.com/geek-xd",
  "repository": {
    "type": "git",
    "url": "https://gitee.com/geek-xd/ruoyi-geek-app.git"
  },
  "engines": {
    "node": "^18.0.0 || >=20.0.0"
  },
  "scripts": {
    "dev:app": "uni -p app",
    "dev:app-android": "uni -p app-android",
    "dev:app-ios": "uni -p app-ios",
    "dev:custom": "uni -p",
    "dev:h5": "uni",
    "dev:h5:ssr": "uni --ssr",
    "dev:mp-alipay": "uni -p mp-alipay",
    "dev:mp-baidu": "uni -p mp-baidu",
    "dev:mp-jd": "uni -p mp-jd",
    "dev:mp-kuaishou": "uni -p mp-kuaishou",
    "dev:mp-lark": "uni -p mp-lark",
    "dev:mp-qq": "uni -p mp-qq",
    "dev:mp-toutiao": "uni -p mp-toutiao",
    "dev:mp-weixin": "uni -p mp-weixin",
    "dev:quickapp-webview": "uni -p quickapp-webview",
    "dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
    "dev:quickapp-webview-union": "uni -p quickapp-webview-union",
    "build:app": "uni build -p app",
    "build:app-android": "uni build -p app-android",
    "build:app-ios": "uni build -p app-ios",
    "build:custom": "uni build -p",
    "build:h5": "uni build",
    "build:h5:ssr": "uni build --ssr",
    "build:mp-alipay": "uni build -p mp-alipay",
    "build:mp-baidu": "uni build -p mp-baidu",
    "build:mp-jd": "uni build -p mp-jd",
    "build:mp-kuaishou": "uni build -p mp-kuaishou",
    "build:mp-lark": "uni build -p mp-lark",
    "build:mp-qq": "uni build -p mp-qq",
    "build:mp-toutiao": "uni build -p mp-toutiao",
    "build:mp-weixin": "uni build -p mp-weixin",
    "build:quickapp-webview": "uni build -p quickapp-webview",
    "build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
    "build:quickapp-webview-union": "uni build -p quickapp-webview-union",
    "type-check": "vue-tsc --noEmit",
    "clean:linux": "rm -rf dist || rm -rf node_modules",
    "clean:windows": "rd /s /q dist || rd /s /q node_modules"
  },
  "dependencies": {
    "@dcloudio/uni-app": "3.0.0-4060420250429001",
    "@dcloudio/uni-app-harmony": "3.0.0-4060420250429001",
    "@dcloudio/uni-app-plus": "3.0.0-4060420250429001",
    "@dcloudio/uni-components": "3.0.0-4060420250429001",
    "@dcloudio/uni-h5": "3.0.0-4060420250429001",
    "@dcloudio/uni-mp-alipay": "3.0.0-4060420250429001",
    "@dcloudio/uni-mp-baidu": "3.0.0-4060420250429001",
    "@dcloudio/uni-mp-harmony": "3.0.0-4060420250429001",
    "@dcloudio/uni-mp-jd": "3.0.0-4060420250429001",
    "@dcloudio/uni-mp-kuaishou": "3.0.0-4060420250429001",
    "@dcloudio/uni-mp-lark": "3.0.0-4060420250429001",
    "@dcloudio/uni-mp-qq": "3.0.0-4060420250429001",
    "@dcloudio/uni-mp-toutiao": "3.0.0-4060420250429001",
    "@dcloudio/uni-mp-weixin": "3.0.0-4060420250429001",
    "@dcloudio/uni-mp-xhs": "3.0.0-4060420250429001",
    "@dcloudio/uni-quickapp-webview": "3.0.0-4060420250429001",
    "@dcloudio/uvm": "^0.3.1",
    "@jridgewell/sourcemap-codec": "^1.5.0",
    "@qiun/wx-ucharts": "2.5.0-20230101",
    "@uview-plus/types": "^3.2.5",
    "clipboard": "^2.0.11",
    "dayjs": "^1.11.13",
    "mqtt": "4.1.0",
    "pinia": "2.2.2",
    "tslib": "^2.7.0",
    "uview-plus": "^3.3.32",
    "vue": "3.4.21",
    "vue-i18n": "^9.14.2"
  },
  "devDependencies": {
    "@dcloudio/types": "^3.4.14",
    "@dcloudio/uni-automator": "3.0.0-4060420250429001",
    "@dcloudio/uni-cli-shared": "3.0.0-4060420250429001",
    "@dcloudio/uni-stacktracey": "3.0.0-4060420250429001",
    "@dcloudio/vite-plugin-uni": "3.0.0-4060420250429001",
    "@vue/runtime-core": "^3.5.12",
    "@vue/tsconfig": "^0.5.1",
    "less": "^4.2.0",
    "sass": "^1.78.0",
    "sass-loader": "^16.0.1",
    "typescript": "^5.6.2",
    "vite": "5.2.8",
    "vue-tsc": "2.1.6"
  }
}
pnpm-lock.yaml
¶Ô±ÈÐÂÎļþ
ÎļþÌ«´ó
src/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"
}
src/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
}
src/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": "秒"
}
src/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": "秒"
}
src/uni_modules/uni-data-checkbox/changelog.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
## 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)
- â€œæš‚无数据”显示居中
src/uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,821 @@
<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="chagne">
                <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="chagne">
                <!-- -->
                <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;
            },
            chagne(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 {
                            .radio__inner-icon {
                                opacity: 1;
                            }
                        }
                        .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>
src/uni_modules/uni-data-checkbox/package.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,84 @@
{
  "id": "uni-data-checkbox",
  "displayName": "uni-data-checkbox æ•°æ®é€‰æ‹©å™¨",
  "version": "1.0.3",
  "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"
        }
      }
    }
  }
}
src/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
src/uni_modules/uni-data-picker/changelog.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
## 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 ç›®å½•规范
src/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
src/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,547 @@
<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'],
    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();
      })
    },
    methods: {
      clear() {
        this.modelValue = null;
        this._dispatchEvent([]);
      },
      onPropsChange() {
        this._treeData = [];
        this.selectedIndex = 0;
        this.load();
      },
      load() {
        if (this.readonly) {
          this._processReadonly(this.localdata, this.dataValue);
          return;
        }
        if (!this.hasValue) {
          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) {
          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>
src/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 []
    }
  }
}
src/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,321 @@
<template>
  <view class="uni-data-pickerview">
    <scroll-view v-if="!isCloudDataList" class="selected-area" scroll-x="true">
      <view class="selected-list">
        <template v-for="(item,index) in selected">
          <view class="selected-item"
            :class="{'selected-item-active':index==selectedIndex}"
            v-if="item.text" @click="handleSelect(index)">
            <text>{{item.text}}</text>
          </view>
        </template>
      </view>
    </scroll-view>
    <view class="tab-c">
      <template v-for="(child, i) in dataList">
        <scroll-view class="list" :key="i" v-if="i==selectedIndex" :scroll-y="true">
          <view class="item" :class="{'is-disabled': !!item.disable}" v-for="(item, j) in child"
            @click="handleNodeClick(item, i, j)">
            <text class="item-text">{{item[map.text]}}</text>
            <view class="check" v-if="selected.length > i && item[map.value] == selected[i].value"></view>
          </view>
        </scroll-view>
      </template>
      <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>
src/uni_modules/uni-data-picker/package.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
{
  "id": "uni-data-picker",
  "displayName": "uni-data-picker æ•°æ®é©±åŠ¨çš„picker选择器",
  "version": "1.0.8",
  "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"
      },
      "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",
        "京东": "u"
        },
        "快应用": {
          "华为": "u",
          "联盟": "u"
        },
        "Vue": {
            "vue2": "y",
            "vue3": "y"
        }
      }
    }
  }
}
src/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
src/uni_modules/uni-data-select/changelog.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
## 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)
- åˆå§‹åŒ–
src/uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,440 @@
<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">{{current}}</view>
                    <view v-else class="uni-select__input-text uni-select__input-placeholder">{{typePlaceholder}}</view>
                    <uni-icons v-if="current && clear" type="clear" color="#c0c4cc" size="24" @click="clearVal" />
                    <uni-icons v-else :type="showSelector? 'top' : 'bottom'" size="14" color="#999" />
                </view>
                <view class="uni-select--mask" v-if="showSelector" @click="toggleSelector" />
                <view class="uni-select__selector" v-if="showSelector">
                    <view class="uni-popper__arrow"></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 æ˜¯å¦ç¦ç”¨
     * @event {Function} change  é€‰ä¸­å‘生变化触发
     */
    export default {
        name: "uni-stat-select",
        mixins: [uniCloud.mixinDatacom || {}],
        data() {
            return {
                showSelector: false,
                current: '',
                mixinDatacomResData: [],
                apps: [],
                channels: []
            };
        },
        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
            }
        },
        created() {
            this.last = `${this.collection}_last_selected_option_value`
            if (this.collection && !this.localdata.length) {
                this.mixinDatacomEasyGet()
            }
        },
        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
            }
        },
        watch: {
            localdata: {
                immediate: true,
                handler(val, old) {
                    if (Array.isArray(val) && old !== val) {
                        this.mixinDatacomResData = val
                    }
                }
            },
            // #ifndef VUE3
            value() {
                this.initDefVal()
            },
            // #endif
            // #ifdef VUE3
            modelValue() {
                this.initDefVal()
            },
            // #endif
            mixinDatacomResData: {
                immediate: true,
                handler(val) {
                    if (val.length) {
                        this.initDefVal()
                    }
                }
            }
        },
        methods: {
            initDefVal() {
                let defValue = ''
                if ((this.value || this.value === 0) && !this.isDisabled(this.value)) {
                    defValue = this.value
                } else if ((this.modelValue || this.modelValue === 0) && !this.isDisabled(this.modelValue)) {
                    defValue = this.modelValue
                } else {
                    let strogeValue
                    if (this.collection) {
                        strogeValue = uni.getStorageSync(this.last)
                    }
                    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
                    }
                    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) {
                    uni.removeStorageSync(this.last)
                }
            },
            change(item) {
                if (!item.disable) {
                    this.showSelector = false
                    this.current = this.formatItemName(item)
                    this.emit(item.value)
                }
            },
            emit(val) {
                this.$emit('change', val)
                this.$emit('input', val)
                this.$emit('update:modelValue', val)
                if (this.collection) {
                    uni.setStorageSync(this.last, val)
                }
            },
            toggleSelector() {
        if(this.disabled){
          return
        }
                this.showSelector = !this.showSelector
            },
            formatItemName(item) {
                let {
                    text,
                    value,
                    channel_code
                } = item
                channel_code = channel_code ? `(${channel_code})` : ''
                return this.collection.indexOf('app-list') > 0 ?
                    `${text}(${value})` :
                    (
                        text ?
                        text :
                        `未命名${channel_code}`
                    )
            }
        }
    }
</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;
        cursor: pointer;
        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;
        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: 3;
        padding: 4px 0;
    }
    .uni-select__selector-scroll {
        /* #ifndef APP-NVUE */
        max-height: 200px;
        box-sizing: border-box;
        /* #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,
    .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-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;
    }
</style>
src/uni_modules/uni-data-select/package.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,85 @@
{
  "id": "uni-data-select",
  "displayName": "uni-data-select ä¸‹æ‹‰æ¡†é€‰æ‹©å™¨",
  "version": "0.1.9",
  "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"
      },
      "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"
        }
      }
    }
  }
}
src/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
src/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 å¹³å°æ—¥æœŸæ ¼å¼åŒ–出错的问题
src/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)
}
src/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>
src/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"
        }
      }
    }
  }
}
src/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
src/uni_modules/uni-datetime-picker/changelog.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,103 @@
## 2.2.11(2022-09-19)
- ä¿®å¤ï¼Œæ”¯ä»˜å®å°ç¨‹åºæ ·å¼é”™ä¹±ï¼Œ[详情](https://github.com/dcloudio/uni-app/issues/3861)
## 2.2.10(2022-09-19)
- ä¿®å¤ï¼Œåå‘选择日期范围,日期显示异常,[详情](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?item_id=209458&rf=false](https://ask.dcloud.net.cn/question/140824?item_id=209458&rf=false)
## 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/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 é€‚配
src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/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--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--isDay': weeks.isDay}"></view>
    </view>
</template>
<script>
    export default {
        props: {
            weeks: {
                type: Object,
                default () {
                    return {}
                }
            },
            calendar: {
                type: Object,
                default: () => {
                    return {}
                }
            },
            selected: {
                type: Array,
                default: () => {
                    return []
                }
            },
            lunar: {
                type: Boolean,
                default: false
            },
            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-lunar-text {
        font-size: 12px;
        color: #333;
    }
    .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 {
        // background-color: rgba(249, 249, 249, $uni-opacity-disabled);
        cursor: default;
    }
    .uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable {
        color: #D1D1D1;
    }
    .uni-calendar-item--isDay {
        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>
src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,924 @@
<template>
    <view class="uni-calendar" @mouseleave="leaveCale">
        <view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}"
            @click="clean();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 v-if="left" 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||'') + yearText + ( nowDate.month||'') + monthText}}</text>
                </picker>
                <view v-if="right" class="uni-calendar__header-btn-box" @click.stop="next">
                    <view class="uni-calendar__header-btn uni-calendar--right"></view>
                </view>
                <view v-if="!insert" class="dialog-close" @click="clean">
                    <view class="dialog-close-plus" data-id="close"></view>
                    <view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
                </view>
                <!-- <text class="uni-calendar__backtoday" @click="backtoday">回到今天</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" 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" :lunar="lunar" :checkHover="range" @change="choiceDate"
                            @handleMouse="handleMouse">
                        </calendar-item>
                    </view>
                </view>
            </view>
            <view v-if="!insert && !range && typeHasTime" 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="reactStartTime" :end="reactEndTime" v-model="time"
                    :disabled="!tempSingleDate" :border="false" :hide-second="hideSecond" class="time-picker-style">
                </time-picker>
            </view>
            <view v-if="!insert && range && typeHasTime" 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="reactStartTime" 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="reactEndTime" 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-calendar__header-btn-box">
                    <text class="uni-calendar__button-text uni-calendar--fixed-width">{{okText}}</text>
                </view> -->
                <view class="uni-datetime-picker--btn" @click="confirm">{{confirmText}}</view>
            </view>
        </view>
    </view>
</template>
<script>
    import Calendar from './util.js';
    import calendarItem from './calendar-item.vue'
    import timePicker from './time-picker.vue'
    import {
        initVueI18n
    } from '@dcloudio/uni-i18n'
    import messages from './i18n/index.js'
    const {
        t
    } = initVueI18n(messages)
    /**
     * 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,
            timePicker
        },
        props: {
            date: {
                type: String,
                default: ''
            },
            defTime: {
                type: [String, Object],
                default: ''
            },
            selectableTimes: {
                type: [Object],
                default () {
                    return {}
                }
            },
            selected: {
                type: Array,
                default () {
                    return []
                }
            },
            lunar: {
                type: Boolean,
                default: false
            },
            startDate: {
                type: String,
                default: ''
            },
            endDate: {
                type: String,
                default: ''
            },
            range: {
                type: Boolean,
                default: false
            },
            typeHasTime: {
                type: Boolean,
                default: false
            },
            insert: {
                type: Boolean,
                default: true
            },
            showMonth: {
                type: Boolean,
                default: true
            },
            clearDate: {
                type: Boolean,
                default: true
            },
            left: {
                type: Boolean,
                default: true
            },
            right: {
                type: Boolean,
                default: true
            },
            checkHover: {
                type: Boolean,
                default: true
            },
            hideSecond: {
                type: [Boolean],
                default: false
            },
            pleStatus: {
                type: Object,
                default () {
                    return {
                        before: '',
                        after: '',
                        data: [],
                        fulldate: ''
                    }
                }
            }
        },
        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, oldVal) {
                    if (!this.range) {
                        this.tempSingleDate = newVal
                        setTimeout(() => {
                            this.init(newVal)
                        }, 100)
                    }
                }
            },
            defTime: {
                immediate: true,
                handler(newVal, oldVal) {
                    if (!this.range) {
                        this.time = newVal
                    } else {
                        // console.log('-----', newVal);
                        this.timeRange.startTime = newVal.start
                        this.timeRange.endTime = newVal.end
                    }
                }
            },
            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
            },
            pleStatus: {
                immediate: true,
                handler(newVal, oldVal) {
                    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 {
                            this.cale.setDefaultMultiple(before, after)
                            if (which === 'left') {
                                this.setDate(before)
                                this.weeks = this.cale.weeks
                            } else {
                                this.setDate(after)
                                this.weeks = this.cale.weeks
                            }
                            this.cale.lastHover = true
                        }
                    }, 16)
                }
            }
        },
        computed: {
            reactStartTime() {
                const activeDate = this.range ? this.tempRange.before : this.calendar.fullDate
                const res = activeDate === this.startDate ? this.selectableTimes.start : ''
                return res
            },
            reactEndTime() {
                const activeDate = this.range ? this.tempRange.after : this.calendar.fullDate
                const res = activeDate === this.endDate ? this.selectableTimes.end : ''
                return res
            },
            /**
             * 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({
                // date: new Date(),
                selected: this.selected,
                startDate: this.startDate,
                endDate: this.endDate,
                range: this.range,
                // multipleStatus: this.pleStatus
            })
            // é€‰ä¸­æŸä¸€å¤©
            // this.cale.setDate(this.date)
            this.init(this.date)
            // this.setDay
        },
        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
            },
            // å–消穿透
            clean() {
                this.close()
            },
            // è’™ç‰ˆç‚¹å‡»äº‹ä»¶
            maskClick() {
                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()
            },
            bindDateChange(e) {
                const value = e.detail.value + '-1'
                this.init(value)
            },
            /**
             * åˆå§‹åŒ–日期显示
             * @param {Object} date
             */
            init(date) {
                this.cale.setDate(date)
                this.weeks = this.cale.weeks
                this.nowDate = this.calendar = this.cale.getInfo(date)
            },
            // choiceDate(weeks) {
            //     if (weeks.disable) return
            //     this.calendar = weeks
            //     // è®¾ç½®å¤šé€‰
            //     this.cale.setMultiple(this.calendar.fullDate, true)
            //     this.weeks = this.cale.weeks
            //     this.tempSingleDate = this.calendar.fullDate
            //     this.tempRange.before = this.cale.multipleStatus.before
            //     this.tempRange.after = this.cale.multipleStatus.after
            //     this.change()
            // },
            /**
             * æ‰“开日历弹窗
             */
            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,
                    time: this.time,
                    timeRange: this.timeRange,
                    fulldate: fullDate,
                    lunar,
                    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 beforeStatus = this.cale.multipleStatus.before
                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()
            },
            /**
             * å›žåˆ°ä»Šå¤©
             */
            backtoday() {
                let date = this.cale.getDate(new Date()).fullDate
                // this.cale.setDate(date)
                this.init(date)
                this.change()
            },
            /**
             * æ¯”较时间大小
             */
            dateCompare(startDate, endDate) {
                // è®¡ç®—截止时间
                startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
                // è®¡ç®—详细项的截止时间
                endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
                if (startDate <= endDate) {
                    return true
                } else {
                    return false
                }
            },
            /**
             * ä¸Šä¸ªæœˆ
             */
            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" >
    $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;
        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>
src/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 datetime",
    "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"
}
src/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
}
src/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": "确认"
}
src/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": "確認"
}
src/uni_modules/uni-datetime-picker/components/uni-datetime-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
src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,946 @@
<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>
        <!-- #ifdef H5 -->
        <!-- <keypress v-if="visible" @esc="tiggerTimePicker" @enter="setTime" /> -->
        <!-- #endif -->
    </view>
</template>
<script>
    // #ifdef H5
    import keypress from './keypress'
    // #endif
    import {
        initVueI18n
    } from '@dcloudio/uni-i18n'
    import messages from './i18n/index.js'
    const {    t    } = initVueI18n(messages)
    /**
     * 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',
        components: {
            // #ifdef H5
            keypress
            // #endif
        },
        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,
            }
        },
        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, oldVal) {
                    if (newVal) {
                        this.parseValue(this.fixIosDateFormat(newVal)) //兼容 iOS、safari æ—¥æœŸæ ¼å¼
                        this.initTime(false)
                    } else {
                        this.time = ''
                        this.parseValue(Date.now())
                    }
                },
                immediate: true
            },
            // #endif
            // #ifdef VUE3
            modelValue: {
                handler(newVal, oldVal) {
                    if (newVal) {
                        this.parseValue(this.fixIosDateFormat(newVal)) //兼容 iOS、safari æ—¥æœŸæ ¼å¼
                        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(this.fixIosDateFormat(newVal), 'start') //兼容 iOS、safari æ—¥æœŸæ ¼å¼
                },
                immediate: true
            },
            end: {
                handler(newVal) {
                    this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'end') //兼容 iOS、safari æ—¥æœŸæ ¼å¼
                },
                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) && typeof value !== NaN) {
                    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) && Number(point) !== NaN) {
                        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();
            },
            //兼容 iOS、safari æ—¥æœŸæ ¼å¼
            fixIosDateFormat(value) {
                if (typeof value === 'string') {
                    value = value.replace(/-/g, '/')
                }
                return value
            },
            /**
             * ç”Ÿæˆæ—¶é—´æˆ³
             * @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 = this.fixIosDateFormat(this.value)
                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>
src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1015 @@
<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 type="calendar" color="#c0c4cc" size="22"></uni-icons>
                        <input class="uni-date__x-input" type="text" v-model="singleVal"
                            :placeholder="singlePlaceholderText" :disabled="true" />
                    </view>
                    <view v-else class="uni-date-x uni-date-range">
                        <uni-icons type="calendar" color="#c0c4cc" size="22"></uni-icons>
                        <input class="uni-date__x-input t-c" type="text" v-model="range.startDate"
                            :placeholder="startPlaceholderText" :disabled="true" />
                        <slot>
                            <view class="">{{rangeSeparator}}</view>
                        </slot>
                        <input class="uni-date__x-input t-c" type="text" v-model="range.endDate"
                            :placeholder="endPlaceholderText" :disabled="true" />
                    </view>
                    <view v-if="showClearIcon" class="uni-date__icon-clear" @click.stop="clear">
                        <uni-icons type="clear" color="#c0c4cc" size="24"></uni-icons>
                    </view>
                </view>
            </slot>
        </view>
        <view v-show="popup" class="uni-date-mask" @click="close"></view>
        <view v-if="!isPhone" ref="datePicker" v-show="popup" class="uni-date-picker__container">
            <view v-if="!isRange" class="uni-date-single--x" :style="popover">
                <view class="uni-popper__arrow"></view>
                <view v-if="hasTime" class="uni-date-changed popup-x-header">
                    <input class="uni-date__input t-c" type="text" v-model="tempSingleDate"
                        :placeholder="selectDateText" />
                    <time-picker type="time" v-model="time" :border="false" :disabled="!tempSingleDate"
                        :start="reactStartTime" :end="reactEndTime" :hideSecond="hideSecond" style="width: 100%;">
                        <input class="uni-date__input t-c" type="text" v-model="time" :placeholder="selectTimeText"
                            :disabled="!tempSingleDate" />
                    </time-picker>
                </view>
                <calendar ref="pcSingle" :showMonth="false" :start-date="caleRange.startDate"
                    :end-date="caleRange.endDate" :date="defSingleDate" @change="singleChange"
                    style="padding: 0 8px;" />
                <view v-if="hasTime" class="popup-x-footer">
                    <!-- <text class="">此刻</text> -->
                    <text class="confirm" @click="confirmSingleChange">{{okText}}</text>
                </view>
                <view class="uni-date-popper__arrow"></view>
            </view>
            <view v-else class="uni-date-range--x" :style="popover">
                <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="reactStartTime" :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="reactEndTime" :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="caleRange.startDate"
                        :end-date="caleRange.endDate" :range="true" @change="leftChange" :pleStatus="endMultipleStatus"
                        @firstEnterCale="updateRightCale" @monthSwitch="leftMonthSwitch" style="padding: 0 8px;" />
                    <calendar ref="right" :showMonth="false" :start-date="caleRange.startDate"
                        :end-date="caleRange.endDate" :range="true" @change="rightChange"
                        :pleStatus="startMultipleStatus" @firstEnterCale="updateLeftCale"
                        @monthSwitch="rightMonthSwitch" style="padding: 0 8px;border-left: 1px solid #F1F1F1;" />
                </view>
                <view v-if="hasTime" class="popup-x-footer">
                    <text class="" @click="clear">{{clearText}}</text>
                    <text class="confirm" @click="confirmRangeChange">{{okText}}</text>
                </view>
            </view>
        </view>
        <calendar v-show="isPhone" ref="mobile" :clearDate="false" :date="defSingleDate" :defTime="reactMobDefTime"
            :start-date="caleRange.startDate" :end-date="caleRange.endDate" :selectableTimes="mobSelectableTime"
            :pleStatus="endMultipleStatus" :showMonth="false" :range="isRange" :typeHasTime="hasTime" :insert="false"
            :hideSecond="hideSecond" @confirm="mobileChange" @maskClose="close" />
    </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端适用)
     * @event {Function} change ç¡®å®šæ—¥æœŸæ—¶è§¦å‘的事件
     * @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 messages from './i18n/index.js'
    const {
        t
    } = initVueI18n(messages)
    export default {
        name: 'UniDatetimePicker',
        options: {
            virtualHost: true
        },
        components: {
            calendar,
            timePicker
        },
        inject: {
            form: {
                from: 'uniForm',
                default: null
            },
            formItem: {
                from: 'uniFormItem',
                default: null
            },
        },
        data() {
            return {
                isRange: false,
                hasTime: false,
                mobileRange: false,
                // å•选
                singleVal: '',
                tempSingleDate: '',
                defSingleDate: '',
                time: '',
                // èŒƒå›´é€‰
                caleRange: {
                    startDate: '',
                    startTime: '',
                    endDate: '',
                    endTime: ''
                },
                range: {
                    startDate: '',
                    // startTime: '',
                    endDate: '',
                    // endTime: ''
                },
                tempRange: {
                    startDate: '',
                    startTime: '',
                    endDate: '',
                    endTime: ''
                },
                // å·¦å³æ—¥åŽ†åŒæ­¥æ•°æ®
                startMultipleStatus: {
                    before: '',
                    after: '',
                    data: [],
                    fulldate: ''
                },
                endMultipleStatus: {
                    before: '',
                    after: '',
                    data: [],
                    fulldate: ''
                },
                visible: false,
                popup: false,
                popover: null,
                isEmitValue: false,
                isPhone: false,
                isFirstShow: true,
            }
        },
        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
            }
        },
        watch: {
            type: {
                immediate: true,
                handler(newVal, oldVal) {
                    if (newVal.indexOf('time') !== -1) {
                        this.hasTime = true
                    } else {
                        this.hasTime = false
                    }
                    if (newVal.indexOf('range') !== -1) {
                        this.isRange = true
                    } else {
                        this.isRange = false
                    }
                }
            },
            // #ifndef VUE3
            value: {
                immediate: true,
                handler(newVal, oldVal) {
                    if (this.isEmitValue) {
                        this.isEmitValue = false
                        return
                    }
                    this.initPicker(newVal)
                }
            },
            // #endif
            // #ifdef VUE3
            modelValue: {
                immediate: true,
                handler(newVal, oldVal) {
                    if (this.isEmitValue) {
                        this.isEmitValue = false
                        return
                    }
                    this.initPicker(newVal)
                }
            },
            // #endif
            start: {
                immediate: true,
                handler(newVal, oldVal) {
                    if (!newVal) return
                    const {
                        defDate,
                        defTime
                    } = this.parseDate(newVal)
                    this.caleRange.startDate = defDate
                    if (this.hasTime) {
                        this.caleRange.startTime = defTime
                    }
                }
            },
            end: {
                immediate: true,
                handler(newVal, oldVal) {
                    if (!newVal) return
                    const {
                        defDate,
                        defTime
                    } = this.parseDate(newVal)
                    this.caleRange.endDate = defDate
                    if (this.hasTime) {
                        this.caleRange.endTime = defTime
                    }
                }
            },
        },
        computed: {
            reactStartTime() {
                const activeDate = this.isRange ? this.tempRange.startDate : this.tempSingleDate
                const res = activeDate === this.caleRange.startDate ? this.caleRange.startTime : ''
                return res
            },
            reactEndTime() {
                const activeDate = this.isRange ? this.tempRange.endDate : this.tempSingleDate
                const res = activeDate === this.caleRange.endDate ? this.caleRange.endTime : ''
                return res
            },
            reactMobDefTime() {
                const times = {
                    start: this.tempRange.startTime,
                    end: this.tempRange.endTime
                }
                return this.isRange ? times : this.time
            },
            mobSelectableTime() {
                return {
                    start: this.caleRange.startTime,
                    end: this.caleRange.endTime
                }
            },
            datePopupWidth() {
                // todo
                return this.isRange ? 653 : 301
            },
            /**
             * for i18n
             */
            singlePlaceholderText() {
                return this.placeholder || (this.type === 'date' ? this.selectDateText : t(
                    "uni-datetime-picker.selectDateTime"))
            },
            startPlaceholderText() {
                return this.startPlaceholder || this.startDateText
            },
            endPlaceholderText() {
                return this.endPlaceholder || this.endDateText
            },
            selectDateText() {
                return t("uni-datetime-picker.selectDate")
            },
            selectTimeText() {
                return t("uni-datetime-picker.selectTime")
            },
            startDateText() {
                return this.startPlaceholder || t("uni-datetime-picker.startDate")
            },
            startTimeText() {
                return t("uni-datetime-picker.startTime")
            },
            endDateText() {
                return this.endPlaceholder || t("uni-datetime-picker.endDate")
            },
            endTimeText() {
                return t("uni-datetime-picker.endTime")
            },
            okText() {
                return t("uni-datetime-picker.ok")
            },
            clearText() {
                return t("uni-datetime-picker.clear")
            },
            showClearIcon() {
                const {
                    clearIcon,
                    disabled,
                    singleVal,
                    range
                } = this
                const bool = clearIcon && !disabled && (singleVal || (range.startDate && range.endDate))
                return bool
            }
        },
        created() {
            // if (this.form && this.formItem) {
            //     this.$watch('formItem.errMsg', (newVal) => {
            //         this.localMsg = newVal
            //     })
            // }
        },
        mounted() {
            this.platform()
        },
        methods: {
            initPicker(newVal) {
                if (!newVal || Array.isArray(newVal) && !newVal.length) {
                    this.$nextTick(() => {
                        this.clear(false)
                    })
                    return
                }
                if (!Array.isArray(newVal) && !this.isRange) {
                    const {
                        defDate,
                        defTime
                    } = this.parseDate(newVal)
                    this.singleVal = defDate
                    this.tempSingleDate = defDate
                    this.defSingleDate = defDate
                    if (this.hasTime) {
                        this.singleVal = defDate + ' ' + defTime
                        this.time = defTime
                    }
                } else {
                    const [before, after] = newVal
                    if (!before && !after) return
                    const defBefore = this.parseDate(before)
                    const defAfter = this.parseDate(after)
                    const startDate = defBefore.defDate
                    const endDate = defAfter.defDate
                    this.range.startDate = this.tempRange.startDate = startDate
                    this.range.endDate = this.tempRange.endDate = endDate
                    if (this.hasTime) {
                        this.range.startDate = defBefore.defDate + ' ' + defBefore.defTime
                        this.range.endDate = defAfter.defDate + ' ' + defAfter.defTime
                        this.tempRange.startTime = defBefore.defTime
                        this.tempRange.endTime = defAfter.defTime
                    }
                    const defaultRange = {
                        before: defBefore.defDate,
                        after: defAfter.defDate
                    }
                    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() {
                const systemInfo = uni.getSystemInfoSync()
                this.isPhone = systemInfo.windowWidth <= 500
                this.windowWidth = systemInfo.windowWidth
            },
            show(event) {
                if (this.disabled) {
                    return
                }
                this.platform()
                if (this.isPhone) {
                    this.$refs.mobile.open()
                    return
                }
                this.popover = {
                    top: '10px'
                }
                const dateEditor = uni.createSelectorQuery().in(this).select(".uni-date-editor")
                dateEditor.boundingClientRect(rect => {
                    if (this.windowWidth - rect.left < this.datePopupWidth) {
                        this.popover.right = 0
                    }
                }).exec()
                setTimeout(() => {
                    this.popup = !this.popup
                    if (!this.isPhone && this.isRange && this.isFirstShow) {
                        this.isFirstShow = false
                        const {
                            startDate,
                            endDate
                        } = this.range
                        if (startDate && endDate) {
                            if (this.diffDate(startDate, endDate) < 30) {
                                this.$refs.right.next()
                            }
                        } else {
                            this.$refs.right.next()
                            this.$refs.right.cale.lastHover = false
                        }
                    }
                }, 50)
            },
            close() {
                setTimeout(() => {
                    this.popup = false
                    this.$emit('maskClick', this.value)
                    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('change', value)
                this.$emit('input', value)
                this.$emit('update:modelValue', value)
                this.isEmitValue = true
            },
            createTimestamp(date) {
                date = this.fixIosDateFormat(date)
                return Date.parse(new Date(date))
            },
            singleChange(e) {
                this.tempSingleDate = e.fulldate
                if (this.hasTime) return
                this.confirmSingleChange()
            },
            confirmSingleChange() {
                if (!this.tempSingleDate) {
                    this.popup = false
                    return
                }
                if (this.hasTime) {
                    this.singleVal = this.tempSingleDate + ' ' + (this.time ? this.time : '00:00:00')
                } else {
                    this.singleVal = this.tempSingleDate
                }
                this.setEmit(this.singleVal)
                this.popup = 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)
            },
            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)
            },
            mobileChange(e) {
                if (this.isRange) {
                    const {
                        before,
                        after
                    } = e.range
                    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.singleVal = e.fulldate + ' ' + e.time
                    } else {
                        this.singleVal = e.fulldate
                    }
                    this.setEmit(this.singleVal)
                }
                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.popup = false
                    return
                }
                let start, end
                if (!this.hasTime) {
                    start = this.range.startDate = this.tempRange.startDate
                    end = this.range.endDate = this.tempRange.endDate
                } else {
                    start = this.range.startDate = this.tempRange.startDate + ' ' +
                        (this.tempRange.startTime ? this.tempRange.startTime : '00:00:00')
                    end = this.range.endDate = this.tempRange.endDate + ' ' +
                        (this.tempRange.endTime ? this.tempRange.endTime : '00:00:00')
                }
                const displayRange = [start, end]
                this.setEmit(displayRange)
                this.popup = false
            },
            handleStartAndEnd(before, after, temp = false) {
                if (!(before && after)) return
                const type = temp ? 'tempRange' : 'range'
                if (this.dateCompare(before, after)) {
                    this[type].startDate = before
                    this[type].endDate = after
                } else {
                    this[type].startDate = after
                    this[type].endDate = before
                }
            },
            /**
             * æ¯”较时间大小
             */
            dateCompare(startDate, endDate) {
                // è®¡ç®—截止时间
                startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
                // è®¡ç®—详细项的截止时间
                endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
                if (startDate <= endDate) {
                    return true
                } else {
                    return false
                }
            },
            /**
             * æ¯”较时间差
             */
            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.singleVal = ''
                    this.tempSingleDate = ''
                    this.time = ''
                    if (this.isPhone) {
                        this.$refs.mobile && this.$refs.mobile.clearCalender()
                    } else {
                        this.$refs.pcSingle && this.$refs.pcSingle.clearCalender()
                    }
                    if (needEmit) {
                        // æ ¡éªŒè§„则
                        // if(this.form  && this.formItem){
                        //     const {
                        //         validateTrigger
                        //     } = this.form
                        //     if (validateTrigger === 'blur') {
                        //         this.formItem.onFieldChange()
                        //     }
                        // }
                        this.$emit('change', '')
                        this.$emit('input', '')
                        this.$emit('update:modelValue', '')
                    }
                } else {
                    this.range.startDate = ''
                    this.range.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.next()
                    }
                    if (needEmit) {
                        this.$emit('change', [])
                        this.$emit('input', [])
                        this.$emit('update:modelValue', [])
                    }
                }
            },
            parseDate(date) {
                date = this.fixIosDateFormat(date)
                const defVal = new Date(date)
                const year = defVal.getFullYear()
                const month = defVal.getMonth() + 1
                const day = defVal.getDate()
                const hour = defVal.getHours()
                const minute = defVal.getMinutes()
                const second = defVal.getSeconds()
                const defDate = year + '-' + this.lessTen(month) + '-' + this.lessTen(day)
                const defTime = this.lessTen(hour) + ':' + this.lessTen(minute) + (this.hideSecond ? '' : (':' + this
                    .lessTen(second)))
                return {
                    defDate,
                    defTime
                }
            },
            lessTen(item) {
                return item < 10 ? '0' + item : item
            },
            //兼容 iOS、safari æ—¥æœŸæ ¼å¼
            fixIosDateFormat(value) {
                if (typeof value === 'string') {
                    value = value.replace(/-/g, '/')
                }
                return value
            },
            leftMonthSwitch(e) {
                // console.log('leftMonthSwitch è¿”回:', e)
            },
            rightMonthSwitch(e) {
                // console.log('rightMonthSwitch è¿”回:', e)
            }
        }
    }
</script>
<style lang="scss">
    $uni-primary: #007aff !default;
    .uni-date {
        /* #ifndef APP-NVUE */
        width: 100%;
        /* #endif */
        flex: 1;
    }
    .uni-date-x {
        display: flex;
        flex-direction: row;
        align-items: center;
        justify-content: center;
        padding: 0 10px;
        border-radius: 4px;
        background-color: #fff;
        color: #666;
        font-size: 14px;
        flex: 1;
    }
    .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: 0 5px;
        display: flex;
        align-items: center;
        /* #ifdef H5 */
        cursor: pointer;
        /* #endif */
    }
    .uni-date__x-input {
        padding: 0 8px;
        /* #ifndef APP-NVUE */
        width: auto;
        /* #endif */
        position: relative;
        overflow: hidden;
        flex: 1;
        line-height: 1;
        font-size: 14px;
        height: 35px;
    }
    .t-c {
        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;
        /*         position: fixed;
        left: 0;
        right: 0;
        top: 0;
        bottom: 0;
        box-sizing: border-box;
        z-index: 996;
        font-size: 14px; */
    }
    .uni-date-mask {
        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 {
        /* padding: 0 8px; */
        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 {
        /* padding: 0 8px; */
        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;
        /* justify-content: space-between; */
    }
    .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;
        /* background-color: #fff; */
        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 {
        margin-left: 20px;
        color: $uni-primary;
    }
    .uni-date-changed {
        /* background-color: #fff; */
        text-align: center;
        color: #333;
        border-bottom-color: #F1F1F1;
        border-bottom-style: solid;
        border-bottom-width: 1px;
        /* padding: 0 50px; */
    }
    .uni-date-changed--time text {
        /* padding: 0 20px; */
        height: 50px;
        line-height: 50px;
    }
    .uni-date-changed .uni-date-changed--time {
        /* display: flex; */
        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-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;
    }
</style>
src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,410 @@
class Calendar {
    constructor({
        date,
        selected,
        startDate,
        endDate,
        range,
        // multipleStatus
    } = {}) {
        // å½“前日期
        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)
        // this.multipleStatus = multipleStatus
        this.lastHover = false
    }
    /**
     * è®¾ç½®æ—¥æœŸ
     * @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) {
                    dd.setDate(dd.getDate() + AddDayCount)
                } else {
                    dd.setMonth(dd.getMonth() + AddDayCount) // èŽ·å–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,
                disable: true
            })
        }
        return dateArr
    }
    /**
     * èŽ·å–æœ¬æœˆå¤©æ•°
     */
    _currentMonthDys(dateData, full) {
        let dateArr = []
        let fullDate = this.date.fullDate
        for (let i = 1; i <= dateData; i++) {
            let isinfo = false
            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.isLogicBefore(nowDate, this.multipleStatus.before, this.multipleStatus.after),
                afterMultiple: this.isLogicAfter(nowDate, this.multipleStatus.before, this.multipleStatus.after),
                month: full.month,
                disable: !(disableBefore && disableAfter),
                isDay,
                userChecked: false
            }
            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,
                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
        }
    }
    /**
     *  æ¯”较真实起始日期
     */
    isLogicBefore(currentDay, before, after) {
        let logicBefore = before
        if (before && after) {
            logicBefore = this.dateCompare(before, after) ? before : after
        }
        return this.dateEqual(logicBefore, currentDay)
    }
    isLogicAfter(currentDay, before, after) {
        let logicAfter = after
        if (before && after) {
            logicAfter = this.dateCompare(before, after) ? after : before
        }
        return this.dateEqual(logicAfter, currentDay)
    }
    /**
     * èŽ·å–æ—¥æœŸèŒƒå›´å†…æ‰€æœ‰æ—¥æœŸ
     * @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
    }
    /**
     *  èŽ·å–å¤šé€‰çŠ¶æ€
     */
    setMultiple(fullDate) {
        let {
            before,
            after
        } = this.multipleStatus
        if (!this.range) return
        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.lastHover = false
            } 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.lastHover = true
            }
        }
        this._getWeek(fullDate)
    }
    /**
     *  é¼ æ ‡ hover æ›´æ–°å¤šé€‰çŠ¶æ€
     */
    setHoverMultiple(fullDate) {
        let {
            before,
            after
        } = this.multipleStatus
        if (!this.range) return
        if (this.lastHover) return
        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)
    }
    /**
     * æ›´æ–°é»˜è®¤å€¼å¤šé€‰çŠ¶æ€
     */
    setDefaultMultiple(before, after) {
        this.multipleStatus.before = before
        this.multipleStatus.after = after
        if (before && after) {
            if (this.dateCompare(before, after)) {
                this.multipleStatus.data = this.geDateAll(before, after);
                this._getWeek(after)
            } else {
                this.multipleStatus.data = this.geDateAll(after, before);
                this._getWeek(before)
            }
        }
    }
    /**
     * èŽ·å–æ¯å‘¨æ•°æ®
     * @param {Object} dateData
     */
    _getWeek(dateData) {
        const {
            fullDate,
            year,
            month,
            date,
            day
        } = 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
src/uni_modules/uni-datetime-picker/package.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
{
  "id": "uni-datetime-picker",
  "displayName": "uni-datetime-picker æ—¥æœŸé€‰æ‹©å™¨",
  "version": "2.2.11",
  "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"
      },
      "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"
        }
      }
    }
  }
}
src/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
src/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目录规范
src/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
src/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" >
    $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>
src/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"
        }
      }
    }
  }
}
src/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
src/uni_modules/uni-easyinput/changelog.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,86 @@
## 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 é¡µé¢
src/uni_modules/uni-easyinput/components/uni-easyinput/common.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
/**
 * @desc å‡½æ•°é˜²æŠ–
 * @param func ç›®æ ‡å‡½æ•°
 * @param wait å»¶è¿Ÿæ‰§è¡Œæ¯«ç§’æ•°
 * @param immediate true - ç«‹å³æ‰§è¡Œï¼Œ false - å»¶è¿Ÿæ‰§è¡Œ
 */
export const debounce = function(func, wait = 1000, immediate = true) {
    let timer;
    console.log(1);
    return function() {
        console.log(123);
        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)
            }
        }
    }
}
src/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,679 @@
<template>
    <view
        class="uni-easyinput"
        :class="{ 'uni-easyinput-error': msg }"
        :style="boxStyle"
    >
        <view
            class="uni-easyinput__content"
            :class="inputContentClass"
            :style="inputContentStyle"
        >
            <slot name="prefixIcon">
                <uni-icons
                    v-if="prefixIcon"
                    class="content-clear-icon"
                    :type="prefixIcon"
                    color="#c0c4cc"
                    @click="onClickIcon('prefix')"
                    size="22"
                ></uni-icons>
            </slot>
            <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"
                @input="onInput"
                @blur="_Blur"
                @focus="_Focus"
                @confirm="onConfirm"
            ></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"
                @focus="_Focus"
                @blur="_Blur"
                @input="onInput"
                @confirm="onConfirm"
            />
            <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-else-if="suffixIcon || $slots.suffixIcon">
                <slot name="suffixIcon">
                    <uni-icons
                        v-if="suffixIcon"
                        class="content-clear-icon"
                        :type="suffixIcon"
                        color="#c0c4cc"
                        @click="onClickIcon('suffix')"
                        size="22"
                    ></uni-icons>
                </slot>
            </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    æ˜¯å¦è‡ªåŠ¨åŽ»é™¤ä¸¤ç«¯çš„ç©ºæ ¼
 * @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    ç‚¹å‡»å›¾æ ‡æ—¶è§¦å‘
 * @slot prefixIcon è¾“入框头部插槽
 * @slot suffixIcon è¾“入框尾部插槽
 * @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",
    ],
    model: {
        prop: "modelValue",
        event: "update:modelValue",
    },
    options: {
        virtualHost: true,
    },
    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: true,
        },
        passwordIcon: {
            type: Boolean,
            default: true,
        },
        primaryColor: {
            type: String,
            default: "#2979ff",
        },
        styles: {
            type: Object,
            default() {
                return {
                    color: "#333",
                    disableColor: "#F7F6F6",
                    borderColor: "#e5e5e5",
                };
            },
        },
        errorMessage: {
            type: [String, Boolean],
            default: "",
        },
    },
    data() {
        return {
            focused: false,
            val: "",
            showMsg: "",
            border: false,
            isFirstBorder: false,
            showClearIcon: false,
            showPassword: false,
            focusShow: false,
            localMsg: "",
        };
    },
    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,
            });
        },
        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) {
            this.val = newVal;
        },
        modelValue(newVal) {
            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.val = this.modelValue;
            } else {
                this.val = null;
            }
        },
        /**
         * ç‚¹å‡»å›¾æ ‡æ—¶è§¦å‘
         * @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("focus", null);
        },
        _Blur(event) {
            let value = event.detail.value;
            this.focusShow = false;
            this.$emit("blur", event);
            // æ ¹æ®ç±»åž‹è¿”回值,在event中获取的值理论上讲都是string
            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.$emit("change", this.val);
        },
        /**
         * æ¸…理内容
         * @param {Object} event
         */
        onClear(event) {
            this.val = "";
            // TODO å…¼å®¹ vue2
            this.$emit("input", "");
            // TODO å…¼å®¹ vue2
            // TODO 兼容 vue3
            this.$emit("update:modelValue", "");
            // ç‚¹å‡»å‰å·è§¦å‘
            this.$emit("clear");
        },
        /**
         * åŽ»é™¤ç©ºæ ¼
         */
        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;
}
.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>
src/uni_modules/uni-easyinput/package.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
{
  "id": "uni-easyinput",
  "displayName": "uni-easyinput å¢žå¼ºè¾“入框",
  "version": "1.1.3",
  "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"
      },
      "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"
        }
      }
    }
  }
}
src/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
src/uni_modules/uni-fab/changelog.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
## 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端
src/uni_modules/uni-fab/components/uni-fab/uni-fab.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,490 @@
<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="plusempty" :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'
                }
            }
        },
        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() {
                const safeBottom = uni.getSystemInfoSync().windowBottom;
                // #ifdef APP-NVUE
                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>
src/uni_modules/uni-fab/package.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,84 @@
{
  "id": "uni-fab",
  "displayName": "uni-fab æ‚¬æµ®æŒ‰é’®",
  "version": "1.2.4",
  "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"
      },
      "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"
        }
      }
    }
  }
}
src/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
src/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目录规范
src/uni_modules/uni-fav/components/uni-fav/i18n/en.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,4 @@
{
    "uni-fav.collect": "collect",
    "uni-fav.collected": "collected"
}
src/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
}
src/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,4 @@
{
    "uni-fav.collect": "收藏",
    "uni-fav.collected": "已收藏"
}
src/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,4 @@
{
    "uni-fav.collect": "收藏",
    "uni-fav.collected": "已收藏"
}
src/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>
src/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"
        }
      }
    }
  }
}
src/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
src/uni_modules/uni-file-picker/changelog.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
## 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
src/uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,224 @@
'use strict';
const ERR_MSG_OK = 'chooseAndUploadFile:ok';
const ERR_MSG_FAIL = 'chooseAndUploadFile:fail';
function chooseImage(opts) {
    const {
        count,
        sizeType = ['original', 'compressed'],
        sourceType = ['album', 'camera'],
        extension
    } = opts
    return new Promise((resolve, reject) => {
        uni.chooseImage({
            count,
            sizeType,
            sourceType,
            extension,
            success(res) {
                resolve(normalizeChooseAndUploadFileRes(res, 'image'));
            },
            fail(res) {
                reject({
                    errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL),
                });
            },
        });
    });
}
function chooseVideo(opts) {
    const {
        camera,
        compressed,
        maxDuration,
        sourceType = ['album', 'camera'],
        extension
    } = opts;
    return new Promise((resolve, reject) => {
        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),
                });
            },
        });
    });
}
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
};
src/uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,656 @@
<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: {
            // #ifdef VUE3
            modelValue: {
                type: [Array, Object],
                default () {
                    return []
                }
            },
            // #endif
            // #ifndef VUE3
            value: {
                type: [Array, Object],
                default () {
                    return []
                }
            },
            // #endif
            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']
                }
            }
        },
        data() {
            return {
                files: [],
                localValue: []
            }
        },
        watch: {
            // #ifndef VUE3
            value: {
                handler(newVal, oldVal) {
                    this.setValue(newVal, oldVal)
                },
                immediate: true
            },
            // #endif
            // #ifdef VUE3
            modelValue: {
                handler(newVal, oldVal) {
                    this.setValue(newVal, oldVal)
                },
                immediate: true
            },
            // #endif
        },
        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,
                        // 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 = []
                }
            },
            /**
             * æ‰¹ä¼ 
             * @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', {
                    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
                    })
                })
                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>
src/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>
src/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>
src/uni_modules/uni-file-picker/components/uni-file-picker/utils.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,109 @@
/**
 * èŽ·å–æ–‡ä»¶åå’ŒåŽç¼€
 * @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,
        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
}
src/uni_modules/uni-file-picker/package.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,86 @@
{
  "id": "uni-file-picker",
  "displayName": "uni-file-picker æ–‡ä»¶é€‰æ‹©ä¸Šä¼ ",
  "version": "1.0.2",
  "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": "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"
        }
      }
    }
  }
}
src/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
src/uni_modules/uni-forms/changelog.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
## 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
src/uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,631 @@
<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 && !isRequired}"
                :style="{width:localLabelWidth,justifyContent: localLabelAlign}">
                <text v-if="isRequired" 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(默认65)
     * @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: {
            virtualHost: true
        },
        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的宽度 ï¼Œé»˜è®¤ 80
            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: '',
                isRequired: false,
                userRules: null,
                localLabelAlign: 'left',
                localLabelWidth: '65px',
                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()
                this.isRequired = this.required
                // å°†éœ€è¦æ ¡éªŒçš„子组件加入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))
                this.isRequired = this._isRequired()
            },
            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 ? 65 : 'auto')))
                // }
                // return '65px'
            },
            // å¤„理 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>
src/uni_modules/uni-forms/components/uni-forms/uni-forms.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,397 @@
<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 å®½åº¦ï¼Œé»˜è®¤ 65px
     * @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: {
            virtualHost: true
        },
        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 å±žæ€§');
                        formVm.setValue(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>
src/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;
    }
}
src/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
src/uni_modules/uni-forms/package.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,88 @@
{
  "id": "uni-forms",
  "displayName": "uni-forms è¡¨å•",
  "version": "1.4.8",
  "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"
      },
      "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"
        }
      }
    }
  }
}
src/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
src/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目录规范
src/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"
}
src/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
}
src/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": "立即购买"
}
src/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": "立即購買"
}
src/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>
src/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"
        }
      }
    }
  }
}
src/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
src/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目录规范
src/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>
src/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>
src/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"
        }
      }
    }
  }
}
src/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
src/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 é¡µé¢
src/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>
src/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"
        }
      }
    }
  }
}
src/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
src/uni_modules/uni-icons/changelog.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
## 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目录规范
src/uni_modules/uni-icons/components/uni-icons/icons.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1169 @@
export default {
  "id": "2852637",
  "name": "uniui图标库",
  "font_family": "uniicons",
  "css_prefix_text": "uniui-",
  "description": "",
  "glyphs": [
    {
      "icon_id": "25027049",
      "name": "yanse",
      "font_class": "color",
      "unicode": "e6cf",
      "unicode_decimal": 59087
    },
    {
      "icon_id": "25027048",
      "name": "wallet",
      "font_class": "wallet",
      "unicode": "e6b1",
      "unicode_decimal": 59057
    },
    {
      "icon_id": "25015720",
      "name": "settings-filled",
      "font_class": "settings-filled",
      "unicode": "e6ce",
      "unicode_decimal": 59086
    },
    {
      "icon_id": "25015434",
      "name": "shimingrenzheng-filled",
      "font_class": "auth-filled",
      "unicode": "e6cc",
      "unicode_decimal": 59084
    },
    {
      "icon_id": "24934246",
      "name": "shop-filled",
      "font_class": "shop-filled",
      "unicode": "e6cd",
      "unicode_decimal": 59085
    },
    {
      "icon_id": "24934159",
      "name": "staff-filled-01",
      "font_class": "staff-filled",
      "unicode": "e6cb",
      "unicode_decimal": 59083
    },
    {
      "icon_id": "24932461",
      "name": "VIP-filled",
      "font_class": "vip-filled",
      "unicode": "e6c6",
      "unicode_decimal": 59078
    },
    {
      "icon_id": "24932462",
      "name": "plus_circle_fill",
      "font_class": "plus-filled",
      "unicode": "e6c7",
      "unicode_decimal": 59079
    },
    {
      "icon_id": "24932463",
      "name": "folder_add-filled",
      "font_class": "folder-add-filled",
      "unicode": "e6c8",
      "unicode_decimal": 59080
    },
    {
      "icon_id": "24932464",
      "name": "yanse-filled",
      "font_class": "color-filled",
      "unicode": "e6c9",
      "unicode_decimal": 59081
    },
    {
      "icon_id": "24932465",
      "name": "tune-filled",
      "font_class": "tune-filled",
      "unicode": "e6ca",
      "unicode_decimal": 59082
    },
    {
      "icon_id": "24932455",
      "name": "a-rilidaka-filled",
      "font_class": "calendar-filled",
      "unicode": "e6c0",
      "unicode_decimal": 59072
    },
    {
      "icon_id": "24932456",
      "name": "notification-filled",
      "font_class": "notification-filled",
      "unicode": "e6c1",
      "unicode_decimal": 59073
    },
    {
      "icon_id": "24932457",
      "name": "wallet-filled",
      "font_class": "wallet-filled",
      "unicode": "e6c2",
      "unicode_decimal": 59074
    },
    {
      "icon_id": "24932458",
      "name": "paihangbang-filled",
      "font_class": "medal-filled",
      "unicode": "e6c3",
      "unicode_decimal": 59075
    },
    {
      "icon_id": "24932459",
      "name": "gift-filled",
      "font_class": "gift-filled",
      "unicode": "e6c4",
      "unicode_decimal": 59076
    },
    {
      "icon_id": "24932460",
      "name": "fire-filled",
      "font_class": "fire-filled",
      "unicode": "e6c5",
      "unicode_decimal": 59077
    },
    {
      "icon_id": "24928001",
      "name": "refreshempty",
      "font_class": "refreshempty",
      "unicode": "e6bf",
      "unicode_decimal": 59071
    },
    {
      "icon_id": "24926853",
      "name": "location-ellipse",
      "font_class": "location-filled",
      "unicode": "e6af",
      "unicode_decimal": 59055
    },
    {
      "icon_id": "24926735",
      "name": "person-filled",
      "font_class": "person-filled",
      "unicode": "e69d",
      "unicode_decimal": 59037
    },
    {
      "icon_id": "24926703",
      "name": "personadd-filled",
      "font_class": "personadd-filled",
      "unicode": "e698",
      "unicode_decimal": 59032
    },
    {
      "icon_id": "24923351",
      "name": "back",
      "font_class": "back",
      "unicode": "e6b9",
      "unicode_decimal": 59065
    },
    {
      "icon_id": "24923352",
      "name": "forward",
      "font_class": "forward",
      "unicode": "e6ba",
      "unicode_decimal": 59066
    },
    {
      "icon_id": "24923353",
      "name": "arrowthinright",
      "font_class": "arrow-right",
      "unicode": "e6bb",
      "unicode_decimal": 59067
    },
        {
          "icon_id": "24923353",
          "name": "arrowthinright",
          "font_class": "arrowthinright",
          "unicode": "e6bb",
          "unicode_decimal": 59067
        },
    {
      "icon_id": "24923354",
      "name": "arrowthinleft",
      "font_class": "arrow-left",
      "unicode": "e6bc",
      "unicode_decimal": 59068
    },
        {
          "icon_id": "24923354",
          "name": "arrowthinleft",
          "font_class": "arrowthinleft",
          "unicode": "e6bc",
          "unicode_decimal": 59068
        },
    {
      "icon_id": "24923355",
      "name": "arrowthinup",
      "font_class": "arrow-up",
      "unicode": "e6bd",
      "unicode_decimal": 59069
    },
        {
          "icon_id": "24923355",
          "name": "arrowthinup",
          "font_class": "arrowthinup",
          "unicode": "e6bd",
          "unicode_decimal": 59069
        },
    {
      "icon_id": "24923356",
      "name": "arrowthindown",
      "font_class": "arrow-down",
      "unicode": "e6be",
      "unicode_decimal": 59070
    },{
      "icon_id": "24923356",
      "name": "arrowthindown",
      "font_class": "arrowthindown",
      "unicode": "e6be",
      "unicode_decimal": 59070
    },
    {
      "icon_id": "24923349",
      "name": "arrowdown",
      "font_class": "bottom",
      "unicode": "e6b8",
      "unicode_decimal": 59064
    },{
      "icon_id": "24923349",
      "name": "arrowdown",
      "font_class": "arrowdown",
      "unicode": "e6b8",
      "unicode_decimal": 59064
    },
    {
      "icon_id": "24923346",
      "name": "arrowright",
      "font_class": "right",
      "unicode": "e6b5",
      "unicode_decimal": 59061
    },
        {
          "icon_id": "24923346",
          "name": "arrowright",
          "font_class": "arrowright",
          "unicode": "e6b5",
          "unicode_decimal": 59061
        },
    {
      "icon_id": "24923347",
      "name": "arrowup",
      "font_class": "top",
      "unicode": "e6b6",
      "unicode_decimal": 59062
    },
        {
          "icon_id": "24923347",
          "name": "arrowup",
          "font_class": "arrowup",
          "unicode": "e6b6",
          "unicode_decimal": 59062
        },
    {
      "icon_id": "24923348",
      "name": "arrowleft",
      "font_class": "left",
      "unicode": "e6b7",
      "unicode_decimal": 59063
    },
        {
          "icon_id": "24923348",
          "name": "arrowleft",
          "font_class": "arrowleft",
          "unicode": "e6b7",
          "unicode_decimal": 59063
        },
    {
      "icon_id": "24923334",
      "name": "eye",
      "font_class": "eye",
      "unicode": "e651",
      "unicode_decimal": 58961
    },
    {
      "icon_id": "24923335",
      "name": "eye-filled",
      "font_class": "eye-filled",
      "unicode": "e66a",
      "unicode_decimal": 58986
    },
    {
      "icon_id": "24923336",
      "name": "eye-slash",
      "font_class": "eye-slash",
      "unicode": "e6b3",
      "unicode_decimal": 59059
    },
    {
      "icon_id": "24923337",
      "name": "eye-slash-filled",
      "font_class": "eye-slash-filled",
      "unicode": "e6b4",
      "unicode_decimal": 59060
    },
    {
      "icon_id": "24923305",
      "name": "info-filled",
      "font_class": "info-filled",
      "unicode": "e649",
      "unicode_decimal": 58953
    },
    {
      "icon_id": "24923299",
      "name": "reload-01",
      "font_class": "reload",
      "unicode": "e6b2",
      "unicode_decimal": 59058
    },
    {
      "icon_id": "24923195",
      "name": "mic_slash_fill",
      "font_class": "micoff-filled",
      "unicode": "e6b0",
      "unicode_decimal": 59056
    },
    {
      "icon_id": "24923165",
      "name": "map-pin-ellipse",
      "font_class": "map-pin-ellipse",
      "unicode": "e6ac",
      "unicode_decimal": 59052
    },
    {
      "icon_id": "24923166",
      "name": "map-pin",
      "font_class": "map-pin",
      "unicode": "e6ad",
      "unicode_decimal": 59053
    },
    {
      "icon_id": "24923167",
      "name": "location",
      "font_class": "location",
      "unicode": "e6ae",
      "unicode_decimal": 59054
    },
    {
      "icon_id": "24923064",
      "name": "starhalf",
      "font_class": "starhalf",
      "unicode": "e683",
      "unicode_decimal": 59011
    },
    {
      "icon_id": "24923065",
      "name": "star",
      "font_class": "star",
      "unicode": "e688",
      "unicode_decimal": 59016
    },
    {
      "icon_id": "24923066",
      "name": "star-filled",
      "font_class": "star-filled",
      "unicode": "e68f",
      "unicode_decimal": 59023
    },
    {
      "icon_id": "24899646",
      "name": "a-rilidaka",
      "font_class": "calendar",
      "unicode": "e6a0",
      "unicode_decimal": 59040
    },
    {
      "icon_id": "24899647",
      "name": "fire",
      "font_class": "fire",
      "unicode": "e6a1",
      "unicode_decimal": 59041
    },
    {
      "icon_id": "24899648",
      "name": "paihangbang",
      "font_class": "medal",
      "unicode": "e6a2",
      "unicode_decimal": 59042
    },
    {
      "icon_id": "24899649",
      "name": "font",
      "font_class": "font",
      "unicode": "e6a3",
      "unicode_decimal": 59043
    },
    {
      "icon_id": "24899650",
      "name": "gift",
      "font_class": "gift",
      "unicode": "e6a4",
      "unicode_decimal": 59044
    },
    {
      "icon_id": "24899651",
      "name": "link",
      "font_class": "link",
      "unicode": "e6a5",
      "unicode_decimal": 59045
    },
    {
      "icon_id": "24899652",
      "name": "notification",
      "font_class": "notification",
      "unicode": "e6a6",
      "unicode_decimal": 59046
    },
    {
      "icon_id": "24899653",
      "name": "staff",
      "font_class": "staff",
      "unicode": "e6a7",
      "unicode_decimal": 59047
    },
    {
      "icon_id": "24899654",
      "name": "VIP",
      "font_class": "vip",
      "unicode": "e6a8",
      "unicode_decimal": 59048
    },
    {
      "icon_id": "24899655",
      "name": "folder_add",
      "font_class": "folder-add",
      "unicode": "e6a9",
      "unicode_decimal": 59049
    },
    {
      "icon_id": "24899656",
      "name": "tune",
      "font_class": "tune",
      "unicode": "e6aa",
      "unicode_decimal": 59050
    },
    {
      "icon_id": "24899657",
      "name": "shimingrenzheng",
      "font_class": "auth",
      "unicode": "e6ab",
      "unicode_decimal": 59051
    },
    {
      "icon_id": "24899565",
      "name": "person",
      "font_class": "person",
      "unicode": "e699",
      "unicode_decimal": 59033
    },
    {
      "icon_id": "24899566",
      "name": "email-filled",
      "font_class": "email-filled",
      "unicode": "e69a",
      "unicode_decimal": 59034
    },
    {
      "icon_id": "24899567",
      "name": "phone-filled",
      "font_class": "phone-filled",
      "unicode": "e69b",
      "unicode_decimal": 59035
    },
    {
      "icon_id": "24899568",
      "name": "phone",
      "font_class": "phone",
      "unicode": "e69c",
      "unicode_decimal": 59036
    },
    {
      "icon_id": "24899570",
      "name": "email",
      "font_class": "email",
      "unicode": "e69e",
      "unicode_decimal": 59038
    },
    {
      "icon_id": "24899571",
      "name": "personadd",
      "font_class": "personadd",
      "unicode": "e69f",
      "unicode_decimal": 59039
    },
    {
      "icon_id": "24899558",
      "name": "chatboxes-filled",
      "font_class": "chatboxes-filled",
      "unicode": "e692",
      "unicode_decimal": 59026
    },
    {
      "icon_id": "24899559",
      "name": "contact",
      "font_class": "contact",
      "unicode": "e693",
      "unicode_decimal": 59027
    },
    {
      "icon_id": "24899560",
      "name": "chatbubble-filled",
      "font_class": "chatbubble-filled",
      "unicode": "e694",
      "unicode_decimal": 59028
    },
    {
      "icon_id": "24899561",
      "name": "contact-filled",
      "font_class": "contact-filled",
      "unicode": "e695",
      "unicode_decimal": 59029
    },
    {
      "icon_id": "24899562",
      "name": "chatboxes",
      "font_class": "chatboxes",
      "unicode": "e696",
      "unicode_decimal": 59030
    },
    {
      "icon_id": "24899563",
      "name": "chatbubble",
      "font_class": "chatbubble",
      "unicode": "e697",
      "unicode_decimal": 59031
    },
    {
      "icon_id": "24881290",
      "name": "upload-filled",
      "font_class": "upload-filled",
      "unicode": "e68e",
      "unicode_decimal": 59022
    },
    {
      "icon_id": "24881292",
      "name": "upload",
      "font_class": "upload",
      "unicode": "e690",
      "unicode_decimal": 59024
    },
    {
      "icon_id": "24881293",
      "name": "weixin",
      "font_class": "weixin",
      "unicode": "e691",
      "unicode_decimal": 59025
    },
    {
      "icon_id": "24881274",
      "name": "compose",
      "font_class": "compose",
      "unicode": "e67f",
      "unicode_decimal": 59007
    },
    {
      "icon_id": "24881275",
      "name": "qq",
      "font_class": "qq",
      "unicode": "e680",
      "unicode_decimal": 59008
    },
    {
      "icon_id": "24881276",
      "name": "download-filled",
      "font_class": "download-filled",
      "unicode": "e681",
      "unicode_decimal": 59009
    },
    {
      "icon_id": "24881277",
      "name": "pengyouquan",
      "font_class": "pyq",
      "unicode": "e682",
      "unicode_decimal": 59010
    },
    {
      "icon_id": "24881279",
      "name": "sound",
      "font_class": "sound",
      "unicode": "e684",
      "unicode_decimal": 59012
    },
    {
      "icon_id": "24881280",
      "name": "trash-filled",
      "font_class": "trash-filled",
      "unicode": "e685",
      "unicode_decimal": 59013
    },
    {
      "icon_id": "24881281",
      "name": "sound-filled",
      "font_class": "sound-filled",
      "unicode": "e686",
      "unicode_decimal": 59014
    },
    {
      "icon_id": "24881282",
      "name": "trash",
      "font_class": "trash",
      "unicode": "e687",
      "unicode_decimal": 59015
    },
    {
      "icon_id": "24881284",
      "name": "videocam-filled",
      "font_class": "videocam-filled",
      "unicode": "e689",
      "unicode_decimal": 59017
    },
    {
      "icon_id": "24881285",
      "name": "spinner-cycle",
      "font_class": "spinner-cycle",
      "unicode": "e68a",
      "unicode_decimal": 59018
    },
    {
      "icon_id": "24881286",
      "name": "weibo",
      "font_class": "weibo",
      "unicode": "e68b",
      "unicode_decimal": 59019
    },
    {
      "icon_id": "24881288",
      "name": "videocam",
      "font_class": "videocam",
      "unicode": "e68c",
      "unicode_decimal": 59020
    },
    {
      "icon_id": "24881289",
      "name": "download",
      "font_class": "download",
      "unicode": "e68d",
      "unicode_decimal": 59021
    },
    {
      "icon_id": "24879601",
      "name": "help",
      "font_class": "help",
      "unicode": "e679",
      "unicode_decimal": 59001
    },
    {
      "icon_id": "24879602",
      "name": "navigate-filled",
      "font_class": "navigate-filled",
      "unicode": "e67a",
      "unicode_decimal": 59002
    },
    {
      "icon_id": "24879603",
      "name": "plusempty",
      "font_class": "plusempty",
      "unicode": "e67b",
      "unicode_decimal": 59003
    },
    {
      "icon_id": "24879604",
      "name": "smallcircle",
      "font_class": "smallcircle",
      "unicode": "e67c",
      "unicode_decimal": 59004
    },
    {
      "icon_id": "24879605",
      "name": "minus-filled",
      "font_class": "minus-filled",
      "unicode": "e67d",
      "unicode_decimal": 59005
    },
    {
      "icon_id": "24879606",
      "name": "micoff",
      "font_class": "micoff",
      "unicode": "e67e",
      "unicode_decimal": 59006
    },
    {
      "icon_id": "24879588",
      "name": "closeempty",
      "font_class": "closeempty",
      "unicode": "e66c",
      "unicode_decimal": 58988
    },
    {
      "icon_id": "24879589",
      "name": "clear",
      "font_class": "clear",
      "unicode": "e66d",
      "unicode_decimal": 58989
    },
    {
      "icon_id": "24879590",
      "name": "navigate",
      "font_class": "navigate",
      "unicode": "e66e",
      "unicode_decimal": 58990
    },
    {
      "icon_id": "24879591",
      "name": "minus",
      "font_class": "minus",
      "unicode": "e66f",
      "unicode_decimal": 58991
    },
    {
      "icon_id": "24879592",
      "name": "image",
      "font_class": "image",
      "unicode": "e670",
      "unicode_decimal": 58992
    },
    {
      "icon_id": "24879593",
      "name": "mic",
      "font_class": "mic",
      "unicode": "e671",
      "unicode_decimal": 58993
    },
    {
      "icon_id": "24879594",
      "name": "paperplane",
      "font_class": "paperplane",
      "unicode": "e672",
      "unicode_decimal": 58994
    },
    {
      "icon_id": "24879595",
      "name": "close",
      "font_class": "close",
      "unicode": "e673",
      "unicode_decimal": 58995
    },
    {
      "icon_id": "24879596",
      "name": "help-filled",
      "font_class": "help-filled",
      "unicode": "e674",
      "unicode_decimal": 58996
    },
    {
      "icon_id": "24879597",
      "name": "plus-filled",
      "font_class": "paperplane-filled",
      "unicode": "e675",
      "unicode_decimal": 58997
    },
    {
      "icon_id": "24879598",
      "name": "plus",
      "font_class": "plus",
      "unicode": "e676",
      "unicode_decimal": 58998
    },
    {
      "icon_id": "24879599",
      "name": "mic-filled",
      "font_class": "mic-filled",
      "unicode": "e677",
      "unicode_decimal": 58999
    },
    {
      "icon_id": "24879600",
      "name": "image-filled",
      "font_class": "image-filled",
      "unicode": "e678",
      "unicode_decimal": 59000
    },
    {
      "icon_id": "24855900",
      "name": "locked-filled",
      "font_class": "locked-filled",
      "unicode": "e668",
      "unicode_decimal": 58984
    },
    {
      "icon_id": "24855901",
      "name": "info",
      "font_class": "info",
      "unicode": "e669",
      "unicode_decimal": 58985
    },
    {
      "icon_id": "24855903",
      "name": "locked",
      "font_class": "locked",
      "unicode": "e66b",
      "unicode_decimal": 58987
    },
    {
      "icon_id": "24855884",
      "name": "camera-filled",
      "font_class": "camera-filled",
      "unicode": "e658",
      "unicode_decimal": 58968
    },
    {
      "icon_id": "24855885",
      "name": "chat-filled",
      "font_class": "chat-filled",
      "unicode": "e659",
      "unicode_decimal": 58969
    },
    {
      "icon_id": "24855886",
      "name": "camera",
      "font_class": "camera",
      "unicode": "e65a",
      "unicode_decimal": 58970
    },
    {
      "icon_id": "24855887",
      "name": "circle",
      "font_class": "circle",
      "unicode": "e65b",
      "unicode_decimal": 58971
    },
    {
      "icon_id": "24855888",
      "name": "checkmarkempty",
      "font_class": "checkmarkempty",
      "unicode": "e65c",
      "unicode_decimal": 58972
    },
    {
      "icon_id": "24855889",
      "name": "chat",
      "font_class": "chat",
      "unicode": "e65d",
      "unicode_decimal": 58973
    },
    {
      "icon_id": "24855890",
      "name": "circle-filled",
      "font_class": "circle-filled",
      "unicode": "e65e",
      "unicode_decimal": 58974
    },
    {
      "icon_id": "24855891",
      "name": "flag",
      "font_class": "flag",
      "unicode": "e65f",
      "unicode_decimal": 58975
    },
    {
      "icon_id": "24855892",
      "name": "flag-filled",
      "font_class": "flag-filled",
      "unicode": "e660",
      "unicode_decimal": 58976
    },
    {
      "icon_id": "24855893",
      "name": "gear-filled",
      "font_class": "gear-filled",
      "unicode": "e661",
      "unicode_decimal": 58977
    },
    {
      "icon_id": "24855894",
      "name": "home",
      "font_class": "home",
      "unicode": "e662",
      "unicode_decimal": 58978
    },
    {
      "icon_id": "24855895",
      "name": "home-filled",
      "font_class": "home-filled",
      "unicode": "e663",
      "unicode_decimal": 58979
    },
    {
      "icon_id": "24855896",
      "name": "gear",
      "font_class": "gear",
      "unicode": "e664",
      "unicode_decimal": 58980
    },
    {
      "icon_id": "24855897",
      "name": "smallcircle-filled",
      "font_class": "smallcircle-filled",
      "unicode": "e665",
      "unicode_decimal": 58981
    },
    {
      "icon_id": "24855898",
      "name": "map-filled",
      "font_class": "map-filled",
      "unicode": "e666",
      "unicode_decimal": 58982
    },
    {
      "icon_id": "24855899",
      "name": "map",
      "font_class": "map",
      "unicode": "e667",
      "unicode_decimal": 58983
    },
    {
      "icon_id": "24855825",
      "name": "refresh-filled",
      "font_class": "refresh-filled",
      "unicode": "e656",
      "unicode_decimal": 58966
    },
    {
      "icon_id": "24855826",
      "name": "refresh",
      "font_class": "refresh",
      "unicode": "e657",
      "unicode_decimal": 58967
    },
    {
      "icon_id": "24855808",
      "name": "cloud-upload",
      "font_class": "cloud-upload",
      "unicode": "e645",
      "unicode_decimal": 58949
    },
    {
      "icon_id": "24855809",
      "name": "cloud-download-filled",
      "font_class": "cloud-download-filled",
      "unicode": "e646",
      "unicode_decimal": 58950
    },
    {
      "icon_id": "24855810",
      "name": "cloud-download",
      "font_class": "cloud-download",
      "unicode": "e647",
      "unicode_decimal": 58951
    },
    {
      "icon_id": "24855811",
      "name": "cloud-upload-filled",
      "font_class": "cloud-upload-filled",
      "unicode": "e648",
      "unicode_decimal": 58952
    },
    {
      "icon_id": "24855813",
      "name": "redo",
      "font_class": "redo",
      "unicode": "e64a",
      "unicode_decimal": 58954
    },
    {
      "icon_id": "24855814",
      "name": "images-filled",
      "font_class": "images-filled",
      "unicode": "e64b",
      "unicode_decimal": 58955
    },
    {
      "icon_id": "24855815",
      "name": "undo-filled",
      "font_class": "undo-filled",
      "unicode": "e64c",
      "unicode_decimal": 58956
    },
    {
      "icon_id": "24855816",
      "name": "more",
      "font_class": "more",
      "unicode": "e64d",
      "unicode_decimal": 58957
    },
    {
      "icon_id": "24855817",
      "name": "more-filled",
      "font_class": "more-filled",
      "unicode": "e64e",
      "unicode_decimal": 58958
    },
    {
      "icon_id": "24855818",
      "name": "undo",
      "font_class": "undo",
      "unicode": "e64f",
      "unicode_decimal": 58959
    },
    {
      "icon_id": "24855819",
      "name": "images",
      "font_class": "images",
      "unicode": "e650",
      "unicode_decimal": 58960
    },
    {
      "icon_id": "24855821",
      "name": "paperclip",
      "font_class": "paperclip",
      "unicode": "e652",
      "unicode_decimal": 58962
    },
    {
      "icon_id": "24855822",
      "name": "settings",
      "font_class": "settings",
      "unicode": "e653",
      "unicode_decimal": 58963
    },
    {
      "icon_id": "24855823",
      "name": "search",
      "font_class": "search",
      "unicode": "e654",
      "unicode_decimal": 58964
    },
    {
      "icon_id": "24855824",
      "name": "redo-filled",
      "font_class": "redo-filled",
      "unicode": "e655",
      "unicode_decimal": 58965
    },
    {
      "icon_id": "24841702",
      "name": "list",
      "font_class": "list",
      "unicode": "e644",
      "unicode_decimal": 58948
    },
    {
      "icon_id": "24841489",
      "name": "mail-open-filled",
      "font_class": "mail-open-filled",
      "unicode": "e63a",
      "unicode_decimal": 58938
    },
    {
      "icon_id": "24841491",
      "name": "hand-thumbsdown-filled",
      "font_class": "hand-down-filled",
      "unicode": "e63c",
      "unicode_decimal": 58940
    },
    {
      "icon_id": "24841492",
      "name": "hand-thumbsdown",
      "font_class": "hand-down",
      "unicode": "e63d",
      "unicode_decimal": 58941
    },
    {
      "icon_id": "24841493",
      "name": "hand-thumbsup-filled",
      "font_class": "hand-up-filled",
      "unicode": "e63e",
      "unicode_decimal": 58942
    },
    {
      "icon_id": "24841494",
      "name": "hand-thumbsup",
      "font_class": "hand-up",
      "unicode": "e63f",
      "unicode_decimal": 58943
    },
    {
      "icon_id": "24841496",
      "name": "heart-filled",
      "font_class": "heart-filled",
      "unicode": "e641",
      "unicode_decimal": 58945
    },
    {
      "icon_id": "24841498",
      "name": "mail-open",
      "font_class": "mail-open",
      "unicode": "e643",
      "unicode_decimal": 58947
    },
    {
      "icon_id": "24841488",
      "name": "heart",
      "font_class": "heart",
      "unicode": "e639",
      "unicode_decimal": 58937
    },
    {
      "icon_id": "24839963",
      "name": "loop",
      "font_class": "loop",
      "unicode": "e633",
      "unicode_decimal": 58931
    },
    {
      "icon_id": "24839866",
      "name": "pulldown",
      "font_class": "pulldown",
      "unicode": "e632",
      "unicode_decimal": 58930
    },
    {
      "icon_id": "24813798",
      "name": "scan",
      "font_class": "scan",
      "unicode": "e62a",
      "unicode_decimal": 58922
    },
    {
      "icon_id": "24813786",
      "name": "bars",
      "font_class": "bars",
      "unicode": "e627",
      "unicode_decimal": 58919
    },
    {
      "icon_id": "24813788",
      "name": "cart-filled",
      "font_class": "cart-filled",
      "unicode": "e629",
      "unicode_decimal": 58921
    },
    {
      "icon_id": "24813790",
      "name": "checkbox",
      "font_class": "checkbox",
      "unicode": "e62b",
      "unicode_decimal": 58923
    },
    {
      "icon_id": "24813791",
      "name": "checkbox-filled",
      "font_class": "checkbox-filled",
      "unicode": "e62c",
      "unicode_decimal": 58924
    },
    {
      "icon_id": "24813794",
      "name": "shop",
      "font_class": "shop",
      "unicode": "e62f",
      "unicode_decimal": 58927
    },
    {
      "icon_id": "24813795",
      "name": "headphones",
      "font_class": "headphones",
      "unicode": "e630",
      "unicode_decimal": 58928
    },
    {
      "icon_id": "24813796",
      "name": "cart",
      "font_class": "cart",
      "unicode": "e631",
      "unicode_decimal": 58929
    }
  ]
}
src/uni_modules/uni-icons/components/uni-icons/uni-icons.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,96 @@
<template>
    <!-- #ifdef APP-NVUE -->
    <text :style="{ color: color, 'font-size': iconSize }" class="uni-icons" @click="_onClick">{{unicode}}</text>
    <!-- #endif -->
    <!-- #ifndef APP-NVUE -->
    <text :style="{ color: color, 'font-size': iconSize }" class="uni-icons" :class="['uniui-'+type,customPrefix,customPrefix?type:'']" @click="_onClick"></text>
    <!-- #endif -->
</template>
<script>
    import icons from './icons.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: ''
            }
        },
        data() {
            return {
                icons: icons.glyphs
            }
        },
        computed:{
            unicode(){
                let code = this.icons.find(v=>v.font_class === this.type)
                if(code){
                    return unescape(`%u${code.unicode}`)
                }
                return ''
            },
            iconSize(){
                return getVal(this.size)
            }
        },
        methods: {
            _onClick() {
                this.$emit('click')
            }
        }
    }
</script>
<style lang="scss">
    /* #ifndef APP-NVUE */
    @import './uniicons.css';
    @font-face {
        font-family: uniicons;
        src: url('./uniicons.ttf') format('truetype');
    }
    /* #endif */
    .uni-icons {
        font-family: uniicons;
        text-decoration: none;
        text-align: center;
    }
</style>
src/uni_modules/uni-icons/components/uni-icons/uniicons.css
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,663 @@
.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-gift-filled:before {
  content: "\e6c4";
}
.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-back:before {
  content: "\e6b9";
}
.uniui-forward:before {
  content: "\e6ba";
}
.uniui-arrow-right:before {
  content: "\e6bb";
}
.uniui-arrowthinright:before {
  content: "\e6bb";
}
.uniui-arrow-left:before {
  content: "\e6bc";
}
.uniui-arrowthinleft:before {
  content: "\e6bc";
}
.uniui-arrow-up:before {
  content: "\e6bd";
}
.uniui-arrowthinup:before {
  content: "\e6bd";
}
.uniui-arrow-down:before {
  content: "\e6be";
}
.uniui-arrowthindown:before {
  content: "\e6be";
}
.uniui-bottom:before {
  content: "\e6b8";
}
.uniui-arrowdown:before {
  content: "\e6b8";
}
.uniui-right:before {
  content: "\e6b5";
}
.uniui-arrowright:before {
  content: "\e6b5";
}
.uniui-top:before {
  content: "\e6b6";
}
.uniui-arrowup:before {
  content: "\e6b6";
}
.uniui-left:before {
  content: "\e6b7";
}
.uniui-arrowleft:before {
  content: "\e6b7";
}
.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-cart-filled:before {
  content: "\e629";
}
.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";
}
src/uni_modules/uni-icons/components/uni-icons/uniicons.ttf
Binary files differ
src/uni_modules/uni-icons/package.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,86 @@
{
  "id": "uni-icons",
  "displayName": "uni-icons å›¾æ ‡",
  "version": "1.3.5",
  "description": "图标组件,用于展示移动端常见的图标,可自定义颜色、大小。",
  "keywords": [
    "uni-ui",
    "uniui",
    "icon",
    "图标"
],
  "repository": "https://github.com/dcloudio/uni-ui",
  "engines": {
    "HBuilderX": "^3.2.14"
  },
  "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"
        }
      }
    }
  }
}
src/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
src/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 ç«¯
src/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" >
    .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>
src/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) => {
                    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" >
    .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>
src/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"
        }
      }
    }
  }
}
src/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
src/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目录规范
src/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>
src/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"
        }
      }
    }
  }
}
src/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
src/uni_modules/uni-list/changelog.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
## 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 è§’标显示不正常的问题
src/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>
src/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;
src/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,538 @@
<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="avatar" 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>
                <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>
                        <text class="uni-list-chat__content-note uni-ellipsis">{{ note }}</text>
                    </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: {
            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';
                }
            }
        },
        data() {
            return {
                isFirstChild: false,
                border: true,
                // avatarList: 3,
                imageWidth: 50
            };
        },
        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) {
                uni[api]({
                    url: this.to,
                    success: res => {
                        this.$emit('click', {
                            data: res
                        });
                    },
                    fail: err => {
                        this.$emit('click', {
                            data: err
                        });
                        console.error(err.errMsg);
                    }
                });
            }
        }
    };
</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;
    }
    .uni-list-chat__content-note {
        margin-top: 3px;
        color: $note-color;
        font-size: $note-size;
        font-weight: $title-weight;
        overflow: hidden;
    }
    .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>
src/uni_modules/uni-list/components/uni-list-item/uni-list-item.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,454 @@
<template>
    <!-- #ifdef APP-NVUE -->
    <cell>
        <!-- #endif -->
        <view :class="{ 'uni-list-item--disabled': disabled }"
            :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' }">
                <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 :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
                    };
                }
            },
            border: {
                type: Boolean,
                default: true
            }
        },
        // inject: ['list'],
        data() {
            return {
                isFirstChild: false
            };
        },
        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;
    }
    .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>
src/uni_modules/uni-list/components/uni-list/uni-list.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,108 @@
<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 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: {
        enableBackToTop: {
            type: [Boolean, String],
            default: false
        },
        scrollY: {
            type: [Boolean, String],
            default: false
        },
        border: {
            type: Boolean,
            default: true
        }
    },
    // provide() {
    //     return {
    //         list: this
    //     };
    // },
    created() {
        this.firstChildAppend = false;
    },
    methods: {
        loadMore(e) {
            this.$emit('scrolltolower');
        }
    }
};
</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>
src/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>
src/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
}
src/uni_modules/uni-list/package.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,91 @@
{
  "id": "uni-list",
  "displayName": "uni-list åˆ—表",
  "version": "1.2.1",
  "description": "List ç»„ä»¶ ï¼Œå¸®åŠ©ä½¿ç”¨è€…å¿«é€Ÿæž„å»ºåˆ—è¡¨ã€‚",
  "keywords": [
    "",
    "uni-ui",
    "uniui",
    "列表",
    "",
    "list"
],
  "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-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"
        }
      }
    }
  }
}
src/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)
src/uni_modules/uni-load-more/changelog.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
## 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目录规范
src/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"
}
src/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
}
src/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": "没有更多数据了"
}
src/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": "沒有更多數據了"
}
src/uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,399 @@
<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(() => {
        platform = uni.getSystemInfoSync().platform
    }, 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>
src/uni_modules/uni-load-more/package.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,86 @@
{
  "id": "uni-load-more",
  "displayName": "uni-load-more åŠ è½½æ›´å¤š",
  "version": "1.3.3",
  "description": "LoadMore ç»„件,常用在列表里面,做滚动加载使用。",
  "keywords": [
    "uni-ui",
    "uniui",
    "加载更多",
    "load-more"
],
  "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"
        }
      }
    }
  }
}
在上述文件截断后对比
src/uni_modules/uni-load-more/readme.md src/uni_modules/uni-nav-bar/changelog.md src/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue src/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue src/uni_modules/uni-nav-bar/package.json src/uni_modules/uni-nav-bar/readme.md src/uni_modules/uni-notice-bar/changelog.md src/uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue src/uni_modules/uni-notice-bar/package.json src/uni_modules/uni-notice-bar/readme.md src/uni_modules/uni-number-box/changelog.md src/uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue src/uni_modules/uni-number-box/package.json src/uni_modules/uni-number-box/readme.md src/uni_modules/uni-pagination/changelog.md src/uni_modules/uni-pagination/components/uni-pagination/i18n/en.json src/uni_modules/uni-pagination/components/uni-pagination/i18n/es.json src/uni_modules/uni-pagination/components/uni-pagination/i18n/fr.json src/uni_modules/uni-pagination/components/uni-pagination/i18n/index.js src/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hans.json src/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hant.json src/uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue src/uni_modules/uni-pagination/package.json src/uni_modules/uni-pagination/readme.md src/uni_modules/uni-popup/changelog.md src/uni_modules/uni-popup/components/uni-popup-dialog/keypress.js src/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue src/uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue src/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue src/uni_modules/uni-popup/components/uni-popup/i18n/en.json src/uni_modules/uni-popup/components/uni-popup/i18n/index.js src/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json src/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json src/uni_modules/uni-popup/components/uni-popup/keypress.js src/uni_modules/uni-popup/components/uni-popup/popup.js src/uni_modules/uni-popup/components/uni-popup/uni-popup.vue src/uni_modules/uni-popup/package.json src/uni_modules/uni-popup/readme.md src/uni_modules/uni-rate/changelog.md src/uni_modules/uni-rate/components/uni-rate/uni-rate.vue src/uni_modules/uni-rate/package.json src/uni_modules/uni-rate/readme.md src/uni_modules/uni-row/changelog.md src/uni_modules/uni-row/components/uni-col/uni-col.vue src/uni_modules/uni-row/components/uni-row/uni-row.vue src/uni_modules/uni-row/package.json src/uni_modules/uni-row/readme.md src/uni_modules/uni-scss/changelog.md src/uni_modules/uni-scss/index.scss src/uni_modules/uni-scss/package.json src/uni_modules/uni-scss/readme.md src/uni_modules/uni-scss/styles/index.scss src/uni_modules/uni-scss/styles/setting/_border.scss src/uni_modules/uni-scss/styles/setting/_color.scss src/uni_modules/uni-scss/styles/setting/_radius.scss src/uni_modules/uni-scss/styles/setting/_space.scss src/uni_modules/uni-scss/styles/setting/_styles.scss src/uni_modules/uni-scss/styles/setting/_text.scss src/uni_modules/uni-scss/styles/setting/_variables.scss src/uni_modules/uni-scss/styles/tools/functions.scss src/uni_modules/uni-scss/theme.scss src/uni_modules/uni-scss/variables.scss src/uni_modules/uni-search-bar/changelog.md src/uni_modules/uni-search-bar/components/uni-search-bar/i18n/en.json src/uni_modules/uni-search-bar/components/uni-search-bar/i18n/index.js src/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hans.json src/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hant.json src/uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue src/uni_modules/uni-search-bar/package.json src/uni_modules/uni-search-bar/readme.md src/uni_modules/uni-segmented-control/changelog.md src/uni_modules/uni-segmented-control/components/uni-segmented-control/uni-segmented-control.vue src/uni_modules/uni-segmented-control/package.json src/uni_modules/uni-segmented-control/readme.md src/uni_modules/uni-steps/changelog.md src/uni_modules/uni-steps/components/uni-steps/uni-steps.vue src/uni_modules/uni-steps/package.json src/uni_modules/uni-steps/readme.md src/uni_modules/uni-swipe-action/changelog.md src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/bindingx.js src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/isPC.js src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpalipay.js src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpother.js src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpwxs.js src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/render.js src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/uni-swipe-action-item.vue src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/wx.wxs src/uni_modules/uni-swipe-action/components/uni-swipe-action/uni-swipe-action.vue src/uni_modules/uni-swipe-action/package.json src/uni_modules/uni-swipe-action/readme.md src/uni_modules/uni-swiper-dot/changelog.md src/uni_modules/uni-swiper-dot/components/uni-swiper-dot/uni-swiper-dot.vue src/uni_modules/uni-swiper-dot/package.json src/uni_modules/uni-swiper-dot/readme.md src/uni_modules/uni-table/changelog.md src/uni_modules/uni-table/components/uni-table/uni-table.vue src/uni_modules/uni-table/components/uni-tbody/uni-tbody.vue src/uni_modules/uni-table/components/uni-td/uni-td.vue src/uni_modules/uni-table/components/uni-th/filter-dropdown.vue src/uni_modules/uni-table/components/uni-th/uni-th.vue src/uni_modules/uni-table/components/uni-thead/uni-thead.vue src/uni_modules/uni-table/components/uni-tr/table-checkbox.vue src/uni_modules/uni-table/components/uni-tr/uni-tr.vue src/uni_modules/uni-table/i18n/en.json src/uni_modules/uni-table/i18n/es.json src/uni_modules/uni-table/i18n/fr.json src/uni_modules/uni-table/i18n/index.js src/uni_modules/uni-table/i18n/zh-Hans.json src/uni_modules/uni-table/i18n/zh-Hant.json src/uni_modules/uni-table/package.json src/uni_modules/uni-table/readme.md src/uni_modules/uni-tag/changelog.md src/uni_modules/uni-tag/components/uni-tag/uni-tag.vue src/uni_modules/uni-tag/package.json src/uni_modules/uni-tag/readme.md src/uni_modules/uni-title/changelog.md src/uni_modules/uni-title/components/uni-title/uni-title.vue src/uni_modules/uni-title/package.json src/uni_modules/uni-title/readme.md src/uni_modules/uni-tooltip/changelog.md src/uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue src/uni_modules/uni-tooltip/package.json src/uni_modules/uni-tooltip/readme.md src/uni_modules/uni-transition/changelog.md src/uni_modules/uni-transition/components/uni-transition/createAnimation.js src/uni_modules/uni-transition/components/uni-transition/uni-transition.vue src/uni_modules/uni-transition/package.json src/uni_modules/uni-transition/readme.md src/utils/auth.ts src/utils/common.ts src/utils/constant.ts src/utils/dict.ts src/utils/errorCode.ts src/utils/geek.ts src/utils/permission.ts src/utils/request.ts src/utils/ruoyi.js src/utils/storage.ts src/utils/upload.ts tsconfig.json vite.config.js yarn.lock