From fee01f75c090b2eed7ec416c6ae8912a08575a7f Mon Sep 17 00:00:00 2001 From: JING <leirance@live.com> Date: Fri, 12 Jul 2024 09:49:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.vue | 73 + README.md | 2 + config/access/index.js | 90 + config/api.js | 200 + config/com.js | 268 + config/env.js | 34 + config/jssdk.js | 701 ++ config/map/amap-wx.130.js | 31 + config/map/qqmap-wx-jssdk.js | 1122 +++ config/md5.js | 257 + config/qqmap.js | 0 config/request.js | 114 + config/user.js | 88 + config/wechat.js | 228 + index.html | 20 + locale/en.json | 23 + locale/index.js | 8 + locale/uni-app.en.json | 36 + locale/uni-app.zh-Hans.json | 36 + locale/uni-app.zh-Hant.json | 36 + locale/zh-Hans.json | 39 + locale/zh-Hant.json | 20 + main.js | 57 + manifest.json | 100 + pages.json | 82 + pages/index/index.vue | 482 + pages/user/account/index.vue | 275 + pages/user/auth/login.vue | 176 + pages/user/auth/regist.vue | 205 + pages/user/index.vue | 536 ++ pages/user/setting/index.vue | 220 + pages/web/index.vue | 77 + pages/web/video.vue | 53 + static/css/iconfont.css | 2281 +++++ static/img/common/home.png | Bin 0 -> 865 bytes static/img/common/home_HL.png | Bin 0 -> 1308 bytes static/img/common/lang.png | Bin 0 -> 2235 bytes static/img/common/user.png | Bin 0 -> 866 bytes static/img/common/user_HL.png | Bin 0 -> 1316 bytes static/img/index/aixz.png | Bin 0 -> 2864 bytes static/img/index/banner.png | Bin 0 -> 147044 bytes static/img/index/kthf.png | Bin 0 -> 3342 bytes static/img/index/liucheng.png | Bin 0 -> 94824 bytes static/img/index/notice.png | Bin 0 -> 3490 bytes static/img/index/pic.png | Bin 0 -> 31440 bytes static/img/index/right.png | Bin 0 -> 211 bytes static/img/index/scan.png | Bin 0 -> 36570 bytes static/img/index/title_kt.png | Bin 0 -> 4199 bytes static/img/index/title_xz.png | Bin 0 -> 4101 bytes static/img/index/title_zj.png | Bin 0 -> 5800 bytes static/img/index/zj_icon.png | Bin 0 -> 1435 bytes static/img/index/zjgs.png | Bin 0 -> 4358 bytes static/img/index/zjhc.png | Bin 0 -> 3605 bytes static/img/user/account.png | Bin 0 -> 1437 bytes static/img/user/avatar.png | Bin 0 -> 6153 bytes static/img/user/banner.jpg | Bin 0 -> 36056 bytes static/img/user/chat.png | Bin 0 -> 1487 bytes static/img/user/code.png | Bin 0 -> 805 bytes static/img/user/img.png | Bin 0 -> 2538 bytes static/img/user/link.png | Bin 0 -> 1120 bytes static/img/user/money_log.png | Bin 0 -> 1782 bytes static/img/user/noshop.png | Bin 0 -> 24192 bytes static/img/user/order_all.png | Bin 0 -> 3461 bytes static/img/user/order_finish.png | Bin 0 -> 4310 bytes static/img/user/order_ing.png | Bin 0 -> 4083 bytes static/img/user/topbg.png | Bin 0 -> 111007 bytes static/json/city.json | 7904 +++++++++++++++++ static/logo.png | Bin 0 -> 16164 bytes uni.scss | 78 + uni_modules/uview-ui/LICENSE | 21 + uni_modules/uview-ui/README.md | 66 + uni_modules/uview-ui/changelog.md | 344 + .../uview-ui/components/u--form/u--form.vue | 78 + .../uview-ui/components/u--image/u--image.vue | 47 + .../uview-ui/components/u--input/u--input.vue | 72 + .../uview-ui/components/u--text/u--text.vue | 44 + .../components/u--textarea/u--textarea.vue | 47 + .../components/u-action-sheet/props.js | 54 + .../u-action-sheet/u-action-sheet.vue | 278 + .../uview-ui/components/u-album/props.js | 59 + .../uview-ui/components/u-album/u-album.vue | 259 + .../uview-ui/components/u-alert/props.js | 44 + .../uview-ui/components/u-alert/u-alert.vue | 243 + .../components/u-avatar-group/props.js | 52 + .../u-avatar-group/u-avatar-group.vue | 103 + .../uview-ui/components/u-avatar/props.js | 78 + .../uview-ui/components/u-avatar/u-avatar.vue | 172 + .../uview-ui/components/u-back-top/props.js | 54 + .../components/u-back-top/u-back-top.vue | 129 + .../uview-ui/components/u-badge/props.js | 72 + .../uview-ui/components/u-badge/u-badge.vue | 171 + .../uview-ui/components/u-button/nvue.scss | 46 + .../uview-ui/components/u-button/props.js | 161 + .../uview-ui/components/u-button/u-button.vue | 490 + .../uview-ui/components/u-button/vue.scss | 80 + .../uview-ui/components/u-calendar/header.vue | 99 + .../uview-ui/components/u-calendar/month.vue | 579 ++ .../uview-ui/components/u-calendar/props.js | 144 + .../components/u-calendar/u-calendar.vue | 383 + .../uview-ui/components/u-calendar/util.js | 85 + .../components/u-car-keyboard/props.js | 14 + .../u-car-keyboard/u-car-keyboard.vue | 311 + .../uview-ui/components/u-cell-group/props.js | 14 + .../components/u-cell-group/u-cell-group.vue | 61 + .../uview-ui/components/u-cell/props.js | 110 + .../uview-ui/components/u-cell/u-cell.vue | 229 + .../components/u-checkbox-group/props.js | 82 + .../u-checkbox-group/u-checkbox-group.vue | 103 + .../uview-ui/components/u-checkbox/props.js | 69 + .../components/u-checkbox/u-checkbox.vue | 344 + .../components/u-circle-progress/props.js | 8 + .../u-circle-progress/u-circle-progress.vue | 198 + .../uview-ui/components/u-code-input/props.js | 79 + .../components/u-code-input/u-code-input.vue | 252 + .../uview-ui/components/u-code/props.js | 34 + .../uview-ui/components/u-code/u-code.vue | 129 + .../uview-ui/components/u-col/props.js | 29 + .../uview-ui/components/u-col/u-col.vue | 162 + .../components/u-collapse-item/props.js | 59 + .../u-collapse-item/u-collapse-item.vue | 225 + .../uview-ui/components/u-collapse/props.js | 19 + .../components/u-collapse/u-collapse.vue | 90 + .../components/u-column-notice/props.js | 55 + .../u-column-notice/u-column-notice.vue | 160 + .../uview-ui/components/u-count-down/props.js | 24 + .../components/u-count-down/u-count-down.vue | 163 + .../uview-ui/components/u-count-down/utils.js | 62 + .../uview-ui/components/u-count-to/props.js | 59 + .../components/u-count-to/u-count-to.vue | 184 + .../components/u-datetime-picker/props.js | 116 + .../u-datetime-picker/u-datetime-picker.vue | 360 + .../uview-ui/components/u-divider/props.js | 44 + .../components/u-divider/u-divider.vue | 116 + .../components/u-dropdown-item/props.js | 36 + .../u-dropdown-item/u-dropdown-item.vue | 146 + .../uview-ui/components/u-dropdown/props.js | 65 + .../components/u-dropdown/u-dropdown.vue | 127 + .../uview-ui/components/u-empty/props.js | 59 + .../uview-ui/components/u-empty/u-empty.vue | 128 + .../uview-ui/components/u-form-item/props.js | 43 + .../components/u-form-item/u-form-item.vue | 235 + .../uview-ui/components/u-form/props.js | 45 + .../uview-ui/components/u-form/u-form.vue | 214 + .../uview-ui/components/u-gap/props.js | 24 + .../uview-ui/components/u-gap/u-gap.vue | 38 + .../uview-ui/components/u-grid-item/props.js | 14 + .../components/u-grid-item/u-grid-item.vue | 209 + .../uview-ui/components/u-grid/props.js | 19 + .../uview-ui/components/u-grid/u-grid.vue | 97 + .../uview-ui/components/u-icon/icons.js | 214 + .../uview-ui/components/u-icon/props.js | 89 + .../uview-ui/components/u-icon/u-icon.vue | 234 + .../uview-ui/components/u-image/props.js | 84 + .../uview-ui/components/u-image/u-image.vue | 232 + .../components/u-index-anchor/props.js | 29 + .../u-index-anchor/u-index-anchor.vue | 91 + .../uview-ui/components/u-index-item/props.js | 5 + .../components/u-index-item/u-index-item.vue | 87 + .../uview-ui/components/u-index-list/props.js | 29 + .../components/u-index-list/u-index-list.vue | 440 + .../uview-ui/components/u-input/props.js | 182 + .../uview-ui/components/u-input/u-input.vue | 353 + .../uview-ui/components/u-keyboard/props.js | 84 + .../components/u-keyboard/u-keyboard.vue | 164 + .../components/u-line-progress/props.js | 28 + .../u-line-progress/u-line-progress.vue | 144 + .../uview-ui/components/u-line/props.js | 33 + .../uview-ui/components/u-line/u-line.vue | 62 + .../uview-ui/components/u-link/props.js | 39 + .../uview-ui/components/u-link/u-link.vue | 83 + .../uview-ui/components/u-list-item/props.js | 9 + .../components/u-list-item/u-list-item.vue | 116 + .../uview-ui/components/u-list/props.js | 76 + .../uview-ui/components/u-list/u-list.vue | 157 + .../components/u-loading-icon/props.js | 59 + .../u-loading-icon/u-loading-icon.vue | 343 + .../components/u-loading-page/props.js | 49 + .../u-loading-page/u-loading-page.vue | 115 + .../uview-ui/components/u-loadmore/props.js | 94 + .../components/u-loadmore/u-loadmore.vue | 150 + .../uview-ui/components/u-modal/props.js | 84 + .../uview-ui/components/u-modal/u-modal.vue | 227 + .../uview-ui/components/u-navbar/props.js | 84 + .../uview-ui/components/u-navbar/u-navbar.vue | 186 + .../uview-ui/components/u-no-network/props.js | 19 + .../components/u-no-network/u-no-network.vue | 219 + .../uview-ui/components/u-notice-bar/props.js | 70 + .../components/u-notice-bar/u-notice-bar.vue | 101 + .../uview-ui/components/u-notify/props.js | 49 + .../uview-ui/components/u-notify/u-notify.vue | 211 + .../uview-ui/components/u-number-box/props.js | 109 + .../components/u-number-box/u-number-box.vue | 416 + .../components/u-number-keyboard/props.js | 19 + .../u-number-keyboard/u-number-keyboard.vue | 196 + .../uview-ui/components/u-overlay/props.js | 24 + .../components/u-overlay/u-overlay.vue | 68 + .../uview-ui/components/u-parse/node/node.vue | 499 ++ .../uview-ui/components/u-parse/parser.js | 1075 +++ .../uview-ui/components/u-parse/props.js | 45 + .../uview-ui/components/u-parse/u-parse.vue | 366 + .../components/u-picker-column/props.js | 5 + .../u-picker-column/u-picker-column.vue | 27 + .../uview-ui/components/u-picker/props.js | 79 + .../uview-ui/components/u-picker/u-picker.vue | 283 + .../uview-ui/components/u-popup/props.js | 79 + .../uview-ui/components/u-popup/u-popup.vue | 304 + .../components/u-radio-group/props.js | 85 + .../u-radio-group/u-radio-group.vue | 108 + .../uview-ui/components/u-radio/props.js | 64 + .../uview-ui/components/u-radio/u-radio.vue | 337 + .../uview-ui/components/u-rate/props.js | 69 + .../uview-ui/components/u-rate/u-rate.vue | 304 + .../uview-ui/components/u-read-more/props.js | 61 + .../components/u-read-more/u-read-more.vue | 157 + .../uview-ui/components/u-row-notice/props.js | 39 + .../components/u-row-notice/u-row-notice.vue | 330 + .../uview-ui/components/u-row/props.js | 19 + .../uview-ui/components/u-row/u-row.vue | 93 + .../components/u-safe-bottom/props.js | 5 + .../u-safe-bottom/u-safe-bottom.vue | 56 + .../uview-ui/components/u-scroll-list/nvue.js | 28 + .../components/u-scroll-list/other.js | 0 .../components/u-scroll-list/props.js | 34 + .../components/u-scroll-list/scrollWxs.wxs | 50 + .../u-scroll-list/u-scroll-list.vue | 224 + .../uview-ui/components/u-search/props.js | 118 + .../uview-ui/components/u-search/u-search.vue | 303 + .../uview-ui/components/u-skeleton/props.js | 59 + .../components/u-skeleton/u-skeleton.vue | 244 + .../uview-ui/components/u-slider/mpother.js | 113 + .../uview-ui/components/u-slider/mpwxs.js | 42 + .../uview-ui/components/u-slider/mpwxs.wxs | 121 + .../components/u-slider/nvue - 副本.js | 180 + .../uview-ui/components/u-slider/nvue.js | 193 + .../uview-ui/components/u-slider/props.js | 54 + .../uview-ui/components/u-slider/u-slider.vue | 55 + .../uview-ui/components/u-status-bar/props.js | 8 + .../components/u-status-bar/u-status-bar.vue | 46 + .../uview-ui/components/u-steps-item/props.js | 24 + .../components/u-steps-item/u-steps-item.vue | 316 + .../uview-ui/components/u-steps/props.js | 39 + .../uview-ui/components/u-steps/u-steps.vue | 80 + .../uview-ui/components/u-sticky/props.js | 40 + .../uview-ui/components/u-sticky/u-sticky.vue | 212 + .../uview-ui/components/u-subsection/props.js | 49 + .../components/u-subsection/u-subsection.vue | 299 + .../u-swipe-action-item/index - backup.wxs | 256 + .../components/u-swipe-action-item/index.wxs | 225 + .../u-swipe-action-item/nvue - backup.js | 270 + .../components/u-swipe-action-item/nvue.js | 174 + .../components/u-swipe-action-item/props.js | 41 + .../u-swipe-action-item.vue | 190 + .../components/u-swipe-action-item/wxs.js | 15 + .../components/u-swipe-action/props.js | 9 + .../u-swipe-action/u-swipe-action.vue | 67 + .../components/u-swiper-indicator/props.js | 29 + .../u-swiper-indicator/u-swiper-indicator.vue | 110 + .../uview-ui/components/u-swiper/props.js | 125 + .../uview-ui/components/u-swiper/u-swiper.vue | 255 + .../uview-ui/components/u-switch/props.js | 54 + .../uview-ui/components/u-switch/u-switch.vue | 177 + .../components/u-tabbar-item/props.js | 35 + .../u-tabbar-item/u-tabbar-item.vue | 142 + .../uview-ui/components/u-tabbar/props.js | 44 + .../uview-ui/components/u-tabbar/u-tabbar.vue | 141 + .../uview-ui/components/u-table/props.js | 5 + .../uview-ui/components/u-table/u-table.vue | 29 + .../uview-ui/components/u-tabs-item/props.js | 5 + .../components/u-tabs-item/u-tabs-item.vue | 29 + .../uview-ui/components/u-tabs/props.js | 64 + .../uview-ui/components/u-tabs/u-tabs.vue | 354 + .../uview-ui/components/u-tag/props.js | 84 + .../uview-ui/components/u-tag/u-tag.vue | 358 + uni_modules/uview-ui/components/u-td/props.js | 5 + uni_modules/uview-ui/components/u-td/u-td.vue | 31 + .../uview-ui/components/u-text/props.js | 110 + .../uview-ui/components/u-text/u-text.vue | 223 + .../uview-ui/components/u-text/value.js | 85 + .../uview-ui/components/u-textarea/props.js | 114 + .../components/u-textarea/u-textarea.vue | 237 + .../uview-ui/components/u-toast/u-toast.vue | 291 + .../uview-ui/components/u-toolbar/props.js | 34 + .../components/u-toolbar/u-toolbar.vue | 102 + .../components/u-tooltip/clipboard.min.js | 58 + .../uview-ui/components/u-tooltip/props.js | 59 + .../components/u-tooltip/u-tooltip.vue | 365 + uni_modules/uview-ui/components/u-tr/props.js | 5 + uni_modules/uview-ui/components/u-tr/u-tr.vue | 31 + .../components/u-transition/nvue.ani-map.js | 68 + .../uview-ui/components/u-transition/props.js | 24 + .../components/u-transition/transition.js | 157 + .../components/u-transition/u-transition.vue | 92 + .../u-transition/vue.ani-style.scss | 113 + .../uview-ui/components/u-upload/mixin.js | 21 + .../uview-ui/components/u-upload/props.js | 124 + .../uview-ui/components/u-upload/u-upload.vue | 558 ++ .../uview-ui/components/u-upload/utils.js | 151 + .../uview-ui/components/uview-ui/uview-ui.vue | 15 + uni_modules/uview-ui/index.js | 79 + uni_modules/uview-ui/index.scss | 23 + uni_modules/uview-ui/libs/config/color.js | 17 + uni_modules/uview-ui/libs/config/config.js | 34 + uni_modules/uview-ui/libs/config/props.js | 190 + .../uview-ui/libs/config/props/actionSheet.js | 25 + .../uview-ui/libs/config/props/album.js | 25 + .../uview-ui/libs/config/props/alert.js | 22 + .../uview-ui/libs/config/props/avatar.js | 28 + .../uview-ui/libs/config/props/avatarGroup.js | 23 + .../uview-ui/libs/config/props/backtop.js | 27 + .../uview-ui/libs/config/props/badge.js | 27 + .../uview-ui/libs/config/props/button.js | 42 + .../uview-ui/libs/config/props/calendar.js | 42 + .../uview-ui/libs/config/props/carKeyboard.js | 15 + .../uview-ui/libs/config/props/cell.js | 35 + .../uview-ui/libs/config/props/cellGroup.js | 17 + .../uview-ui/libs/config/props/checkbox.js | 27 + .../libs/config/props/checkboxGroup.js | 29 + .../libs/config/props/circleProgress.js | 15 + .../uview-ui/libs/config/props/code.js | 21 + .../uview-ui/libs/config/props/codeInput.js | 29 + uni_modules/uview-ui/libs/config/props/col.js | 19 + .../uview-ui/libs/config/props/collapse.js | 17 + .../libs/config/props/collapseItem.js | 25 + .../libs/config/props/columnNotice.js | 24 + .../uview-ui/libs/config/props/countDown.js | 18 + .../uview-ui/libs/config/props/countTo.js | 25 + .../libs/config/props/datetimePicker.js | 36 + .../uview-ui/libs/config/props/divider.js | 23 + .../uview-ui/libs/config/props/empty.js | 26 + .../uview-ui/libs/config/props/form.js | 22 + .../uview-ui/libs/config/props/formItem.js | 22 + uni_modules/uview-ui/libs/config/props/gap.js | 19 + .../uview-ui/libs/config/props/grid.js | 17 + .../uview-ui/libs/config/props/gridItem.js | 16 + .../uview-ui/libs/config/props/icon.js | 36 + .../uview-ui/libs/config/props/image.js | 30 + .../uview-ui/libs/config/props/indexAnchor.js | 19 + .../uview-ui/libs/config/props/indexList.js | 19 + .../uview-ui/libs/config/props/input.js | 48 + .../uview-ui/libs/config/props/keyboard.js | 30 + .../uview-ui/libs/config/props/line.js | 20 + .../libs/config/props/lineProgress.js | 19 + .../uview-ui/libs/config/props/link.js | 26 + .../uview-ui/libs/config/props/list.js | 28 + .../uview-ui/libs/config/props/listItem.js | 15 + .../uview-ui/libs/config/props/loadingIcon.js | 30 + .../uview-ui/libs/config/props/loadingPage.js | 23 + .../uview-ui/libs/config/props/loadmore.js | 32 + .../uview-ui/libs/config/props/modal.js | 30 + .../uview-ui/libs/config/props/navbar.js | 32 + .../uview-ui/libs/config/props/noNetwork.js | 18 + .../uview-ui/libs/config/props/noticeBar.js | 27 + .../uview-ui/libs/config/props/notify.js | 22 + .../uview-ui/libs/config/props/numberBox.js | 35 + .../libs/config/props/numberKeyboard.js | 17 + .../uview-ui/libs/config/props/overlay.js | 18 + .../uview-ui/libs/config/props/parse.js | 22 + .../uview-ui/libs/config/props/picker.js | 29 + .../uview-ui/libs/config/props/popup.js | 29 + .../uview-ui/libs/config/props/radio.js | 27 + .../uview-ui/libs/config/props/radioGroup.js | 30 + .../uview-ui/libs/config/props/rate.js | 26 + .../uview-ui/libs/config/props/readMore.js | 22 + uni_modules/uview-ui/libs/config/props/row.js | 17 + .../uview-ui/libs/config/props/rowNotice.js | 21 + .../uview-ui/libs/config/props/scrollList.js | 20 + .../uview-ui/libs/config/props/search.js | 37 + .../uview-ui/libs/config/props/section.js | 24 + .../uview-ui/libs/config/props/skeleton.js | 25 + .../uview-ui/libs/config/props/slider.js | 25 + .../uview-ui/libs/config/props/statusBar.js | 15 + .../uview-ui/libs/config/props/steps.js | 21 + .../uview-ui/libs/config/props/stepsItem.js | 18 + .../uview-ui/libs/config/props/sticky.js | 20 + .../uview-ui/libs/config/props/subsection.js | 23 + .../uview-ui/libs/config/props/swipeAction.js | 15 + .../libs/config/props/swipeActionItem.js | 21 + .../uview-ui/libs/config/props/swiper.js | 39 + .../libs/config/props/swipterIndicator.js | 19 + .../uview-ui/libs/config/props/switch.js | 24 + .../uview-ui/libs/config/props/tabbar.js | 22 + .../uview-ui/libs/config/props/tabbarItem.js | 20 + .../uview-ui/libs/config/props/tabs.js | 32 + uni_modules/uview-ui/libs/config/props/tag.js | 29 + .../uview-ui/libs/config/props/text.js | 38 + .../uview-ui/libs/config/props/textarea.js | 36 + .../uview-ui/libs/config/props/toast.js | 30 + .../uview-ui/libs/config/props/toolbar.js | 21 + .../uview-ui/libs/config/props/tooltip.js | 25 + .../uview-ui/libs/config/props/transition.js | 18 + .../uview-ui/libs/config/props/upload.js | 36 + uni_modules/uview-ui/libs/config/zIndex.js | 20 + uni_modules/uview-ui/libs/css/color.scss | 155 + uni_modules/uview-ui/libs/css/common.scss | 97 + uni_modules/uview-ui/libs/css/components.scss | 15 + uni_modules/uview-ui/libs/css/flex.scss | 257 + uni_modules/uview-ui/libs/css/h5.scss | 0 uni_modules/uview-ui/libs/css/mixin.scss | 8 + uni_modules/uview-ui/libs/css/mp.scss | 0 uni_modules/uview-ui/libs/css/nvue.scss | 0 uni_modules/uview-ui/libs/css/vue.scss | 27 + .../uview-ui/libs/function/colorGradient.js | 134 + .../uview-ui/libs/function/debounce.js | 29 + uni_modules/uview-ui/libs/function/digit.js | 167 + uni_modules/uview-ui/libs/function/index.js | 705 ++ .../uview-ui/libs/function/platform.js | 75 + uni_modules/uview-ui/libs/function/test.js | 288 + .../uview-ui/libs/function/throttle.js | 30 + .../libs/luch-request/adapters/index.js | 97 + .../luch-request/core/InterceptorManager.js | 50 + .../libs/luch-request/core/Request.js | 198 + .../libs/luch-request/core/buildFullPath.js | 20 + .../libs/luch-request/core/defaults.js | 29 + .../libs/luch-request/core/dispatchRequest.js | 3 + .../libs/luch-request/core/mergeConfig.js | 103 + .../uview-ui/libs/luch-request/core/settle.js | 16 + .../libs/luch-request/helpers/buildURL.js | 69 + .../libs/luch-request/helpers/combineURLs.js | 14 + .../luch-request/helpers/isAbsoluteURL.js | 14 + .../uview-ui/libs/luch-request/index.d.ts | 116 + .../uview-ui/libs/luch-request/index.js | 3 + .../uview-ui/libs/luch-request/utils.js | 131 + .../uview-ui/libs/luch-request/utils/clone.js | 264 + uni_modules/uview-ui/libs/mixin/button.js | 13 + uni_modules/uview-ui/libs/mixin/mixin.js | 160 + uni_modules/uview-ui/libs/mixin/mpMixin.js | 8 + uni_modules/uview-ui/libs/mixin/mpShare.js | 13 + uni_modules/uview-ui/libs/mixin/openType.js | 25 + uni_modules/uview-ui/libs/mixin/style.js | 228 + uni_modules/uview-ui/libs/mixin/touch.js | 59 + .../uview-ui/libs/util/async-validator.js | 1343 +++ uni_modules/uview-ui/libs/util/calendar.js | 546 ++ uni_modules/uview-ui/libs/util/dayjs.js | 308 + uni_modules/uview-ui/libs/util/emitter.js | 51 + uni_modules/uview-ui/libs/util/route.js | 124 + uni_modules/uview-ui/package.json | 87 + uni_modules/uview-ui/theme.scss | 44 + 437 files changed, 54399 insertions(+) create mode 100644 App.vue create mode 100644 README.md create mode 100644 config/access/index.js create mode 100644 config/api.js create mode 100644 config/com.js create mode 100644 config/env.js create mode 100644 config/jssdk.js create mode 100644 config/map/amap-wx.130.js create mode 100644 config/map/qqmap-wx-jssdk.js create mode 100644 config/md5.js create mode 100644 config/qqmap.js create mode 100644 config/request.js create mode 100644 config/user.js create mode 100644 config/wechat.js create mode 100644 index.html create mode 100644 locale/en.json create mode 100644 locale/index.js create mode 100644 locale/uni-app.en.json create mode 100644 locale/uni-app.zh-Hans.json create mode 100644 locale/uni-app.zh-Hant.json create mode 100644 locale/zh-Hans.json create mode 100644 locale/zh-Hant.json create mode 100644 main.js create mode 100644 manifest.json create mode 100644 pages.json create mode 100644 pages/index/index.vue create mode 100644 pages/user/account/index.vue create mode 100644 pages/user/auth/login.vue create mode 100644 pages/user/auth/regist.vue create mode 100644 pages/user/index.vue create mode 100644 pages/user/setting/index.vue create mode 100644 pages/web/index.vue create mode 100644 pages/web/video.vue create mode 100644 static/css/iconfont.css create mode 100644 static/img/common/home.png create mode 100644 static/img/common/home_HL.png create mode 100644 static/img/common/lang.png create mode 100644 static/img/common/user.png create mode 100644 static/img/common/user_HL.png create mode 100644 static/img/index/aixz.png create mode 100644 static/img/index/banner.png create mode 100644 static/img/index/kthf.png create mode 100644 static/img/index/liucheng.png create mode 100644 static/img/index/notice.png create mode 100644 static/img/index/pic.png create mode 100644 static/img/index/right.png create mode 100644 static/img/index/scan.png create mode 100644 static/img/index/title_kt.png create mode 100644 static/img/index/title_xz.png create mode 100644 static/img/index/title_zj.png create mode 100644 static/img/index/zj_icon.png create mode 100644 static/img/index/zjgs.png create mode 100644 static/img/index/zjhc.png create mode 100644 static/img/user/account.png create mode 100644 static/img/user/avatar.png create mode 100644 static/img/user/banner.jpg create mode 100644 static/img/user/chat.png create mode 100644 static/img/user/code.png create mode 100644 static/img/user/img.png create mode 100644 static/img/user/link.png create mode 100644 static/img/user/money_log.png create mode 100644 static/img/user/noshop.png create mode 100644 static/img/user/order_all.png create mode 100644 static/img/user/order_finish.png create mode 100644 static/img/user/order_ing.png create mode 100644 static/img/user/topbg.png create mode 100644 static/json/city.json create mode 100644 static/logo.png create mode 100644 uni.scss create mode 100644 uni_modules/uview-ui/LICENSE create mode 100644 uni_modules/uview-ui/README.md create mode 100644 uni_modules/uview-ui/changelog.md create mode 100644 uni_modules/uview-ui/components/u--form/u--form.vue create mode 100644 uni_modules/uview-ui/components/u--image/u--image.vue create mode 100644 uni_modules/uview-ui/components/u--input/u--input.vue create mode 100644 uni_modules/uview-ui/components/u--text/u--text.vue create mode 100644 uni_modules/uview-ui/components/u--textarea/u--textarea.vue create mode 100644 uni_modules/uview-ui/components/u-action-sheet/props.js create mode 100644 uni_modules/uview-ui/components/u-action-sheet/u-action-sheet.vue create mode 100644 uni_modules/uview-ui/components/u-album/props.js create mode 100644 uni_modules/uview-ui/components/u-album/u-album.vue create mode 100644 uni_modules/uview-ui/components/u-alert/props.js create mode 100644 uni_modules/uview-ui/components/u-alert/u-alert.vue create mode 100644 uni_modules/uview-ui/components/u-avatar-group/props.js create mode 100644 uni_modules/uview-ui/components/u-avatar-group/u-avatar-group.vue create mode 100644 uni_modules/uview-ui/components/u-avatar/props.js create mode 100644 uni_modules/uview-ui/components/u-avatar/u-avatar.vue create mode 100644 uni_modules/uview-ui/components/u-back-top/props.js create mode 100644 uni_modules/uview-ui/components/u-back-top/u-back-top.vue create mode 100644 uni_modules/uview-ui/components/u-badge/props.js create mode 100644 uni_modules/uview-ui/components/u-badge/u-badge.vue create mode 100644 uni_modules/uview-ui/components/u-button/nvue.scss create mode 100644 uni_modules/uview-ui/components/u-button/props.js create mode 100644 uni_modules/uview-ui/components/u-button/u-button.vue create mode 100644 uni_modules/uview-ui/components/u-button/vue.scss create mode 100644 uni_modules/uview-ui/components/u-calendar/header.vue create mode 100644 uni_modules/uview-ui/components/u-calendar/month.vue create mode 100644 uni_modules/uview-ui/components/u-calendar/props.js create mode 100644 uni_modules/uview-ui/components/u-calendar/u-calendar.vue create mode 100644 uni_modules/uview-ui/components/u-calendar/util.js create mode 100644 uni_modules/uview-ui/components/u-car-keyboard/props.js create mode 100644 uni_modules/uview-ui/components/u-car-keyboard/u-car-keyboard.vue create mode 100644 uni_modules/uview-ui/components/u-cell-group/props.js create mode 100644 uni_modules/uview-ui/components/u-cell-group/u-cell-group.vue create mode 100644 uni_modules/uview-ui/components/u-cell/props.js create mode 100644 uni_modules/uview-ui/components/u-cell/u-cell.vue create mode 100644 uni_modules/uview-ui/components/u-checkbox-group/props.js create mode 100644 uni_modules/uview-ui/components/u-checkbox-group/u-checkbox-group.vue create mode 100644 uni_modules/uview-ui/components/u-checkbox/props.js create mode 100644 uni_modules/uview-ui/components/u-checkbox/u-checkbox.vue create mode 100644 uni_modules/uview-ui/components/u-circle-progress/props.js create mode 100644 uni_modules/uview-ui/components/u-circle-progress/u-circle-progress.vue create mode 100644 uni_modules/uview-ui/components/u-code-input/props.js create mode 100644 uni_modules/uview-ui/components/u-code-input/u-code-input.vue create mode 100644 uni_modules/uview-ui/components/u-code/props.js create mode 100644 uni_modules/uview-ui/components/u-code/u-code.vue create mode 100644 uni_modules/uview-ui/components/u-col/props.js create mode 100644 uni_modules/uview-ui/components/u-col/u-col.vue create mode 100644 uni_modules/uview-ui/components/u-collapse-item/props.js create mode 100644 uni_modules/uview-ui/components/u-collapse-item/u-collapse-item.vue create mode 100644 uni_modules/uview-ui/components/u-collapse/props.js create mode 100644 uni_modules/uview-ui/components/u-collapse/u-collapse.vue create mode 100644 uni_modules/uview-ui/components/u-column-notice/props.js create mode 100644 uni_modules/uview-ui/components/u-column-notice/u-column-notice.vue create mode 100644 uni_modules/uview-ui/components/u-count-down/props.js create mode 100644 uni_modules/uview-ui/components/u-count-down/u-count-down.vue create mode 100644 uni_modules/uview-ui/components/u-count-down/utils.js create mode 100644 uni_modules/uview-ui/components/u-count-to/props.js create mode 100644 uni_modules/uview-ui/components/u-count-to/u-count-to.vue create mode 100644 uni_modules/uview-ui/components/u-datetime-picker/props.js create mode 100644 uni_modules/uview-ui/components/u-datetime-picker/u-datetime-picker.vue create mode 100644 uni_modules/uview-ui/components/u-divider/props.js create mode 100644 uni_modules/uview-ui/components/u-divider/u-divider.vue create mode 100644 uni_modules/uview-ui/components/u-dropdown-item/props.js create mode 100644 uni_modules/uview-ui/components/u-dropdown-item/u-dropdown-item.vue create mode 100644 uni_modules/uview-ui/components/u-dropdown/props.js create mode 100644 uni_modules/uview-ui/components/u-dropdown/u-dropdown.vue create mode 100644 uni_modules/uview-ui/components/u-empty/props.js create mode 100644 uni_modules/uview-ui/components/u-empty/u-empty.vue create mode 100644 uni_modules/uview-ui/components/u-form-item/props.js create mode 100644 uni_modules/uview-ui/components/u-form-item/u-form-item.vue create mode 100644 uni_modules/uview-ui/components/u-form/props.js create mode 100644 uni_modules/uview-ui/components/u-form/u-form.vue create mode 100644 uni_modules/uview-ui/components/u-gap/props.js create mode 100644 uni_modules/uview-ui/components/u-gap/u-gap.vue create mode 100644 uni_modules/uview-ui/components/u-grid-item/props.js create mode 100644 uni_modules/uview-ui/components/u-grid-item/u-grid-item.vue create mode 100644 uni_modules/uview-ui/components/u-grid/props.js create mode 100644 uni_modules/uview-ui/components/u-grid/u-grid.vue create mode 100644 uni_modules/uview-ui/components/u-icon/icons.js create mode 100644 uni_modules/uview-ui/components/u-icon/props.js create mode 100644 uni_modules/uview-ui/components/u-icon/u-icon.vue create mode 100644 uni_modules/uview-ui/components/u-image/props.js create mode 100644 uni_modules/uview-ui/components/u-image/u-image.vue create mode 100644 uni_modules/uview-ui/components/u-index-anchor/props.js create mode 100644 uni_modules/uview-ui/components/u-index-anchor/u-index-anchor.vue create mode 100644 uni_modules/uview-ui/components/u-index-item/props.js create mode 100644 uni_modules/uview-ui/components/u-index-item/u-index-item.vue create mode 100644 uni_modules/uview-ui/components/u-index-list/props.js create mode 100644 uni_modules/uview-ui/components/u-index-list/u-index-list.vue create mode 100644 uni_modules/uview-ui/components/u-input/props.js create mode 100644 uni_modules/uview-ui/components/u-input/u-input.vue create mode 100644 uni_modules/uview-ui/components/u-keyboard/props.js create mode 100644 uni_modules/uview-ui/components/u-keyboard/u-keyboard.vue create mode 100644 uni_modules/uview-ui/components/u-line-progress/props.js create mode 100644 uni_modules/uview-ui/components/u-line-progress/u-line-progress.vue create mode 100644 uni_modules/uview-ui/components/u-line/props.js create mode 100644 uni_modules/uview-ui/components/u-line/u-line.vue create mode 100644 uni_modules/uview-ui/components/u-link/props.js create mode 100644 uni_modules/uview-ui/components/u-link/u-link.vue create mode 100644 uni_modules/uview-ui/components/u-list-item/props.js create mode 100644 uni_modules/uview-ui/components/u-list-item/u-list-item.vue create mode 100644 uni_modules/uview-ui/components/u-list/props.js create mode 100644 uni_modules/uview-ui/components/u-list/u-list.vue create mode 100644 uni_modules/uview-ui/components/u-loading-icon/props.js create mode 100644 uni_modules/uview-ui/components/u-loading-icon/u-loading-icon.vue create mode 100644 uni_modules/uview-ui/components/u-loading-page/props.js create mode 100644 uni_modules/uview-ui/components/u-loading-page/u-loading-page.vue create mode 100644 uni_modules/uview-ui/components/u-loadmore/props.js create mode 100644 uni_modules/uview-ui/components/u-loadmore/u-loadmore.vue create mode 100644 uni_modules/uview-ui/components/u-modal/props.js create mode 100644 uni_modules/uview-ui/components/u-modal/u-modal.vue create mode 100644 uni_modules/uview-ui/components/u-navbar/props.js create mode 100644 uni_modules/uview-ui/components/u-navbar/u-navbar.vue create mode 100644 uni_modules/uview-ui/components/u-no-network/props.js create mode 100644 uni_modules/uview-ui/components/u-no-network/u-no-network.vue create mode 100644 uni_modules/uview-ui/components/u-notice-bar/props.js create mode 100644 uni_modules/uview-ui/components/u-notice-bar/u-notice-bar.vue create mode 100644 uni_modules/uview-ui/components/u-notify/props.js create mode 100644 uni_modules/uview-ui/components/u-notify/u-notify.vue create mode 100644 uni_modules/uview-ui/components/u-number-box/props.js create mode 100644 uni_modules/uview-ui/components/u-number-box/u-number-box.vue create mode 100644 uni_modules/uview-ui/components/u-number-keyboard/props.js create mode 100644 uni_modules/uview-ui/components/u-number-keyboard/u-number-keyboard.vue create mode 100644 uni_modules/uview-ui/components/u-overlay/props.js create mode 100644 uni_modules/uview-ui/components/u-overlay/u-overlay.vue create mode 100644 uni_modules/uview-ui/components/u-parse/node/node.vue create mode 100644 uni_modules/uview-ui/components/u-parse/parser.js create mode 100644 uni_modules/uview-ui/components/u-parse/props.js create mode 100644 uni_modules/uview-ui/components/u-parse/u-parse.vue create mode 100644 uni_modules/uview-ui/components/u-picker-column/props.js create mode 100644 uni_modules/uview-ui/components/u-picker-column/u-picker-column.vue create mode 100644 uni_modules/uview-ui/components/u-picker/props.js create mode 100644 uni_modules/uview-ui/components/u-picker/u-picker.vue create mode 100644 uni_modules/uview-ui/components/u-popup/props.js create mode 100644 uni_modules/uview-ui/components/u-popup/u-popup.vue create mode 100644 uni_modules/uview-ui/components/u-radio-group/props.js create mode 100644 uni_modules/uview-ui/components/u-radio-group/u-radio-group.vue create mode 100644 uni_modules/uview-ui/components/u-radio/props.js create mode 100644 uni_modules/uview-ui/components/u-radio/u-radio.vue create mode 100644 uni_modules/uview-ui/components/u-rate/props.js create mode 100644 uni_modules/uview-ui/components/u-rate/u-rate.vue create mode 100644 uni_modules/uview-ui/components/u-read-more/props.js create mode 100644 uni_modules/uview-ui/components/u-read-more/u-read-more.vue create mode 100644 uni_modules/uview-ui/components/u-row-notice/props.js create mode 100644 uni_modules/uview-ui/components/u-row-notice/u-row-notice.vue create mode 100644 uni_modules/uview-ui/components/u-row/props.js create mode 100644 uni_modules/uview-ui/components/u-row/u-row.vue create mode 100644 uni_modules/uview-ui/components/u-safe-bottom/props.js create mode 100644 uni_modules/uview-ui/components/u-safe-bottom/u-safe-bottom.vue create mode 100644 uni_modules/uview-ui/components/u-scroll-list/nvue.js create mode 100644 uni_modules/uview-ui/components/u-scroll-list/other.js create mode 100644 uni_modules/uview-ui/components/u-scroll-list/props.js create mode 100644 uni_modules/uview-ui/components/u-scroll-list/scrollWxs.wxs create mode 100644 uni_modules/uview-ui/components/u-scroll-list/u-scroll-list.vue create mode 100644 uni_modules/uview-ui/components/u-search/props.js create mode 100644 uni_modules/uview-ui/components/u-search/u-search.vue create mode 100644 uni_modules/uview-ui/components/u-skeleton/props.js create mode 100644 uni_modules/uview-ui/components/u-skeleton/u-skeleton.vue create mode 100644 uni_modules/uview-ui/components/u-slider/mpother.js create mode 100644 uni_modules/uview-ui/components/u-slider/mpwxs.js create mode 100644 uni_modules/uview-ui/components/u-slider/mpwxs.wxs create mode 100644 uni_modules/uview-ui/components/u-slider/nvue - 副本.js create mode 100644 uni_modules/uview-ui/components/u-slider/nvue.js create mode 100644 uni_modules/uview-ui/components/u-slider/props.js create mode 100644 uni_modules/uview-ui/components/u-slider/u-slider.vue create mode 100644 uni_modules/uview-ui/components/u-status-bar/props.js create mode 100644 uni_modules/uview-ui/components/u-status-bar/u-status-bar.vue create mode 100644 uni_modules/uview-ui/components/u-steps-item/props.js create mode 100644 uni_modules/uview-ui/components/u-steps-item/u-steps-item.vue create mode 100644 uni_modules/uview-ui/components/u-steps/props.js create mode 100644 uni_modules/uview-ui/components/u-steps/u-steps.vue create mode 100644 uni_modules/uview-ui/components/u-sticky/props.js create mode 100644 uni_modules/uview-ui/components/u-sticky/u-sticky.vue create mode 100644 uni_modules/uview-ui/components/u-subsection/props.js create mode 100644 uni_modules/uview-ui/components/u-subsection/u-subsection.vue create mode 100644 uni_modules/uview-ui/components/u-swipe-action-item/index - backup.wxs create mode 100644 uni_modules/uview-ui/components/u-swipe-action-item/index.wxs create mode 100644 uni_modules/uview-ui/components/u-swipe-action-item/nvue - backup.js create mode 100644 uni_modules/uview-ui/components/u-swipe-action-item/nvue.js create mode 100644 uni_modules/uview-ui/components/u-swipe-action-item/props.js create mode 100644 uni_modules/uview-ui/components/u-swipe-action-item/u-swipe-action-item.vue create mode 100644 uni_modules/uview-ui/components/u-swipe-action-item/wxs.js create mode 100644 uni_modules/uview-ui/components/u-swipe-action/props.js create mode 100644 uni_modules/uview-ui/components/u-swipe-action/u-swipe-action.vue create mode 100644 uni_modules/uview-ui/components/u-swiper-indicator/props.js create mode 100644 uni_modules/uview-ui/components/u-swiper-indicator/u-swiper-indicator.vue create mode 100644 uni_modules/uview-ui/components/u-swiper/props.js create mode 100644 uni_modules/uview-ui/components/u-swiper/u-swiper.vue create mode 100644 uni_modules/uview-ui/components/u-switch/props.js create mode 100644 uni_modules/uview-ui/components/u-switch/u-switch.vue create mode 100644 uni_modules/uview-ui/components/u-tabbar-item/props.js create mode 100644 uni_modules/uview-ui/components/u-tabbar-item/u-tabbar-item.vue create mode 100644 uni_modules/uview-ui/components/u-tabbar/props.js create mode 100644 uni_modules/uview-ui/components/u-tabbar/u-tabbar.vue create mode 100644 uni_modules/uview-ui/components/u-table/props.js create mode 100644 uni_modules/uview-ui/components/u-table/u-table.vue create mode 100644 uni_modules/uview-ui/components/u-tabs-item/props.js create mode 100644 uni_modules/uview-ui/components/u-tabs-item/u-tabs-item.vue create mode 100644 uni_modules/uview-ui/components/u-tabs/props.js create mode 100644 uni_modules/uview-ui/components/u-tabs/u-tabs.vue create mode 100644 uni_modules/uview-ui/components/u-tag/props.js create mode 100644 uni_modules/uview-ui/components/u-tag/u-tag.vue create mode 100644 uni_modules/uview-ui/components/u-td/props.js create mode 100644 uni_modules/uview-ui/components/u-td/u-td.vue create mode 100644 uni_modules/uview-ui/components/u-text/props.js create mode 100644 uni_modules/uview-ui/components/u-text/u-text.vue create mode 100644 uni_modules/uview-ui/components/u-text/value.js create mode 100644 uni_modules/uview-ui/components/u-textarea/props.js create mode 100644 uni_modules/uview-ui/components/u-textarea/u-textarea.vue create mode 100644 uni_modules/uview-ui/components/u-toast/u-toast.vue create mode 100644 uni_modules/uview-ui/components/u-toolbar/props.js create mode 100644 uni_modules/uview-ui/components/u-toolbar/u-toolbar.vue create mode 100644 uni_modules/uview-ui/components/u-tooltip/clipboard.min.js create mode 100644 uni_modules/uview-ui/components/u-tooltip/props.js create mode 100644 uni_modules/uview-ui/components/u-tooltip/u-tooltip.vue create mode 100644 uni_modules/uview-ui/components/u-tr/props.js create mode 100644 uni_modules/uview-ui/components/u-tr/u-tr.vue create mode 100644 uni_modules/uview-ui/components/u-transition/nvue.ani-map.js create mode 100644 uni_modules/uview-ui/components/u-transition/props.js create mode 100644 uni_modules/uview-ui/components/u-transition/transition.js create mode 100644 uni_modules/uview-ui/components/u-transition/u-transition.vue create mode 100644 uni_modules/uview-ui/components/u-transition/vue.ani-style.scss create mode 100644 uni_modules/uview-ui/components/u-upload/mixin.js create mode 100644 uni_modules/uview-ui/components/u-upload/props.js create mode 100644 uni_modules/uview-ui/components/u-upload/u-upload.vue create mode 100644 uni_modules/uview-ui/components/u-upload/utils.js create mode 100644 uni_modules/uview-ui/components/uview-ui/uview-ui.vue create mode 100644 uni_modules/uview-ui/index.js create mode 100644 uni_modules/uview-ui/index.scss create mode 100644 uni_modules/uview-ui/libs/config/color.js create mode 100644 uni_modules/uview-ui/libs/config/config.js create mode 100644 uni_modules/uview-ui/libs/config/props.js create mode 100644 uni_modules/uview-ui/libs/config/props/actionSheet.js create mode 100644 uni_modules/uview-ui/libs/config/props/album.js create mode 100644 uni_modules/uview-ui/libs/config/props/alert.js create mode 100644 uni_modules/uview-ui/libs/config/props/avatar.js create mode 100644 uni_modules/uview-ui/libs/config/props/avatarGroup.js create mode 100644 uni_modules/uview-ui/libs/config/props/backtop.js create mode 100644 uni_modules/uview-ui/libs/config/props/badge.js create mode 100644 uni_modules/uview-ui/libs/config/props/button.js create mode 100644 uni_modules/uview-ui/libs/config/props/calendar.js create mode 100644 uni_modules/uview-ui/libs/config/props/carKeyboard.js create mode 100644 uni_modules/uview-ui/libs/config/props/cell.js create mode 100644 uni_modules/uview-ui/libs/config/props/cellGroup.js create mode 100644 uni_modules/uview-ui/libs/config/props/checkbox.js create mode 100644 uni_modules/uview-ui/libs/config/props/checkboxGroup.js create mode 100644 uni_modules/uview-ui/libs/config/props/circleProgress.js create mode 100644 uni_modules/uview-ui/libs/config/props/code.js create mode 100644 uni_modules/uview-ui/libs/config/props/codeInput.js create mode 100644 uni_modules/uview-ui/libs/config/props/col.js create mode 100644 uni_modules/uview-ui/libs/config/props/collapse.js create mode 100644 uni_modules/uview-ui/libs/config/props/collapseItem.js create mode 100644 uni_modules/uview-ui/libs/config/props/columnNotice.js create mode 100644 uni_modules/uview-ui/libs/config/props/countDown.js create mode 100644 uni_modules/uview-ui/libs/config/props/countTo.js create mode 100644 uni_modules/uview-ui/libs/config/props/datetimePicker.js create mode 100644 uni_modules/uview-ui/libs/config/props/divider.js create mode 100644 uni_modules/uview-ui/libs/config/props/empty.js create mode 100644 uni_modules/uview-ui/libs/config/props/form.js create mode 100644 uni_modules/uview-ui/libs/config/props/formItem.js create mode 100644 uni_modules/uview-ui/libs/config/props/gap.js create mode 100644 uni_modules/uview-ui/libs/config/props/grid.js create mode 100644 uni_modules/uview-ui/libs/config/props/gridItem.js create mode 100644 uni_modules/uview-ui/libs/config/props/icon.js create mode 100644 uni_modules/uview-ui/libs/config/props/image.js create mode 100644 uni_modules/uview-ui/libs/config/props/indexAnchor.js create mode 100644 uni_modules/uview-ui/libs/config/props/indexList.js create mode 100644 uni_modules/uview-ui/libs/config/props/input.js create mode 100644 uni_modules/uview-ui/libs/config/props/keyboard.js create mode 100644 uni_modules/uview-ui/libs/config/props/line.js create mode 100644 uni_modules/uview-ui/libs/config/props/lineProgress.js create mode 100644 uni_modules/uview-ui/libs/config/props/link.js create mode 100644 uni_modules/uview-ui/libs/config/props/list.js create mode 100644 uni_modules/uview-ui/libs/config/props/listItem.js create mode 100644 uni_modules/uview-ui/libs/config/props/loadingIcon.js create mode 100644 uni_modules/uview-ui/libs/config/props/loadingPage.js create mode 100644 uni_modules/uview-ui/libs/config/props/loadmore.js create mode 100644 uni_modules/uview-ui/libs/config/props/modal.js create mode 100644 uni_modules/uview-ui/libs/config/props/navbar.js create mode 100644 uni_modules/uview-ui/libs/config/props/noNetwork.js create mode 100644 uni_modules/uview-ui/libs/config/props/noticeBar.js create mode 100644 uni_modules/uview-ui/libs/config/props/notify.js create mode 100644 uni_modules/uview-ui/libs/config/props/numberBox.js create mode 100644 uni_modules/uview-ui/libs/config/props/numberKeyboard.js create mode 100644 uni_modules/uview-ui/libs/config/props/overlay.js create mode 100644 uni_modules/uview-ui/libs/config/props/parse.js create mode 100644 uni_modules/uview-ui/libs/config/props/picker.js create mode 100644 uni_modules/uview-ui/libs/config/props/popup.js create mode 100644 uni_modules/uview-ui/libs/config/props/radio.js create mode 100644 uni_modules/uview-ui/libs/config/props/radioGroup.js create mode 100644 uni_modules/uview-ui/libs/config/props/rate.js create mode 100644 uni_modules/uview-ui/libs/config/props/readMore.js create mode 100644 uni_modules/uview-ui/libs/config/props/row.js create mode 100644 uni_modules/uview-ui/libs/config/props/rowNotice.js create mode 100644 uni_modules/uview-ui/libs/config/props/scrollList.js create mode 100644 uni_modules/uview-ui/libs/config/props/search.js create mode 100644 uni_modules/uview-ui/libs/config/props/section.js create mode 100644 uni_modules/uview-ui/libs/config/props/skeleton.js create mode 100644 uni_modules/uview-ui/libs/config/props/slider.js create mode 100644 uni_modules/uview-ui/libs/config/props/statusBar.js create mode 100644 uni_modules/uview-ui/libs/config/props/steps.js create mode 100644 uni_modules/uview-ui/libs/config/props/stepsItem.js create mode 100644 uni_modules/uview-ui/libs/config/props/sticky.js create mode 100644 uni_modules/uview-ui/libs/config/props/subsection.js create mode 100644 uni_modules/uview-ui/libs/config/props/swipeAction.js create mode 100644 uni_modules/uview-ui/libs/config/props/swipeActionItem.js create mode 100644 uni_modules/uview-ui/libs/config/props/swiper.js create mode 100644 uni_modules/uview-ui/libs/config/props/swipterIndicator.js create mode 100644 uni_modules/uview-ui/libs/config/props/switch.js create mode 100644 uni_modules/uview-ui/libs/config/props/tabbar.js create mode 100644 uni_modules/uview-ui/libs/config/props/tabbarItem.js create mode 100644 uni_modules/uview-ui/libs/config/props/tabs.js create mode 100644 uni_modules/uview-ui/libs/config/props/tag.js create mode 100644 uni_modules/uview-ui/libs/config/props/text.js create mode 100644 uni_modules/uview-ui/libs/config/props/textarea.js create mode 100644 uni_modules/uview-ui/libs/config/props/toast.js create mode 100644 uni_modules/uview-ui/libs/config/props/toolbar.js create mode 100644 uni_modules/uview-ui/libs/config/props/tooltip.js create mode 100644 uni_modules/uview-ui/libs/config/props/transition.js create mode 100644 uni_modules/uview-ui/libs/config/props/upload.js create mode 100644 uni_modules/uview-ui/libs/config/zIndex.js create mode 100644 uni_modules/uview-ui/libs/css/color.scss create mode 100644 uni_modules/uview-ui/libs/css/common.scss create mode 100644 uni_modules/uview-ui/libs/css/components.scss create mode 100644 uni_modules/uview-ui/libs/css/flex.scss create mode 100644 uni_modules/uview-ui/libs/css/h5.scss create mode 100644 uni_modules/uview-ui/libs/css/mixin.scss create mode 100644 uni_modules/uview-ui/libs/css/mp.scss create mode 100644 uni_modules/uview-ui/libs/css/nvue.scss create mode 100644 uni_modules/uview-ui/libs/css/vue.scss create mode 100644 uni_modules/uview-ui/libs/function/colorGradient.js create mode 100644 uni_modules/uview-ui/libs/function/debounce.js create mode 100644 uni_modules/uview-ui/libs/function/digit.js create mode 100644 uni_modules/uview-ui/libs/function/index.js create mode 100644 uni_modules/uview-ui/libs/function/platform.js create mode 100644 uni_modules/uview-ui/libs/function/test.js create mode 100644 uni_modules/uview-ui/libs/function/throttle.js create mode 100644 uni_modules/uview-ui/libs/luch-request/adapters/index.js create mode 100644 uni_modules/uview-ui/libs/luch-request/core/InterceptorManager.js create mode 100644 uni_modules/uview-ui/libs/luch-request/core/Request.js create mode 100644 uni_modules/uview-ui/libs/luch-request/core/buildFullPath.js create mode 100644 uni_modules/uview-ui/libs/luch-request/core/defaults.js create mode 100644 uni_modules/uview-ui/libs/luch-request/core/dispatchRequest.js create mode 100644 uni_modules/uview-ui/libs/luch-request/core/mergeConfig.js create mode 100644 uni_modules/uview-ui/libs/luch-request/core/settle.js create mode 100644 uni_modules/uview-ui/libs/luch-request/helpers/buildURL.js create mode 100644 uni_modules/uview-ui/libs/luch-request/helpers/combineURLs.js create mode 100644 uni_modules/uview-ui/libs/luch-request/helpers/isAbsoluteURL.js create mode 100644 uni_modules/uview-ui/libs/luch-request/index.d.ts create mode 100644 uni_modules/uview-ui/libs/luch-request/index.js create mode 100644 uni_modules/uview-ui/libs/luch-request/utils.js create mode 100644 uni_modules/uview-ui/libs/luch-request/utils/clone.js create mode 100644 uni_modules/uview-ui/libs/mixin/button.js create mode 100644 uni_modules/uview-ui/libs/mixin/mixin.js create mode 100644 uni_modules/uview-ui/libs/mixin/mpMixin.js create mode 100644 uni_modules/uview-ui/libs/mixin/mpShare.js create mode 100644 uni_modules/uview-ui/libs/mixin/openType.js create mode 100644 uni_modules/uview-ui/libs/mixin/style.js create mode 100644 uni_modules/uview-ui/libs/mixin/touch.js create mode 100644 uni_modules/uview-ui/libs/util/async-validator.js create mode 100644 uni_modules/uview-ui/libs/util/calendar.js create mode 100644 uni_modules/uview-ui/libs/util/dayjs.js create mode 100644 uni_modules/uview-ui/libs/util/emitter.js create mode 100644 uni_modules/uview-ui/libs/util/route.js create mode 100644 uni_modules/uview-ui/package.json create mode 100644 uni_modules/uview-ui/theme.scss diff --git a/App.vue b/App.vue new file mode 100644 index 0000000..92563fd --- /dev/null +++ b/App.vue @@ -0,0 +1,73 @@ +<style lang="scss"> + /* 注意要写在第一行,同时给style标签加入lang="scss"属性 */ + @import "@/uni_modules/uview-ui/index.scss"; + @import "@/static/css/iconfont.css"; +</style> +<script> + export default { + onLaunch: function(options) { + console.log('App Launch'); + var _this =this; + //系统信息 + uni.getSystemInfo({ + success:(res) => { + console.log(res); + _this.$user.session('app_system_info',res); + //检测当前平台,如果是安卓则启动安卓更新 + _this.$user.session('app_system',res.platform); + } + }); + + // #ifdef APP-PLUS + // 锁定屏幕方向 + plus.screen.lockOrientation('portrait-primary'); //锁定 + // #endif + }, + onShow: function() { + console.log('App Show') + var _this =this; + // #ifdef MP + //这里目前微信小程序和抖音功能基本一样 + uni.getProvider({ + service: 'oauth', + success: function (res) { + console.log(res.provider) + var provider = res.provider[0]; + + uni.login({ + provider:provider, + scopes:'auth_base', + success: function (rs) { + console.log(rs); + //发起网络请求 + var post = {loginCode: rs.code}; + _this.$api.post('ycl/user/wx-login', post,function(res){ + console.log(res); + _this.$user.session('openid',res.wxOpenid); + _this.$user.session('user_id',res.id); + if(!_this.$com.isNull(res.token)) + { + _this.$user.session('token',res.token); + } + }); + }, + fail:function(rs){ + console.log('登录失败'+rs.errMsg); + } + }); + + + + + } + }); + + + // #endif + }, + onHide: function() { + console.log('App Hide') + } + } +</script> + diff --git a/README.md b/README.md new file mode 100644 index 0000000..fe48738 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# kjc_uni2 + diff --git a/config/access/index.js b/config/access/index.js new file mode 100644 index 0000000..ef53214 --- /dev/null +++ b/config/access/index.js @@ -0,0 +1,90 @@ + +var access = { + + getAccess:function(k,userinfo){ + if (userinfo && userinfo.access) { + return userinfo.access.indexOf(k) != -1; + } + return false; + }, + + hasAccess:function(arr,userinfo) { + if (userinfo && userinfo.access) { + return userinfo.access.some(_ => arr.indexOf(_) > -1); + } + return false; + }, + + /** 是否一级商户 */ + isSuperType:function(userinfo){ + if (this.getAccess(1002,userinfo)) { + return userinfo.userType == 2 + } + if (this.getAccess(1003,userinfo) || this.getAccess(1004,userinfo)) { + return userinfo.userType == 1 + } + return false; + }, + /** 是否区级商户 */ + isAreaType:function(userinfo){ + if (this.getAccess(1002,userinfo)) { + return userinfo.userType == 1 + } + if (this.getAccess(1003,userinfo) || this.getAccess(1004,userinfo)) { + return userinfo.userType == 2 + } + return false; + }, + /** 是否员工 */ + isYuangongType:function(userinfo){ + if (this.getAccess(1002,userinfo)) { + return userinfo.userType == 3 + } + if (this.getAccess(1004,userinfo)) { + return userinfo.userType == 4 + } + return false; + }, + /** 是否三级 */ + isThirdType:function(userinfo){ + if (this.getAccess(1004,userinfo)) { + return userinfo.userType == 3 + } + return false; + }, + + + // 判断身份 + checkidentity:function(userinfo) { + var res = {} + if (this.isSuperType(userinfo)) { + res = { + levelTxt: '一级', + levelVal: 1 + } + } else if (this.isAreaType(userinfo)) { + res = { + levelTxt: '二级', + levelVal: 2 + } + } else if (this.isThirdType(userinfo)) { + res = { + levelTxt: '店长', + levelVal: 3 + } + /* } else if (this.isYuangongType(userinfo)) { + res = { + levelTxt: '员工', + levelVal: 3 + } */ + } else { + res = { + levelTxt: '用户', + levelVal: 4 + } + } + return res + } +} + +module.exports = access \ No newline at end of file diff --git a/config/api.js b/config/api.js new file mode 100644 index 0000000..1ef08ae --- /dev/null +++ b/config/api.js @@ -0,0 +1,200 @@ +const md5 = require('./md5.js'); +const ENV = require('./env.js'); +var api = { + + /** + * post 请求数据 + * + * @param string urlName url地址 + * @param {object} data 数据对象 + * @param function thenFun then回调 + * @param function catchFun catch回调 + */ + post:function(urlName, data, thenFun,catchFun) { + //数据组合 + var newData = Object.assign(api.setSign(data), ENV.appData()); + uni.$u.http.post(urlName, newData).then(res => { + if(thenFun) thenFun(res); + }).catch((rs) =>{ + if(catchFun) catchFun(rs); + }); + }, + + /** + * get 请求数据 + * + * @param string urlName url地址 + * @param {object} data 数据对象 + * @param function thenFun then回调 + * @param function catchFun catch回调 + */ + get:function(urlName, data, thenFun,catchFun) { + //数据组合 + var newData = Object.assign(api.setSign(data), ENV.appData()); + uni.$u.http.get(urlName,{params:newData}).then(res => { + if(thenFun) thenFun(res); + }).catch((rs) =>{ + if(catchFun) catchFun(rs); + }); + }, + + /** + * put 请求数据 + * + * @param string urlName url地址 + * @param {object} data 数据对象 + * @param function thenFun then回调 + * @param function catchFun catch回调 + */ + put:function(urlName, data, thenFun,catchFun) { + //数据组合 + var newData = Object.assign(api.setSign(data), ENV.appData()); + uni.$u.http.put(urlName, newData).then(res => { + if(thenFun) thenFun(res); + }).catch((rs) =>{ + if(catchFun) catchFun(rs); + }); + }, + /** + * delete 请求数据 + * + * @param string urlName url地址 + * @param {object} data 数据对象 + * @param function thenFun then回调 + * @param function catchFun catch回调 + */ + delete:function(urlName, data, thenFun,catchFun) { + //数据组合 + var newData = Object.assign(api.setSign(data), ENV.appData()); + uni.$u.http.delete(urlName, newData).then(res => { + if(thenFun) thenFun(res); + }).catch((rs) =>{ + if(catchFun) catchFun(rs); + }); + }, + /** + * upload 请求数据 + * + * @param string urlName url地址 + * @param {object} data 数据对象 {filePath:'',fileType:'image'} + * @param function thenFun then回调 + * @param function catchFun catch回调 + */ + upload:function(urlName, data, thenFun,catchFun){ + var newData = data; + uni.$u.http.upload(urlName, { + params: {}, /* 会加在url上 */ + // #ifdef MP-ALIPAY + fileType: newData.fileType, // 仅支付宝小程序,且必填。image/video/audio + // #endif + filePath: newData.filePath, // 要上传文件资源的路径。 + // 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部) + custom: {auth:true,toast:true,}, // 可以加一些自定义参数,在拦截器等地方使用。比如这里我加了一个auth,可在拦截器里拿到,如果true就传token + name: 'files', // 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容 + // #ifdef H5 || APP-PLUS + timeout: 60000, // H5(HBuilderX 2.9.9+)、APP(HBuilderX 2.9.9+) + // #endif + header: { + "Content-Type": "multipart/form-data" + }, /* 会与全局header合并,如有同名属性,局部覆盖全局 */ + formData: {}, // HTTP 请求中其他额外的 form data + // 返回当前请求的task, options。请勿在此处修改options。非必填 + getTask: (task, options) => { + task.onProgressUpdate((res) => { + console.log('上传进度' + res.progress); + console.log('已经上传的数据长度' + res.totalBytesSent); + console.log('预期需要上传的数据总长度' + res.totalBytesExpectedToSend); + }); + }, + }).then(res => { + if(thenFun) thenFun(res); + }).catch((rs) =>{ + if(catchFun) catchFun(rs); + }); + }, + + + setSign:function(data) { + const date = parseInt(new Date().getTime() / 1000); + const str = date + 'xingzhi&&2021'; + data.i_app_timestamp = date; + data.i_app_key = md5(str); + //判断登录 openid 和 unionid 的来源 + // #ifdef APP-PLUS + data.i_app_platform = 'APP-PLUS'; + // #endif + + // #ifdef MP + //微信小程序 + // #ifdef MP-WEIXIN + data.i_app_platform = 'MP-WEIXIN'; + // #endif + + //支付宝小程序 + // #ifdef MP-ALIPAY + data.i_app_platform = 'MP-ALIPAY'; + // #endif + + //百度小程序 + // #ifdef MP-BAIDU + data.i_app_platform = 'MP-BAIDU'; + // #endif + + //头条小程序 + // #ifdef MP-TOUTIAO + data.i_app_platform = 'MP-TOUTIAO'; + // #endif + + //QQ小程序 + // #ifdef MP-QQ + data.i_app_platform = 'MP-QQ'; + // #endif + + //360小程序 + // #ifdef MP-360 + data.i_app_platform = 'MP-360'; + // #endif + + + + // #endif + + // #ifdef H5 + data.i_app_platform = 'H5'; + // #endif + //语言包 + data.i_app_lang ='zh'; + //系统 + data.i_app_system = uni.getStorageSync('app_system'); + + //这里是token 如果登录每个接口都需要这个 + const token = uni.getStorageSync('token'); + if(token.length > 0) + { + data.token = token; + } + return data; + }, + /** + * 返回域名 + * + */ + domain:function() { + return ENV.CdnUrl; + }, + /** + * 返回接口地址 + * + */ + api:function() { + return ENV.ApiUrl; + }, + /** + * 返回接口环境 + * + */ + env:function(){ + return ENV.EnvShow; + }, +} +module.exports = api; diff --git a/config/com.js b/config/com.js new file mode 100644 index 0000000..944f5da --- /dev/null +++ b/config/com.js @@ -0,0 +1,268 @@ +const u_check = uni.$u.test; +var com = { + + /**************uview集成********************************/ + /** + * 验证检测 + * + * @param array || obj || ... value 需要验证的值(可以是 array || obj || ... ) + * @param string type 需要验证的类型(可以是 array || obj || ...) + */ + check:function(value,type) + { + switch(type) { + //数组 + case 'array': + return u_check.array(value); + break; + //json字符串 + case 'json': + return u_check.jsonString(value); + break; + //object对象 + case 'object': + return u_check.object(value); + break; + //email 邮箱号 + case 'email': + return u_check.email(value); + break; + //mobile 手机号 + case 'mobile': + return u_check.mobile(value); + break; + //url 地址 + case 'url': + return u_check.url(value); + break; + //empty 是否为空 + case 'empty': + return u_check.isEmpty(value); + break; + //注解 + //总的来说,年月日之间可以用"/"或者"-"分隔(不能用中文分隔),时分秒之间用":"分隔,数值不能超出范围,如月份不能为13,则检验成功,否则失败。 + //date 日期时间 + case 'date': + return u_check.date(value); + break; + //number 是否十进制数值 + case 'number': + return u_check.number(value); + break; + //digits 是否整数 + case 'digits': + return u_check.digits(value); + break; + //idCard 是否身份证号 + case 'id_card': + return u_check.idCard(value); + break; + //是否车牌号 (例:京A88888) + case 'car_no': + return u_check.carNo(value); + break; + //amount 金额字符串 + case 'amount': + return u_check.amount(value); + break; + //zh 是否汉字 + case 'zh': + return u_check.chinese(value); + break; + //en 是否字母 letter + case 'en': + return u_check.letter(value); + break; + //str 是否字母或者数字 + case 'str': + return u_check.enOrNum(value); + break; + default: + return false; + } + }, + //是否为空 + isNull:function(value) + { + if(value != null || value != 'null') + { + return u_check.isEmpty(value); + } + else + { + return true; + } + }, + + /********************反馈信息****************************************************/ + alert: function(info) { + uni.showModal({ + title: '提示', + content: info, + showCancel: false + }); + }, + + alertDo: function(info, callback) { + uni.showModal({ + title: '提示', + content: info, + showCancel: false, + success() { + callback(); + } + }); + }, + confirm: function(info, callback, title) { + uni.showModal({ + title: title ? title : '提示', + content: info, + success: function(res) { + if (res.confirm) { + callback(); + } + } + }); + }, + + // 加载信息,带遮罩 + showLoading: function(title = '', mask = true) { + uni.showLoading({ + title, + mask + }); + }, + + // 隐藏遮罩 + hideLoading:function () { + uni.hideLoading(); + }, + + showSuccess: function(info) { + uni.showToast({ + title: info, + icon: 'success', + duration: 3000 + }); + }, + showError: function(info) { + uni.showToast({ + title: info, + icon: 'none', + duration: 3000 + }); + }, + showSuccessDo: function(info, time, callback) { + uni.showToast({ + title: info, + icon: 'success', + duration: time, + success: function() { + callback(); + } + }); + }, + /******************加减乘除****************************************************/ + /** + * 加法精度计算 + * + * @param {Object} number 数字 + */ + bcadd: function(a, b){ + var c, d, e; + try { + c = a.toString().split(".")[1].length + } catch(f) { + c = 0 + } + try { + d = b.toString().split(".")[1].length + } catch(f) { + d = 0 + } + return e = Math.pow(10, Math.max(c, d)),(this.bcmul(a, e) + this.bcmul(b, e)) / e + }, + + /** + * 减法精度计算 + * + * @param {Object} number 数字 + */ + bcsub: function(a, b){ + var c, d, e; + try { + c = a.toString().split(".")[1].length + } catch(f) { + c = 0 + } + try { + d = b.toString().split(".")[1].length + } catch(f) { + d = 0 + } + return e = Math.pow(10, Math.max(c, d)),(this.bcmul(a, e) - this.bcmul(b, e)) / e + }, + + /** + * 乘法精度计算 + * + * @param {Object} number 数字 + */ + bcmul: function(a, b){ + var c = 0, + d = a.toString(), + e = b.toString(); + try { + c += d.split(".")[1].length + } catch(f) {} + try { + c += e.split(".")[1].length + } catch(f) {} + return Number(d.replace(".", "")) * Number(e.replace(".", "")) / Math.pow(10, c) + }, + + /** + * 除法精度计算 + * + * @param {Object} number 数字 + */ + bcdiv: function(a, b){ + var c, d, e = 0, + f = 0; + try { + e = a.toString().split(".")[1].length + } catch(g) {} + try { + f = b.toString().split(".")[1].length + } catch(g) {} + return c = Number(a.toString().replace(".", "")),d = Number(b.toString().replace(".", "")),this.bcmul(c / d, Math.pow(10, f - e)) + }, + + /** + * 隐藏手机号 + * @param phone string 数值1 + */ + hidephone:function (phone){ + var reg = /^(\d{3})\d{4}(\d{4})$/; + return phone.replace(reg, "$1****$2") + }, + + removeObserver(arr) { + return arr.map((item) => { + if (Array.isArray(item)) return removeObserver(item) + else if (typeof item === 'object') { + const target = {} + for (let key in item) { + if (key !== '__ob__') { + target[key] = item[key] + } + } + return target + } + return item + }) + } + +} + +module.exports = com \ No newline at end of file diff --git a/config/env.js b/config/env.js new file mode 100644 index 0000000..8690491 --- /dev/null +++ b/config/env.js @@ -0,0 +1,34 @@ +//api接口环境配置 +// production正式/development线上测试/local本地开发 +var NowENV = 'development'; +var ENV_API_URL = { + production:'https://netcharge.spacechong.com/', //正式环境 + development:'https://devnetcharge.spacechong.com/', //开发测试环境 + local:'http://192.168.10.123:8901/',//本地调试环境 + +} +//CdnUrl 环境配置[都默认为线上都行] +var ENV_BASE_URL = { + production:'https://netcharge.spacechong.com', //正式环境 + development:'https://netcharge.spacechong.com', //开发测试环境 + local:'https://netcharge.spacechong.com',//本地调试环境 +} + + + +var ENV = { + EnvShow:NowENV,//当前环境 + ApiUrl:ENV_API_URL[NowENV], //正式 + CdnUrl:ENV_BASE_URL[NowENV], //正式 + + appData:function() { + var data = {}; //版本号 + data.i_app_version = '2.1.2'; + return data; + }, + QQmapkey:'2EBBZ-RAL33-TLM3A-3KVVR-V2LMO-3TFQ4', + + +} + +module.exports = ENV \ No newline at end of file diff --git a/config/jssdk.js b/config/jssdk.js new file mode 100644 index 0000000..94706f0 --- /dev/null +++ b/config/jssdk.js @@ -0,0 +1,701 @@ +! function(e, n) { + "function" == typeof define && (define.amd || define.cmd) ? define(function() { + return n(e) + }) : n(e, !0) +}(window, function(o, e) { + if (!o.jWeixin) { + var n, c = { + config: "preVerifyJSAPI", + onMenuShareTimeline: "menu:share:timeline", + onMenuShareAppMessage: "menu:share:appmessage", + onMenuShareQQ: "menu:share:qq", + onMenuShareWeibo: "menu:share:weiboApp", + onMenuShareQZone: "menu:share:QZone", + previewImage: "imagePreview", + getLocation: "geoLocation", + openProductSpecificView: "openProductViewWithPid", + addCard: "batchAddCard", + openCard: "batchViewCard", + chooseWXPay: "getBrandWCPayRequest", + openEnterpriseRedPacket: "getRecevieBizHongBaoRequest", + startSearchBeacons: "startMonitoringBeacons", + stopSearchBeacons: "stopMonitoringBeacons", + onSearchBeacons: "onBeaconsInRange", + consumeAndShareCard: "consumedShareCard", + openAddress: "editAddress" + }, + a = function() { + var e = {}; + for (var n in c) e[c[n]] = n; + return e + }(), + i = o.document, + t = i.title, + r = navigator.userAgent.toLowerCase(), + s = navigator.platform.toLowerCase(), + d = !(!s.match("mac") && !s.match("win")), + u = -1 != r.indexOf("wxdebugger"), + l = -1 != r.indexOf("micromessenger"), + p = -1 != r.indexOf("android"), + f = -1 != r.indexOf("iphone") || -1 != r.indexOf("ipad"), + m = (n = r.match(/micromessenger\/(\d+\.\d+\.\d+)/) || r.match(/micromessenger\/(\d+\.\d+)/)) ? n[1] : "", + g = { + initStartTime: L(), + initEndTime: 0, + preVerifyStartTime: 0, + preVerifyEndTime: 0 + }, + h = { + version: 1, + appId: "", + initTime: 0, + preVerifyTime: 0, + networkType: "", + isPreVerifyOk: 1, + systemType: f ? 1 : p ? 2 : -1, + clientVersion: m, + url: encodeURIComponent(location.href) + }, + v = {}, + S = { + _completes: [] + }, + y = { + state: 0, + data: {} + }; + O(function() { + g.initEndTime = L() + }); + var I = !1, + _ = [], + w = { + config: function(e) { + B("config", v = e); + var t = !1 !== v.check; + O(function() { + if (t) M(c.config, { + verifyJsApiList: C(v.jsApiList), + verifyOpenTagList: C(v.openTagList) + }, function() { + S._complete = function(e) { + g.preVerifyEndTime = L(), y.state = 1, y.data = e + }, S.success = function(e) { + h.isPreVerifyOk = 0 + }, S.fail = function(e) { + S._fail ? S._fail(e) : y.state = -1 + }; + var t = S._completes; + return t.push(function() { + ! function() { + if (!(d || u || v.debug || m < "6.0.2" || h.systemType < 0)) { + var i = new Image; + h.appId = v.appId, h.initTime = g.initEndTime - g.initStartTime, h.preVerifyTime = g.preVerifyEndTime - g.preVerifyStartTime, w.getNetworkType({ + isInnerInvoke: !0, + success: function(e) { + h.networkType = e.networkType; + var n = "https://open.weixin.qq.com/sdk/report?v=" + h.version + "&o=" + h.isPreVerifyOk + "&s=" + h.systemType + "&c=" + h.clientVersion + "&a=" + h.appId + "&n=" + h.networkType + "&i=" + h.initTime + "&p=" + h.preVerifyTime + "&u=" + h.url; + i.src = n + } + }) + } + }() + }), S.complete = function(e) { + for (var n = 0, i = t.length; n < i; ++n) t[n](); + S._completes = [] + }, S + }()), g.preVerifyStartTime = L(); + else { + y.state = 1; + for (var e = S._completes, n = 0, i = e.length; n < i; ++n) e[n](); + S._completes = [] + } + }), w.invoke || (w.invoke = function(e, n, i) { + o.WeixinJSBridge && WeixinJSBridge.invoke(e, x(n), i) + }, w.on = function(e, n) { + o.WeixinJSBridge && WeixinJSBridge.on(e, n) + }) + }, + ready: function(e) { + 0 != y.state ? e() : (S._completes.push(e), !l && v.debug && e()) + }, + error: function(e) { + m < "6.0.2" || (-1 == y.state ? e(y.data) : S._fail = e) + }, + checkJsApi: function(e) { + M("checkJsApi", { + jsApiList: C(e.jsApiList) + }, (e._complete = function(e) { + if (p) { + var n = e.checkResult; + n && (e.checkResult = JSON.parse(n)) + } + e = function(e) { + var n = e.checkResult; + for (var i in n) { + var t = a[i]; + t && (n[t] = n[i], delete n[i]) + } + return e + }(e) + }, e)) + }, + onMenuShareTimeline: function(e) { + P(c.onMenuShareTimeline, { + complete: function() { + M("shareTimeline", { + title: e.title || t, + desc: e.title || t, + img_url: e.imgUrl || "", + link: e.link || location.href, + type: e.type || "link", + data_url: e.dataUrl || "" + }, e) + } + }, e) + }, + onMenuShareAppMessage: function(n) { + P(c.onMenuShareAppMessage, { + complete: function(e) { + "favorite" === e.scene ? M("sendAppMessage", { + title: n.title || t, + desc: n.desc || "", + link: n.link || location.href, + img_url: n.imgUrl || "", + type: n.type || "link", + data_url: n.dataUrl || "" + }) : M("sendAppMessage", { + title: n.title || t, + desc: n.desc || "", + link: n.link || location.href, + img_url: n.imgUrl || "", + type: n.type || "link", + data_url: n.dataUrl || "" + }, n) + } + }, n) + }, + onMenuShareQQ: function(e) { + P(c.onMenuShareQQ, { + complete: function() { + M("shareQQ", { + title: e.title || t, + desc: e.desc || "", + img_url: e.imgUrl || "", + link: e.link || location.href + }, e) + } + }, e) + }, + onMenuShareWeibo: function(e) { + P(c.onMenuShareWeibo, { + complete: function() { + M("shareWeiboApp", { + title: e.title || t, + desc: e.desc || "", + img_url: e.imgUrl || "", + link: e.link || location.href + }, e) + } + }, e) + }, + onMenuShareQZone: function(e) { + P(c.onMenuShareQZone, { + complete: function() { + M("shareQZone", { + title: e.title || t, + desc: e.desc || "", + img_url: e.imgUrl || "", + link: e.link || location.href + }, e) + } + }, e) + }, + updateTimelineShareData: function(e) { + M("updateTimelineShareData", { + title: e.title, + link: e.link, + imgUrl: e.imgUrl + }, e) + }, + updateAppMessageShareData: function(e) { + M("updateAppMessageShareData", { + title: e.title, + desc: e.desc, + link: e.link, + imgUrl: e.imgUrl + }, e) + }, + startRecord: function(e) { + M("startRecord", {}, e) + }, + stopRecord: function(e) { + M("stopRecord", {}, e) + }, + onVoiceRecordEnd: function(e) { + P("onVoiceRecordEnd", e) + }, + playVoice: function(e) { + M("playVoice", { + localId: e.localId + }, e) + }, + pauseVoice: function(e) { + M("pauseVoice", { + localId: e.localId + }, e) + }, + stopVoice: function(e) { + M("stopVoice", { + localId: e.localId + }, e) + }, + onVoicePlayEnd: function(e) { + P("onVoicePlayEnd", e) + }, + uploadVoice: function(e) { + M("uploadVoice", { + localId: e.localId, + isShowProgressTips: 0 == e.isShowProgressTips ? 0 : 1 + }, e) + }, + downloadVoice: function(e) { + M("downloadVoice", { + serverId: e.serverId, + isShowProgressTips: 0 == e.isShowProgressTips ? 0 : 1 + }, e) + }, + translateVoice: function(e) { + M("translateVoice", { + localId: e.localId, + isShowProgressTips: 0 == e.isShowProgressTips ? 0 : 1 + }, e) + }, + chooseImage: function(e) { + M("chooseImage", { + scene: "1|2", + count: e.count || 9, + sizeType: e.sizeType || ["original", "compressed"], + sourceType: e.sourceType || ["album", "camera"] + }, (e._complete = function(e) { + if (p) { + var n = e.localIds; + try { + n && (e.localIds = JSON.parse(n)) + } catch (e) {} + } + }, e)) + }, + getLocation: function(e) {}, + previewImage: function(e) { + M(c.previewImage, { + current: e.current, + urls: e.urls + }, e) + }, + uploadImage: function(e) { + M("uploadImage", { + localId: e.localId, + isShowProgressTips: 0 == e.isShowProgressTips ? 0 : 1 + }, e) + }, + downloadImage: function(e) { + M("downloadImage", { + serverId: e.serverId, + isShowProgressTips: 0 == e.isShowProgressTips ? 0 : 1 + }, e) + }, + getLocalImgData: function(e) { + !1 === I ? (I = !0, M("getLocalImgData", { + localId: e.localId + }, (e._complete = function(e) { + if (I = !1, 0 < _.length) { + var n = _.shift(); + wx.getLocalImgData(n) + } + }, e))) : _.push(e) + }, + getNetworkType: function(e) { + M("getNetworkType", {}, (e._complete = function(e) { + e = function(e) { + var n = e.errMsg; + e.errMsg = "getNetworkType:ok"; + var i = e.subtype; + if (delete e.subtype, i) e.networkType = i; + else { + var t = n.indexOf(":"), + o = n.substring(t + 1); + switch (o) { + case "wifi": + case "edge": + case "wwan": + e.networkType = o; + break; + default: + e.errMsg = "getNetworkType:fail" + } + } + return e + }(e) + }, e)) + }, + openLocation: function(e) { + M("openLocation", { + latitude: e.latitude, + longitude: e.longitude, + name: e.name || "", + address: e.address || "", + scale: e.scale || 28, + infoUrl: e.infoUrl || "" + }, e) + }, + getLocation: function(e) { + M(c.getLocation, { + type: (e = e || {}).type || "wgs84" + }, (e._complete = function(e) { + delete e.type + }, e)) + }, + hideOptionMenu: function(e) { + M("hideOptionMenu", {}, e) + }, + showOptionMenu: function(e) { + M("showOptionMenu", {}, e) + }, + closeWindow: function(e) { + M("closeWindow", {}, e = e || {}) + }, + hideMenuItems: function(e) { + M("hideMenuItems", { + menuList: e.menuList + }, e) + }, + showMenuItems: function(e) { + M("showMenuItems", { + menuList: e.menuList + }, e) + }, + hideAllNonBaseMenuItem: function(e) { + M("hideAllNonBaseMenuItem", {}, e) + }, + showAllNonBaseMenuItem: function(e) { + M("showAllNonBaseMenuItem", {}, e) + }, + scanQRCode: function(e) { + M("scanQRCode", { + needResult: (e = e || {}).needResult || 0, + scanType: e.scanType || ["qrCode", "barCode"] + }, (e._complete = function(e) { + if (f) { + var n = e.resultStr; + if (n) { + var i = JSON.parse(n); + e.resultStr = i && i.scan_code && i.scan_code.scan_result + } + } + }, e)) + }, + openAddress: function(e) { + M(c.openAddress, {}, (e._complete = function(e) { + e = function(e) { + return e.postalCode = e.addressPostalCode, delete e.addressPostalCode, e.provinceName = e.proviceFirstStageName, delete e.proviceFirstStageName, e.cityName = e.addressCitySecondStageName, delete e.addressCitySecondStageName, e.countryName = e.addressCountiesThirdStageName, delete e.addressCountiesThirdStageName, e.detailInfo = e.addressDetailInfo, delete e.addressDetailInfo, e + }(e) + }, e)) + }, + openProductSpecificView: function(e) { + M(c.openProductSpecificView, { + pid: e.productId, + view_type: e.viewType || 0, + ext_info: e.extInfo + }, e) + }, + addCard: function(e) { + for (var n = e.cardList, i = [], t = 0, o = n.length; t < o; ++t) { + var r = n[t], + a = { + card_id: r.cardId, + card_ext: r.cardExt + }; + i.push(a) + } + M(c.addCard, { + card_list: i + }, (e._complete = function(e) { + var n = e.card_list; + if (n) { + for (var i = 0, t = (n = JSON.parse(n)).length; i < t; ++i) { + var o = n[i]; + o.cardId = o.card_id, o.cardExt = o.card_ext, o.isSuccess = !!o.is_succ, delete o.card_id, delete o.card_ext, delete o.is_succ + } + e.cardList = n, delete e.card_list + } + }, e)) + }, + chooseCard: function(e) { + M("chooseCard", { + app_id: v.appId, + location_id: e.shopId || "", + sign_type: e.signType || "SHA1", + card_id: e.cardId || "", + card_type: e.cardType || "", + card_sign: e.cardSign, + time_stamp: e.timestamp + "", + nonce_str: e.nonceStr + }, (e._complete = function(e) { + e.cardList = e.choose_card_info, delete e.choose_card_info + }, e)) + }, + openCard: function(e) { + for (var n = e.cardList, i = [], t = 0, o = n.length; t < o; ++t) { + var r = n[t], + a = { + card_id: r.cardId, + code: r.code + }; + i.push(a) + } + M(c.openCard, { + card_list: i + }, e) + }, + consumeAndShareCard: function(e) { + M(c.consumeAndShareCard, { + consumedCardId: e.cardId, + consumedCode: e.code + }, e) + }, + chooseWXPay: function(e) { + M(c.chooseWXPay, V(e), e) + }, + openEnterpriseRedPacket: function(e) { + M(c.openEnterpriseRedPacket, V(e), e) + }, + startSearchBeacons: function(e) { + M(c.startSearchBeacons, { + ticket: e.ticket + }, e) + }, + stopSearchBeacons: function(e) { + M(c.stopSearchBeacons, {}, e) + }, + onSearchBeacons: function(e) { + P(c.onSearchBeacons, e) + }, + openEnterpriseChat: function(e) { + M("openEnterpriseChat", { + useridlist: e.userIds, + chatname: e.groupName + }, e) + }, + launchMiniProgram: function(e) { + M("launchMiniProgram", { + targetAppId: e.targetAppId, + path: function(e) { + if ("string" == typeof e && 0 < e.length) { + var n = e.split("?")[0], + i = e.split("?")[1]; + return n += ".html", void 0 !== i ? n + "?" + i : n + } + }(e.path), + envVersion: e.envVersion + }, e) + }, + openBusinessView: function(e) { + M("openBusinessView", { + businessType: e.businessType, + queryString: e.queryString || "", + envVersion: e.envVersion + }, (e._complete = function(n) { + if (p) { + var e = n.extraData; + if (e) try { + n.extraData = JSON.parse(e) + } catch (e) { + n.extraData = {} + } + } + }, e)) + }, + miniProgram: { + navigateBack: function(e) { + e = e || {}, O(function() { + M("invokeMiniProgramAPI", { + name: "navigateBack", + arg: { + delta: e.delta || 1 + } + }, e) + }) + }, + navigateTo: function(e) { + O(function() { + M("invokeMiniProgramAPI", { + name: "navigateTo", + arg: { + url: e.url + } + }, e) + }) + }, + redirectTo: function(e) { + O(function() { + M("invokeMiniProgramAPI", { + name: "redirectTo", + arg: { + url: e.url + } + }, e) + }) + }, + switchTab: function(e) { + O(function() { + M("invokeMiniProgramAPI", { + name: "switchTab", + arg: { + url: e.url + } + }, e) + }) + }, + reLaunch: function(e) { + O(function() { + M("invokeMiniProgramAPI", { + name: "reLaunch", + arg: { + url: e.url + } + }, e) + }) + }, + postMessage: function(e) { + O(function() { + M("invokeMiniProgramAPI", { + name: "postMessage", + arg: e.data || {} + }, e) + }) + }, + getEnv: function(e) { + O(function() { + e({ + miniprogram: "miniprogram" === o.__wxjs_environment + }) + }) + } + } + }, + T = 1, + k = {}; + return i.addEventListener("error", function(e) { + if (!p) { + var n = e.target, + i = n.tagName, + t = n.src; + if ("IMG" == i || "VIDEO" == i || "AUDIO" == i || "SOURCE" == i) + if (-1 != t.indexOf("wxlocalresource://")) { + e.preventDefault(), e.stopPropagation(); + var o = n["wx-id"]; + if (o || (o = T++, n["wx-id"] = o), k[o]) return; + k[o] = !0, wx.ready(function() { + wx.getLocalImgData({ + localId: t, + success: function(e) { + n.src = e.localData + } + }) + }) + } + } + }, !0), i.addEventListener("load", function(e) { + if (!p) { + var n = e.target, + i = n.tagName; + n.src; + if ("IMG" == i || "VIDEO" == i || "AUDIO" == i || "SOURCE" == i) { + var t = n["wx-id"]; + t && (k[t] = !1) + } + } + }, !0), e && (o.wx = o.jWeixin = w), w + } + + function M(n, e, i) { + o.WeixinJSBridge ? WeixinJSBridge.invoke(n, x(e), function(e) { + A(n, e, i) + }) : B(n, i) + } + + function P(n, i, t) { + o.WeixinJSBridge ? WeixinJSBridge.on(n, function(e) { + t && t.trigger && t.trigger(e), A(n, e, i) + }) : B(n, t || i) + } + + function x(e) { + return (e = e || {}).appId = v.appId, e.verifyAppId = v.appId, e.verifySignType = "sha1", e.verifyTimestamp = v.timestamp + "", e.verifyNonceStr = v.nonceStr, e.verifySignature = v.signature, e + } + + function V(e) { + return { + timeStamp: e.timestamp + "", + nonceStr: e.nonceStr, + package: e.package, + paySign: e.paySign, + signType: e.signType || "SHA1" + } + } + + function A(e, n, i) { + "openEnterpriseChat" != e && "openBusinessView" !== e || (n.errCode = n.err_code), delete n.err_code, delete n.err_desc, delete n.err_detail; + var t = n.errMsg; + t || (t = n.err_msg, delete n.err_msg, t = function(e, n) { + var i = e, + t = a[i]; + t && (i = t); + var o = "ok"; + if (n) { + var r = n.indexOf(":"); + "confirm" == (o = n.substring(r + 1)) && (o = "ok"), "failed" == o && (o = "fail"), -1 != o.indexOf("failed_") && (o = o.substring(7)), -1 != o.indexOf("fail_") && (o = o.substring(5)), "access denied" != (o = (o = o.replace(/_/g, " ")).toLowerCase()) && "no permission to execute" != o || (o = "permission denied"), "config" == i && "function not exist" == o && (o = "ok"), "" == o && (o = "fail") + } + return n = i + ":" + o + }(e, t), n.errMsg = t), (i = i || {})._complete && (i._complete(n), delete i._complete), t = n.errMsg || "", v.debug && !i.isInnerInvoke && alert(JSON.stringify(n)); + var o = t.indexOf(":"); + switch (t.substring(o + 1)) { + case "ok": + i.success && i.success(n); + break; + case "cancel": + i.cancel && i.cancel(n); + break; + default: + i.fail && i.fail(n) + } + i.complete && i.complete(n) + } + + function C(e) { + if (e) { + for (var n = 0, i = e.length; n < i; ++n) { + var t = e[n], + o = c[t]; + o && (e[n] = o) + } + return e + } + } + + function B(e, n) { + if (!(!v.debug || n && n.isInnerInvoke)) { + var i = a[e]; + i && (e = i), n && n._complete && delete n._complete, console.log('"' + e + '",', n || "") + } + } + + function L() { + return (new Date).getTime() + } + + function O(e) { + l && (o.WeixinJSBridge ? e() : i.addEventListener && i.addEventListener("WeixinJSBridgeReady", e, !1)) + } +}); \ No newline at end of file diff --git a/config/map/amap-wx.130.js b/config/map/amap-wx.130.js new file mode 100644 index 0000000..718d6ef --- /dev/null +++ b/config/map/amap-wx.130.js @@ -0,0 +1,31 @@ +function AMapWX(a){this.key=a.key;this.requestConfig={key:a.key,s:"rsx",platform:"WXJS",appname:a.key,sdkversion:"1.2.0",logversion:"2.0"};this.MeRequestConfig={key:a.key,serviceName:"https://restapi.amap.com/rest/me"}} +AMapWX.prototype.getWxLocation=function(a,b){wx.getLocation({type:"gcj02",success:function(c){c=c.longitude+","+c.latitude;wx.setStorage({key:"userLocation",data:c});b(c)},fail:function(c){wx.getStorage({key:"userLocation",success:function(d){d.data&&b(d.data)}});a.fail({errCode:"0",errMsg:c.errMsg||""})}})}; +AMapWX.prototype.getMEKeywordsSearch=function(a){if(!a.options)return a.fail({errCode:"0",errMsg:"\u7f3a\u5c11\u5fc5\u8981\u53c2\u6570"});var b=a.options,c=this.MeRequestConfig,d={key:c.key,s:"rsx",platform:"WXJS",appname:a.key,sdkversion:"1.2.0",logversion:"2.0"};b.layerId&&(d.layerId=b.layerId);b.keywords&&(d.keywords=b.keywords);b.city&&(d.city=b.city);b.filter&&(d.filter=b.filter);b.sortrule&&(d.sortrule=b.sortrule);b.pageNum&&(d.pageNum=b.pageNum);b.pageSize&&(d.pageSize=b.pageSize);b.sig&&(d.sig= +b.sig);wx.request({url:c.serviceName+"/cpoint/datasearch/local",data:d,method:"GET",header:{"content-type":"application/json"},success:function(e){(e=e.data)&&e.status&&"1"===e.status&&0===e.code?a.success(e.data):a.fail({errCode:"0",errMsg:e})},fail:function(e){a.fail({errCode:"0",errMsg:e.errMsg||""})}})}; +AMapWX.prototype.getMEIdSearch=function(a){if(!a.options)return a.fail({errCode:"0",errMsg:"\u7f3a\u5c11\u5fc5\u8981\u53c2\u6570"});var b=a.options,c=this.MeRequestConfig,d={key:c.key,s:"rsx",platform:"WXJS",appname:a.key,sdkversion:"1.2.0",logversion:"2.0"};b.layerId&&(d.layerId=b.layerId);b.id&&(d.id=b.id);b.sig&&(d.sig=b.sig);wx.request({url:c.serviceName+"/cpoint/datasearch/id",data:d,method:"GET",header:{"content-type":"application/json"},success:function(e){(e=e.data)&&e.status&&"1"===e.status&& +0===e.code?a.success(e.data):a.fail({errCode:"0",errMsg:e})},fail:function(e){a.fail({errCode:"0",errMsg:e.errMsg||""})}})}; +AMapWX.prototype.getMEPolygonSearch=function(a){if(!a.options)return a.fail({errCode:"0",errMsg:"\u7f3a\u5c11\u5fc5\u8981\u53c2\u6570"});var b=a.options,c=this.MeRequestConfig,d={key:c.key,s:"rsx",platform:"WXJS",appname:a.key,sdkversion:"1.2.0",logversion:"2.0"};b.layerId&&(d.layerId=b.layerId);b.keywords&&(d.keywords=b.keywords);b.polygon&&(d.polygon=b.polygon);b.filter&&(d.filter=b.filter);b.sortrule&&(d.sortrule=b.sortrule);b.pageNum&&(d.pageNum=b.pageNum);b.pageSize&&(d.pageSize=b.pageSize); +b.sig&&(d.sig=b.sig);wx.request({url:c.serviceName+"/cpoint/datasearch/polygon",data:d,method:"GET",header:{"content-type":"application/json"},success:function(e){(e=e.data)&&e.status&&"1"===e.status&&0===e.code?a.success(e.data):a.fail({errCode:"0",errMsg:e})},fail:function(e){a.fail({errCode:"0",errMsg:e.errMsg||""})}})}; +AMapWX.prototype.getMEaroundSearch=function(a){if(!a.options)return a.fail({errCode:"0",errMsg:"\u7f3a\u5c11\u5fc5\u8981\u53c2\u6570"});var b=a.options,c=this.MeRequestConfig,d={key:c.key,s:"rsx",platform:"WXJS",appname:a.key,sdkversion:"1.2.0",logversion:"2.0"};b.layerId&&(d.layerId=b.layerId);b.keywords&&(d.keywords=b.keywords);b.center&&(d.center=b.center);b.radius&&(d.radius=b.radius);b.filter&&(d.filter=b.filter);b.sortrule&&(d.sortrule=b.sortrule);b.pageNum&&(d.pageNum=b.pageNum);b.pageSize&& +(d.pageSize=b.pageSize);b.sig&&(d.sig=b.sig);wx.request({url:c.serviceName+"/cpoint/datasearch/around",data:d,method:"GET",header:{"content-type":"application/json"},success:function(e){(e=e.data)&&e.status&&"1"===e.status&&0===e.code?a.success(e.data):a.fail({errCode:"0",errMsg:e})},fail:function(e){a.fail({errCode:"0",errMsg:e.errMsg||""})}})}; +AMapWX.prototype.getGeo=function(a){var b=this.requestConfig,c=a.options;b={key:this.key,extensions:"all",s:b.s,platform:b.platform,appname:this.key,sdkversion:b.sdkversion,logversion:b.logversion};c.address&&(b.address=c.address);c.city&&(b.city=c.city);c.batch&&(b.batch=c.batch);c.sig&&(b.sig=c.sig);wx.request({url:"https://restapi.amap.com/v3/geocode/geo",data:b,method:"GET",header:{"content-type":"application/json"},success:function(d){(d=d.data)&&d.status&&"1"===d.status?a.success(d):a.fail({errCode:"0", +errMsg:d})},fail:function(d){a.fail({errCode:"0",errMsg:d.errMsg||""})}})}; +AMapWX.prototype.getRegeo=function(a){function b(d){var e=c.requestConfig;wx.request({url:"https://restapi.amap.com/v3/geocode/regeo",data:{key:c.key,location:d,extensions:"all",s:e.s,platform:e.platform,appname:c.key,sdkversion:e.sdkversion,logversion:e.logversion},method:"GET",header:{"content-type":"application/json"},success:function(g){if(g.data.status&&"1"==g.data.status){g=g.data.regeocode;var h=g.addressComponent,f=[],k=g.roads[0].name+"\u9644\u8fd1",m=d.split(",")[0],n=d.split(",")[1];if(g.pois&& +g.pois[0]){k=g.pois[0].name+"\u9644\u8fd1";var l=g.pois[0].location;l&&(m=parseFloat(l.split(",")[0]),n=parseFloat(l.split(",")[1]))}h.provice&&f.push(h.provice);h.city&&f.push(h.city);h.district&&f.push(h.district);h.streetNumber&&h.streetNumber.street&&h.streetNumber.number?(f.push(h.streetNumber.street),f.push(h.streetNumber.number)):f.push(g.roads[0].name);f=f.join("");a.success([{iconPath:a.iconPath,width:a.iconWidth,height:a.iconHeight,name:f,desc:k,longitude:m,latitude:n,id:0,regeocodeData:g}])}else a.fail({errCode:g.data.infocode, +errMsg:g.data.info})},fail:function(g){a.fail({errCode:"0",errMsg:g.errMsg||""})}})}var c=this;a.location?b(a.location):c.getWxLocation(a,function(d){b(d)})}; +AMapWX.prototype.getWeather=function(a){function b(g){var h="base";a.type&&"forecast"==a.type&&(h="all");wx.request({url:"https://restapi.amap.com/v3/weather/weatherInfo",data:{key:d.key,city:g,extensions:h,s:e.s,platform:e.platform,appname:d.key,sdkversion:e.sdkversion,logversion:e.logversion},method:"GET",header:{"content-type":"application/json"},success:function(f){if(f.data.status&&"1"==f.data.status)if(f.data.lives){if((f=f.data.lives)&&0<f.length){f=f[0];var k={city:{text:"\u57ce\u5e02",data:f.city}, +weather:{text:"\u5929\u6c14",data:f.weather},temperature:{text:"\u6e29\u5ea6",data:f.temperature},winddirection:{text:"\u98ce\u5411",data:f.winddirection+"\u98ce"},windpower:{text:"\u98ce\u529b",data:f.windpower+"\u7ea7"},humidity:{text:"\u6e7f\u5ea6",data:f.humidity+"%"}};k.liveData=f;a.success(k)}}else f.data.forecasts&&f.data.forecasts[0]&&a.success({forecast:f.data.forecasts[0]});else a.fail({errCode:f.data.infocode,errMsg:f.data.info})},fail:function(f){a.fail({errCode:"0",errMsg:f.errMsg||""})}})} +function c(g){wx.request({url:"https://restapi.amap.com/v3/geocode/regeo",data:{key:d.key,location:g,extensions:"all",s:e.s,platform:e.platform,appname:d.key,sdkversion:e.sdkversion,logversion:e.logversion},method:"GET",header:{"content-type":"application/json"},success:function(h){if(h.data.status&&"1"==h.data.status){h=h.data.regeocode;if(h.addressComponent)var f=h.addressComponent.adcode;else h.aois&&0<h.aois.length&&(f=h.aois[0].adcode);b(f)}else a.fail({errCode:h.data.infocode,errMsg:h.data.info})}, +fail:function(h){a.fail({errCode:"0",errMsg:h.errMsg||""})}})}var d=this,e=d.requestConfig;a.city?b(a.city):d.getWxLocation(a,function(g){c(g)})}; +AMapWX.prototype.getPoiAround=function(a){function b(e){e={key:c.key,location:e,s:d.s,platform:d.platform,appname:c.key,sdkversion:d.sdkversion,logversion:d.logversion};a.querytypes&&(e.types=a.querytypes);a.querykeywords&&(e.keywords=a.querykeywords);wx.request({url:"https://restapi.amap.com/v3/place/around",data:e,method:"GET",header:{"content-type":"application/json"},success:function(g){if(g.data.status&&"1"==g.data.status){if((g=g.data)&&g.pois){for(var h=[],f=0;f<g.pois.length;f++){var k=0== +f?a.iconPathSelected:a.iconPath;h.push({latitude:parseFloat(g.pois[f].location.split(",")[1]),longitude:parseFloat(g.pois[f].location.split(",")[0]),iconPath:k,width:22,height:32,id:f,name:g.pois[f].name,address:g.pois[f].address})}a.success({markers:h,poisData:g.pois})}}else a.fail({errCode:g.data.infocode,errMsg:g.data.info})},fail:function(g){a.fail({errCode:"0",errMsg:g.errMsg||""})}})}var c=this,d=c.requestConfig;a.location?b(a.location):c.getWxLocation(a,function(e){b(e)})}; +AMapWX.prototype.getStaticmap=function(a){function b(e){c.push("location="+e);a.zoom&&c.push("zoom="+a.zoom);a.size&&c.push("size="+a.size);a.scale&&c.push("scale="+a.scale);a.markers&&c.push("markers="+a.markers);a.labels&&c.push("labels="+a.labels);a.paths&&c.push("paths="+a.paths);a.traffic&&c.push("traffic="+a.traffic);e="https://restapi.amap.com/v3/staticmap?"+c.join("&");a.success({url:e})}var c=[];c.push("key="+this.key);var d=this.requestConfig;c.push("s="+d.s);c.push("platform="+d.platform); +c.push("appname="+d.appname);c.push("sdkversion="+d.sdkversion);c.push("logversion="+d.logversion);a.location?b(a.location):this.getWxLocation(a,function(e){b(e)})}; +AMapWX.prototype.getInputtips=function(a){var b=Object.assign({},this.requestConfig);a.location&&(b.location=a.location);a.keywords&&(b.keywords=a.keywords);a.type&&(b.type=a.type);a.city&&(b.city=a.city);a.citylimit&&(b.citylimit=a.citylimit);wx.request({url:"https://restapi.amap.com/v3/assistant/inputtips",data:b,method:"GET",header:{"content-type":"application/json"},success:function(c){c&&c.data&&c.data.tips&&a.success({tips:c.data.tips})},fail:function(c){a.fail({errCode:"0",errMsg:c.errMsg|| +""})}})}; +AMapWX.prototype.getDrivingRoute=function(a){var b=Object.assign({},this.requestConfig);a.origin&&(b.origin=a.origin);a.destination&&(b.destination=a.destination);a.strategy&&(b.strategy=a.strategy);a.waypoints&&(b.waypoints=a.waypoints);a.avoidpolygons&&(b.avoidpolygons=a.avoidpolygons);a.avoidroad&&(b.avoidroad=a.avoidroad);wx.request({url:"https://restapi.amap.com/v3/direction/driving",data:b,method:"GET",header:{"content-type":"application/json"},success:function(c){c&&c.data&&c.data.route&&a.success({paths:c.data.route.paths, +taxi_cost:c.data.route.taxi_cost||""})},fail:function(c){a.fail({errCode:"0",errMsg:c.errMsg||""})}})}; +AMapWX.prototype.getWalkingRoute=function(a){var b=Object.assign({},this.requestConfig);a.origin&&(b.origin=a.origin);a.destination&&(b.destination=a.destination);wx.request({url:"https://restapi.amap.com/v3/direction/walking",data:b,method:"GET",header:{"content-type":"application/json"},success:function(c){c&&c.data&&c.data.route&&a.success({paths:c.data.route.paths})},fail:function(c){a.fail({errCode:"0",errMsg:c.errMsg||""})}})}; +AMapWX.prototype.getTransitRoute=function(a){var b=Object.assign({},this.requestConfig);a.origin&&(b.origin=a.origin);a.destination&&(b.destination=a.destination);a.strategy&&(b.strategy=a.strategy);a.city&&(b.city=a.city);a.cityd&&(b.cityd=a.cityd);wx.request({url:"https://restapi.amap.com/v3/direction/transit/integrated",data:b,method:"GET",header:{"content-type":"application/json"},success:function(c){c&&c.data&&c.data.route&&(c=c.data.route,a.success({distance:c.distance||"",taxi_cost:c.taxi_cost|| +"",transits:c.transits}))},fail:function(c){a.fail({errCode:"0",errMsg:c.errMsg||""})}})}; +AMapWX.prototype.getRidingRoute=function(a){var b=Object.assign({},this.requestConfig);a.origin&&(b.origin=a.origin);a.destination&&(b.destination=a.destination);wx.request({url:"https://restapi.amap.com/v3/direction/riding",data:b,method:"GET",header:{"content-type":"application/json"},success:function(c){c&&c.data&&c.data.route&&a.success({paths:c.data.route.paths})},fail:function(c){a.fail({errCode:"0",errMsg:c.errMsg||""})}})};module.exports.AMapWX=AMapWX; \ No newline at end of file diff --git a/config/map/qqmap-wx-jssdk.js b/config/map/qqmap-wx-jssdk.js new file mode 100644 index 0000000..595b65b --- /dev/null +++ b/config/map/qqmap-wx-jssdk.js @@ -0,0 +1,1122 @@ +/** + * 微信小程序JavaScriptSDK + * + * @version 1.2 + * @date 2019-03-06 + */ + +var ERROR_CONF = { + KEY_ERR: 311, + KEY_ERR_MSG: 'key格式错误', + PARAM_ERR: 310, + PARAM_ERR_MSG: '请求参数信息有误', + SYSTEM_ERR: 600, + SYSTEM_ERR_MSG: '系统错误', + WX_ERR_CODE: 1000, + WX_OK_CODE: 200 +}; +var BASE_URL = 'https://apis.map.qq.com/ws/'; +var URL_SEARCH = BASE_URL + 'place/v1/search'; +var URL_SUGGESTION = BASE_URL + 'place/v1/suggestion'; +var URL_GET_GEOCODER = BASE_URL + 'geocoder/v1/'; +var URL_CITY_LIST = BASE_URL + 'district/v1/list'; +var URL_AREA_LIST = BASE_URL + 'district/v1/getchildren'; +var URL_DISTANCE = BASE_URL + 'distance/v1/'; +var URL_DIRECTION = BASE_URL + 'direction/v1/'; +var MODE = { + driving: 'driving', + transit: 'transit' +}; +var EARTH_RADIUS = 6378136.49; +var Utils = { + /** + * md5加密方法 + * 版权所有©2011 Sebastian Tschan,https://blueimp.net + */ + safeAdd(x, y) { + var lsw = (x & 0xffff) + (y & 0xffff); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xffff); + }, + bitRotateLeft(num, cnt) { + return (num << cnt) | (num >>> (32 - cnt)); + }, + md5cmn(q, a, b, x, s, t) { + return this.safeAdd(this.bitRotateLeft(this.safeAdd(this.safeAdd(a, q), this.safeAdd(x, t)), s), b); + }, + md5ff(a, b, c, d, x, s, t) { + return this.md5cmn((b & c) | (~b & d), a, b, x, s, t); + }, + md5gg(a, b, c, d, x, s, t) { + return this.md5cmn((b & d) | (c & ~d), a, b, x, s, t); + }, + md5hh(a, b, c, d, x, s, t) { + return this.md5cmn(b ^ c ^ d, a, b, x, s, t); + }, + md5ii(a, b, c, d, x, s, t) { + return this.md5cmn(c ^ (b | ~d), a, b, x, s, t); + }, + binlMD5(x, len) { + /* append padding */ + x[len >> 5] |= 0x80 << (len % 32); + x[((len + 64) >>> 9 << 4) + 14] = len; + + var i; + var olda; + var oldb; + var oldc; + var oldd; + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + + for (i = 0; i < x.length; i += 16) { + olda = a; + oldb = b; + oldc = c; + oldd = d; + + a = this.md5ff(a, b, c, d, x[i], 7, -680876936); + d = this.md5ff(d, a, b, c, x[i + 1], 12, -389564586); + c = this.md5ff(c, d, a, b, x[i + 2], 17, 606105819); + b = this.md5ff(b, c, d, a, x[i + 3], 22, -1044525330); + a = this.md5ff(a, b, c, d, x[i + 4], 7, -176418897); + d = this.md5ff(d, a, b, c, x[i + 5], 12, 1200080426); + c = this.md5ff(c, d, a, b, x[i + 6], 17, -1473231341); + b = this.md5ff(b, c, d, a, x[i + 7], 22, -45705983); + a = this.md5ff(a, b, c, d, x[i + 8], 7, 1770035416); + d = this.md5ff(d, a, b, c, x[i + 9], 12, -1958414417); + c = this.md5ff(c, d, a, b, x[i + 10], 17, -42063); + b = this.md5ff(b, c, d, a, x[i + 11], 22, -1990404162); + a = this.md5ff(a, b, c, d, x[i + 12], 7, 1804603682); + d = this.md5ff(d, a, b, c, x[i + 13], 12, -40341101); + c = this.md5ff(c, d, a, b, x[i + 14], 17, -1502002290); + b = this.md5ff(b, c, d, a, x[i + 15], 22, 1236535329); + + a = this.md5gg(a, b, c, d, x[i + 1], 5, -165796510); + d = this.md5gg(d, a, b, c, x[i + 6], 9, -1069501632); + c = this.md5gg(c, d, a, b, x[i + 11], 14, 643717713); + b = this.md5gg(b, c, d, a, x[i], 20, -373897302); + a = this.md5gg(a, b, c, d, x[i + 5], 5, -701558691); + d = this.md5gg(d, a, b, c, x[i + 10], 9, 38016083); + c = this.md5gg(c, d, a, b, x[i + 15], 14, -660478335); + b = this.md5gg(b, c, d, a, x[i + 4], 20, -405537848); + a = this.md5gg(a, b, c, d, x[i + 9], 5, 568446438); + d = this.md5gg(d, a, b, c, x[i + 14], 9, -1019803690); + c = this.md5gg(c, d, a, b, x[i + 3], 14, -187363961); + b = this.md5gg(b, c, d, a, x[i + 8], 20, 1163531501); + a = this.md5gg(a, b, c, d, x[i + 13], 5, -1444681467); + d = this.md5gg(d, a, b, c, x[i + 2], 9, -51403784); + c = this.md5gg(c, d, a, b, x[i + 7], 14, 1735328473); + b = this.md5gg(b, c, d, a, x[i + 12], 20, -1926607734); + + a = this.md5hh(a, b, c, d, x[i + 5], 4, -378558); + d = this.md5hh(d, a, b, c, x[i + 8], 11, -2022574463); + c = this.md5hh(c, d, a, b, x[i + 11], 16, 1839030562); + b = this.md5hh(b, c, d, a, x[i + 14], 23, -35309556); + a = this.md5hh(a, b, c, d, x[i + 1], 4, -1530992060); + d = this.md5hh(d, a, b, c, x[i + 4], 11, 1272893353); + c = this.md5hh(c, d, a, b, x[i + 7], 16, -155497632); + b = this.md5hh(b, c, d, a, x[i + 10], 23, -1094730640); + a = this.md5hh(a, b, c, d, x[i + 13], 4, 681279174); + d = this.md5hh(d, a, b, c, x[i], 11, -358537222); + c = this.md5hh(c, d, a, b, x[i + 3], 16, -722521979); + b = this.md5hh(b, c, d, a, x[i + 6], 23, 76029189); + a = this.md5hh(a, b, c, d, x[i + 9], 4, -640364487); + d = this.md5hh(d, a, b, c, x[i + 12], 11, -421815835); + c = this.md5hh(c, d, a, b, x[i + 15], 16, 530742520); + b = this.md5hh(b, c, d, a, x[i + 2], 23, -995338651); + + a = this.md5ii(a, b, c, d, x[i], 6, -198630844); + d = this.md5ii(d, a, b, c, x[i + 7], 10, 1126891415); + c = this.md5ii(c, d, a, b, x[i + 14], 15, -1416354905); + b = this.md5ii(b, c, d, a, x[i + 5], 21, -57434055); + a = this.md5ii(a, b, c, d, x[i + 12], 6, 1700485571); + d = this.md5ii(d, a, b, c, x[i + 3], 10, -1894986606); + c = this.md5ii(c, d, a, b, x[i + 10], 15, -1051523); + b = this.md5ii(b, c, d, a, x[i + 1], 21, -2054922799); + a = this.md5ii(a, b, c, d, x[i + 8], 6, 1873313359); + d = this.md5ii(d, a, b, c, x[i + 15], 10, -30611744); + c = this.md5ii(c, d, a, b, x[i + 6], 15, -1560198380); + b = this.md5ii(b, c, d, a, x[i + 13], 21, 1309151649); + a = this.md5ii(a, b, c, d, x[i + 4], 6, -145523070); + d = this.md5ii(d, a, b, c, x[i + 11], 10, -1120210379); + c = this.md5ii(c, d, a, b, x[i + 2], 15, 718787259); + b = this.md5ii(b, c, d, a, x[i + 9], 21, -343485551); + + a = this.safeAdd(a, olda); + b = this.safeAdd(b, oldb); + c = this.safeAdd(c, oldc); + d = this.safeAdd(d, oldd); + } + return [a, b, c, d]; + }, + binl2rstr(input) { + var i; + var output = ''; + var length32 = input.length * 32; + for (i = 0; i < length32; i += 8) { + output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xff); + } + return output; + }, + rstr2binl(input) { + var i; + var output = []; + output[(input.length >> 2) - 1] = undefined; + for (i = 0; i < output.length; i += 1) { + output[i] = 0; + } + var length8 = input.length * 8; + for (i = 0; i < length8; i += 8) { + output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << (i % 32); + } + return output; + }, + rstrMD5(s) { + return this.binl2rstr(this.binlMD5(this.rstr2binl(s), s.length * 8)); + }, + rstrHMACMD5(key, data) { + var i; + var bkey = this.rstr2binl(key); + var ipad = []; + var opad = []; + var hash; + ipad[15] = opad[15] = undefined; + if (bkey.length > 16) { + bkey = this.binlMD5(bkey, key.length * 8); + } + for (i = 0; i < 16; i += 1) { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5c5c5c5c; + } + hash = this.binlMD5(ipad.concat(this.rstr2binl(data)), 512 + data.length * 8); + return this.binl2rstr(this.binlMD5(opad.concat(hash), 512 + 128)); + }, + rstr2hex(input) { + var hexTab = '0123456789abcdef'; + var output = ''; + var x; + var i; + for (i = 0; i < input.length; i += 1) { + x = input.charCodeAt(i); + output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f); + } + return output; + }, + str2rstrUTF8(input) { + return unescape(encodeURIComponent(input)); + }, + rawMD5(s) { + return this.rstrMD5(this.str2rstrUTF8(s)); + }, + hexMD5(s) { + return this.rstr2hex(this.rawMD5(s)); + }, + rawHMACMD5(k, d) { + return this.rstrHMACMD5(this.str2rstrUTF8(k), str2rstrUTF8(d)); + }, + hexHMACMD5(k, d) { + return this.rstr2hex(this.rawHMACMD5(k, d)); + }, + + md5(string, key, raw) { + if (!key) { + if (!raw) { + return this.hexMD5(string); + } + return this.rawMD5(string); + } + if (!raw) { + return this.hexHMACMD5(key, string); + } + return this.rawHMACMD5(key, string); + }, + /** + * 得到md5加密后的sig参数 + * @param {Object} requestParam 接口参数 + * @param {String} sk签名字符串 + * @param {String} featrue 方法名 + * @return 返回加密后的sig参数 + */ + getSig(requestParam, sk, feature, mode) { + var sig = null; + var requestArr = []; + Object.keys(requestParam).sort().forEach(function(key){ + requestArr.push(key + '=' + requestParam[key]); + }); + if (feature == 'search') { + sig = '/ws/place/v1/search?' + requestArr.join('&') + sk; + } + if (feature == 'suggest') { + sig = '/ws/place/v1/suggestion?' + requestArr.join('&') + sk; + } + if (feature == 'reverseGeocoder') { + sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk; + } + if (feature == 'geocoder') { + sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk; + } + if (feature == 'getCityList') { + sig = '/ws/district/v1/list?' + requestArr.join('&') + sk; + } + if (feature == 'getDistrictByCityId') { + sig = '/ws/district/v1/getchildren?' + requestArr.join('&') + sk; + } + if (feature == 'calculateDistance') { + sig = '/ws/distance/v1/?' + requestArr.join('&') + sk; + } + if (feature == 'direction') { + sig = '/ws/direction/v1/' + mode + '?' + requestArr.join('&') + sk; + } + sig = this.md5(sig); + return sig; + }, + /** + * 得到终点query字符串 + * @param {Array|String} 检索数据 + */ + location2query(data) { + if (typeof data == 'string') { + return data; + } + var query = ''; + for (var i = 0; i < data.length; i++) { + var d = data[i]; + if (!!query) { + query += ';'; + } + if (d.location) { + query = query + d.location.lat + ',' + d.location.lng; + } + if (d.latitude && d.longitude) { + query = query + d.latitude + ',' + d.longitude; + } + } + return query; + }, + + /** + * 计算角度 + */ + rad(d) { + return d * Math.PI / 180.0; + }, + /** + * 处理终点location数组 + * @return 返回终点数组 + */ + getEndLocation(location){ + var to = location.split(';'); + var endLocation = []; + for (var i = 0; i < to.length; i++) { + endLocation.push({ + lat: parseFloat(to[i].split(',')[0]), + lng: parseFloat(to[i].split(',')[1]) + }) + } + return endLocation; + }, + + /** + * 计算两点间直线距离 + * @param a 表示纬度差 + * @param b 表示经度差 + * @return 返回的是距离,单位m + */ + getDistance(latFrom, lngFrom, latTo, lngTo) { + var radLatFrom = this.rad(latFrom); + var radLatTo = this.rad(latTo); + var a = radLatFrom - radLatTo; + var b = this.rad(lngFrom) - this.rad(lngTo); + var distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLatFrom) * Math.cos(radLatTo) * Math.pow(Math.sin(b / 2), 2))); + distance = distance * EARTH_RADIUS; + distance = Math.round(distance * 10000) / 10000; + return parseFloat(distance.toFixed(0)); + }, + /** + * 使用微信接口进行定位 + */ + getWXLocation(success, fail, complete) { + wx.getLocation({ + type: 'gcj02', + success: success, + fail: fail, + complete: complete + }); + }, + + /** + * 获取location参数 + */ + getLocationParam(location) { + if (typeof location == 'string') { + var locationArr = location.split(','); + if (locationArr.length === 2) { + location = { + latitude: location.split(',')[0], + longitude: location.split(',')[1] + }; + } else { + location = {}; + } + } + return location; + }, + + /** + * 回调函数默认处理 + */ + polyfillParam(param) { + param.success = param.success || function () { }; + param.fail = param.fail || function () { }; + param.complete = param.complete || function () { }; + }, + + /** + * 验证param对应的key值是否为空 + * + * @param {Object} param 接口参数 + * @param {String} key 对应参数的key + */ + checkParamKeyEmpty(param, key) { + if (!param[key]) { + var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + key +'参数格式有误'); + param.fail(errconf); + param.complete(errconf); + return true; + } + return false; + }, + + /** + * 验证参数中是否存在检索词keyword + * + * @param {Object} param 接口参数 + */ + checkKeyword(param){ + return !this.checkParamKeyEmpty(param, 'keyword'); + }, + + /** + * 验证location值 + * + * @param {Object} param 接口参数 + */ + checkLocation(param) { + var location = this.getLocationParam(param.location); + if (!location || !location.latitude || !location.longitude) { + var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + ' location参数格式有误'); + param.fail(errconf); + param.complete(errconf); + return false; + } + return true; + }, + + /** + * 构造错误数据结构 + * @param {Number} errCode 错误码 + * @param {Number} errMsg 错误描述 + */ + buildErrorConfig(errCode, errMsg) { + return { + status: errCode, + message: errMsg + }; + }, + + /** + * + * 数据处理函数 + * 根据传入参数不同处理不同数据 + * @param {String} feature 功能名称 + * search 地点搜索 + * suggest关键词提示 + * reverseGeocoder逆地址解析 + * geocoder地址解析 + * getCityList获取城市列表:父集 + * getDistrictByCityId获取区县列表:子集 + * calculateDistance距离计算 + * @param {Object} param 接口参数 + * @param {Object} data 数据 + */ + handleData(param,data,feature){ + if (feature == 'search') { + var searchResult = data.data; + var searchSimplify = []; + for (var i = 0; i < searchResult.length; i++) { + searchSimplify.push({ + id: searchResult[i].id || null, + title: searchResult[i].title || null, + latitude: searchResult[i].location && searchResult[i].location.lat || null, + longitude: searchResult[i].location && searchResult[i].location.lng || null, + address: searchResult[i].address || null, + category: searchResult[i].category || null, + tel: searchResult[i].tel || null, + adcode: searchResult[i].ad_info && searchResult[i].ad_info.adcode || null, + city: searchResult[i].ad_info && searchResult[i].ad_info.city || null, + district: searchResult[i].ad_info && searchResult[i].ad_info.district || null, + province: searchResult[i].ad_info && searchResult[i].ad_info.province || null + }) + } + param.success(data, { + searchResult: searchResult, + searchSimplify: searchSimplify + }) + } else if (feature == 'suggest') { + var suggestResult = data.data; + var suggestSimplify = []; + for (var i = 0; i < suggestResult.length; i++) { + suggestSimplify.push({ + adcode: suggestResult[i].adcode || null, + address: suggestResult[i].address || null, + category: suggestResult[i].category || null, + city: suggestResult[i].city || null, + district: suggestResult[i].district || null, + id: suggestResult[i].id || null, + latitude: suggestResult[i].location && suggestResult[i].location.lat || null, + longitude: suggestResult[i].location && suggestResult[i].location.lng || null, + province: suggestResult[i].province || null, + title: suggestResult[i].title || null, + type: suggestResult[i].type || null + }) + } + param.success(data, { + suggestResult: suggestResult, + suggestSimplify: suggestSimplify + }) + } else if (feature == 'reverseGeocoder') { + var reverseGeocoderResult = data.result; + var reverseGeocoderSimplify = { + address: reverseGeocoderResult.address || null, + latitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lat || null, + longitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lng || null, + adcode: reverseGeocoderResult.ad_info && reverseGeocoderResult.ad_info.adcode || null, + city: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.city || null, + district: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.district || null, + nation: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.nation || null, + province: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.province || null, + street: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street || null, + street_number: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street_number || null, + recommend: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.recommend || null, + rough: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.rough || null + }; + if (reverseGeocoderResult.pois) {//判断是否返回周边poi + var pois = reverseGeocoderResult.pois; + var poisSimplify = []; + for (var i = 0;i < pois.length;i++) { + poisSimplify.push({ + id: pois[i].id || null, + title: pois[i].title || null, + latitude: pois[i].location && pois[i].location.lat || null, + longitude: pois[i].location && pois[i].location.lng || null, + address: pois[i].address || null, + category: pois[i].category || null, + adcode: pois[i].ad_info && pois[i].ad_info.adcode || null, + city: pois[i].ad_info && pois[i].ad_info.city || null, + district: pois[i].ad_info && pois[i].ad_info.district || null, + province: pois[i].ad_info && pois[i].ad_info.province || null + }) + } + param.success(data,{ + reverseGeocoderResult: reverseGeocoderResult, + reverseGeocoderSimplify: reverseGeocoderSimplify, + pois: pois, + poisSimplify: poisSimplify + }) + } else { + param.success(data, { + reverseGeocoderResult: reverseGeocoderResult, + reverseGeocoderSimplify: reverseGeocoderSimplify + }) + } + } else if (feature == 'geocoder') { + var geocoderResult = data.result; + var geocoderSimplify = { + title: geocoderResult.title || null, + latitude: geocoderResult.location && geocoderResult.location.lat || null, + longitude: geocoderResult.location && geocoderResult.location.lng || null, + adcode: geocoderResult.ad_info && geocoderResult.ad_info.adcode || null, + province: geocoderResult.address_components && geocoderResult.address_components.province || null, + city: geocoderResult.address_components && geocoderResult.address_components.city || null, + district: geocoderResult.address_components && geocoderResult.address_components.district || null, + street: geocoderResult.address_components && geocoderResult.address_components.street || null, + street_number: geocoderResult.address_components && geocoderResult.address_components.street_number || null, + level: geocoderResult.level || null + }; + param.success(data,{ + geocoderResult: geocoderResult, + geocoderSimplify: geocoderSimplify + }); + } else if (feature == 'getCityList') { + var provinceResult = data.result[0]; + var cityResult = data.result[1]; + var districtResult = data.result[2]; + param.success(data,{ + provinceResult: provinceResult, + cityResult: cityResult, + districtResult: districtResult + }); + } else if (feature == 'getDistrictByCityId') { + var districtByCity = data.result[0]; + param.success(data, districtByCity); + } else if (feature == 'calculateDistance') { + var calculateDistanceResult = data.result.elements; + var distance = []; + for (var i = 0; i < calculateDistanceResult.length; i++){ + distance.push(calculateDistanceResult[i].distance); + } + param.success(data, { + calculateDistanceResult: calculateDistanceResult, + distance: distance + }); + } else if (feature == 'direction') { + var direction = data.result.routes; + param.success(data,direction); + } else { + param.success(data); + } + }, + + /** + * 构造微信请求参数,公共属性处理 + * + * @param {Object} param 接口参数 + * @param {Object} param 配置项 + * @param {String} feature 方法名 + */ + buildWxRequestConfig(param, options, feature) { + var that = this; + options.header = { "content-type": "application/json" }; + options.method = 'GET'; + options.success = function (res) { + var data = res.data; + if (data.status === 0) { + that.handleData(param, data, feature); + } else { + param.fail(data); + } + }; + options.fail = function (res) { + res.statusCode = ERROR_CONF.WX_ERR_CODE; + param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg)); + }; + options.complete = function (res) { + var statusCode = +res.statusCode; + switch(statusCode) { + case ERROR_CONF.WX_ERR_CODE: { + param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg)); + break; + } + case ERROR_CONF.WX_OK_CODE: { + var data = res.data; + if (data.status === 0) { + param.complete(data); + } else { + param.complete(that.buildErrorConfig(data.status, data.message)); + } + break; + } + default:{ + param.complete(that.buildErrorConfig(ERROR_CONF.SYSTEM_ERR, ERROR_CONF.SYSTEM_ERR_MSG)); + } + + } + }; + return options; + }, + + /** + * 处理用户参数是否传入坐标进行不同的处理 + */ + locationProcess(param, locationsuccess, locationfail, locationcomplete) { + var that = this; + locationfail = locationfail || function (res) { + res.statusCode = ERROR_CONF.WX_ERR_CODE; + param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg)); + }; + locationcomplete = locationcomplete || function (res) { + if (res.statusCode == ERROR_CONF.WX_ERR_CODE) { + param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg)); + } + }; + if (!param.location) { + that.getWXLocation(locationsuccess, locationfail, locationcomplete); + } else if (that.checkLocation(param)) { + var location = Utils.getLocationParam(param.location); + locationsuccess(location); + } + } +}; + + +class QQMapWX { + + /** + * 构造函数 + * + * @param {Object} options 接口参数,key 为必选参数 + */ + constructor(options) { + if (!options.key) { + throw Error('key值不能为空'); + } + this.key = options.key; + }; + + /** + * POI周边检索 + * + * @param {Object} options 接口参数对象 + * + * 参数对象结构可以参考 + * @see http://lbs.qq.com/webservice_v1/guide-search.html + */ + search(options) { + var that = this; + options = options || {}; + + Utils.polyfillParam(options); + + if (!Utils.checkKeyword(options)) { + return; + } + + var requestParam = { + keyword: options.keyword, + orderby: options.orderby || '_distance', + page_size: options.page_size || 10, + page_index: options.page_index || 1, + output: 'json', + key: that.key + }; + + if (options.address_format) { + requestParam.address_format = options.address_format; + } + + if (options.filter) { + requestParam.filter = options.filter; + } + + var distance = options.distance || "1000"; + var auto_extend = options.auto_extend || 1; + var region = null; + var rectangle = null; + + //判断城市限定参数 + if (options.region) { + region = options.region; + } + + //矩形限定坐标(暂时只支持字符串格式) + if (options.rectangle) { + rectangle = options.rectangle; + } + + var locationsuccess = function (result) { + if (region && !rectangle) { + //城市限定参数拼接 + requestParam.boundary = "region(" + region + "," + auto_extend + "," + result.latitude + "," + result.longitude + ")"; + if (options.sig) { + requestParam.sig = Utils.getSig(requestParam, options.sig, 'search'); + } + } else if (rectangle && !region) { + //矩形搜索 + requestParam.boundary = "rectangle(" + rectangle + ")"; + if (options.sig) { + requestParam.sig = Utils.getSig(requestParam, options.sig, 'search'); + } + } else { + requestParam.boundary = "nearby(" + result.latitude + "," + result.longitude + "," + distance + "," + auto_extend + ")"; + if (options.sig) { + requestParam.sig = Utils.getSig(requestParam, options.sig, 'search'); + } + } + wx.request(Utils.buildWxRequestConfig(options, { + url: URL_SEARCH, + data: requestParam + }, 'search')); + }; + Utils.locationProcess(options, locationsuccess); + }; + + /** + * sug模糊检索 + * + * @param {Object} options 接口参数对象 + * + * 参数对象结构可以参考 + * http://lbs.qq.com/webservice_v1/guide-suggestion.html + */ + getSuggestion(options) { + var that = this; + options = options || {}; + Utils.polyfillParam(options); + + if (!Utils.checkKeyword(options)) { + return; + } + + var requestParam = { + keyword: options.keyword, + region: options.region || '全国', + region_fix: options.region_fix || 0, + policy: options.policy || 0, + page_size: options.page_size || 10,//控制显示条数 + page_index: options.page_index || 1,//控制页数 + get_subpois : options.get_subpois || 0,//返回子地点 + output: 'json', + key: that.key + }; + //长地址 + if (options.address_format) { + requestParam.address_format = options.address_format; + } + //过滤 + if (options.filter) { + requestParam.filter = options.filter; + } + //排序 + if (options.location) { + var locationsuccess = function (result) { + requestParam.location = result.latitude + ',' + result.longitude; + if (options.sig) { + requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest'); + } + wx.request(Utils.buildWxRequestConfig(options, { + url: URL_SUGGESTION, + data: requestParam + }, "suggest")); + }; + Utils.locationProcess(options, locationsuccess); + } else { + if (options.sig) { + requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest'); + } + wx.request(Utils.buildWxRequestConfig(options, { + url: URL_SUGGESTION, + data: requestParam + }, "suggest")); + } + }; + + /** + * 逆地址解析 + * + * @param {Object} options 接口参数对象 + * + * 请求参数结构可以参考 + * http://lbs.qq.com/webservice_v1/guide-gcoder.html + */ + reverseGeocoder(options) { + var that = this; + options = options || {}; + Utils.polyfillParam(options); + var requestParam = { + coord_type: options.coord_type || 5, + get_poi: options.get_poi || 0, + output: 'json', + key: that.key + }; + if (options.poi_options) { + requestParam.poi_options = options.poi_options + } + + var locationsuccess = function (result) { + requestParam.location = result.latitude + ',' + result.longitude; + if (options.sig) { + requestParam.sig = Utils.getSig(requestParam, options.sig, 'reverseGeocoder'); + } + wx.request(Utils.buildWxRequestConfig(options, { + url: URL_GET_GEOCODER, + data: requestParam + }, 'reverseGeocoder')); + }; + Utils.locationProcess(options, locationsuccess); + }; + + /** + * 地址解析 + * + * @param {Object} options 接口参数对象 + * + * 请求参数结构可以参考 + * http://lbs.qq.com/webservice_v1/guide-geocoder.html + */ + geocoder(options) { + var that = this; + options = options || {}; + Utils.polyfillParam(options); + + if (Utils.checkParamKeyEmpty(options, 'address')) { + return; + } + + var requestParam = { + address: options.address, + output: 'json', + key: that.key + }; + + //城市限定 + if (options.region) { + requestParam.region = options.region; + } + + if (options.sig) { + requestParam.sig = Utils.getSig(requestParam, options.sig, 'geocoder'); + } + + wx.request(Utils.buildWxRequestConfig(options, { + url: URL_GET_GEOCODER, + data: requestParam + },'geocoder')); + }; + + + /** + * 获取城市列表 + * + * @param {Object} options 接口参数对象 + * + * 请求参数结构可以参考 + * http://lbs.qq.com/webservice_v1/guide-region.html + */ + getCityList(options) { + var that = this; + options = options || {}; + Utils.polyfillParam(options); + var requestParam = { + output: 'json', + key: that.key + }; + + if (options.sig) { + requestParam.sig = Utils.getSig(requestParam, options.sig, 'getCityList'); + } + + wx.request(Utils.buildWxRequestConfig(options, { + url: URL_CITY_LIST, + data: requestParam + },'getCityList')); + }; + + /** + * 获取对应城市ID的区县列表 + * + * @param {Object} options 接口参数对象 + * + * 请求参数结构可以参考 + * http://lbs.qq.com/webservice_v1/guide-region.html + */ + getDistrictByCityId(options) { + var that = this; + options = options || {}; + Utils.polyfillParam(options); + + if (Utils.checkParamKeyEmpty(options, 'id')) { + return; + } + + var requestParam = { + id: options.id || '', + output: 'json', + key: that.key + }; + + if (options.sig) { + requestParam.sig = Utils.getSig(requestParam, options.sig, 'getDistrictByCityId'); + } + + wx.request(Utils.buildWxRequestConfig(options, { + url: URL_AREA_LIST, + data: requestParam + },'getDistrictByCityId')); + }; + + /** + * 用于单起点到多终点的路线距离(非直线距离)计算: + * 支持两种距离计算方式:步行和驾车。 + * 起点到终点最大限制直线距离10公里。 + * + * 新增直线距离计算。 + * + * @param {Object} options 接口参数对象 + * + * 请求参数结构可以参考 + * http://lbs.qq.com/webservice_v1/guide-distance.html + */ + calculateDistance(options) { + var that = this; + options = options || {}; + Utils.polyfillParam(options); + + if (Utils.checkParamKeyEmpty(options, 'to')) { + return; + } + + var requestParam = { + mode: options.mode || 'walking', + to: Utils.location2query(options.to), + output: 'json', + key: that.key + }; + + if (options.from) { + options.location = options.from; + } + + //计算直线距离 + if(requestParam.mode == 'straight'){ + var locationsuccess = function (result) { + var locationTo = Utils.getEndLocation(requestParam.to);//处理终点坐标 + var data = { + message:"query ok", + result:{ + elements:[] + }, + status:0 + }; + for (var i = 0; i < locationTo.length; i++) { + data.result.elements.push({//将坐标存入 + distance: Utils.getDistance(result.latitude, result.longitude, locationTo[i].lat, locationTo[i].lng), + duration:0, + from:{ + lat: result.latitude, + lng:result.longitude + }, + to:{ + lat: locationTo[i].lat, + lng: locationTo[i].lng + } + }); + } + var calculateResult = data.result.elements; + var distanceResult = []; + for (var i = 0; i < calculateResult.length; i++) { + distanceResult.push(calculateResult[i].distance); + } + return options.success(data,{ + calculateResult: calculateResult, + distanceResult: distanceResult + }); + }; + + Utils.locationProcess(options, locationsuccess); + } else { + var locationsuccess = function (result) { + requestParam.from = result.latitude + ',' + result.longitude; + if (options.sig) { + requestParam.sig = Utils.getSig(requestParam, options.sig, 'calculateDistance'); + } + wx.request(Utils.buildWxRequestConfig(options, { + url: URL_DISTANCE, + data: requestParam + },'calculateDistance')); + }; + + Utils.locationProcess(options, locationsuccess); + } + }; + + /** + * 路线规划: + * + * @param {Object} options 接口参数对象 + * + * 请求参数结构可以参考 + * https://lbs.qq.com/webservice_v1/guide-road.html + */ + direction(options) { + var that = this; + options = options || {}; + Utils.polyfillParam(options); + + if (Utils.checkParamKeyEmpty(options, 'to')) { + return; + } + + var requestParam = { + output: 'json', + key: that.key + }; + + //to格式处理 + if (typeof options.to == 'string') { + requestParam.to = options.to; + } else { + requestParam.to = options.to.latitude + ',' + options.to.longitude; + } + //初始化局部请求域名 + var SET_URL_DIRECTION = null; + //设置默认mode属性 + options.mode = options.mode || MODE.driving; + + //设置请求域名 + SET_URL_DIRECTION = URL_DIRECTION + options.mode; + + if (options.from) { + options.location = options.from; + } + + if (options.mode == MODE.driving) { + if (options.from_poi) { + requestParam.from_poi = options.from_poi; + } + if (options.heading) { + requestParam.heading = options.heading; + } + if (options.speed) { + requestParam.speed = options.speed; + } + if (options.accuracy) { + requestParam.accuracy = options.accuracy; + } + if (options.road_type) { + requestParam.road_type = options.road_type; + } + if (options.to_poi) { + requestParam.to_poi = options.to_poi; + } + if (options.from_track) { + requestParam.from_track = options.from_track; + } + if (options.waypoints) { + requestParam.waypoints = options.waypoints; + } + if (options.policy) { + requestParam.policy = options.policy; + } + if (options.plate_number) { + requestParam.plate_number = options.plate_number; + } + } + + if (options.mode == MODE.transit) { + if (options.departure_time) { + requestParam.departure_time = options.departure_time; + } + if (options.policy) { + requestParam.policy = options.policy; + } + } + + var locationsuccess = function (result) { + requestParam.from = result.latitude + ',' + result.longitude; + if (options.sig) { + requestParam.sig = Utils.getSig(requestParam, options.sig, 'direction',options.mode); + } + wx.request(Utils.buildWxRequestConfig(options, { + url: SET_URL_DIRECTION, + data: requestParam + }, 'direction')); + }; + + Utils.locationProcess(options, locationsuccess); + } +}; + +module.exports = QQMapWX; \ No newline at end of file diff --git a/config/md5.js b/config/md5.js new file mode 100644 index 0000000..6b3848d --- /dev/null +++ b/config/md5.js @@ -0,0 +1,257 @@ +/* + * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message + * Digest Algorithm, as defined in RFC 1321. + * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for more info. + */ + +/* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ +var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ +var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ +var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ + +/* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ +function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));} +function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));} +function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));} +function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); } +function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); } +function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); } + +/* + * Perform a simple self-test to see if the VM is working + */ +function md5_vm_test() +{ + return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72"; +} + +/* + * Calculate the MD5 of an array of little-endian words, and a bit length + */ +function core_md5(x, len) +{ + /* append padding */ + x[len >> 5] |= 0x80 << ((len) % 32); + x[(((len + 64) >>> 9) << 4) + 14] = len; + + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + + for(var i = 0; i < x.length; i += 16) + { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + + a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936); + d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586); + c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819); + b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330); + a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897); + d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426); + c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341); + b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983); + a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416); + d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417); + c = md5_ff(c, d, a, b, x[i+10], 17, -42063); + b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162); + a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682); + d = md5_ff(d, a, b, c, x[i+13], 12, -40341101); + c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290); + b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329); + + a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510); + d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632); + c = md5_gg(c, d, a, b, x[i+11], 14, 643717713); + b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302); + a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691); + d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083); + c = md5_gg(c, d, a, b, x[i+15], 14, -660478335); + b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848); + a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438); + d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690); + c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961); + b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501); + a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467); + d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784); + c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473); + b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734); + + a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558); + d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463); + c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562); + b = md5_hh(b, c, d, a, x[i+14], 23, -35309556); + a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060); + d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353); + c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632); + b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640); + a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174); + d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222); + c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979); + b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189); + a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487); + d = md5_hh(d, a, b, c, x[i+12], 11, -421815835); + c = md5_hh(c, d, a, b, x[i+15], 16, 530742520); + b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651); + + a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844); + d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415); + c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905); + b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055); + a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571); + d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606); + c = md5_ii(c, d, a, b, x[i+10], 15, -1051523); + b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799); + a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359); + d = md5_ii(d, a, b, c, x[i+15], 10, -30611744); + c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380); + b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649); + a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070); + d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379); + c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259); + b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551); + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + } + return Array(a, b, c, d); + +} + +/* + * These functions implement the four basic operations the algorithm uses. + */ +function md5_cmn(q, a, b, x, s, t) +{ + return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b); +} +function md5_ff(a, b, c, d, x, s, t) +{ + return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); +} +function md5_gg(a, b, c, d, x, s, t) +{ + return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); +} +function md5_hh(a, b, c, d, x, s, t) +{ + return md5_cmn(b ^ c ^ d, a, b, x, s, t); +} +function md5_ii(a, b, c, d, x, s, t) +{ + return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); +} + +/* + * Calculate the HMAC-MD5, of a key and some data + */ +function core_hmac_md5(key, data) +{ + var bkey = str2binl(key); + if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz); + + var ipad = Array(16), opad = Array(16); + for(var i = 0; i < 16; i++) + { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz); + return core_md5(opad.concat(hash), 512 + 128); +} + +/* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ +function safe_add(x, y) +{ + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +/* + * Bitwise rotate a 32-bit number to the left. + */ +function bit_rol(num, cnt) +{ + return (num << cnt) | (num >>> (32 - cnt)); +} + +/* + * Convert a string to an array of little-endian words + * If chrsz is ASCII, characters >255 have their hi-byte silently ignored. + */ +function str2binl(str) +{ + var bin = Array(); + var mask = (1 << chrsz) - 1; + for(var i = 0; i < str.length * chrsz; i += chrsz) + bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32); + return bin; +} + +/* + * Convert an array of little-endian words to a string + */ +function binl2str(bin) +{ + var str = ""; + var mask = (1 << chrsz) - 1; + for(var i = 0; i < bin.length * 32; i += chrsz) + str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask); + return str; +} + +/* + * Convert an array of little-endian words to a hex string. + */ +function binl2hex(binarray) +{ + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i++) + { + str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF); + } + return str; +} + +/* + * Convert an array of little-endian words to a base-64 string + */ +function binl2b64(binarray) +{ + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i += 3) + { + var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16) + | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 ) + | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF); + for(var j = 0; j < 4; j++) + { + if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; + else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); + } + } + return str; +} +module.exports = hex_md5; \ No newline at end of file diff --git a/config/qqmap.js b/config/qqmap.js new file mode 100644 index 0000000..e69de29 diff --git a/config/request.js b/config/request.js new file mode 100644 index 0000000..45eb2f6 --- /dev/null +++ b/config/request.js @@ -0,0 +1,114 @@ +const ENV = require('./env.js'); +/* +{ + baseUrl: '', + header: {}, + method: 'GET', + dataType: 'json', + // #ifndef MP-ALIPAY || APP-PLUS + responseType: 'text', + // #endif + // 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部) + custom: {}, // 全局自定义参数默认值 + // #ifdef MP-ALIPAY || MP-WEIXIN + timeout: 30000, + // #endif + // #ifdef APP-PLUS + sslVerify: true, + // #endif + // #ifdef H5 + // 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+) + withCredentials: false, + // #endif + // 局部优先级高于全局,返回当前请求的task,options。请勿在此处修改options。非必填 + // getTask: (task, options) => { + // 相当于设置了请求超时时间500ms + // setTimeout(() => { + // task.abort() + // }, 500) + // } + }*/ +// 此vm参数为页面的实例,可以通过它引用vuex中的变量 +module.exports = (vm) => { + + // 初始化请求配置 + uni.$u.http.setConfig((config) => { + + //console.log(config); + /* config 为默认全局配置*/ + config.baseURL = ENV.ApiUrl; /* 根域名 */ + config.custom = { + auth:true, + toast:true, + }; + return config + }) + + // 请求拦截 + uni.$u.http.interceptors.request.use((config) => { // 可使用async await 做异步操作 + // 初始化请求拦截器时,会执行此方法,此时data为undefined,赋予默认{} + config.data = config.data || {} + // 根据custom参数中配置的是否需要token,添加对应的请求头 + if(config?.custom?.auth) { + const token = uni.getStorageSync('token'); + if(token.length > 0) + { + config.header.token = token; + } + } + return config + }, config => { // 可使用async await 做异步操作 + return Promise.reject(config) + }) + + // 响应拦截 + uni.$u.http.interceptors.response.use((response) => { /* 对响应成功做点什么 可使用async await 做异步操作*/ + const data = response.data + + // 自定义参数 + const custom = response.config?.custom + if (data.result !== 1) { + // 如果没有显式定义custom的toast参数为false的话,默认对报错进行toast弹出提示 + if (custom.toast !== false) { + uni.$u.toast(data.msg) + } + + // 如果需要catch返回,则进行reject + if (custom?.catch) { + return Promise.reject(data) + } else { + // 否则返回一个pending中的promise,请求不会进入catch中 + return new Promise(() => { }) + } + } + return data.data === undefined ? {} : data.data + }, (response) => { + //console.log(response); + // 对响应错误做点什么 (statusCode !== 200) + if(response.statusCode == 401) + { + // 假设201为token失效,这里跳转登录 + uni.$u.toast('用户信息验证失败,请重新登录'); + setTimeout(() => { + // 此为uView的方法,详见路由相关文档 + //uni.$u.route('/pages/user/auth/login') + }, 1500) + + } + else if(response.statusCode == 500) + { + // 假设201为token失效,这里跳转登录 + uni.$u.toast('当前接口出错'); + + } + else + { + + // 如果返回false,则会调用Promise的reject回调, + // 并将进入this.$u.post(url).then().catch(res=>{})的catch回调中,res为服务端的返回值 + uni.$u.toast('当前接口状态码'+response.statusCode); + + } + return Promise.reject(response) + }) +} \ No newline at end of file diff --git a/config/user.js b/config/user.js new file mode 100644 index 0000000..de2aa4f --- /dev/null +++ b/config/user.js @@ -0,0 +1,88 @@ +var user = { + + /** + * 本算法来源于简书开源代码,详见:https://www.jianshu.com/p/fdbf293d0a85 + * 全局唯一标识符(uuid,Globally Unique Identifier),也称作 uuid(Universally Unique IDentifier) + * 一般用于多个组件之间,给它一个唯一的标识符,或者v-for循环的时候,如果使用数组的index可能会导致更新列表出现问题 + * 最可能的情况是左滑删除item或者对某条信息流"不喜欢"并去掉它的时候,会导致组件内的数据可能出现错乱 + * v-for的时候,推荐使用后端返回的id而不是循环的index + * @param {Number} len uuid的长度 + * @param {Boolean} firstU 将返回的首字母置为"u" + * @param {Nubmer} radix 生成uuid的基数(意味着返回的字符串都是这个基数),2-二进制,8-八进制,10-十进制,16-十六进制 + */ + guid:function (len = 32, firstU = true, radix = null) { + let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); + let uuid = []; + radix = radix || chars.length; + + if (len) { + // 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位 + for (let i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]; + } else { + let r; + // rfc4122标准要求返回的uuid中,某些位为固定的字符 + uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; + uuid[14] = '4'; + + for (let i = 0; i < 36; i++) { + if (!uuid[i]) { + r = 0 | Math.random() * 16; + uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; + } + } + } + // 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class + if (firstU) { + uuid.shift(); + return 'u' + uuid.join(''); + } else { + return uuid.join(''); + } + }, + //设置用户本地信息 + + session: function(key, val) { + if (typeof(val) == 'undefined') { + var res = uni.getStorageSync(key); + return res; + } else { + uni.setStorageSync(key, val); + } + }, + //删除用户本地所有信息 + clearSession: function() { + var keys = uni.getStorageInfoSync().keys; + var session_id = uni.getStorageSync('session_id'); + for (var key in keys) { + uni.removeStorageSync(keys[key]); + } + uni.setStorageSync('session_id', session_id); + }, + + isLogin: function() { + var res = uni.getStorageSync('token'); + if (typeof(res) == 'undefined' || res == '') { + return false; + } else { + return res; + } + }, + + getUserInfo: function() { + var res = uni.getStorageSync('token'); + if (typeof(res) == 'undefined' || res == '') { + return false; + } else { + var userInfo = {}; + userInfo.nickName = uni.getStorageSync('nickName'); + userInfo.phone = uni.getStorageSync('phone'); + userInfo.userId = uni.getStorageSync('userId'); + userInfo.token = uni.getStorageSync('token'); + userInfo.avatar = uni.getStorageSync('avatar'); + userInfo.realName = uni.getStorageSync('realName'); + return userInfo; + } + } +} + +module.exports = user diff --git a/config/wechat.js b/config/wechat.js new file mode 100644 index 0000000..d3c05dd --- /dev/null +++ b/config/wechat.js @@ -0,0 +1,228 @@ +// /common/wechat.js +//https://zhuanlan.zhihu.com/p/512790783?utm_id=0 +import Vue from "vue" +var jweixin = require('./jssdk.js') +export default { + // 调试模式 + debug: false, + // api列表 + jsApiList: [ + 'updateAppMessageShareData', + 'updateTimelineShareData', + 'closeWindow', + 'getLocation', + 'openLocation', + 'openAddress', + 'scanQRCode', + 'chooseImage', + 'chooseWXPay' + ], + // 判断是否在微信中 + isWechat: function() { + var ua = window.navigator.userAgent.toLowerCase() + return ua.match(/micromessenger/i) == 'micromessenger' ? true : false + }, + // 初始化sdk配置 + initJssdk: function(callback) { + var url = window.location.href; + var post = {url:url}; + if (this.isWechat()) { + Vue.prototype.$u.post('login/getApi', post).then(res => { + console.log('getApi : '); + console.log(res); + var share = JSON.parse(res.data); + console.log('getApi-share : '); + console.log(share); + jweixin.config({ + debug: share.debug || this.debug, + appId: share.appId, + timestamp:share.timestamp, + nonceStr: share.nonceStr, + signature: share.signature, + jsApiList: share.jsApiList || this.jsApiList + }) + if (typeof callback === 'function') { + console.log('getApi-callback : '); + console.log(share); + callback(share); + } + }) + } + }, + // 关闭页面事件 + closeWindow: function(callback) { + if (this.isWechat()) { + this.initJssdk(function(init) { + jweixin.ready(function(wx) { + wx.closeWindow() + if (typeof callback === 'function') { + callback(jweixin) + } + }) + }) + } + }, + // 微信分享 + share: function(data, callback) { + if (this.isWechat()) { + this.initJssdk(function(init) { + + jweixin.ready(function() { + + var shareData = { + title: data.title, + desc: data.desc, + link: window.location.href, + imgUrl: data.image, + success: function(res) { + callback(res) + + }, + cancel: function(res) { + callback(res) + } + } + jweixin.updateAppMessageShareData(shareData) + jweixin.updateTimelineShareData(shareData) + + }) + }) + } + }, + // 获取位置信息 + getLocation: function(callback) { + if (this.isWechat()) { + this.initJssdk(function(init) { + jweixin.ready(function() { + jweixin.getLocation({ + type: 'gcj02', + success: function(res) { + callback(res) + }, + fail: function(err) { + callback(err) + } + }) + }) + }) + } + }, + // 查看位置信息 + openLocation: function(data, callback) { + if (this.isWechat()) { + this.initJssdk(function(init) { + jweixin.ready(function() { + jweixin.openLocation({ + latitude: data.latitude, + longitude: data.longitude + }) + }) + }) + } + }, + // 获取微信收货地址 + openAddress: function(callback) { + if (this.isWechat()) { + this.initJssdk(function(init) { + jweixin.ready(function() { + jweixin.openAddress({ + success: function(res) { + callback(res) + }, + fail: function(err) { + callback(err) + } + }) + }) + }) + } + }, + // 微信扫码 + scanQRCode: function(callback) { + if (this.isWechat()) { + this.initJssdk(function(init) { + jweixin.ready(function() { + jweixin.scanQRCode({ + needResult: 1, // 0:微信处理|1:返回扫描结果 + scanType: ["qrCode", "barCode"], + success: function(res) { + let durl = /https:\/\/([^\/]+)\//i + let domain + res.resultStr.replace(durl, (e) => { + domain = e + }) + callback(res) + }, + fail: function(err) { + callback(err) + } + }) + }) + }) + } + }, + // 选择图片 + chooseImage: function(callback) { + if (this.isWechat()) { + this.initJssdk(function(init) { + jweixin.ready(function() { + jweixin.chooseImage({ + count: 1, + sizeType: ['compressed'], + sourceType: ['album'], + success: function(res) { + callback(res) + } + }) + }) + }) + } + }, + // 微信支付 + wxpay: function(data, callback) { + if (this.isWechat()) { + this.initJssdk(function(init) { + jweixin.ready(function() { + jweixin.chooseWXPay({ + timestamp: data.timeStamp, + nonceStr: data.nonceStr, + package: data.package, + signType: data.signType, + paySign: data.paySign, + success: function(res) { + callback(res) + }, + cancel: function(res) { + callback(res) + }, + fail: function(err) { + callback(err) + } + }) + }) + }) + } + }, + // 微信支付 另一种方式 + wxpayBridge: function(data, callback) { + if (this.isWechat()) { + this.initJssdk(function(init) { + jweixin.ready(function() { + WeixinJSBridge.invoke( + 'getBrandWCPayRequest', { + "appId": data.appId, + "timeStamp": data.timeStamp, + "nonceStr": data.nonceStr, + "package": data.package, + "signType": "MD5", + "paySign": data.paySign + }, + function(res) { + callback(res) + } + ) + }) + }) + } + } +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..c3ff205 --- /dev/null +++ b/index.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <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="/main.js"></script> + </body> +</html> diff --git a/locale/en.json b/locale/en.json new file mode 100644 index 0000000..75fe58f --- /dev/null +++ b/locale/en.json @@ -0,0 +1,23 @@ +{ + "app.name": "空间充", + "tabbar.home": "HOME", + "tabbar.list": "NEARBY", + "tabbar.user": "USER", + "page.list.index.title": "NEARBY", + + "page.user.index.title": "User Center", + "pages.user.setting.index.title":"Setting", + "pages.user.setting.index.setLang":"Language", + + + "locale.auto": "System", + "locale.en": "English", + "locale.zh-hans": "简体中文", + "locale.zh-hant": "繁体中文", + "locale.language-change-confirm": "Applying this setting will restart the app" + + + + + +} diff --git a/locale/index.js b/locale/index.js new file mode 100644 index 0000000..d42cd89 --- /dev/null +++ b/locale/index.js @@ -0,0 +1,8 @@ +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +import en from './en.json' +export default { + 'zh-Hans': zhHans, + 'zh-Hant': zhHant, + en +} diff --git a/locale/uni-app.en.json b/locale/uni-app.en.json new file mode 100644 index 0000000..c57d3d3 --- /dev/null +++ b/locale/uni-app.en.json @@ -0,0 +1,36 @@ +{ + "common": { + "uni.app.quit": "Press again to quit the app", + "uni.async.error": "The connection to the server timed out, click the screen to retry", + "uni.showActionSheet.cancel": "Cancel", + "uni.showToast.unpaired": "Please note that showToast and hideToast must be paired", + "uni.showLoading.unpaired": "Please note that showLoading and hideLoading must be paired", + "uni.showModal.cancel": "Cancel", + "uni.showModal.confirm": "OK", + "uni.chooseImage.cancel": "Cancel", + "uni.chooseImage.sourceType.album": "Choose from Album", + "uni.chooseImage.sourceType.camera": "shoot", + "uni.chooseVideo.cancel": "Cancel", + "uni.chooseVideo.sourceType.album": "Choose from Album", + "uni.chooseVideo.sourceType.camera": "Camera", + "uni.previewImage.cancel": "Cancel", + "uni.previewImage.button.save": "Save Image", + "uni.previewImage.save.success": "Successful saving image to album", + "uni.previewImage.save.fail": "Failed to save image to album", + "uni.setClipboardData.success": "Content copied", + "uni.scanCode.title": "scan", + "uni.scanCode.album": "Album", + "uni.scanCode.fail": "Recognition failed", + "uni.scanCode.flash.on": "Light touch to light", + "uni.scanCode.flash.off": "Flick Off", + "uni.startSoterAuthentication.authContent": "Fingerprint identification...", + "uni.picker.done": "Done", + "uni.picker.cancel": "Cancel", + "uni.video.danmu": "Barrage", + "uni.video.volume": "Volume", + "uni.button.feedback.title": "Problem Feedback", + "uni.button.feedback.send": "send" + }, + "ios": {}, + "android": {} +} \ No newline at end of file diff --git a/locale/uni-app.zh-Hans.json b/locale/uni-app.zh-Hans.json new file mode 100644 index 0000000..78cfef2 --- /dev/null +++ b/locale/uni-app.zh-Hans.json @@ -0,0 +1,36 @@ +{ + "common": { + "uni.app.quit": "再按一次退出应用", + "uni.async.error": "连接服务器超时,点击屏幕重试", + "uni.showActionSheet.cancel": "取消", + "uni.showToast.unpaired": "请注意 showToast 与 hideToast 必须配对使用", + "uni.showLoading.unpaired": "请注意 showLoading 与 hideLoading 必须配对使用", + "uni.showModal.cancel": "取消", + "uni.showModal.confirm": "确定", + "uni.chooseImage.cancel": "取消", + "uni.chooseImage.sourceType.album": "从相册选择", + "uni.chooseImage.sourceType.camera": "拍摄", + "uni.chooseVideo.cancel": "取消", + "uni.chooseVideo.sourceType.album": "从相册选择", + "uni.chooseVideo.sourceType.camera": "拍摄", + "uni.previewImage.cancel": "取消", + "uni.previewImage.button.save": "保存图像", + "uni.previewImage.save.success": "保存图像到相册成功", + "uni.previewImage.save.fail": "保存图像到相册失败", + "uni.setClipboardData.success": "内容已复制", + "uni.scanCode.title": "扫码", + "uni.scanCode.album": "相册", + "uni.scanCode.fail": "识别失败", + "uni.scanCode.flash.on": "轻触照亮", + "uni.scanCode.flash.off": "轻触关闭", + "uni.startSoterAuthentication.authContent": "指纹识别中...", + "uni.picker.done": "完成", + "uni.picker.cancel": "取消", + "uni.video.danmu": "弹幕", + "uni.video.volume": "音量", + "uni.button.feedback.title": "问题反馈", + "uni.button.feedback.send": "发送" + }, + "ios": {}, + "android": {} +} \ No newline at end of file diff --git a/locale/uni-app.zh-Hant.json b/locale/uni-app.zh-Hant.json new file mode 100644 index 0000000..ed0ac42 --- /dev/null +++ b/locale/uni-app.zh-Hant.json @@ -0,0 +1,36 @@ +{ + "common": { + "uni.app.quit": "再按一次退出應用", + "uni.async.error": "連接服務器超時,點擊屏幕重試", + "uni.showActionSheet.cancel": "取消", + "uni.showToast.unpaired": "請註意 showToast 與 hideToast 必須配對使用", + "uni.showLoading.unpaired": "請註意 showLoading 與 hideLoading 必須配對使用", + "uni.showModal.cancel": "取消", + "uni.showModal.confirm": "確定", + "uni.chooseImage.cancel": "取消", + "uni.chooseImage.sourceType.album": "從相冊選擇", + "uni.chooseImage.sourceType.camera": "拍攝", + "uni.chooseVideo.cancel": "取消", + "uni.chooseVideo.sourceType.album": "從相冊選擇", + "uni.chooseVideo.sourceType.camera": "拍攝", + "uni.previewImage.cancel": "取消", + "uni.previewImage.button.save": "保存圖像", + "uni.previewImage.save.success": "保存圖像到相冊成功", + "uni.previewImage.save.fail": "保存圖像到相冊失敗", + "uni.setClipboardData.success": "內容已復製", + "uni.scanCode.title": "掃碼", + "uni.scanCode.album": "相冊", + "uni.scanCode.fail": "識別失敗", + "uni.scanCode.flash.on": "輕觸照亮", + "uni.scanCode.flash.off": "輕觸關閉", + "uni.startSoterAuthentication.authContent": "指紋識別中...", + "uni.picker.done": "完成", + "uni.picker.cancel": "取消", + "uni.video.danmu": "彈幕", + "uni.video.volume": "音量", + "uni.button.feedback.title": "問題反饋", + "uni.button.feedback.send": "發送" + }, + "ios": {}, + "android": {} +} \ No newline at end of file diff --git a/locale/zh-Hans.json b/locale/zh-Hans.json new file mode 100644 index 0000000..de5724c --- /dev/null +++ b/locale/zh-Hans.json @@ -0,0 +1,39 @@ +{ + "app.name": "合成照相馆", + "tabbar.home": "首页", + "tabbar.list": "附近", + "tabbar.user": "我的", + "page.list.index.title": "附近", + + "page.user.index.title": "用户中心", + "pages.user.setting.index.title":"设置", + "pages.user.setting.index.setLang":"多语言", + + "locale.auto": "系统", + "locale.en": "English", + "locale.zh-hans": "简体中文", + "locale.zh-hant": "繁体中文", + "locale.language-change-confirm": "应用此设置将重启App", + "locale.tips": "提示", + + "locale.login": "登录账号", + "locale.regist": "账号注册", + "locale.logout": "退出登录", + + "pages.user.auth.login.login":"登录页面", + "pages.user.auth.login.mobile":"手机号码", + "pages.user.auth.login.input_mobile":"请输入手机号码", + "pages.user.auth.login.mobile_must":"手机号码格式不正确", + + "pages.user.auth.login.email":"邮箱地址", + "pages.user.auth.login.input_email":"请输入Email地址", + "pages.user.auth.login.email_must":"Email格式不正确", + + "pages.user.auth.login.password":"账号密码", + "pages.user.auth.login.input_password":"请输入账号密码", + "pages.user.auth.login.password_must":"密码不小于6位", + + "pages.user.auth.regist.regist":"注册页面", + "pages.user.auth.regist.regist_success":"注册成功" + +} diff --git a/locale/zh-Hant.json b/locale/zh-Hant.json new file mode 100644 index 0000000..7d07307 --- /dev/null +++ b/locale/zh-Hant.json @@ -0,0 +1,20 @@ +{ + "app.name": "空间充", + "tabbar.home": "首頁", + "tabbar.list": "附近", + "tabbar.user": "我的", + "page.list.index.title": "附近", + + "page.user.index.title": "用戶中心", + "pages.user.setting.index.title":"設置", + "pages.user.setting.index.setLang":"多語言", + + "locale.auto": "系統", + "locale.en": "English", + "locale.zh-hans": "简体中文", + "locale.zh-hant": "繁體中文", + "locale.language-change-confirm": "應用此設置將重啟App" + + + +} diff --git a/main.js b/main.js new file mode 100644 index 0000000..be89afe --- /dev/null +++ b/main.js @@ -0,0 +1,57 @@ +import App from './App' +import messages from './locale/index' +// main.js +import uView from '@/uni_modules/uview-ui'; +Vue.use(uView); + + +let i18nConfig = { + locale: uni.getLocale(), + messages +} +// 引入请求封装,将app参数传递到配置中 +require('@/config/request.js')(app); //uview js +//自己封装的部分 +import api from '@/config/api.js'; +Vue.prototype.$api = api; +import user from '@/config/user.js'; +Vue.prototype.$user = user; +import com from '@/config/com.js'; +Vue.prototype.$com = com; + +import access from '@/config/access/index.js'; +Vue.prototype.$access = access; + +// #ifdef H5 +import wechat from '@/config/wechat.js' +Vue.prototype.$wechat =wechat; +// #endif + + + +// #ifndef VUE3 +import Vue from 'vue' +import VueI18n from 'vue-i18n' +Vue.use(VueI18n) +const i18n = new VueI18n(i18nConfig) +Vue.config.productionTip = false +App.mpType = 'app' +const app = new Vue({ + i18n, + ...App +}) +app.$mount(); +// #endif + +// #ifdef VUE3 +import { createSSRApp } from 'vue' +import { createI18n } from 'vue-i18n' +const i18n = createI18n(i18nConfig) +export function createApp() { + const app = createSSRApp(App) + app.use(i18n) + return { + app + } +} +// #endif \ No newline at end of file diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..306a074 --- /dev/null +++ b/manifest.json @@ -0,0 +1,100 @@ +{ + "name" : "合成照相馆", + "appid" : "__UNI__304D8B8", + "description" : "", + "versionName" : "2.1.2", + "versionCode" : 212, + "transformPx" : false, + /* 5+App特有相关 */ + "app-plus" : { + "usingComponents" : true, + "nvueStyleCompiler" : "uni-app", + "compilerVersion" : 3, + "splashscreen" : { + "alwaysShowBeforeRender" : true, + "waiting" : true, + "autoclose" : true, + "delay" : 0 + }, + /* 模块配置 */ + "modules" : {}, + /* 应用发布信息 */ + "distribute" : { + /* android打包配置 */ + "android" : { + "permissions" : [ + "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", + "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", + "<uses-permission android:name=\"android.permission.VIBRATE\"/>", + "<uses-permission android:name=\"android.permission.READ_LOGS\"/>", + "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", + "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", + "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", + "<uses-permission android:name=\"android.permission.CAMERA\"/>", + "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", + "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", + "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", + "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", + "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", + "<uses-feature android:name=\"android.hardware.camera\"/>", + "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" + ] + }, + /* ios打包配置 */ + "ios" : {}, + /* SDK配置 */ + "sdkConfigs" : {} + } + }, + /* 快应用特有相关 */ + "quickapp" : {}, + /* 小程序特有相关 */ + "mp-weixin" : { + "appid" : "wx19306460077082e9", + "setting" : { + "urlCheck" : false, + "es6" : true, + "postcss" : true, + "minified" : true + }, + "usingComponents" : true, + "permission" : { + "scope.userLocation" : { + "desc" : "你的位置信息将用于小程序位置接口的效果展示" + } + }, + "requiredPrivateInfos" : [ "getLocation", "chooseLocation" ], + "libVersion" : "latest" + }, + "mp-alipay" : { + "usingComponents" : true + }, + "mp-baidu" : { + "usingComponents" : true + }, + "mp-toutiao" : { + "usingComponents" : true + }, + "uniStatistics" : { + "enable" : false + }, + "vueVersion" : "2", + "locale" : "zh-Hans", + "fallbackLocale" : "zh-Hans", + "h5" : { + "title" : "空间充", + "router" : { + "mode" : "history", + "base" : "/h5/" + }, + "template" : "", + "devServer" : { + "https" : false + }, + "optimization" : { + "treeShaking" : { + "enable" : false + } + } + } +} diff --git a/pages.json b/pages.json new file mode 100644 index 0000000..fb3aab3 --- /dev/null +++ b/pages.json @@ -0,0 +1,82 @@ +{ + "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages + { + "path": "pages/index/index", + "style": { + "navigationBarTitleText": "%app.name%", + "navigationStyle": "custom" + } + } + ,{ + "path" : "pages/user/index", + "style" : + { + "navigationBarTitleText": "%page.user.index.title%", + "enablePullDownRefresh": false + + } + + } + ,{ + "path" : "pages/user/setting/index", + "style" : + { + "navigationBarTitleText": "%pages.user.setting.index.setLang%", + "enablePullDownRefresh": false + } + + }, + { + "path" : "pages/user/account/index", + "style" : + { + "navigationBarTitleText" : "账户设置", + "enablePullDownRefresh" : false + } + }, + { + "path" : "pages/web/index", + "style" : + { + "navigationBarTitleText" : "外部页面", + "enablePullDownRefresh" : false + } + }, + { + "path" : "pages/web/video", + "style" : + { + "navigationBarTitleText" : "视频", + "enablePullDownRefresh" : false + } + } + + ], + "subPackages": [], + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "%app.name%", + "navigationBarBackgroundColor": "#FFFFFF", + "backgroundColor": "#FFFFFF" + }, + "tabBar": { + "color": "#AFB1DB", + "selectedColor": "#7A74F0", + "borderStyle": "black", + "backgroundColor": "#FFFFFF", + "list": [{ + "pagePath": "pages/index/index", + "iconPath": "static/img/common/home.png", + "selectedIconPath": "static/img/common/home_HL.png", + "text": "%tabbar.home%" + }, + { + "pagePath": "pages/user/index", + "iconPath": "static/img/common/user.png", + "selectedIconPath": "static/img/common/user_HL.png", + "text": "%tabbar.user%" + } + ] + }, + "uniIdRouter": {} +} diff --git a/pages/index/index.vue b/pages/index/index.vue new file mode 100644 index 0000000..d12df52 --- /dev/null +++ b/pages/index/index.vue @@ -0,0 +1,482 @@ +<template> + <view class="container"> + + <view class="bannerBox"> + <u-swiper + :list="banner" + keyName="adImg" + :showTitle="false" + :autoplay="true" + circular + height="280" + radius="0" + @click="openAd" + ></u-swiper> + </view> + + + <view class="contentBox"> + + <view class="navBox"> + <view class="navItem"> + <image src="/static/img/index/zjhc.png" mode="widthFix"></image> + </view> + <view class="navItem"> + <image src="/static/img/index/zjgs.png" mode="widthFix"></image> + </view> + <view class="navItem"> + <image src="/static/img/index/aixz.png" mode="widthFix"></image> + </view> + <view class="navItem"> + <image src="/static/img/index/kthf.png" mode="widthFix"></image> + </view> + </view> + + <view class="indexTitle"> + <image src="/static/img/index/title_zj.png" mode="widthFix"></image> + </view> + <view class="listBox"> + <view class="photoBox"> + <view class="photoTop"> + <view class="topLeft"> + <image src="/static/img/index/zj_icon.png" mode="widthFix"></image> + </view> + <view class="topCenter"> + <view class="title">一寸照</view> + <view class="desc">25×35mm | 413×579px</view> + + </view> + <view class="topRight"> + <view class="distance"> + <image src="/static/img/index/right.png" mode="widthFix"></image> + </view> + </view> + </view> + </view> + + <view class="photoBox"> + <view class="photoTop"> + <view class="topLeft"> + <image src="/static/img/index/zj_icon.png" mode="widthFix"></image> + </view> + <view class="topCenter"> + <view class="title">二寸照</view> + <view class="desc">25×35mm | 413×579px</view> + + </view> + <view class="topRight"> + <view class="distance"> + <image src="/static/img/index/right.png" mode="widthFix"></image> + </view> + </view> + </view> + </view> + + + <view class="photoBox"> + <view class="photoTop"> + <view class="topLeft"> + <image src="/static/img/index/zj_icon.png" mode="widthFix"></image> + </view> + <view class="topCenter"> + <view class="title">大一寸照</view> + <view class="desc">25×35mm | 413×579px</view> + + </view> + <view class="topRight"> + <view class="distance"> + <image src="/static/img/index/right.png" mode="widthFix"></image> + </view> + </view> + </view> + </view> + + <view class="photoBox"> + <view class="photoTop"> + <view class="topLeft"> + <image src="/static/img/index/zj_icon.png" mode="widthFix"></image> + </view> + <view class="topCenter"> + <view class="title">小一寸照</view> + <view class="desc">25×35mm | 413×579px</view> + + </view> + <view class="topRight"> + <view class="distance"> + <image src="/static/img/index/right.png" mode="widthFix"></image> + </view> + </view> + </view> + </view> + + </view> + <view class="indexTitle"> + <image src="/static/img/index/title_xz.png" mode="widthFix"></image> + </view> + + <view class="picList"> + <scroll-view class="scrollBox" scroll-x="true" @scroll="scroll1" scroll-left="0"> + <view class="picBox"> + <view class="pic"> + <image src="/static/img/index/pic.png" mode="widthFix"></image> + </view> + <view class="pic_title">风格名称</view> + </view> + <view class="picBox"> + <view class="pic"> + <image src="/static/img/index/pic.png" mode="widthFix"></image> + </view> + <view class="pic_title">风格名称</view> + </view> + <view class="picBox"> + <view class="pic"> + <image src="/static/img/index/pic.png" mode="widthFix"></image> + </view> + <view class="pic_title">风格名称</view> + </view> + <view class="picBox"> + <view class="pic"> + <image src="/static/img/index/pic.png" mode="widthFix"></image> + </view> + <view class="pic_title">风格名称</view> + </view> + </scroll-view> + </view> + + <view class="indexTitle"> + <image src="/static/img/index/title_kt.png" mode="widthFix"></image> + </view> + + <view class="picList"> + <scroll-view class="scrollBox" scroll-x="true" @scroll="scroll2" scroll-left="0"> + <view class="picBox"> + <view class="pic"> + <image src="/static/img/index/pic.png" mode="widthFix"></image> + </view> + <view class="pic_title">风格名称</view> + </view> + <view class="picBox"> + <view class="pic"> + <image src="/static/img/index/pic.png" mode="widthFix"></image> + </view> + <view class="pic_title">风格名称</view> + </view> + <view class="picBox"> + <view class="pic"> + <image src="/static/img/index/pic.png" mode="widthFix"></image> + </view> + <view class="pic_title">风格名称</view> + </view> + <view class="picBox"> + <view class="pic"> + <image src="/static/img/index/pic.png" mode="widthFix"></image> + </view> + <view class="pic_title">风格名称</view> + </view> + </scroll-view> + </view> + + + </view> + + </view> +</template> + +<script> + export default { + data() { + return { + banner: [ + { + adImg:'/static/img/index/banner.png', + } + ], + notice:'', + } + }, + computed:{ + + }, + onLoad() { + + this.getHomeInfo(); + + }, + onShareAppMessage() { + return { + title: '空间充-首页', + path: uni.$u.page(), + } + }, + onShareTimeline() { + return { + title: '空间充-首页', + path: uni.$u.page(), + } + }, + methods: { + //获取门店信息 + getHomeInfo(){ + var _this =this; + //post 请求例子 + _this.$api.post('ycl/home', {},function(res){ + console.log(res); + var d = res; + var banner = d.adList + banner.forEach((item, index) => { + item.title = item.adName; + }) + //_this.banner = banner; + //_this.notice = d.notice; + //_this.$user.session('servicePhone',d.servicePhone); + }); + }, + //滚动监控 + scroll1(e) { + console.log(e); + }, + scroll2(e) { + console.log(e); + }, + //扫码 + scanQrcode() + { + var _this =this; + _this.$com.showLoading('识别中...'); + uni.scanCode({ + onlyFromCamera: false, + success: function (res) { + console.log(res); + console.log('条码类型:' + res.scanType); + console.log('条码内容:' + res.result); + if (res.scanType == 'QR_CODE') + { + var url = res.result; + var codeTypeArr = ['deviceKey', 'deviceCode', 'qrcode', 'code']; //支持的条码参数 + var url_str = ''; + codeTypeArr.forEach(function(item) { + if(url.indexOf(item) != -1 ) + { + var keyindex = item; + var deviceKey = _this.getParameterByName(url,item); + if(!_this.$com.isNull(deviceKey)) + { + if(['qrcode', 'code'].includes(item)) { + keyindex = 'deviceCode'; + } + console.log(keyindex+'='+deviceKey); + url_str = `${keyindex}=${deviceKey}`; + return; + } + + } + }); + if(!_this.$com.isNull(url_str)) + { + uni.$u.route({ + url: '/pages/order/order?'+url_str, + }); + } + else + { + + _this.$com.alert('二维码格式有误'); + + } + } + else + { + _this.$com.alert('暂不支持此种条码类型'); + } + }, + fail:function(err){ + _this.$com.alert('扫码失败,请重试'); + }, + complete:function(end){ + _this.$com.hideLoading(); + } + }); + }, + //获取url中是否有某个参数的值 + getParameterByName(url,name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regexS = "[\\?&]" + name + "=([^&#]*)"; + var regex = new RegExp(regexS); + var results = regex.exec(url); + + if (results == null) return ""; + else return decodeURIComponent(results[1].replace(/\+/g, " ")); + }, + openAd(index) + { + var _this =this; + var item = _this.banner[index]; + console.log(item); + if(!_this.$com.isNull(item.jumpUrl)) + { + console.log(item.jumpUrl); + var url = ''; + switch(item.jumpType) + { + case 0: + url=item.jumpUrl; + break; + + case 1: + url='/pages/web/index?title='+item.adName+'&url='+encodeURIComponent(item.jumpUrl); + break; + + case 2: + url='/pages/web/video?title='+item.adName+'&url='+encodeURIComponent(item.jumpUrl); + break; + + default: + url = ''; + } + if(!_this.$com.isNull(url)) + { + uni.navigateTo({ + url:url, + }) + } + } + }, + + } + } +</script> + +<style lang="scss"> + .container{width: 100%;} + + .bannerBox{ + width: 100%; + background: #082436; + height: auto; + position: relative; + } + ::v-deep .u-swiper{background-color: inherit !important;} + + .contentBox{ + width:746rpx; + height: 500rpx; + background: linear-gradient( 180deg, #DBD9FF 0%, #F5F6FB 100%); + border-radius: 28rpx 28rpx 0rpx 0rpx; + border: 2rpx solid #FFFFFF; + margin-top: -80rpx; + position: relative; + z-index: 2; + + +} + +.navBox{ width: 100%; height: auto; display: flex;} +.navBox .navItem{ width: 164rpx; height: 140rpx; margin: 40rpx 20rpx 20rpx 20rpx;} +.navBox .navItem image{ width: 100%; height: auto;} +.indexTitle {width: 730rpx; height: auto; margin: 20rpx auto; padding-left: 20rpx; } +.indexTitle image{ width: 226rpx; height: auto;} + + +.listBox{ width: 100%; height: auto;} + .photoBox{ + padding:20rpx 10rpx 10rpx 10rpx; + height: auto; + border-radius: 24rpx 24rpx 24rpx 24rpx; + opacity: 1; + background: #FFFFFF; + width: 670rpx; + margin: 15rpx auto; + } + .photoTop{width: 100%; height: auto; display: flex; + + } + .photoTop .topLeft{ + width: 100rpx; + height: 88rpx; + + } + .topLeft image{ width: 60rpx; height: auto; margin-left: 14rpx;} + .photoTop .topCenter{ + width: 400rpx; + height: auto; + padding-left: 18rpx; + + } + .topCenter .title{ + font-size: 30rpx; + font-weight: 400; + color:#333333; + line-height: 50rpx; + width: 100%; + overflow: hidden; //超出文本隐藏 + white-space: nowrap; //不换行,只显示一行 + text-overflow: ellipsis; //超出部分省略号显示 + } + .topCenter .desc{ + font-size: 24rpx; + color:#999999; + line-height: 30rpx; + width: 100%; + overflow: hidden; //超出文本隐藏 + white-space: nowrap; //不换行,只显示一行 + text-overflow: ellipsis; //超 + } + + + .photoTop .topRight{ + width: 160rpx; + height: auto; + text-align: right; + + } + .topRight .distance{ color: #90A1AA; font-size: 24rpx; font-family: PingFang SC-Regular; padding-top: 20rpx;} + .topRight .distance image{ width: 32rpx; height: 32rpx; margin-right: 24rpx;} + + + .picList{ width: 100%; height: 360rpx;} + + .scrollBox { + margin-top: 20rpx; + width: 100%; + height: 360rpx; + white-space: nowrap; + + } + + .picBox { + display: inline-block; + width:260rpx; + height: 360rpx; + text-align: center; + position: relative; + margin-left: 20rpx; + + } + .picBox .pic{ + position: absolute; + width:100%; + height: 360rpx; + + + } + .picBox .pic image{ + width:260rpx; + height: 360rpx; + border-radius:24rpx; + } +.picBox .pic_title{ + position: absolute; + width:240rpx; + height: 72rpx; + bottom: 0; + z-index: 2; + font-size: 24rpx; line-height: 72rpx; + padding-left: 20rpx; + color: #FFFFFF; + text-align: left; + overflow: hidden; //超出文本隐藏 + white-space: nowrap; //不换行,只显示一行 + text-overflow: ellipsis; //超 + } + +</style> diff --git a/pages/user/account/index.vue b/pages/user/account/index.vue new file mode 100644 index 0000000..0b26e28 --- /dev/null +++ b/pages/user/account/index.vue @@ -0,0 +1,275 @@ +<template> + <view class="container"> + + + <view class="form"> + <!--input box start--> + <view class='input_box'> + <view class='i_box_1'>头像</view> + <view class='i_box_2'></view> + <view class='i_box_3'> + + </view> + <view class='i_box_4'></view> + <view class='i_box_5'> + <view class="up"> + <view class="img"> + <u-upload + + :fileList="wxAvatar" + @afterRead="afterRead" + @delete="deletePic" + name="avatar" + width="26" + height="26" + :sizeType="['compressed']" + :deletable="true" + :maxCount="1" + :previewFullImage="false" + > + + </u-upload> + </view> + <view class="icon" @click="clearImg"> + <u-icon name="arrow-right" top="5"></u-icon> + </view> + </view> + </view> + </view> + <!--input box end--> + + + <!--input box start--> + <view class='input_box'> + <view class='i_box_1'>昵称</view> + <view class='i_box_2'></view> + <view class='i_box_3x'> + <u--input + placeholder="请输入昵称" + inputAlign="right" + border="none" + color="#FFFFFF" + maxlength="10" + v-model="info.wxName" + ></u--input> + </view> + + </view> + <!--input box end--> + + <!--input box start--> + <view class='input_box'> + <view class='i_box_1'>手机号</view> + <view class='i_box_2'></view> + <view class='i_box_3x'> + <u--input + v-if="!this.$com.isNull(info.phoneNumber)" + inputAlign="right" + border="none" + color="#FFFFFF" + maxlength="11" + v-model="info.phoneNumber" + :readonly="true" + ></u--input> + <u-button v-else :text="info.phoneNumber || '点击获取手机号'" openType="getPhoneNumber" @getphonenumber="getPhoneNumber"></u-button> + </view> + + </view> + <!--input box end--> + + </view> + + <view class="btn-big"><u-button type="info" :loading="btn_loading" loadingText="加载中" :disabled="btn_disabled" text="保存" @click="add"></u-button></view> + </view> +</template> + +<script> + + export default { + data() { + return { + //form框 + info: { + wxName:'', + wxAvatar:'', + phoneNumber:'点击获取手机号', + + }, + wxAvatar: [], + //按钮设置 + btn_loading:false, + btn_disabled:false, + } + }, + onLoad(option) { + var _this = this; + _this.getUserInfo(); + + }, + methods: { + + //获取用户信息 + getUserInfo() + { + var _this =this; + var post ={}; + _this.$api.post('ycl/user/wx-info',post,function(rs){ + console.log(rs); + _this.info = rs; + _this.wxAvatar =[{url:rs.wxAvatar}]; + }); + }, + // 删除图片 + deletePic(event) { + var _this = this; + _this.wxAvatar = []; + }, + // 新增图片 + afterRead(event) { + + var _this = this + console.log('event',event); + var edetail = event.file.thumb; + if (edetail.indexOf('http://tmp') != -1 || edetail.indexOf('wxfile://tmp') != -1) + { + var FSM = uni.getFileSystemManager(); + FSM.readFile({ + filePath: edetail, + encoding: "base64", + success: function(data) { + _this.info.imgBase64 = "data:image/png;base64," + data.data + _this.wxAvatar = [{url:edetail}]; + }, + fail: function(err){ + console.log('http://tmp',err) + } + }) + + } + + }, + uploadFilePromise(url) { + + }, + loadData(){ + + }, + getPhoneNumber(e){ + var _this =this; + var detail = e.detail; + console.log(detail); + if (detail.errMsg === 'getPhoneNumber:ok') { + + _this.getPhoneApi(detail); + } else { + _this.$com.alert('授权失败') + } + }, + getPhoneApi(detail){ + var _this = this; + + var post ={ + + code: detail.code, + }; + _this.$api.post('ycl/user/wx-phone',post,function(rs){ + console.log(rs); + _this.info.phoneNumber = rs; + },function(err){ + console.log(err); + }); + + }, + + //提交表单 + add(){ + var _this =this; + var info = _this.info; + + if(_this.$com.isNull(info.wxName)) + { + _this.$com.showError('请输入昵称'); + return; + } + if(_this.$com.isNull(info.wxAvatar) && _this.$com.isNull(info.imgBase64)) + { + _this.$com.showError('请上传头像'); + return; + } + if(_this.$com.isNull(info.phoneNumber)) + { + _this.$com.showError('手机号必须'); + return; + } + _this.btn_loading = true; + _this.btn_disabled = true; + var post = info; + _this.$api.put('ycl/user/wx-update',post,function(rs){ + console.log('更新token'); + _this.$user.session('token',rs.token); + _this.$com.alert('更新成功'); + setTimeout(function(){ + uni.$u.route({ + type:'navigateBack', + delta:1, + }); + },1000); + }); + //按钮还原 + _this.btn_loading = false; + _this.btn_disabled = false; + + }, + + + } + } +</script> + +<style lang="scss"> +.container{padding:10rpx 20rpx;} +.form{ padding: 20rpx; +background: #082436; +border-radius: 24rpx 24rpx 24rpx 24rpx; +} +/*form基本元素*/ +/*input_box*/ +.input_box{ display: flex;flex-direction:row; height: 100rpx; line-height: 100rpx; +border-radius: 15rpx;box-shadow: 0 2rpx 2rpx rgba(0, 0, 0, 0.05); margin: 20rpx auto; color:#FFFFFF;} + +.i_box_1{ width: 25%;height: 100rpx; font-size: 28rpx; text-align: left; } +.i_box_2{width: 0.3%;height: 80rpx; margin-top: 10rpx;} +.i_box_3{ height: 100rpx;min-width: 49.4%; width: auto;font-size: 26rpx;} + + + + + +/*特殊的 改变部分*/ +.i_box_3x{height: 100rpx;min-width: 74.7%; width: auto;font-size: 26rpx;} +::v-deep .u-input{ border:none !important; color:#FFFFFF !important; margin-top: 10rpx;} +.up{ width: 100%; height: auto; display: flex; margin-top: 20rpx;} +.up .img{width:80%; } +.up .img .u-upload{ margin-left: 80rpx;} +.up .icon{ width:20%;} +::v-deep .i_box_3x .u-button--info{ background-color: transparent !important; border: none !important; color: #FFFFFF !important; text-align: right !important;} +::v-deep .i_box_3x .u-button{display: block !important; padding:0rpx !important;} +::v-deep .u-upload__button{border-radius: 50% !important;} +::v-deep .u-upload__wrap__preview{border-radius: 50% !important;} + + + +.btn-big{ width:100%; + height: 92rpx; + line-height: 92rpx; + margin:30rpx auto; + display:block; + border-radius:56rpx; + background:#00F0E2; + font-size:28rpx; + color:#fff;text-align: center;} + +::v-deep .btn-big .u-button--info{ background-color: transparent !important; border: none !important; color: #FFFFFF !important; } + + +</style> diff --git a/pages/user/auth/login.vue b/pages/user/auth/login.vue new file mode 100644 index 0000000..4cfd77c --- /dev/null +++ b/pages/user/auth/login.vue @@ -0,0 +1,176 @@ +<template> + <view class="container"> + + <view class="input_box"> + <text class="input_title">{{$t('pages.user.auth.login.mobile')}}</text> + <view class="input_content"> + <u--input + :placeholder="$t('pages.user.auth.login.input_mobile')" + prefixIcon="phone" + prefixIconStyle="font-size: 24rpx;color: #909399" + v-model="form.mobile" + @change="mobileChange" + ></u--input> + </view> + </view> + + + + <view class="input_box"> + <text class="input_title">{{$t('pages.user.auth.login.password')}}</text> + <view class="input_content"> + <u--input + :placeholder="$t('pages.user.auth.login.input_password')" + prefixIcon="lock" + prefixIconStyle="font-size: 24rpx;color: #909399" + border="surround" + password + clearable + v-model="form.password" + @change="pwdChange" + ></u--input> + </view> + </view> + + + + <view class="button_box"> + <view class="submit"> + <u-button type="primary" :plain="true" :hairline="true" :text="$t('locale.login')" size="normal" @click="login"></u-button> + </view> + </view> + <view class="footer"> + <!-- <text class="footer-text">找回密码</text> --> + <text class="footer-text" @click="regist">{{$t('locale.regist')}}</text> + <!-- <text class="footer-text">投诉建议</text> --> + </view> + </view> +</template> +<script> + export default { + components: { + + }, + data() { + return { + + form:{ + mobile:'', + password:'', + }, + }; + }, + onShow() { + var _this =this; + + }, + methods:{ + //跳转到注册 + regist(){ + uni.$u.route({ + type:'redirectTo', + url: 'pages/user/auth/regist', + params: {} + }); + }, + //手机号 + mobileChange(e) + { + var _this = this; + _this.form['mobile'] = e; + + }, + //密码 + pwdChange(e) + { + var _this = this; + _this.form['password'] = e; + }, + login(){ + var _this =this; + var form = _this.form; + if(!_this.$com.check(form.mobile,'mobile')) + { + uni.$u.toast(_this.$t('pages.user.auth.login.mobile_must')); + return false; + } + if(form.password.length < 6) + { + uni.$u.toast(_this.$t('pages.user.auth.login.password_must')); + return false; + } + + var post = form; + _this.$api.post('user/log',post,function(rs){ + console.log(rs); + _this.$user.session('userInfo',rs.userinfo); + _this.$user.session('token',rs.token); + uni.$u.route({ + type:'navigateBack', + delta:2, + params: {} + }); + },function(err){ + console.log(err); + }); + }, + + }, + }; +</script> + +<style scoped lang="scss"> + /*输入框*/ + .input_box{ + width: 90%; + margin: 40rpx auto 40rpx auto; + height: auto; + } + .input_title{ + font-size: 30rpx; + color: #ABABAB; + height: 50rpx; + line-height: 50rpx; + margin: 10rpx auto; + display: block; + + } + + .button_box { + width: 90%; + margin: 40rpx auto 20rpx auto; + + } + + .submit { + width: 100%; + background-color: #4CAF50; + justify-content: center; + align-items: center; + border-radius: 10rpx; + } + + .submit-text { + color: white; + padding: 30rpx; + } + + .submit:active { + background-color: green; + opacity: 0.5; + } + + .footer { + flex-direction: row; + justify-content: center; + text-align: center; + align-items: center; + margin-top: 100rpx; + } + + .footer-text { + font-size: 28rpx; + color: #296db5; + padding: 30rpx; + } +</style> diff --git a/pages/user/auth/regist.vue b/pages/user/auth/regist.vue new file mode 100644 index 0000000..39f6ecb --- /dev/null +++ b/pages/user/auth/regist.vue @@ -0,0 +1,205 @@ +<template> + <view class="container"> + + <view class="input_box"> + <text class="input_title">{{$t('pages.user.auth.login.mobile')}}</text> + <view class="input_content"> + <u--input + :placeholder="$t('pages.user.auth.login.input_mobile')" + prefixIcon="phone" + prefixIconStyle="font-size: 24rpx;color: #909399" + type="number" + v-model="form.mobile" + @change="mobileChange" + ></u--input> + </view> + </view> + + + + <view class="input_box"> + <text class="input_title">{{$t('pages.user.auth.login.password')}}</text> + <view class="input_content"> + <u--input + :placeholder="$t('pages.user.auth.login.input_password')" + prefixIcon="lock" + prefixIconStyle="font-size: 24rpx;color: #909399" + border="surround" + password + clearable + v-model="form.password" + @change="pwdChange" + ></u--input> + </view> + </view> + + <view class="input_box"> + <text class="input_title">{{$t('pages.user.auth.login.email')}}</text> + <view class="input_content"> + <u--input + :placeholder="$t('pages.user.auth.login.input_email')" + prefixIcon="email" + prefixIconStyle="font-size: 24rpx;color: #909399" + v-model="form.email" + @change="emailChange" + ></u--input> + </view> + </view> + + <view class="button_box"> + <view class="submit"> + <u-button type="primary" :plain="true" :hairline="true" :text="$t('locale.regist')" size="normal" @click="regist"></u-button> + </view> + </view> + <view class="footer"> + <!-- <text class="footer-text">找回密码</text> --> + <text class="footer-text" @click="login">{{$t('locale.login')}}</text> + <!-- <text class="footer-text">投诉建议</text> --> + </view> + </view> +</template> +<script> + export default { + components: { + + }, + data() { + return { + form:{ + mobile:'', + password:'', + email:'', + }, + + }; + }, + onShow() { + var _this =this; + + }, + methods:{ + //跳转到登录 + login(){ + uni.$u.route({ + type:'redirectTo', + url: 'pages/user/auth/login', + params: {} + }); + }, + //手机号 + mobileChange(e) + { + var _this = this; + _this.form['mobile'] = e; + + }, + //密码 + pwdChange(e) + { + var _this = this; + _this.form['password'] = e; + }, + //email + emailChange(e) + { + var _this = this; + _this.form['email'] = e; + }, + regist() + { + var _this =this; + var form = _this.form; + if(!_this.$com.check(form.mobile,'mobile')) + { + uni.$u.toast(_this.$t('pages.user.auth.login.mobile_must')); + return false; + } + if(form.password.length < 6) + { + uni.$u.toast(_this.$t('pages.user.auth.login.password_must')); + return false; + } + if(!_this.$com.check(form.email,'email')) + { + uni.$u.toast(_this.$t('pages.user.auth.login.email_must')); + return false; + } + + var post = form; + _this.$api.post('user/reg',post,function(rs){ + console.log(rs); + uni.showModal({ + title: _this.$t('locale.tips'), + content: _this.$t('pages.user.auth.regist.regist_success'), + success: function (res) { + if (res.confirm) { + console.log('用户点击确定'); + _this.login(); + } else if (res.cancel) { + console.log('用户点击取消'); + } + } + }); + },function(err){ + console.log(err); + }); + }, + }, + }; +</script> + +<style scoped lang="scss"> + /*输入框*/ + .input_box{ + width: 90%; + margin: 40rpx auto 40rpx auto; + height: auto; + } + .input_title{ + font-size: 30rpx; + color: #ABABAB; + height: 50rpx; + line-height: 50rpx; + margin: 10rpx auto; + display: block; + + } + + .button_box { + width: 90%; + margin: 40rpx auto 20rpx auto; + + } + + .submit { + width: 100%; + background-color: #4CAF50; + justify-content: center; + align-items: center; + border-radius: 10rpx; + } + + .submit-text { + color: white; + padding: 30rpx; + } + + .submit:active { + background-color: green; + opacity: 0.5; + } + + .footer { + flex-direction: row; + justify-content: center; + text-align: center; + align-items: center; + margin-top: 100rpx; + } + + .footer-text { + font-size: 28rpx; + color: #296db5; + padding: 30rpx; + } +</style> diff --git a/pages/user/index.vue b/pages/user/index.vue new file mode 100644 index 0000000..339d8fd --- /dev/null +++ b/pages/user/index.vue @@ -0,0 +1,536 @@ +<template> + <view> + + <view class="header"> + <image class='background' src="/static/img/user/topbg.png" mode="aspectFill"></image> + <view class="userInfo"> + <view class="uleft"> + <u-image :src="userInfo.wxAvatar" width="60px" height="60px" radius="30px" @error="imgErr"></u-image> + </view> + <view class="ucenter"> + <view class="name"><text class="username">{{userInfo.wxName}}</text><text class="level">{{identityinfo.levelTxt}}</text></view> + </view> + <view class="uright"> + <view class="change" v-if="!this.$com.isNull(userInfo.phoneNumber)"> + <u-button text="切换" openType="getPhoneNumber" @getphonenumber="getPhoneNumber"></u-button> + </view> + </view> + </view> + + <view class="userItem"> + <view class="uItemBox"> + <view class="num">{{userInfo.totalAmount > 0 ? userInfo.totalAmount / 100 : '0.00'}}</view> + <view class="text">消费金额(元)</view> + </view> + <view class="uItemBox"> + <view class="num">{{userInfo.totalDur > 0 ? userInfo.totalDur : 0}}</view> + <view class="text">使用时长(分)</view> + </view> + </view> + + </view> + + <view class="orderBox"> + <view class="orderBoxTitle">我的订单</view> + <view class="orderItem"> + <view class="oItemBox" v-for="(item,key) in ordermenu" :key="key" @click="goTo(item.url)"> + <view class="image"> + <image :src="item.icon" mode="widthFix"></image> + </view> + <view class="order_text">{{item.label}}</view> + </view> + + </view> + </view> + + <view class="moneyBox" v-if="identityinfo.levelVal < 4"> + <view class="moneyBoxTitle">收入抽成</view> + <view class="moneyBoxItem"> + <view class="mItemBox"> + <view class="money"> + <text class="unit">¥</text><text class="num">{{userInfo.todayIncome > 0 ? userInfo.todayIncome / 100 :'0.00'}}</text> + </view> + <view class="order_text">今日收入</view> + </view> + <view class="mItemBox"> + <view class="money"> + <text class="unit">¥</text><text class="num">{{userInfo.totalIncome > 0 ? userInfo.totalIncome / 100 :'0.00'}}</text> + </view> + <view class="order_text">总收入</view> + </view> + + <view class="mItemBox"> + <view class="money"> + <text class="unit">¥</text><text class="num">{{userInfo.todayLevelIncome > 0 ? userInfo.todayLevelIncome / 100 :'0.00'}}</text> + </view> + <view class="order_text">今日抽成</view> + </view> + <view class="mItemBox"> + <view class="money"> + <text class="unit">¥</text><text class="num">{{userInfo.totalLevelIncome > 0 ? userInfo.totalLevelIncome / 100 :'0.00'}}</text> + </view> + <view class="order_text">总抽成</view> + </view> + + </view> + </view> + + + <view class="serviceBox"> + <view class="serviceBoxTitle">我的服务</view> + <view class="serviceItem"> + <view class="sItemBox" v-for="(item,key) in shopmenu" :key="key" v-if="item.level >= identityinfo.levelVal" @click="goTo(item.url)"> + <view class="image"> + <image :src="item.icon" mode="widthFix"></image> + </view> + <view class="order_text">{{item.label}}</view> + </view> + + + </view> + <view class="ad"> + <image src="@/static/img/user/banner.jpg" mode="widthFix" @click="openVideo"></image> + </view> + </view> + + <view class="serviceBox"> + <view class="serviceBoxTitle">我的工具</view> + <view class="serviceItem"> + <view class="sItemBox" v-for="(item,key) in setmenu" :key="key" @click="tools(item)"> + <view class="image"> + <image :src="item.icon" mode="widthFix"></image> + </view> + <view class="order_text">{{item.label}}</view> + </view> + + + + </view> + </view> + + <view class="nowENV" v-if="this.$api.env() == 'development' || this.$api.env() == 'local'"> + <u-tag v-if="this.$api.env() == 'development'" text="线上测试环境" type="warning" plain plainFill></u-tag> + <u-tag v-if="this.$api.env() == 'local'" text="本地开发环境" type="error" plain plainFill></u-tag> + </view> + + <view class="empty"> + </view> + + + <u-popup :show="video_show" :round="10" mode="center" :closeable="true" customStyle="{width:750rpx,height:auto}" @close="closeVideo"> + <view class="videoBox"> + <video id="myVideo" :src="video_src" @error="videoErrorCallback" :autoplay="false" controls></video> + </view> + </u-popup> + + </view> +</template> + +<script> + +export default { + data() { + return { + isLogin:false, //登录 + userInfo:{ + wxAvatar:'/static/img/user/avatar.png', + wxName:'点击登录', + totalAmount:0.00, + totalDur:0, + }, + isShop:false, //店铺 + ordermenu: [ + { label: '全部', icon: '/static/img/user/order_all.png', url: '/pages/order/list?tab=0'}, + { label: '进行中', icon: '/static/img/user/order_ing.png', url: '/pages/order/list?tab=1'}, + { label: '已完成', icon: '/static/img/user/order_finish.png', url: '/pages/order/list?tab=2'}, + ], + shopmenu: [ + { label: '店铺管理', icon: '/static/img/order/store.png', url: '/pages/user/shop/shopmanage', level: 3}, + { label: '收入记录', icon: '/static/img/user/money_log.png', url: '/pages/user/mine/income', level: 3}, + { label: '提现申请', icon: '/static/img/user/withdraw.png', url: '/pages/user/mine/withdraw', level: 3}, + { label: '我的下级', icon: '/static/img/user/link.png', url: '/pages/user/mine/leveluser', level: 2}, + { label: '查看码值', icon: '/static/img/user/code.png', url: '/pages/user/shop/lookcode', level: 3}, + { label: '提现记录', icon: '/static/img/user/withdraw_log.png', url: '/pages/user/mine/withdrawrecord', level: 3}, + + ], + setmenu: [ + { label: '账户设置', icon: '/static/img/user/account.png', type: 'normal',url: '/pages/user/account/index'}, + { label: '联系客服', icon: '/static/img/user/chat.png', type: 'service',url:''}, + { label: '代理入驻', icon: '/static/img/user/settled.png', type: 'normal',url: '/pages/user/mine/settled'} + ], + //等级 + identityinfo:'', + + //视频显示 + video_show:false, + video_src:'https://kjc.oss-cn-shenzhen.aliyuncs.com/video/demo.mp4', + + }; + }, + onReady: function (res) { + this.videoContext = uni.createVideoContext('myVideo') + }, + onShow() { + var _this =this; + var openid = _this.$user.session('openid'); + if(!_this.$com.isNull(openid)) + { + _this.isLogin = true; + _this.getUserInfo(); + } + + + }, + onShareAppMessage() { + return { + title: '空间充-我的', + path: uni.$u.page(), + } + }, + onShareTimeline() { + return { + title: '空间充-我的', + path: uni.$u.page(), + } + }, + methods:{ + //头像加载失败 + imgErr(){ + this.userInfo.wxAvatar = '/static/img/user/avatar.png'; + }, + //获取用户信息 + getUserInfo() + { + var _this =this; + var post ={}; + _this.$api.post('ycl/user/wx-info',post,function(rs){ + console.log(rs); + _this.userInfo = rs; + _this.$user.session('lifeData',rs); + var identityinfo = _this.$access.checkidentity(rs); + console.log(identityinfo); + _this.identityinfo = identityinfo; + _this.$user.session('identityinfo',identityinfo); + if(identityinfo.levelVal != 4) + { + var setmenu = _this.setmenu; + var updatedSetmenu = setmenu.filter(item => item.label !== '代理入驻'); + _this.setmenu = updatedSetmenu; + } + + },function(err){ + console.log(err); + }); + }, + goTo(url) + { + uni.$u.route({ + type:'navigateTo', + url: url, + }); + }, + tools(item) + { + var _this =this; + if(item.type == 'service') + { + var phone = _this.$user.session('servicePhone'); + uni.makePhoneCall({ + phoneNumber: phone, + success(res) { + console.log(res); + }, + fail(err) { + console.log(err); + }, + }); + } + else + { + uni.$u.route({ + type:'navigateTo', + url: item.url, + }); + } + + }, + //打开视频 + openVideo(){ + var _this =this; + _this.video_show = true; + }, + videoErrorCallback(e) + { + console.log(e); + }, + closeVideo(){ + var _this =this; + _this.video_show = false; + _this.videoContext.pause = true; + }, + + getPhoneNumber(e){ + var _this =this; + var detail = e.detail; + console.log(detail); + if (detail.errMsg === 'getPhoneNumber:ok') { + + _this.getPhoneApi(detail); + } else { + _this.$com.alert('授权失败') + } + }, + getPhoneApi(detail){ + var _this = this; + var openid = _this.$user.session('openid'); + var post ={ + + code: detail.code, + }; + _this.$api.post('ycl/user/wx-phone',post,function(rs){ + console.log(rs); + var phoneNumber = rs; + _this.userInfo.phoneNumber = phoneNumber; + if(_this.$com.isNull(openid)) + { + _this.$com.alert('切换失败,请退出并重新进入小程序重试'); + } + // #ifdef MP + //这里目前微信小程序和抖音功能基本一样 + uni.getProvider({ + service: 'oauth', + success: function (res) { + console.log(res.provider) + var provider = res.provider[0]; + + uni.login({ + provider:provider, + scopes:'auth_base', + success: function (login) { + console.log(login); + //发起网络请求 + var xpost = { + loginCode: login.code, + phoneNumber:phoneNumber, + }; + _this.$api.post('ycl/user/wx-login', xpost,function(resx){ + console.log(resx); + _this.$user.session('openid',resx.wxOpenid); + _this.$user.session('user_id',resx.id); + if(!_this.$com.isNull(resx.token)) + { + _this.$user.session('token',resx.token); + } + _this.isLogin = true; + _this.getUserInfo(); + }); + }, + fail:function(rs){ + console.log('登录失败'+rs.errMsg); + } + }); + + + + + } + }); + + + // #endif + },function(err){ + console.log(err); + }); + + }, + + }, +}; +</script> + +<style lang="scss"> + page{ height:100%; width: 100%;} + .container{padding:10rpx 20rpx;} + .header{ width: 100%; height: 500rpx; overflow: hidden; position: relative;} + .background { + width: 100%; + height: 100%; + position:absolute; + background-size:100% 100%; + z-index: -1; + } + .userInfo{ width: 670rpx; height: auto; display: flex; margin: 100rpx auto 50rpx auto;} + .userInfo .uleft{ width: 120rpx; height: 120rpx;} + + .userInfo .ucenter{ width: 430rpx; height: 120rpx; line-height: 120rpx;} + .userInfo .ucenter .name{ width: 100%; height: 120rpx;} + .userInfo .ucenter .name .username{font-size: 36rpx; color:#FFFFFF; margin-left:20rpx;} + .userInfo .ucenter .name .level{width: auto; padding: 2rpx 10rpx; +height: 44rpx; +background: #095E59; text-align: center; font-size: 24rpx;color: #FFFFFF; +border-radius: 42rpx 42rpx 42rpx 42rpx; +border: 1rpx solid #00F0E2; margin-left: 20rpx;} + .userInfo .uright{ width: 120rpx; height: 120rpx;} + .userInfo .uright .change{width: 104rpx; +height: 50rpx; +background: #00F0E2; +border-radius: 42rpx 42rpx 42rpx 42rpx; color: #1E2629; font-size: 28rpx; line-height: 50rpx; text-align: center;} + ::v-deep .change .u-button{display: block !important; padding:0rpx !important; background-color: inherit !important; height:auto !important; border: none !important; border-radius: 42rpx !important;} + .userItem{ + color: #FFFFFF; + width: 100%; + height: auto; + display: flex; + } + .uItemBox{ width:50%; text-align: center;} + .uItemBox .num{ + font-size: 40rpx; + font-family: DIN, DIN; + font-weight: bold; + } + .uItemBox .text{ + font-size: 24rpx; + font-family: PingFang SC, PingFang SC; + font-weight: 400; + line-height: 28rpx; + } + + /*订单部分*/ + + .orderBox{ + width: 670rpx; + height: auto; + background: #082436; + border-radius: 24rpx 24rpx 24rpx 24rpx; + opacity: 1; + margin: 0rpx auto; + padding: 20rpx; + } + .orderBoxTitle{ + font-size: 28rpx; + font-weight: bold; + color:#FFFFFF; + line-height: 60rpx; + } + + .orderItem{ + width: 100%; height: auto; + display: flex; + text-align: center; + } + .oItemBox{ + width: 33.33%; + + } + .oItemBox .image{ width: 100%; text-align: center; margin-top: 20rpx;} + .oItemBox .image image{width: 64rpx; height: auto; margin: 0rpx auto; } + .oItemBox .order_text{ + text-align: center; + font-size: 24rpx; + font-weight: 400; + line-height: 30rpx; + color: #FFFFFF; + } + + + /*收入部分*/ + + .moneyBox{ + width: 670rpx; + height: auto; + background: #082436; + border-radius: 24rpx 24rpx 24rpx 24rpx; + opacity: 1; + margin: 20rpx auto; + padding: 20rpx; + } + .moneyBoxTitle{ + font-size: 28rpx; + font-weight: bold; + color:#FFFFFF; + line-height: 40rpx; + } + + .moneyBoxItem{ + margin-top: 20rpx; + width: 100%; height: auto; + display: flex; + text-align: center; + gap:20rpx; + } + .mItemBox{ + width: 152rpx; + height: 128rpx; + background: linear-gradient(180deg, rgba(49,156,255,0) 0%, rgba(0,255,240,0.5) 100%); + border-radius: 16rpx 16rpx 16rpx 16rpx; + opacity: 1; + border: 1rpx solid #00F0E2; + + + } + .mItemBox .money{ width: 100%; text-align: center; color: #FFFFFF; line-height: 80rpx;} + .mItemBox .money .unit{ font-size: 24rpx; +font-family: PingFang SC, PingFang SC; +font-weight: bold;} + .mItemBox .money .num{ font-size: 30rpx; +font-family: DIN, DIN; +font-weight: 400; +} + .mItemBox .order_text{ + text-align: center; + font-size: 24rpx; + font-weight: 400; + line-height: 30rpx; + color: #FFECAA; + } + + + + +/*服务*/ + + .serviceBox{ + width: 670rpx; + height: auto; + background: #082436; + border-radius: 24rpx 24rpx 24rpx 24rpx; + opacity: 1; + margin: 20rpx auto; + padding: 20rpx; + } + .serviceBoxTitle{ + font-size: 28rpx; + font-weight: bold; + color:#FFFFFF; + line-height: 60rpx; + } + + .serviceItem{ + width: 100%; height: auto; + display: flex; + text-align: center; + flex-wrap:wrap; + } + .sItemBox{ + width: 25%; + + } + .sItemBox .image{ width: 100%; text-align: center; margin-top: 20rpx;} + .sItemBox .image image{width: 48rpx; height: auto; margin: 0rpx auto; } + .sItemBox .order_text{ + text-align: center; + font-size: 24rpx; + font-weight: 400; + line-height: 30rpx; + color: #FFFFFF; + } + .nowENV{ width: 200rpx; height: 60rpx; margin: 10rpx auto; text-align: center;} + .empty{width: 100%; height: 20rpx;} + + + .ad{ width: 100%; height: auto; text-align: center; margin:0rpx auto;} + .ad image{ width:100%; margin-top:20rpx;border-radius: 30rpx;} + .videoBox{ width: 750rpx; height: 900rpx;} + .videoBox video{ width: 100%; height: 100%;} + +</style> diff --git a/pages/user/setting/index.vue b/pages/user/setting/index.vue new file mode 100644 index 0000000..34716a3 --- /dev/null +++ b/pages/user/setting/index.vue @@ -0,0 +1,220 @@ + + + +<template> + <view class="u-page"> + + <view class="empty-select"> + <u-cell + :titleStyle="{fontWeight: 500}" + @click="changeLang" + :title="$t('pages.user.setting.index.setLang')" + :value="nowLang" + isLink + > + <image + slot="icon" + class="u-cell-icon" + src="@/static/img/common/lang.png" + mode="widthFix" + ></image> + </u-cell> + </view> + + <view class="btn_box"> + <u-button type="primary" :plain="true" :hairline="true" :text="$t('locale.logout')" size="normal" @click="logout"></u-button> + </view> + + + <u-action-sheet + :show="changeLangShow" + @close="changeLangShow = false" + :actions="langList" + @select="confirmLang" + cancelText="取消" + > + </u-action-sheet> + + + </view> +</template> + +<script> + export default { + data() { + + return { + systemLocale: '', + applicationLocale: '', + nowLang:'',//当前语言 + // list: [{ + // imgName: 'lang', + // title: this.$t('pages.user.setting.index.setLang'), + // iconUrl: '/static/img/common/lang.png', + // }, + // ], + changeLangShow:false, + langList: [ + { + name: this.$t('locale.zh-hans'), + val:'zh-Hans', + }, + { + name: this.$t('locale.zh-hant'), + val:'zh-Hant', + }, + { + name: this.$t('locale.en'), + val:'en' + }], + langListVal:['zh-Hans','zh-Hant','en'], + } + }, + onLoad() { + var _this =this; + _this.loadData(); + }, + methods: { + loadData(){ + var _this =this; + let systemInfo = uni.getSystemInfoSync(); + _this.systemLocale = systemInfo.language; + var lang = uni.getLocale(); + console.log('lang:'+lang); + _this.applicationLocale =lang; + _this.returnLang(lang); + _this.isAndroid = systemInfo.platform.toLowerCase() === 'android'; + uni.onLocaleChange((e) => { + _this.applicationLocale = e.locale; + _this.returnLang(e.locale); + }) + }, + returnLang(code) + { + var _this =this; + var list = _this.langListVal; + list.forEach((item, $index, arr) => { + //console.log(`${item} => ${$index} => ${arr}`); + if(item == code) + { + console.log(_this.langList[$index].name); + _this.nowLang = _this.langList[$index].name; + return; + } + }) + }, + //改变当前语言 + changeLang() { + //this[`show${index}`] = true + this[`changeLangShow`] = true; + }, + confirmLang(e){ + console.log(e); + var _this =this; + _this.onLocaleChange(e.val); + }, + + onLocaleChange(code) { + var _this =this; + _this[`changeLangShow`] = false; + uni.showModal({ + content: _this.$t('locale.language-change-confirm'), + success: (res) => { + if (res.confirm) { + uni.setLocale(code); + _this.$i18n.locale = code; + _this.returnLang(code); + uni.switchTab({ + url:'pages/index/index' + }) + } + } + }) + + + }, + + //退出登录 + logout(){ + var _this =this; + var post = {}; + _this.$api.post('user/logout',post,function(rs){ + console.log(rs); + uni.removeStorageSync('userInfo'); + uni.removeStorageSync('token'); + uni.$u.route({ + type:'switchTab', + url:'pages/index/index', + params: {} + }); + },function(err){ + console.log(err); + }); + uni.$u.toast(_this.$t('locale.logout')); + + }, + } + } +</script> + +<style lang="scss"> + .u-block{ + padding: 14px; + &__section{ + margin-bottom:10px; + } + &__title { + margin-top:10px; + font-size: 15px; + color: $u-content-color; + margin-bottom:10px; + } + &__flex{ + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + } + } + + // 使用了cell组件的icon图片样式 + .u-cell-icon { + width: 36rpx; + height: 36rpx; + margin-right: 8rpx; + } + + .u-page { + padding: 15px 15px 40px 15px; + } + + .u-demo-block { + flex: 1; + margin-bottom: 23px; + + &__content { + @include flex(column); + } + + &__title { + font-size: 14px; + color: rgb(143, 156, 162); + margin-bottom: 8px; + @include flex; + } + } + + + .u-page { + padding: 40rpx 0px; + + &__top-box { + padding-left: 40rpx; + } + } + + .empty-select { + margin-top: 0px; + } + + .btn_box{ width: 80%; margin: 120rpx auto 20rpx auto;} +</style> diff --git a/pages/web/index.vue b/pages/web/index.vue new file mode 100644 index 0000000..ef56c6a --- /dev/null +++ b/pages/web/index.vue @@ -0,0 +1,77 @@ +<template> + <view> + <web-view ref="webview" :src="xurl" :update-title="true" :webview-styles="webviewStyles" @message="getMessage"></web-view> + </view> +</template> + + +<script> + var wv;//计划创建的webview + export default { + data() { + return { + xurl: '', + old_url:'', + webviewStyles: { + progress: { + color: '#FF3333' + } + }, + }; + }, + onReady() { + + }, + onLoad(option) { + //数据 + var _this =this; + var url = option.url; + var title = option.title; + if(_this.$com.isNull(url)) + { + uni.navigateBack({ + delta:1, + }) + } + _this.old_url = url; + _this.xurl = decodeURIComponent(url); + if(!_this.$com.isNull(title)) + { + var pageTitle = title; + } + else + { + var pages = getCurrentPages(); + var page = pages[pages.length - 1]; + // #ifndef APP-PLUS + var pageTitle = page.$page.meta.navigationBar.titleText; + // #endif + + // #ifdef APP-PLUS + var webView = currentPage.$getAppWebview(); + var pageTitle = webView.getStyle().name; + // #endif + + if(_this.$com.isNull(pageTitle)) + { + pageTitle = 'webview'; + } + } + uni.setNavigationBarTitle({ + title:pageTitle, + }) + + }, + methods: { + getMessage(event) { + // #ifdef APP-PLUS + console.log('event', event); + // #endif + }, + }, + }; +</script> + +<style> + +</style> diff --git a/pages/web/video.vue b/pages/web/video.vue new file mode 100644 index 0000000..1b2b0f6 --- /dev/null +++ b/pages/web/video.vue @@ -0,0 +1,53 @@ +<template> + <view class="container"> + <view class="videoBox"> + <video id="myVideo" :style="{width: '100%', height: '100%'}" + :src="video_src" @error="videoErrorCallback" :autoplay="false" controls></video> + </view> + </view> +</template> + +<script> + export default { + data() { + return { + old_url:'', + video_src:'', + } + }, + onLoad(option) { + //数据 + var _this =this; + var url = option.url; + var title = option.title; + if(_this.$com.isNull(url)) + { + uni.navigateBack({ + delta:1, + }) + } + _this.old_url = url; + _this.video_src = decodeURIComponent(url); + if(!_this.$com.isNull(title)) + { + var pageTitle = title; + uni.setNavigationBarTitle({ + title:pageTitle, + }) + } + + }, + methods: { + videoErrorCallback(e) + { + console.log(e); + }, + } + } +</script> + +<style lang="scss"> + page{ height:100%; width: 100%;} + .container{width: 100%; height: 100%;} + .videoBox{ width: 100%; height: 100%;} +</style> diff --git a/static/css/iconfont.css b/static/css/iconfont.css new file mode 100644 index 0000000..b5b42fa --- /dev/null +++ b/static/css/iconfont.css @@ -0,0 +1,2281 @@ +@font-face { + font-family: "iconfont"; + src: url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2') +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.iconback:before { + content: "\e6ef"; +} + +.iconless:before { + content: "\e6f2"; +} + +.iconmoreunfold:before { + content: "\e6f4"; +} + +.iconmore1:before { + content: "\e6f5"; +} + +.iconkefufenxiermaikefu:before { + content: "\e88d"; +} + +.iconkefuerji:before { + content: "\e6f6"; +} + +.iconkefu2:before { + content: "\e6f7"; +} + +.icongouwuche2:before { + content: "\e6eb"; +} + +.icongouwuche6:before { + content: "\e6f0"; +} + +.iconqunfengyouhuiquan:before { + content: "\e7cc"; +} + +.iconqunfengzuji:before { + content: "\e7fd"; +} + +.iconhuo:before { + content: "\e6c8"; +} + +.icondingdan1:before { + content: "\e6ce"; +} + +.iconyouhuiquan-xuanzhong:before { + content: "\e6cf"; +} + +.icontubiaozhizuomoban:before { + content: "\e6d0"; +} + +.iconflag:before { + content: "\e7a6"; +} + +.iconguanggaotongji:before { + content: "\e954"; +} + +.iconxiadan:before { + content: "\e6d1"; +} + +.iconlijixiadan:before { + content: "\e6d2"; +} + +.iconkefu1:before { + content: "\e6dc"; +} + +.iconzhiding:before { + content: "\e6dd"; +} + +.icontuan:before { + content: "\e6df"; +} + +.icongouwuche1:before { + content: "\e6e0"; +} + +.iconyouhuiquan-sousuo:before { + content: "\e6e1"; +} + +.iconHOT-copy:before { + content: "\e88a"; +} + +.iconweixinzhifu3:before { + content: "\e6e2"; +} + +.iconsaoma11:before { + content: "\e6e6"; +} + +.iconloading1:before { + content: "\e891"; +} + +.icongouwuchex:before { + content: "\e6e7"; +} + +.icongouwuchetubiao:before { + content: "\e6e8"; +} + +.iconremen:before { + content: "\e8c9"; +} + +.icongouwuche4:before { + content: "\e6e9"; +} + +.icongouwuche5:before { + content: "\e6ea"; +} + +.iconyouhuiquan_xuanzhong:before { + content: "\e70a"; +} + +.iconchexiao1:before { + content: "\e6aa"; +} + +.iconcuowu:before { + content: "\e6ab"; +} + +.iconzhengque:before { + content: "\e6ad"; +} + +.iconsuoding:before { + content: "\e6ae"; +} + +.iconkaishi:before { + content: "\e6b1"; +} + +.iconwode1:before { + content: "\e6b3"; +} + +.icontianjia:before { + content: "\e6b5"; +} + +.iconguanzhu1:before { + content: "\e6b6"; +} + +.iconzhuyi:before { + content: "\e6ba"; +} + +.iconjine:before { + content: "\e6be"; +} + +.iconyiwen:before { + content: "\e6c1"; +} + +.iconzanting:before { + content: "\e6c4"; +} + +.iconsaoma1:before { + content: "\e6c6"; +} + +.iconfenlei1:before { + content: "\e6c7"; +} + +.iconicon_shakehands:before { + content: "\ebc7"; +} + +.iconicon_video:before { + content: "\ebc8"; +} + +.iconicon_task_done:before { + content: "\ebc9"; +} + +.iconicon_synergy:before { + content: "\ebca"; +} + +.iconicon_workfile_line:before { + content: "\ebcb"; +} + +.iconicon_addresslist_fil:before { + content: "\ebcc"; +} + +.iconicon_addressbook_fil:before { + content: "\ebcd"; +} + +.iconicon_calendar_fill:before { + content: "\ebce"; +} + +.iconicon_delete_fill:before { + content: "\ebcf"; +} + +.iconicon_doc_fill:before { + content: "\ebd0"; +} + +.iconicon_camera_fill:before { + content: "\ebd1"; +} + +.iconicon_certification_f:before { + content: "\ebd2"; +} + +.iconicon_likegood_fill:before { + content: "\ebd3"; +} + +.iconicon_gift_fill:before { + content: "\ebd4"; +} + +.iconicon_message_fill:before { + content: "\ebd5"; +} + +.iconicon_newapplication_:before { + content: "\ebd6"; +} + +.iconicon_people_fill:before { + content: "\ebd7"; +} + +.iconicon_photo_fill:before { + content: "\ebd8"; +} + +.iconicon_roundreduce_fil:before { + content: "\ebd9"; +} + +.iconicon_redpacket_fill:before { + content: "\ebda"; +} + +.iconicon_replieslist:before { + content: "\ebdb"; +} + +.iconicon_roundadd_fill:before { + content: "\ebdc"; +} + +.iconicon_study_fill:before { + content: "\ebdd"; +} + +.iconicon_setting_fill:before { + content: "\ebde"; +} + +.iconicon_shakehands_fill:before { + content: "\ebdf"; +} + +.iconicon_work_fill:before { + content: "\ebe0"; +} + +.iconicon_trashcan:before { + content: "\ebe1"; +} + +.iconicon_roundclose_fill:before { + content: "\ebe2"; +} + +.iconicon_add:before { + content: "\eb8f"; +} + +.iconicon_addmessage:before { + content: "\eb90"; +} + +.iconicon_addresslist:before { + content: "\eb91"; +} + +.iconicon_affiliations_li:before { + content: "\eb92"; +} + +.iconicon_addperson:before { + content: "\eb93"; +} + +.iconicon_boss:before { + content: "\eb94"; +} + +.iconicon_addressbook:before { + content: "\eb95"; +} + +.iconicon_calendar:before { + content: "\eb96"; +} + +.iconicon_attestation:before { + content: "\eb97"; +} + +.iconicon_camera:before { + content: "\eb98"; +} + +.iconicon_certificate_fil:before { + content: "\eb99"; +} + +.iconicon_coinpurse_line:before { + content: "\eb9a"; +} + +.iconicon_collect:before { + content: "\eb9b"; +} + +.iconicon_compile:before { + content: "\eb9c"; +} + +.iconicon_details:before { + content: "\eb9d"; +} + +.iconicon_community_line:before { + content: "\eb9e"; +} + +.iconicon_discovery:before { + content: "\eb9f"; +} + +.iconicon_delete:before { + content: "\eba0"; +} + +.iconicon_dispose:before { + content: "\eba1"; +} + +.iconicon_doc:before { + content: "\eba2"; +} + +.iconicon_gift:before { + content: "\eba3"; +} + +.iconicon_file:before { + content: "\eba4"; +} + +.iconicon_GPS:before { + content: "\eba5"; +} + +.iconicon_im_more:before { + content: "\eba6"; +} + +.iconicon_horn:before { + content: "\eba7"; +} + +.iconicon_im_face:before { + content: "\eba8"; +} + +.iconicon_invite:before { + content: "\eba9"; +} + +.iconicon_likegood:before { + content: "\ebaa"; +} + +.iconicon_index_line:before { + content: "\ebab"; +} + +.iconicon_link:before { + content: "\ebac"; +} + +.iconicon_mobilephone:before { + content: "\ebad"; +} + +.iconicon_dmail:before { + content: "\ebae"; +} + +.iconicon_message:before { + content: "\ebaf"; +} + +.iconicon_more:before { + content: "\ebb0"; +} + +.iconicon_notice:before { + content: "\ebb1"; +} + +.iconicon_photo:before { + content: "\ebb2"; +} + +.iconicon_medal:before { + content: "\ebb3"; +} + +.iconicon_roundclose:before { + content: "\ebb4"; +} + +.iconicon_roundreduce:before { + content: "\ebb5"; +} + +.iconicon_QRcode:before { + content: "\ebb6"; +} + +.iconicon_roundadd:before { + content: "\ebb7"; +} + +.iconicon_refresh:before { + content: "\ebb8"; +} + +.iconicon_search:before { + content: "\ebb9"; +} + +.iconicon_scan:before { + content: "\ebba"; +} + +.iconicon_secret:before { + content: "\ebbb"; +} + +.iconicon_share:before { + content: "\ebbc"; +} + +.iconicon_task:before { + content: "\ebbd"; +} + +.iconicon_threeline_fill:before { + content: "\ebbe"; +} + +.iconicon_study:before { + content: "\ebbf"; +} + +.iconicon_wechat:before { + content: "\ebc0"; +} + +.iconicon_sport:before { + content: "\ebc1"; +} + +.iconicon_work:before { + content: "\ebc2"; +} + +.iconicon_workmore:before { + content: "\ebc3"; +} + +.iconicon_safety:before { + content: "\ebc4"; +} + +.iconicon_workset:before { + content: "\ebc5"; +} + +.iconicon_shield:before { + content: "\ebc6"; +} + +.iconjiahao:before { + content: "\e72b"; +} + +.iconjiahao1:before { + content: "\e72c"; +} + +.iconjiahao2fill:before { + content: "\e72d"; +} + +.iconjianhao:before { + content: "\e72e"; +} + +.icontishifill:before { + content: "\e72f"; +} + +.icontishi:before { + content: "\e734"; +} + +.iconwenhaofill:before { + content: "\e737"; +} + +.iconwenhao:before { + content: "\e739"; +} + +.iconxuanze:before { + content: "\e73b"; +} + +.iconyuanxingweixuanzhong:before { + content: "\e73e"; +} + +.iconyuanxingxuanzhongfill:before { + content: "\e73f"; +} + +.iconyuanxingxuanzhong:before { + content: "\e742"; +} + +.iconbiaoxingfill:before { + content: "\e743"; +} + +.iconbiaoxing:before { + content: "\e744"; +} + +.iconchexiao:before { + content: "\e745"; +} + +.icondianpufill:before { + content: "\e746"; +} + +.icondianpu:before { + content: "\e747"; +} + +.icondingdan:before { + content: "\e748"; +} + +.iconfankui:before { + content: "\e749"; +} + +.iconfenxiang3:before { + content: "\e74b"; +} + +.icongengduo2:before { + content: "\e74c"; +} + +.icongonglve:before { + content: "\e74d"; +} + +.icongouwuchefill:before { + content: "\e74e"; +} + +.icongouwuche:before { + content: "\e754"; +} + +.icongouwudai:before { + content: "\e755"; +} + +.iconqiapianxingshi:before { + content: "\e756"; +} + +.iconkefufill:before { + content: "\e75a"; +} + +.iconkefu:before { + content: "\e75c"; +} + +.iconliebiaoxingshi:before { + content: "\e75e"; +} + +.iconliuyanfill:before { + content: "\e75f"; +} + +.iconliuyan:before { + content: "\e760"; +} + +.iconpengyoufill:before { + content: "\e761"; +} + +.iconpengyou:before { + content: "\e762"; +} + +.iconqingchu:before { + content: "\e764"; +} + +.iconquan:before { + content: "\e765"; +} + +.iconsaoma:before { + content: "\e766"; +} + +.iconshaixuan:before { + content: "\e769"; +} + +.iconshanchu:before { + content: "\e76a"; +} + +.iconshezhi:before { + content: "\e76b"; +} + +.iconshizhongfill:before { + content: "\e76c"; +} + +.iconshizhong:before { + content: "\e76d"; +} + +.iconshouyefill:before { + content: "\e76e"; +} + +.iconshouye:before { + content: "\e76f"; +} + +.iconsousuo1:before { + content: "\e770"; +} + +.iconsousuoleimufill:before { + content: "\e771"; +} + +.iconsousuoleimu:before { + content: "\e772"; +} + +.icontongzhifill:before { + content: "\e773"; +} + +.icontongzhi:before { + content: "\e774"; +} + +.icontuikuan:before { + content: "\e77a"; +} + +.iconwodefill:before { + content: "\e77b"; +} + +.iconwode:before { + content: "\e77e"; +} + +.iconxihuanfill:before { + content: "\e77f"; +} + +.iconxihuan:before { + content: "\e780"; +} + +.iconxinxifill:before { + content: "\e781"; +} + +.iconxinxi:before { + content: "\e782"; +} + +.iconzuji:before { + content: "\e783"; +} + +.iconzuobiaofill:before { + content: "\e784"; +} + +.iconzuobiao:before { + content: "\e785"; +} + +.icondibu:before { + content: "\e786"; +} + +.icondingbu:before { + content: "\e787"; +} + +.iconxiangshang1:before { + content: "\e788"; +} + +.iconxiangshang2:before { + content: "\e789"; +} + +.iconxiangshang3:before { + content: "\e78a"; +} + +.iconxiangshang5:before { + content: "\e78d"; +} + +.iconxiangxia1:before { + content: "\e78e"; +} + +.iconxiangxia2:before { + content: "\e78f"; +} + +.iconxiangxia3:before { + content: "\e790"; +} + +.iconxiangxia5:before { + content: "\e792"; +} + +.iconxiangyou2:before { + content: "\e793"; +} + +.iconxiangyou3fill:before { + content: "\e794"; +} + +.iconxiangyou3:before { + content: "\e795"; +} + +.iconxiangzuo1:before { + content: "\e796"; +} + +.iconxiangzuo2:before { + content: "\e797"; +} + +.iconxiangji1fill:before { + content: "\e798"; +} + +.iconxiangji1:before { + content: "\e799"; +} + +.iconjiazai:before { + content: "\e79a"; +} + +.iconshuaxin1:before { + content: "\e79b"; +} + +.iconsalefill:before { + content: "\e79c"; +} + +.iconsale:before { + content: "\e79d"; +} + +.iconandroidgengduo:before { + content: "\e79e"; +} + +.iconleimu:before { + content: "\e79f"; +} + +.iconbangzhuzhongxin:before { + content: "\e7a0"; +} + +.iconcaidan:before { + content: "\e7a1"; +} + +.iconzantongfill:before { + content: "\e7a2"; +} + +.iconzantong:before { + content: "\e7a3"; +} + +.iconxiangshang4:before { + content: "\e7a4"; +} + +.iconxiangxia4:before { + content: "\e7a5"; +} + +.icondanpin:before { + content: "\e7ab"; +} + +.iconpinpai:before { + content: "\e7b8"; +} + +.iconxiangbao:before { + content: "\e7ba"; +} + +.iconyishouchu:before { + content: "\e7bb"; +} + +.iconothers:before { + content: "\e7bc"; +} + +.icondanxuanfill:before { + content: "\e71e"; +} + +.icondanxuan:before { + content: "\e723"; +} + +.iconfangxingweixuanzhong:before { + content: "\e724"; +} + +.iconfangxingxuanzhongfill:before { + content: "\e725"; +} + +.iconfangxingxuanzhong:before { + content: "\e726"; +} + +.iconguanbi1:before { + content: "\e727"; +} + +.iconguanbi2fill:before { + content: "\e728"; +} + +.iconguanbi2:before { + content: "\e72a"; +} + +.iconfavor:before { + content: "\e67b"; +} + +.iconloading:before { + content: "\e67c"; +} + +.iconlocationfill:before { + content: "\e67d"; +} + +.iconroundcheckfill:before { + content: "\e67e"; +} + +.iconroundcheck:before { + content: "\e67f"; +} + +.iconroundclosefill:before { + content: "\e680"; +} + +.iconroundclose:before { + content: "\e681"; +} + +.iconroundrightfill:before { + content: "\e682"; +} + +.iconroundright:before { + content: "\e683"; +} + +.iconsearch1:before { + content: "\e684"; +} + +.icontimefill:before { + content: "\e685"; +} + +.icontime:before { + content: "\e686"; +} + +.iconunfold:before { + content: "\e687"; +} + +.iconwarnfill:before { + content: "\e688"; +} + +.iconwarn:before { + content: "\e689"; +} + +.iconcamerafill:before { + content: "\e68a"; +} + +.iconcamera1:before { + content: "\e68b"; +} + +.iconcommentfill:before { + content: "\e68c"; +} + +.iconcomment:before { + content: "\e68d"; +} + +.iconlikefill:before { + content: "\e68e"; +} + +.iconlike:before { + content: "\e68f"; +} + +.iconnotificationfill:before { + content: "\e690"; +} + +.iconnotification:before { + content: "\e691"; +} + +.iconorder:before { + content: "\e692"; +} + +.icondeliver:before { + content: "\e693"; +} + +.iconevaluate:before { + content: "\e694"; +} + +.iconpay:before { + content: "\e695"; +} + +.iconsend:before { + content: "\e696"; +} + +.iconshop:before { + content: "\e697"; +} + +.iconticket:before { + content: "\e698"; +} + +.iconcascades:before { + content: "\e699"; +} + +.iconlist:before { + content: "\e69a"; +} + +.iconmore:before { + content: "\e69b"; +} + +.iconscan:before { + content: "\e69c"; +} + +.iconsettings:before { + content: "\e69d"; +} + +.iconquestionfill:before { + content: "\e69e"; +} + +.iconquestion:before { + content: "\e69f"; +} + +.iconshopfill:before { + content: "\e6a0"; +} + +.iconform:before { + content: "\e6a1"; +} + +.iconpic:before { + content: "\e6a2"; +} + +.iconfootprint:before { + content: "\e6a3"; +} + +.icontop:before { + content: "\e6a4"; +} + +.iconpulldown:before { + content: "\e6a5"; +} + +.iconpullup:before { + content: "\e6a6"; +} + +.iconrefresh:before { + content: "\e6a7"; +} + +.iconmoreandroid:before { + content: "\e6a8"; +} + +.icondeletefill:before { + content: "\e6a9"; +} + +.iconrefund:before { + content: "\e6ac"; +} + +.iconcart1:before { + content: "\e6af"; +} + +.iconqrcode:before { + content: "\e6b0"; +} + +.iconremind:before { + content: "\e6b2"; +} + +.icondelete:before { + content: "\e6b4"; +} + +.iconprofile:before { + content: "\e6b7"; +} + +.iconhome1:before { + content: "\e6b8"; +} + +.iconcartfill:before { + content: "\e6b9"; +} + +.iconhomefill:before { + content: "\e6bb"; +} + +.iconmessage:before { + content: "\e6bc"; +} + +.iconaddressbook:before { + content: "\e6bd"; +} + +.iconlink:before { + content: "\e6bf"; +} + +.iconlock:before { + content: "\e6c0"; +} + +.iconunlock:before { + content: "\e6c2"; +} + +.iconvip:before { + content: "\e6c3"; +} + +.iconactivity:before { + content: "\e6c5"; +} + +.iconfriendaddfill:before { + content: "\e6c9"; +} + +.iconfriendadd:before { + content: "\e6ca"; +} + +.iconfriendfamous:before { + content: "\e6cb"; +} + +.iconfriend:before { + content: "\e6cc"; +} + +.icongoods:before { + content: "\e6cd"; +} + +.iconpresent:before { + content: "\e6d3"; +} + +.iconsquarecheckfill:before { + content: "\e6d4"; +} + +.iconsquare:before { + content: "\e6d5"; +} + +.iconsquarecheck:before { + content: "\e6d6"; +} + +.iconround:before { + content: "\e6d7"; +} + +.iconroundaddfill:before { + content: "\e6d8"; +} + +.iconroundadd:before { + content: "\e6d9"; +} + +.iconadd:before { + content: "\e6da"; +} + +.iconnotificationforbidfill:before { + content: "\e6db"; +} + +.iconfold:before { + content: "\e6de"; +} + +.iconappreciatefill:before { + content: "\e6e3"; +} + +.iconinfofill:before { + content: "\e6e4"; +} + +.iconinfo:before { + content: "\e6e5"; +} + +.iconrechargefill:before { + content: "\e6ec"; +} + +.iconrecharge:before { + content: "\e6ed"; +} + +.iconvipcard:before { + content: "\e6ee"; +} + +.iconfriendfavor:before { + content: "\e6f1"; +} + +.iconshare:before { + content: "\e6f3"; +} + +.iconservice:before { + content: "\e6ff"; +} + +.icondown:before { + content: "\e703"; +} + +.iconcopy:before { + content: "\e706"; +} + +.iconchoicenessfill:before { + content: "\e714"; +} + +.iconchoiceness:before { + content: "\e715"; +} + +.iconpullleft:before { + content: "\e71f"; +} + +.iconpullright:before { + content: "\e720"; +} + +.iconrankfill:before { + content: "\e721"; +} + +.iconrank:before { + content: "\e722"; +} + +.iconapps:before { + content: "\e729"; +} + +.iconmarkfill:before { + content: "\e730"; +} + +.iconmark:before { + content: "\e731"; +} + +.iconpresentfill:before { + content: "\e732"; +} + +.iconrepeal:before { + content: "\e733"; +} + +.iconpeoplefill:before { + content: "\e735"; +} + +.iconpeople:before { + content: "\e736"; +} + +.iconrepair:before { + content: "\e738"; +} + +.iconrepairfill:before { + content: "\e73a"; +} + +.iconattentionfill:before { + content: "\e73c"; +} + +.iconattention:before { + content: "\e73d"; +} + +.iconcommunityfill:before { + content: "\e740"; +} + +.iconcommunity:before { + content: "\e741"; +} + +.iconcalendar:before { + content: "\e74a"; +} + +.iconplayfill:before { + content: "\e74f"; +} + +.iconstop:before { + content: "\e750"; +} + +.icontagfill:before { + content: "\e751"; +} + +.icontag:before { + content: "\e752"; +} + +.icongroup:before { + content: "\e753"; +} + +.iconhotfill:before { + content: "\e757"; +} + +.iconhot:before { + content: "\e758"; +} + +.iconpost:before { + content: "\e759"; +} + +.iconradiobox:before { + content: "\e75b"; +} + +.iconupload:before { + content: "\e75d"; +} + +.iconradioboxfill:before { + content: "\e763"; +} + +.iconadd1:before { + content: "\e767"; +} + +.iconmove:before { + content: "\e768"; +} + +.iconactivityfill:before { + content: "\e775"; +} + +.iconcrownfill:before { + content: "\e776"; +} + +.iconcrown:before { + content: "\e777"; +} + +.icongoodsfill:before { + content: "\e778"; +} + +.iconmessagefill:before { + content: "\e779"; +} + +.iconsponsorfill:before { + content: "\e77c"; +} + +.iconsponsor:before { + content: "\e77d"; +} + +.iconmy:before { + content: "\e78b"; +} + +.iconmyfill:before { + content: "\e78c"; +} + +.icontext:before { + content: "\e791"; +} + +.iconroundaddlight:before { + content: "\e7a7"; +} + +.iconattentionforbid:before { + content: "\e7b2"; +} + +.iconattentionforbidfill:before { + content: "\e7b3"; +} + +.iconmail:before { + content: "\e7bd"; +} + +.iconpeoplelist:before { + content: "\e7be"; +} + +.iconnewshotfill:before { + content: "\e7c4"; +} + +.iconnewshot:before { + content: "\e7c5"; +} + +.iconvideofill:before { + content: "\e7c7"; +} + +.iconvideo:before { + content: "\e7c8"; +} + +.iconaskfill:before { + content: "\e7c9"; +} + +.iconask:before { + content: "\e7ca"; +} + +.iconexit:before { + content: "\e7cb"; +} + +.iconmoneybagfill:before { + content: "\e7ce"; +} + +.iconredpacket_fill:before { + content: "\e7d3"; +} + +.iconhome_light:before { + content: "\e7d4"; +} + +.iconmy_light:before { + content: "\e7d5"; +} + +.iconcommunity_light:before { + content: "\e7d6"; +} + +.iconcart_light:before { + content: "\e7d7"; +} + +.iconwe_light:before { + content: "\e7d8"; +} + +.iconhome_fill_light:before { + content: "\e7d9"; +} + +.iconcart_fill_light:before { + content: "\e7da"; +} + +.iconcommunity_fill_light:before { + content: "\e7db"; +} + +.iconmy_fill_light:before { + content: "\e7dc"; +} + +.iconwe_fill_light:before { + content: "\e7dd"; +} + +.iconsearch_light:before { + content: "\e7de"; +} + +.iconscan_light:before { + content: "\e7df"; +} + +.iconpeople_list_light:before { + content: "\e7e0"; +} + +.iconmessage_light:before { + content: "\e7e1"; +} + +.iconclose_light:before { + content: "\e7e2"; +} + +.iconadd_light:before { + content: "\e7e3"; +} + +.iconprofile_light:before { + content: "\e7e4"; +} + +.iconservice_light:before { + content: "\e7e5"; +} + +.iconfriend_add_light:before { + content: "\e7e6"; +} + +.iconedit_light:before { + content: "\e7e7"; +} + +.iconcamera_light:before { + content: "\e7e8"; +} + +.iconshare_light:before { + content: "\e7e9"; +} + +.iconcomment_light:before { + content: "\e7ea"; +} + +.iconappreciate_light:before { + content: "\e7eb"; +} + +.iconappreciate_fill_light:before { + content: "\e7ec"; +} + +.iconcomment_fill_light:before { + content: "\e7ed"; +} + +.iconmore_android_light:before { + content: "\e7ee"; +} + +.iconfriend_light:before { + content: "\e7ef"; +} + +.iconmore_light:before { + content: "\e7f0"; +} + +.icongoods_favor_light:before { + content: "\e7f1"; +} + +.icongoods_new_fill_light:before { + content: "\e7f2"; +} + +.icongoods_new_light:before { + content: "\e7f3"; +} + +.icongoods_light:before { + content: "\e7f4"; +} + +.iconfavor_fill_light:before { + content: "\e7f5"; +} + +.icondelete_light:before { + content: "\e7f6"; +} + +.iconback_android:before { + content: "\e7f7"; +} + +.iconback_android_light:before { + content: "\e7f8"; +} + +.icondown_light:before { + content: "\e7f9"; +} + +.iconround_close_light:before { + content: "\e7fa"; +} + +.iconround_close_fill_light:before { + content: "\e7fb"; +} + +.iconqr_code_light:before { + content: "\e7fc"; +} + +.iconfriend_settings_light:before { + content: "\e7fe"; +} + +.iconround_list_light:before { + content: "\e800"; +} + +.iconround_friend_fill:before { + content: "\e80a"; +} + +.iconround_crown_fill:before { + content: "\e80b"; +} + +.iconround_link_fill:before { + content: "\e80c"; +} + +.iconround_light_fill:before { + content: "\e80d"; +} + +.iconround_favor_fill:before { + content: "\e80e"; +} + +.iconround_menu_fill:before { + content: "\e80f"; +} + +.iconround_location_fill:before { + content: "\e810"; +} + +.iconround_pay_fill:before { + content: "\e811"; +} + +.iconround_like_fill:before { + content: "\e812"; +} + +.iconround_people_fill:before { + content: "\e813"; +} + +.iconround_pay:before { + content: "\e814"; +} + +.iconround_rank_fill:before { + content: "\e815"; +} + +.iconround_redpacket_fill:before { + content: "\e816"; +} + +.iconround_skin_fill:before { + content: "\e817"; +} + +.iconround_record_fill:before { + content: "\e818"; +} + +.iconround_ticket_fill:before { + content: "\e819"; +} + +.iconround_text_fill:before { + content: "\e81a"; +} + +.iconround_transfer_fill:before { + content: "\e81b"; +} + +.iconround_transfer:before { + content: "\e81c"; +} + +.iconarrow_left_fill:before { + content: "\e81d"; +} + +.iconarrow_up_fill:before { + content: "\e81e"; +} + +.iconreturn:before { + content: "\e81f"; +} + +.iconbroadcast_fill:before { + content: "\e820"; +} + +.iconappreciate:before { + content: "\e675"; +} + +.iconcheck:before { + content: "\e676"; +} + +.iconclose:before { + content: "\e677"; +} + +.iconedit:before { + content: "\e678"; +} + +.iconemoji:before { + content: "\e679"; +} + +.iconfavorfill:before { + content: "\e67a"; +} + +.iconliebiaomoshi2:before { + content: "\e61b"; +} + +.icondaifahuo:before { + content: "\e61c"; +} + +.icondaifukuan:before { + content: "\e61d"; +} + +.iconpaixing:before { + content: "\e61e"; +} + +.iconzanxuanzhong:before { + content: "\e61f"; +} + +.iconfenxiang1:before { + content: "\e620"; +} + +.iconfenxiang2:before { + content: "\e621"; +} + +.icongengduo1:before { + content: "\e622"; +} + +.iconcart:before { + content: "\e623"; +} + +.iconhome:before { + content: "\e624"; +} + +.iconhome2:before { + content: "\e625"; +} + +.iconcamera:before { + content: "\e626"; +} + +.iconcamera2:before { + content: "\e627"; +} + +.iconsearch:before { + content: "\e628"; +} + +.iconshuaxin:before { + content: "\e629"; +} + +.iconmine:before { + content: "\e62a"; +} + +.iconmine2:before { + content: "\e62b"; +} + +.icontabulation:before { + content: "\e62c"; +} + +.iconliebiao2:before { + content: "\e62d"; +} + +.iconiconfontscan:before { + content: "\e62e"; +} + +.iconquanbudingdan1:before { + content: "\e62f"; +} + +.icon31shoucangxuanzhong:before { + content: "\e630"; +} + +.icon31shoucang:before { + content: "\e631"; +} + +.icon31guanbi:before { + content: "\e632"; +} + +.icon31xuanze:before { + content: "\e633"; +} + +.icon31guanzhudianpu:before { + content: "\e634"; +} + +.icon31xuanzhong:before { + content: "\e635"; +} + +.icon31yiguanzhudianpu:before { + content: "\e636"; +} + +.icon31dianzan:before { + content: "\e637"; +} + +.icon31dianpu:before { + content: "\e638"; +} + +.icon31fenxiang:before { + content: "\e639"; +} + +.icon31zhuanfa:before { + content: "\e63a"; +} + +.icon31daifahuo:before { + content: "\e63b"; +} + +.icon31daifukuan:before { + content: "\e63c"; +} + +.icon31daishouhuo:before { + content: "\e63d"; +} + +.icon31daipingjia:before { + content: "\e63e"; +} + +.icontuikuantuihuo:before { + content: "\e63f"; +} + +.icon31huiyuanqia:before { + content: "\e640"; +} + +.icon31jifen:before { + content: "\e641"; +} + +.icon31youhuiquan:before { + content: "\e642"; +} + +.icon31tianmaobao:before { + content: "\e643"; +} + +.icon31hongbao:before { + content: "\e644"; +} + +.icon31fanerxuanzhong:before { + content: "\e645"; +} + +.icon31faner:before { + content: "\e646"; +} + +.icon31gouwuchexuanzhong:before { + content: "\e647"; +} + +.icon31gouwuche:before { + content: "\e648"; +} + +.icon31shouyexuanzhong:before { + content: "\e649"; +} + +.icon31shouye:before { + content: "\e64a"; +} + +.icon31wodexuanzhong:before { + content: "\e64b"; +} + +.icon31wode:before { + content: "\e64c"; +} + +.iconliwuhuodong:before { + content: "\e64d"; +} + +.iconliebiaomoshi:before { + content: "\e64e"; +} + +.iconzhongtumoshi:before { + content: "\e64f"; +} + +.iconchakan:before { + content: "\e650"; +} + +.iconguanbi:before { + content: "\e651"; +} + +.iconguanzhu:before { + content: "\e652"; +} + +.iconlaba:before { + content: "\e653"; +} + +.icon31paishexuanzhong:before { + content: "\e654"; +} + +.icon31paishe:before { + content: "\e655"; +} + +.icon31rexiao:before { + content: "\e656"; +} + +.icon31saoma:before { + content: "\e657"; +} + +.icon31shangxin:before { + content: "\e658"; +} + +.icon31shuaxin:before { + content: "\e659"; +} + +.icon31sousuo:before { + content: "\e65a"; +} + +.icon31tishi:before { + content: "\e65b"; +} + +.icon31xiaoxi:before { + content: "\e65c"; +} + +.icon31yiwen:before { + content: "\e65d"; +} + +.icon31dingdan:before { + content: "\e65e"; +} + +.icon31guanzhu1xuanzhong:before { + content: "\e65f"; +} + +.icon31guanzhu1:before { + content: "\e660"; +} + +.icon31huidaodingbu:before { + content: "\e661"; +} + +.icon31zuji:before { + content: "\e662"; +} + +.icon31leimu:before { + content: "\e663"; +} + +.icon31liebiao:before { + content: "\e664"; +} + +.icon31chiping:before { + content: "\e665"; +} + +.icon31erweima:before { + content: "\e666"; +} + +.iconbianji:before { + content: "\e667"; +} + +.icon31fanhui:before { + content: "\e668"; +} + +.icon31huiyuan:before { + content: "\e669"; +} + +.icon31pinglun:before { + content: "\e66a"; +} + +.icon31qiandao:before { + content: "\e66b"; +} + +.icon31quanbushangpin:before { + content: "\e66c"; +} + +.icon31shangsheng:before { + content: "\e66d"; +} + +.icon31shezhi:before { + content: "\e66e"; +} + +.icon31shijian:before { + content: "\e66f"; +} + +.icon31shouqi:before { + content: "\e670"; +} + +.icon31xiajiang:before { + content: "\e671"; +} + +.icon31xiala:before { + content: "\e672"; +} + +.icon31tishi1:before { + content: "\e673"; +} + +.icon31haoyou:before { + content: "\e674"; +} + +.iconsaoyisao:before { + content: "\e600"; +} + +.iconsousuo:before { + content: "\e601"; +} + +.iconfenxiang:before { + content: "\e602"; +} + +.iconfenlei:before { + content: "\e603"; +} + +.iconhuanyipi:before { + content: "\e604"; +} + +.iconxiugaioryijian:before { + content: "\e605"; +} + +.iconweixin:before { + content: "\e606"; +} + +.iconshangsheng:before { + content: "\e607"; +} + +.iconerweima:before { + content: "\e608"; +} + +.iconlianjie:before { + content: "\e609"; +} + +.icondianzan:before { + content: "\e60a"; +} + +.iconfanhui8:before { + content: "\e60b"; +} + +.iconfanhui7:before { + content: "\e60c"; +} + +.iconfanhui6:before { + content: "\e60d"; +} + +.iconfanhui5:before { + content: "\e60e"; +} + +.icongengduo:before { + content: "\e60f"; +} + +.iconshoucangxuanzhong:before { + content: "\e610"; +} + +.iconshoucang:before { + content: "\e611"; +} + +.iconfanhui1:before { + content: "\e612"; +} + +.iconfanhui2:before { + content: "\e613"; +} + +.iconfanhui3:before { + content: "\e614"; +} + +.iconfanhui4:before { + content: "\e615"; +} + +.iconhuidaodingbu:before { + content: "\e616"; +} + +.icongouwuchexuanzhong:before { + content: "\e617"; +} + +.iconwodexuanzhong:before { + content: "\e618"; +} + +.iconquanbudingdan:before { + content: "\e619"; +} + +.icondaishouhuo:before { + content: "\e61a"; +} + diff --git a/static/img/common/home.png b/static/img/common/home.png new file mode 100644 index 0000000000000000000000000000000000000000..22492e407a10d94aa41216066f065a4d688a840d GIT binary patch literal 865 zcmV-n1D^beP)<h;3K|Lk000e1NJLTq000~S000~a1^@s6at+^<00001b5ch_0Itp) z=>Px&8%ab#R7gwRl|g76br{9p`=`4x#<V4ZR;0#gW>fT_C;@MhimlYTJ2NSZC>1XT z1Z{f~4?P4*Ptr@(LlKcGDunLN&Xl!nq_N;hM5;oI$!t=!Qc8PJw3T#sXP!(X5Yx@> zb}2>uABS(=?|UEL|NV(ue0Ym_`1b=RC(oq2yL$%7YBihP^dkV>$duZ4c=YIn+w0LE zZwY*f<Q)k_(k~$PySdDvcEN1{%jI($s<qlNkdI1;0aQTs61;a23>~gttBvRL4_8~c zZ3!%t=Lbx^{sq86NM|F%{En=3CJ`=_Dh{C!NUVeOS$CT5wr$q6X3(a<VsYjX7#<^e zkA!d1Ytm0|-#+l`a=}6nK4xM~fxJ`FmsBe2ID@~f23-|c2*M{At4y+A(o}EHT6gQ# z4Obfp`TqQHr04{R0Z6A6n`c~i=u)HFhCt5?w}>c_=n+)BEyWjFjVcJ{?yDzIaFd|< zHS8MAW*_`{CG<L=?^lLm#NPt!1>*O=JN@bS#Kc6hC@s(N<LCSTTv(VQ@wlY#>($!G z%0;>k=#|0`LF|<Dv1?~`w!7|2-AsNyw=Q)x`U>Q*5Wno^h7T|Av=Q(kh`YO(dLWy< z|6EI34GO$qW*jnyAbsN4nHQS_{V*9Hot$NE)ZLP>?^i}+Xj;Iuo3oy31ze0OCug<1 zqpP_u2H}$wPC+>3*w*NO16c58UpL02o!roY<sExoxF?39F(>o(()XL<u;_&cK<pOq z{FrSW`8(QX9Ksv{^p0z1cHdN>@Bh#j!JH&Gl4L59r<8a|(AV{~>o?~|@4C_%ur&KS z$i7rpx_@PE9y}O!Zyew&P`m|9h&plpC-dH}UH=Sh&#Txl@+1VVZRMISP0<fe64)%^ zHP^P@?c^R7OS46ij-<V=o!Q?MSSp3ji3kKNFxDGD>Z4}qzf6olFt@Q5MQ<9zS0!Dk zr&3n_*};oVfs4!F&+aqe-cH;l|0K-{+A)^Plv{@PQbFQ0CiXxYA=%dkToNt=nl|7* r+qTZO{XDfH-f>CC@zp8u{}22Nb#8PlLYwu;00000NkvXXu0mjf_>-jo literal 0 HcmV?d00001 diff --git a/static/img/common/home_HL.png b/static/img/common/home_HL.png new file mode 100644 index 0000000000000000000000000000000000000000..92ff22c0315f267629f60bdcf0a83a8ea9a5d3e2 GIT binary patch literal 1308 zcmV+%1>^dOP)<h;3K|Lk000e1NJLTq000~S000~a1^@s6at+^<00001b5ch_0Itp) z=>Px()=5M`R7gv;mTzcPbr{D#&pG$r{c(<6)3qt-N?l=^nt`N2lTdU{m)TZtVp6br zBZP$X#uyb;5)|}qK|w*-RxxEdUl>H8pbOCyX<~Y(ZDwp<(XG3C{{7;2&biy!-JRPT z4_vr#?zx}e_xpUG=Q+XyypH~)Y7G<F)+l{Bj66Ud$BwA?P^kJw`QU*AySvl|5sqt+ zz_$8EIT?PRy+(CR6{W{m?t(S{53Wd?c!i$sKBC?h#it^e`o2=QN_eMXr+hvuORs&c zD&{8n2xw7?5>J7G0S^vV@8Sy)QTgCwht);}Jt1&w!;5liRtj%)s71wbjw+AUo(rA_ zE}*3;13ZIo`!CYEsZGWTKo1~D{Z4s*RyDkSNG&Z*aKd${aU5_Ra5OXrG(p7xlj9?I z*!|3#@?R9%b67p@xv1?g$>kY=gB@yJ%%t0KsB&#^EbyUPp#o2Qb)bQ9AAB-v(YU@v zhVPe(f;AlMP|rz>Bd$%-$%3PST3ra#3VmDk(>fOM;3IL)r88{b@`emf2h9U2>F;EF z+@Qm;DDgoS*fzLspw7|i)CACnYLzjNgb%%T*JgA59{H;fbQ18&A+@q3&S~4C)CYw& zMy=G!e0341lAx9n10#U`dG24H-nUOO`OtA-Q<E|eHuD`0%^Kvxv_cmgZvvp}mrJQ% z%P>HGJxw3xLzT%}y)As%#^hcNfl69iDS7t=ZrEAo+1bEGAEvF0DDoACpeWo28=*>F z&lp|pKk{l%k7T1NeV|sitdfd78yT|3z|uhpTOeOizNPt4-J3?B^3>VWY(4lDBQP-- zd|+#<idQ69@Y;F?tW0319fl;QC~9LN#pA<30+kZuOz#=$7kooHYHLnm;<0L~URg)K z23op)8W`5Akd4#XtWzr{7%_VLF453G%y4I?=%4$S4{U2v341ZsjjKpyH8AYE9B9c} zz<|ErgL>u;^2EBJRW6*JsB0AX_f>YR8DywAAGmj$Dk@(=RsBj*SzW_Cg-<>Y%h$rm zZb)4YppPtrO^tBzEd2KKM6ja3Ti4i;IL~m~FXBv6m;-KENouTsr8?$yFT?z5xHIH$ z3X=2T)=lX89>%g0t)kPaEj8ox4$lPKw1U3u7)9Aop*~iw!L_B!o`Qun!CCuLaN#`I zdfY{9)`!udF=PCCH$`(ng-uN=QCms%hShXA4r}y@{T_^ru^EFC227{I2P)d{T;)K) z&}iVJRaCB6Ov~IkeCPumu=5&xQZn-<PP8q|7#W7xJ({NXdYbf<l-AX$*t4rh8nASE z4f_p~UD_%;Tt$<5u|TWjY}Yf@FtN=HA3JG&a5MCO(#}01<86Gj?SGvzvy??*vZQhz zn~O?m@Pr!IqiDPzr%~l^f5oV&xWp)(&Ru$ONM(n)^;zfS0X{yw^|UEBNG1#x66axo za@QkaijK?7ml9mhB^!gZ<#E$WaC2aQdr>>{-f5KV-Akej%27OI2o=hu#4|AsBNz|` zFCAqQPbnNjF=`2E)8Y1Qmyz~%v8P>6!3}?F#3~$$%{dgiP~>`;#l`nm=-4RB!Zkdy zk|9%egKV_fr)-O<f2X9b4zsovNvDyjk^3kF{wYHxlc;nWud`FH$f>W5ImZ9Bw9z}u S-iLYs0000<MNUMnLSTaM5p}Ns literal 0 HcmV?d00001 diff --git a/static/img/common/lang.png b/static/img/common/lang.png new file mode 100644 index 0000000000000000000000000000000000000000..37a8c323a4dff73d23cc997f510c7853330aec97 GIT binary patch literal 2235 zcmaJ@dpMM78y{zpGbNPQI3$NTIW?1^88Z{s<TRLKnKAQXmYH{&c`>FEm9s<+g{7sn z(Gi=h+ML!($x2GHCDL|DO8Hi5mFt^n)9xRi@4c?~eeUPEe!t&+-_QN~<IM@!xkF3C zL<509X!&?kf@CX3{;I3WzF~^*vt-K#n978Mp%^%sEdmiFE))#{K74j87zDDpDSK{! z?g)euz+*6BCf$#~f%qu49D|bZg)%k*;qECBvN`b}3`B#mJOL3oceMiv@VG=|m<t_4 z7kYtlJa4H843_R>aHR1ZJQwNd0k}&DG66mavjGV|K_Dhbh{#X61X(OUMk9ew5ICNQ z{JSV7Jpk~6L?GaT!Z>m;t{A`-i^4iNySceJ05}X5gT~;{Sezr)jo^eMU@*Yv2Pw-Y z;>HkyD74SHWIG}<4u*vUG&(ss8I|mWf<&=sEFO=SYv6E>GK8ZzMF6uUjsmgeiUI`` zb3{BL%!34gT#+3OCBj6ctkS<*;0wRV3dEnUNp@jq30sK9qA>E7R)BQ+|A+GVU(jMW z2>hqs|0yhHqzFND5GaNcMI71SVl3sUgaj`U$c7;i1A-D(ZZRMZf+2AnBm}&IU4Ss2 zfD0vyKcVS#f{#E9vjrT`heAZk_$VHaOQ4c4PE<0EjK#aSV6jvR34_~8rs8R?6dDyz z+KOM{QXozu9~8hVT<$+yuT{BnBk+Z?%oI?>O9Htx5yS^RHB8{GUJHIzz0X|k>a~zo z<)UT9pygxz*O*t1WZsd7Ura08e6c+!ka=AsGj&g(`bGpo1?xj0F(hNNyXm4aZ^P6_ zwwyUyQjB@TnudzfQUi(ysc(%c+9Gd47N(a|`*2T{EjBlb6o0V`u?Xo}I-Ffa)SF51 z2jXr#I;J~u;tXu0f~#Lh?O42aKZdM6`s`PB|NRkrR^R&kriX47kVh%WSZ88vdhcen z>Y2T$S5->c^RxcMwV^uZld6gl7X#lj8FBd8BS=%f3@6m|snIuI@AJGeIo4UPP^szP zuQ6cRY12B_s<vtOxqi~mOQ9EkYBeFN8e5&%Al2#&{iu5FL4UJSMRVr(*2nQWzYW|! zc^=t}d9d+}E^e^BqH)T+_tLuVZu8dK7eK>dUC!LEQ=8U=ME~qCy*Rndni`(|&q<S% z-CZqv&S?02tzKuBCh+*BHHI`NT|4?_vG<f<K5ywIIz)kFbn@NNC+(3y4|W5~TBT3= zZBw2RsoT`WZts{ed2aqu&LRD4e&bobX3t9s!I5VO4d|l6AGa{diDT^g{F$Q_wzCG# zRr$U)9-d*5)5Xlr4_%k=yN^-Pzdflhep}V_B36S`wme?{U02Ulu=J<kjD)+Ah+x|{ zt^;|=7xe8*8oS*e*x2bsJhpuL;;UUt^B3KewiWyK*egr&;Iz|>gl9U1^M^vml9x+| z!cDGV+OTiqjXeYmCmY4c$;!&Bh3D7MuAdl<8LC{2(lK*>dR7J3tv6H<f${AFjQ26A z(n~wVbpcOOHzysrYdNf>mzX_@OzEDul@A%-Nq2gHyK!Xy>#WF+bCYM@H$Gl|SY&rO zi!n8^<Hy-8Mx*gs^!BL(TDxqvFYY)sAJMYK+OP=Irk!9{Q#?qP&VLC1$PU{;uJcR{ zwJzIrO*6aX`!VB@%j;+=nDUOOaU*6aPqh*oUZ?1HM;p3n+A&49PYZnIOG_<0TbNUl z$$d6rK;Ggv5w6|BJbpZ=PjRyD_y-La=kwvY7SP$#`qO_zUTC1yl1nt*6kc`>H@@_{ zd-VC`gsp9;{e9n<8YRZ!RhVW)Ww!20wdQx7vaDueD${xO<=p8bIn53;_v$v$f}4qh zwUO%)=Vl8t4_R-?sq?!2j-}ZXfT)b!HgKS<Mrra-c3|=KcTJ~Xcj|i88krWVSUb%e zD88@B$u*_5*K{8Ck3g;E?TcpQA+^?c@(skdmDdH$qcYED=$Ab=@hWoNLtD7nz^WZo zK5Q|N5|PeSA2|2u_+M$Uru>M5%*k^$*i9<J?4UgRQ<-eoZ0Pwpt5+1i;vWRuI^-!& z#&UoF<_@y7@IxVqu2nUt%&1WvAR6z~*8Bo-=U#!IPE1vRg|oGiRg|;sa7$L-B41;h z^eDdsS3YqjNt=Dva@Vcun*J*zC!{Jy>^yxp{B4}!f>n#YTA1~Yw~EN?zw2%+3*Vpn z;Hf>Uy`_M*xuvjZy{?K`!wZ~V(fHI-v2{RSIG9jf8S{Nn3&lSngY&w?V)!DnLCwr! zAH9E>U7{nQ`<j6Toi4T|N;xA_>9!@hrL&ab=r>h2GVGcRvP^spZ<1?U?0_wS1@xPZ z6RhYC9WP&B0YTH-uRORYyFSkk3s+U2yi>`ozI|&Tr)e<hqjuS!vAM$MBG#j9o3k|R zl_Jf9)MA^-t^>5MwEKmPz4%a;usF-A%|Ff=F3$|njw{F!y17~H`4RQaJp&&tp2LOh zJ;rU{?XGc02JsRJ(!f2gHQQ^-jy?3}2pxRY>K8h8SsM3tjvMB$n@_uAKg`Bx*g4ER xvmz(uJp7>|DX#Uh?pW`_(n(_K!e3Eo3J9)udG20PtAYG?!iTz(a)Hb`_+P0njDi3F literal 0 HcmV?d00001 diff --git a/static/img/common/user.png b/static/img/common/user.png new file mode 100644 index 0000000000000000000000000000000000000000..6bf0d1a6be45a214b00c212a1af92997251aafaf GIT binary patch literal 866 zcmV-o1D*VdP)<h;3K|Lk000e1NJLTq000~S000~a1^@s6at+^<00001b5ch_0Itp) z=>Px&97#k$R7gwhmdj5QQ543%GZhL85@TE;4=YU77<ok9xgolOV5coqMK?rI1gs(P z59l9YLQ)>0XxIp9vF%hND-9cWYA6t+c0i;cD`QMpP>Rekw-G4VCks~0^3FZyyXU)S zerE`~{Ln7zVNWpTxX1G;HzzO0&P-M?p8-$^fCyM%q7M?2Hymcw>6BN}T~S&~eF3eJ zfO`N+QYB;X4}dyt7I`GqzNCUKmrJUwy3!3on@GUmNz$o*ZJ+dJ>1TL;X=#}YO=Tu& zO*%*5>maI%2{HO6f<w1mE|;E2IH{mt)p~^(0}+6nljBbwCrcs@ce1Lfg+N_I$QPnk zMQ+bn@SlwyW-K#No!wGCmhNuEdv9R6hQOl)<T2n#Wbc0!y4{+wsFY_2bVz4zwJYWa znTW0L@dc(^2>cL0cy%QzI~*sX@$vr&s_OJbCVmFy;6!lvR5BZr9YWY!QQb67pb8K- zSudEMrWEuAG!Frd%;>Nwa&I=(t?%{wwOf$T$H0iqB0EwF`qj`k5Si8_{dhzD(Jww# zdnW>HihOp%wghYONngC0kqx>YV&?BQ#cWDlp&E)3QO^9FmgjWVuJ{8QivX)d-mpK> zT71&M!Lhvk2a1-NIcihP#&m)!M3npc*Ft`Cb4|n_&?3xC@qvlf;*+}FqsHP=S;Vc{ z6mxDm!C4|Yye3KJhWawi8wk}I*uaWXHo6{|Xl<*dUym7d=`)yTt%~_bMoj}9ZC0y% ze<w}jQ?+&?^kmjFFGH8XG%QdyIX+yylh2~Es_COZ9Wh-}l=9~(D~wGBW3DD(&7d1L zOZh-HReIIXO^K*qz<gnj%C=9Cge=`a8xUboZB}Swel2*KG1@j8L+h4+ju=nDom0W# zfmHCWsJ?m)V7K6C*6<dh+sBhY9fKa}2L0{g?96OIK|U26EGahp(a*EQtud4V(1D(* z@u53ekEcx-YmESR37Dt3%P<3XcRYZ%e#n}!q=Xcm*iOu+87PX4T3``G?}^yI8d>#j spA3l(nD!SVn-0xn@19^LE@idnFKyd+ERC6k4gdfE07*qoM6N<$f`AW&dH?_b literal 0 HcmV?d00001 diff --git a/static/img/common/user_HL.png b/static/img/common/user_HL.png new file mode 100644 index 0000000000000000000000000000000000000000..c6d1238b03b6666d49663e014e7c8f8fbcc53810 GIT binary patch literal 1316 zcmV+<1>5?GP)<h;3K|Lk000e1NJLTq000~S000~a1^@s6at+^<00001b5ch_0Itp) z=>Px(-bqA3R7gv;mVIbdWgN#p&vWj%cW!R;YQAi0Zd6)CC5WUIC54oxX~hcB!l;bW zk%AEg$)%tWiXjG(&>Nv)*R+|<N{Av7BB4J*Qi~?#t}jdPber2fFV9oYIrq%7>%Fdj z_P`JBJ?!lBe80c9D;VYT<!(JwT8Kr<#8HD1qs*gYuik`4FJ2~I8+G8m&-HCW@Kq7C zXz?=f<ESazw^y$hnm2{OGC-~IwP53jz~4J`D(%uDG|M&2MbltXp`lB`Az!e&kY{)E z6JoG5Jss`(b`ATB!26ag7YDBh?B1hSNWo^Mn2rLi%vCWTgYeB)2}+W4JT}gJRD4nf z`U(QE_yO^1#T;fbI?+0tx0T`<?S=nY`=Au~I*b4)0$2dmRuJ#*Jj}XGMwqvPv%g)> zQa<|rhs3e6zzkZh-NQThtT8YX$^+ko0<Q=(>XZnf21Ww33CXth6+D|km^)o|N-WhP zw~LiFe5_p1F8B_3ei)HD0}DRKRT`)b!Uh*s-SHSZ!c-4G!+`CZb?y9XIEqU{05ai$ zXOu;D7GZ(Y##*D&sFk5g1C>cm{MyS6YuAdtioli@ZN2p{kEaChdj$x>z>GQri>V51 zjjJpckt$j%li*oA`9TLCWwXNk>ChQc*t$rkUR+HUkEPxqxRI4HWkaAc@St){5m_KZ zYxjQh8INq;FA8OVTC}dD_Jt)J_8q3V2I#_!2ZJu)$gX0s05`P+P0Y`qLS$>8Hc}}; zBtWJ(@!j{_xOF?d@mvN9jh$DgNw+TGq%%OmalrLp_PX$H2e(`*k_GF-XB};^!iiZN z<kA8wg?s218vn|ZkJ}mm*R0X@xFi#wn#XZx0NVgf{^mRvj`tt7Udq`$t>LpTVwZ?3 z4SFF2dWU~wMvp`Hwr#?VY>gBqXHI1Did*@~Ezl4F>zYCp-JM1IfC)`u)93ySFHu@R z!7wsFo80ju%sbZ2>1<XwWr0iQ@S*g$I|4>GAC{}4edQ|EJc|?}`S#QwtS$?TEWPVG zo=#VhDMbCJFIP}bm2n~7m%;+iWm9(-?^UuiD4P0uX5D!$dt8T!rfA%e`Y#5GDpd;* zlCwRhSU%Xxk+MYtVOvy>WBM&ktV~z)QebOrc9yn4TvdDsn1b;Kc%$zO+b{O;ciFZu z2{nyh*UThrns096Wm|Hu6OFa_RJlS9jXLe~b>R%J<I;JdlQR_>KkUV+HPohRm}=QH zT|bTIEsKS2akNE6Qf}G=d(pr=4mhXT;wz39`swQGr?=v0D-M8UV<YJ?nrW7%ampkX zR#mgg*QAHp8#Q_~UEr)gzz&_~fG@}m2u|d3LBkKV!?3ovH0!k3uz@PuB`qZl0wyOC zjIXO>ej>$OU(&2#TyZD%C`G4V;E;EogIaS=!|DH)_DR<!-`2+9i0jFK%>q8Zfs|6z zRe4;MNH76Ky%vl?1PjOvFLM+cPiO{^ocBD=4kq|VN-os5QTU$=IMS*`OAGeY8meom zsr3YP62>B_vaIk|nD67G7*x=g&|K)hNN-m^7qeNje=lFW6)YXTE=?g#=_A2-Qpp;U zR+6NxLZB=0eFw+s$Kf1>Q)BT;3#4@Un(rh2OblNN6w7oPna_vsq|@kp9+l0SX;xWL aDfC~Dq3IAz3$n-n0000<MNUMnLSTXm)^V-? literal 0 HcmV?d00001 diff --git a/static/img/index/aixz.png b/static/img/index/aixz.png new file mode 100644 index 0000000000000000000000000000000000000000..47fdbbbba6047d98e1b1350747f2b671b69dfe5d GIT binary patch literal 2864 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 O0000<MNUMnLSTZzFlCzn literal 0 HcmV?d00001 diff --git a/static/img/index/banner.png b/static/img/index/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..68c56d86306d515e286d755417c9a62374ee78e2 GIT binary patch literal 147044 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%0D$!|r1A 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%0D?+-x{!>Cm`a<Zktd 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νSx0?*OsbH@ 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 literal 0 HcmV?d00001 diff --git a/static/img/index/kthf.png b/static/img/index/kthf.png new file mode 100644 index 0000000000000000000000000000000000000000..d3301d4248986c042f2dbfa10ba7243817ba4b59 GIT binary patch literal 3342 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 Y0|F%t&)qn8Z2$lO07*qoM6N<$f|WX1BLDyZ literal 0 HcmV?d00001 diff --git a/static/img/index/liucheng.png b/static/img/index/liucheng.png new file mode 100644 index 0000000000000000000000000000000000000000..6edb45d1d14707ae0c39ca79e885feac464b9dfb GIT binary patch literal 94824 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 NX{hRe>y>Sy{}240mjD0& literal 0 HcmV?d00001 diff --git a/static/img/index/notice.png b/static/img/index/notice.png new file mode 100644 index 0000000000000000000000000000000000000000..91ba1e5989d8629cac4e4aed8e4fe49790fb001e GIT binary patch literal 3490 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 Qx&QzG07*qoM6N<$g7wt3X8-^I literal 0 HcmV?d00001 diff --git a/static/img/index/pic.png b/static/img/index/pic.png new file mode 100644 index 0000000000000000000000000000000000000000..4633f6d6906bcb6fb2f61e0c2c2781e140b1d785 GIT binary patch literal 31440 zcmbq)RZtvE(Cy+9Ah`Q34#9&%@Wpj;hed-s1Pu<0EbJx_+}&N05Oi^O2pWPWkl>R0 z-TOb?=fCG^YI@G8nmSe8Jw5Yp<=-}dSY1U;1%QSI0HFOh0RPqjivP|2clLil{&xuf z?f&-_K!OX{02E-Lu>#OZ&@f2Q{tW_{004A!jQ<A?_#eZ;#m2+L!pA^Ee*yT<L7w<O z2Xr(H3`_tf1{Mw)9sms;0~3n`8;6tyKqiPw&MJ>bp=S#e@}{H$DMShDXB0K{&;G`z zVPoeIQQW!s4}t*=@c&Z)uyHW4a52!)0C@i`OOpWpFFpo3CMG5p1}4UTC-*-L|BnEN zl^iIqhf6^z1X8e#ViWexXrfXqqGo@sujKRl-x`1b<3Dr~3=)7GU@3x(so^b$N{Ds8 z>_^$W`@q@6`;v&LZK0=t6R1b_1;MZZ%xYi!c=_TsoWgOMDUCK5R!h+-MDy8N^c^fk z^lhG5P+IH0n&UHj435mPM<8kOt2%!UU_dC4O|)jCCtZu%K`C}kC&0WXN!iB|UmOY| z$1g><J=RhadeS&1>}ps<y?89)@OFS1tiz?qXcQDzE{jq{ZSgz0fTQ;OK&k-Qc#~Rf z0}9qXr#N5|a<y@edE-*0nMS5_#2Nai;}q~ncuR*d+ff-_?m7l@sRdeUyem)2G_<y< zD37~<5O~W|OcjG-l(isivJks8RvhQtNMHbr6@7XBi>e<&HhEV6HL`h0$9v9*rn(y1 zJUhvYprEW)%l8%edofMznBDJU&_nx>t*>zKdHYY8MVB=j@00Rj0H0;m=~-7it6D_^ zDT+pn{Tcc!bmzPqXLs0&pfzG1tGv|q=cO<r3Y$b+WblPd%kfTOS8*$m>Y389J!61h z+EYKs4M1=%W|*Ku2de}WTq5j~>8veQx(#MsVaJ9brc1a9=W{-uY_kpt_43AWFeqQ1 zXToyTa@zUvV-;{@diWn;Is5}8yy*CPfWfjd%5kgmL(hlVB^6#}EBSLfYd|*O-T7hp zWWih?6`6>W%2BN<O_C~d$Q@<ZqdTZYLWKS+CAaY`JvkCTxAoxhN34eRbHG6WtnrU# zdl#jnGI9VD+l_>-j2Cr_PHQtrL4ao?6SnubTuIs7)R!MNPO0O*L&>%uoPQ8=BM>bn zrj{ghZ%dp032<T)GT4KuSJBwQTMKYVB#B9_e#2t<m%0cuy;jGC@JY3n4c_K=6WXKx z5H_~960r<BP2+edh$MP4YVTdrRAjGRD9npzeelO5w>$a=I6xcd5%>o<o|=%bhdcL) z&(u(>&aUY`Z?Csr74A#2%CV<7Ae4<5@ndnAi7Eno8QzTGLal63o_5D3a8#x6sxtBD zi|%pSr)7IJ`cw<G`rl83#DRUv`9;2uxsIjMA2Ju=!T9FwxoJYkx5l7^!X|Eumy!vq zC8rtL=<w2j%#+<e1iULfT(8`y`B=alQT&h_T~u*#kW%dh4Zx;Y+3L?mwc1(={x~Fl zqhCausU>{91|5$f{=)J17B0u3qWPbb6Sqm%s6f{+&9c{+d?7u|)CvjckNTB^9%G$f zarcR+-_tT_`eDXiA8Eb%3b!;GaSU6@V7Q?yb?S$Ud`B|wGwI^4FCFxvXmR%r-&O6G z(%8m!5!rWVLvY8fpM`a~AoxbiUlN$%uVvo#j7#(u4{}We8@GASR=^~%t(ylM>Uzg- z^5AM4RDZGr*$m-eoF%~qOrLRS?a<9gUK0R?>V7yHpg@X4^`^l}tAqw&8CtTcPw&?2 z9LMoju2XK|mPwG#{DgFyy?$W`tgC44(aBa~k`1OX8F5!~uxDr_K6t}mu3YE`PU!9C zP~$SnbexoN233O{R8S?DQWEFeXo)828a7w09+nHPORm5_L}ndpqH$b~`@xP`gF_}N z&20nEWK6uDa7{crw(kcE;Qsivj;0b$eCms5!nI6iL?(twGi-xRiZ^-DZziU<lKWan z*X_bwS#r+qcG{t)rFEMdcw3Lhf^;O8g&5nO_GZ7sJn{!$$A)Yz<lhIbwD1S|NU-(1 zFIvH5x@?@Cu>o9Nhk4f7+SS#x@tl9}On#c}bbPbZ0(smOPn=SGt9j_UH}ooY24N%Q z4CANPPa?>1or?FVa#&B&3{^@k>DQma7V^CgH=SmWwA6MRCpP!=_|w2Xfmf@NG}<_! z)&Ct}#La3qMt)rhNcZCQeKyeHp0zrpZx**n0w(59gC_8^P|58>L3F<eMA@Jaz_IK; zLk}=Cf1!+SA<?P9N?b}YAVEXW@}~qN_WFK={FkFWWBBfE2BhAYEw5e?lvR-gG@D3~ z(%?=XGBrjq{{(DFpDeq2>bChHMQM8sQmXLB!@8lC<!T+IuD#jVmxD|ZU!>wf>bgRl zN*o~Sd=#Y<<3gi5f1gU@lwu24$o12lVbNIh0Q|~(lDe%T-;@e&IDwP&fmV{)EW12a z#v#$`tKpB}X(6_LtI0Cm$cdk4w^17kxA~ACPbv=|;8w<Ca^GOPakJA7Yf6c!b1a-4 zTR_Ko%=6RlcfC;!1WpX16<xOa+y$Yhh|(JBC|bVwQ+$!KNGVwiLN36a&|2rAt!-JJ zlE7g8hNh}pxQ&Qjx^xrfFNcEFOPqSd=Tg^b+f+i+ssN>=zW(rT4@Z@A0z@?pG-IFJ zx6K4;faIuuZLQW?`vM~4kv3N+V>rzmdGug3I@?5TXk7jdCrP{?hX#N>1t4AxmXoB| zE_*4u-)|P!EH>)1nLbfE%9h09+f0L*GC?a!WRm&4d8@=UUuVJDriIm?3@MZB++^l_ zXB5um1cQ`-%hRnGPi;*<tY>VZ!~42rOS>=$T|qTQ>c=Lni6uax$o@1hKt{3H-kV<z z_bCpIyW)hAxWT2K{rk+At22=0xI3Algg^UvL5Gx|7r&NInpef2;mgT%d$-}Er6uj* zo<?Fs)(-T)o&GSdMZNj;UQ7)!K$eVENsm;+1I=D)unH3pbVsXk3o`BXnV*Q^kIjtj z-Y1S@rMM07Np@MZr7Fj8SvfnJ1>SyVd3GCVp<agf^4U`$hBk*yO)}mZA_d6(vi}xb z_0P-IzKWV2l;GD6p_%8eewB47Fz@7kF@-hQ*((D+%DwWr{eHM1gzIg@w<{!mx;Q~E z{T&C^WI)IC#hNQw^ddMh=vp|Zr<HUNoHARYync&T1x;1G9QdmcyRy~(IwJJ%P#A+F zQ{+`&FduIW1(JwAug<Zoa@S#cxs~%<XK5gp2${Ui^5o4tsR>ssl8VWNufjN@NVe#B z;5a;`v}Pf2Y>f6{QdHEE0kT$HjHkCNcrIwP1fo$gq)-1L!bLmw!20&$Ht}zVO2nL{ zzqwjjEjg8xRY5~-QGIEvzW$;rC@8BUBo$W9!@@WGnisYakbsTwZg~vdI@STXi4+ah z-tk2I*6ZaXz>Ktyb%@ocBw7m_u12%LT?IWg43|1(|48mTMjv})$*gbE$kE~8D)ZbM zp%6yfoaycCG=<Xl{WxkdK0&z&TR|9#d91mRyPEucz@mu4r+)j=tj3=phE|_B8a(I_ zOYpAn<z}%Qt5g)nNV=>v!LMV&{x4Q1Z`ID+&8sM^{a!WmA5sMQhdA$6Ky8F*b2zsp zCODhYv8(>>F44_T;v^&jm5<^$jU2dlM?5!TT|vRK3%TU^D%oIGl~>dm`GP+clq8^n z;ZXRXUTs;sSo!H0&-#9l{ck*}l`B|5<{4`v*Q$$64t9{a8w*KPQA_dHrbp8V$~*jb z7{}%jDH?RDw|FO`L-ugFp=1gWPeXd`%TYU3Slah8)!HzpA+HIGTY5_#GN;-#3$cyi z2K%+{GL9y4Q9Xt-xVWh;K`k2GW@K!*g=n42wf;)aKyTO)sY<htr95`9l6$5rWFD<e zgzY8C+_!ArdPdMe_ddzhxdhZi;|N64qxuKv5yZ!SgQ5XWD?Hcwdr}QL^)S%kiH<d^ z5xmvup)=DBq*q`Cy;3NaIERs7#y=WM;?~>OSlsYuegFkis%L5drTtyZa_|w01)Zw& zFE~7HR=x-M`~$o)$%8q`?n_uHXTKJ5q36fcz|<Z11!5KPm}g1#;xHEy^KO5Om3K-* zP#u{t7)X)ameE8n5Tm>TS3AtGYan=&dc=<C2QQDfQsH!~7~)Ti5v4S_^n?{BO3f(B zC>CdzAxN#oK`j1XEh2+PqMm${MC1DUjx|bQTBZ<XW%8A>@^Yk^Z-?x1x=MeJ*5?<D zV4OAa6Lyucw&2Uxyk^*so9A=tJ9Qo2vZqlSA~hQaNEHIpz9CEE2{C%7D*EMw)dqX4 zQ6UQbu9Yg#ndAE^J2SB|DK#|XbGD@7PC2+_CxLXZ#ln7zshf4uUqns6l~ZjdFW5GL zyEq#t&%$&N44e}#aTdDP0*O7L$^DdmM|SBai}qK3CAyjrLH-*>pM;iVlJUV~1^|Wj z1H6PvQtVUwXxk9T`}zYue@)@x{#YeZAy8~H!>17O{#R<YnVImxb{(U{nXS(|_t^q_ z8zpBK^c^cmFoosr@DJ8#z&U3R4daV3ij~>yjnVhry>pF0dv-z)R#Q`M4u4S?{y)H4 zd@%m=n-;eSDURVU;Z-k~T$f}ILR(L@j5gIun#_iRX}Jn>sD7oIlv)Z&N@5%7<1#Z9 z7%k3yF3vCdon$KEAo@plgi(&4z!uZpwV7L{Y(}2e3SusANJf)2vT>Yrp1;)X_(EO- z)<xjgqSkB@NXm>zJrIOz1Qg&nbF7RR(hEhEhjA}2hGdjBZ$Zf{TndHy4cZ_zTzmMi zO#pY`E{Mt>FahhW-w*KlEMz8X{)p{tCi4YB8ty?Jz}@L@_sM>P=!*?H+SRj@YlFyd z@~ps&Ek8B=%@L>m+f`=5_j^vmB?fqQ2#`5i0N`?LknDl(d%$B}ct-=yNL|be88E&{ zct3ZVXKy;)e%{-k?;oCm08HP@POIl~dx*d&=Ka0lcVVBu%;)-@RY1xKnmAgySPb{% z^uq=ZC1fL-?(tH(#yc?8&lB*m@aXMCPZ=p9elcN8{R24dhkprZ&^#gzpJ}?Tb**V5 z!tP1D)L??+E&s$g^rnATC&2To`T^dI{!HRu9N%Ll_9Tz7=K#<t;Q-%;_~sjRn(l^T z)oA{Xangmt7*bLViS3p3!5*8WwbE(2KjevD4Q@L$0Y@=|o^$^J9wk2zjB`REYwIdR zZ-Bs)RQYLXr(b_hl+)l+tTZ_Gs24B8=*c0B@3=p|Ctcg>C<I?hhkSQ*TdrZ?fs{B@ zNr(R}+YrB@eN#uvgjxGm;r%5VX4Idfk0W7#%PsemXDITR{4pY>tOpYFF4(>;v%{5( z08H3MT+!CY+J3|Sy$i$ne)fvzHo~8-f?i+qkm;D+fGcs>9)x-=CGmm|P-~}>%0bD? z;aBR}wu2^xxW?sJmObb$R#xsZQ08GMCL`Y=&nrC7bo_cZg2C)`<5WGJT2sQyxDJt4 z7jhyq5N@JoA*Dn(h%-%?P#)q7r@qf>x-x6!*P~!4GNtrS<>JUV0HLUb!daxq1qg6g z=Y#*s?SI_5kX&WH22$@k>*FXg_7pjnU`baoG-LFgK3mJy%9UCxwAKBFj$TP+TuO&~ zSC*|ZMpYte@Q6`oO1fb8QtpK2&$~EZ+KLr)%95)i)E-Z$Mr-W79_yWN+vR;Ib0+^2 zNwK0)%JrVfr@oks4c8yyMiMxI-Whs&CX88&hJ<=-%r%OvTvDs_DnkIdSDMg73}$z% zG#T%Fvv!!9GVhS-0yQuP$ek#M*xPFQO-D3#N?uTDg$NN;wgk12drjM~3JejHgzDmX zvVkq{0FgMO%Xy@pZ;s5Z+`P*Nmphp-!oAKHb$6xe_cH2@g;dUfJwkguHe6zD8QJ61 zr2avw#Qjs=bf`?WggVpu(i<-H)?Y7gKS`bY#P@cL%FW#I{Eg*VKE_I@U2977+`pO2 z6)jCy4H?(cHmdMkPldpnAsWUAkRa=NnQ6~NBE64zA6f3e@z#4-nX>hQp{oPGW@K)6 zb$o5AiOgB7uKjtJ6f>8UGHaDgXhmwnY?_`cB@Im~ymPE-J#Q65DP>h%4ftWD%ZcT| znY{HI7&BoYzv3_0>8^r%V(J9jXv=8ukLie-9hRvH=LV65*82MTK~hJc_wCHzcz@&w zL{fs+xKa8aAZxkQk$Ewb-)G2=8QvW|k@@D`snAC-xBrIm#p}d{SB17067-WHKd*ko zozAe~WyB6;s7&yq!PS&Lt-jc>z{U)oxiamG7`U>pn&P^6&hZUx1x9$+sSaNba{)No zN&+g;-uT~PpGnDOT<X;HX)aooyJ2<am~v`2Ead$$JL1J6+-=O<tVzXSVLm4nA@0fS zD~5e6tS>D#;|I6CYcKMy=nhYr$4)R)))ZAKxpYO3R8-_$m*5D&gTesZZ2o8|FXNtk z{L@lbV9hN%T{8N{8}9{Ek|^aG{Fz8Oug7XGfFlB1)9u#Nl?8gieikK6Nk48X!+O)~ zK`!<sZGtuSK%b{|%4SNAIyN^uHdW;YbZJSF@<U|z$5Ij>?bIu|_}%?UpH#xp-$9l8 z;QVJ*D#tnp<eMRqeCHYTr@EqsJDr~)!$pBCA9jQBnRX|<kOr(%MFr33lR-@eK$XG< zsH552+A(sG*yKd~x79D$qPa@COsjs{$s8oPRD$GbjIr?D6H~Z{N-bA(Z#~O>=juBQ z-MFfbc|&rl&jefzL`6l{Xry-b9I`=#2BZtpL6Hc8)0B%y-{>;L{o8xFlb1^$I42vl z1W9n4U}Qh)r<v23uzKFkl?ryHV8OwVA5h0Yv;c-g$lo9SWVZ(X)8+J5e`&Z|4{}H7 z;VXqLR*rGZW52U8!)9NNwS<yHz|NviwkXWD<sPK|-X0W`YnPcc5a7V|kB9ch#SPtF z=v&D|zjNwp7**Q0yheOBlK?XlhIDTA%<c(#uXo96MNlOT8O7#=2J=uHFQo*M-Hi)# zqeqN4tjd%eS|RQ~nSDR<6kYI3BPaha9uEp!LvoZ8m^aS9X7<Hx<DjWW?XhH+ii9Kb zSKV`V)Y#npmtxs#s%{ev9NN^aI|#g+hx<yyViUGZBtT9Wcgy}5#gD%##6a)UZTS>Q z0pI-)rw?r50lOw8IqW#<M$UUSYb#dUGDJuADSxoS0)A#T>oTDGnLNH@ng!hg3K+ZF zPqN<eBPU(IFl`%k$>R6t^!TU=>OuCFh_SVnx`LG1I>#vuL(<g?Ik^u)%@^PK<NW** z=TpltZo(+{Jbh3Y9U6NvCt+&D_RIOH3mj7&`3@Hmxz$^pX1r9OKL_4RJ=3&FKf^9B zAc!9}11DqazZBPB!6XJ;SVsJX%&BqvJ$Z%@(<&F%jg6<P08^_JYxWtXk}AGB9m?qQ z9@j{*`TNX94`Mn3TO9K4R;(ZP(OSO1NLQbPd^j3L-|j$t8llyRK-dcR!_|AdfS#?s zg3-p+v)I6WlAjr)pW!VQI;FuLZmIU4rVoS-I~Ohd88m-%e3y51hWBs^n+6>Z30^;a z9=oas9~9k))O+=or7}#intSt^i`S%UmO=8^U<7XmYdZe{h=Qb`syah6akzR>Sfc(5 zv^mq>aY^!WE)Kflq@Ia(-pnZvF?N>KwJLLvuhNX~-Cx&yj#@y{KIFQqZNfP+A_?(_ z4ClYjh*X;r^u-37Nk@O$+YpX+muNyPp_3#NtK?ELnw&C_`eFAHqsh&X6|vlSEp1mR z3|-}b_c2{)*=5JlXGVaWD(9;N#7GbT``It!0N9l@;^3-CTdytN!!T7nJXe$f4gCF; z7?<VE<Mb$_=DoIzsH}{7t)Sf{6%ffYZW|~FgL-;J;sKpa;usY<nKFLMu)>8=*-1nv ztnScJZm)x=-%S~<mv|h^cMNW0IS2#diZ`c?iKSyrSQcCgwH3%rI4+i##63}?fLn5t z<64=onK-~~cB1ry9ODW4Ym6Z7aKL5hyZe@iB9H97Q>*DeJb!obtg;r5#+niM_!4dW zE*C-w?!2P-c0ZjssfJunsvcy6J(o{c+)5Izji?$-JrM9#jTQ#LlayqOYZu$LoSqNV zyp;B=Fu@i0c(^a&p^QHIaU-+`%EoztvZGCkiuK0gtkv<OR@aO<(JTyM>>FQC?x3&T z$u=zH9TLzLlhYlHCBRk_zb7h}7g5sy&8xKjYLv*kAVu8J=S?PDS5x|EQ=kQiPiwv6 zkCl(xd1p-)czV&i@t8v$mVffvU3i_82PL9KrZVq7As^*VEV6b*d>^^H+TSnq+vcF4 zgCapgLXB6M0`bMNEJ;{zH;c)myC}H)`l+u_7w44rj-0}Mb%3Du5sacRDKA0mvOD-$ z4(HUwPf8!4)uF!zHb_$MIG3O4cp?4(PE^zz9-4n-2?c{MdXos$=>(KKpcxpJ71nfm zPaoM_m-%{U7gMxYl_qG_N|T8HaArY2vkTz#j%ANdX&;P&e(O+QMtql1P#YMcU;j@y z<F|INo9JlX+_YnF`*i86gnyj(d7w{znft)mC|<Gmp24cA%g$xb&4|yqx(YUAU!x?Y zL^94h%{tEP2s5lkHBwpCc?UK28mC?gs|r($N?5*gnBQu+5$spl&M%kWucI)*GTDw= z%cb;C0!2a7L4%`kVVP-Pk<JQT@!H&aIx>6U;NS{sKk*VGcq7Eh`S}lvk>nDn4hpKj zkoRa3R3;y$4BB`O-C(4xw~jm9?<4Y%JLCWwE==j08F^}Mor+%SDk)M|ji!;7wlL>| z3rW={219P>)5sIqiKwGT#7DOoeS-HdZlwKLK<I32@;IJx;&0&s;?IYvzG**$XO3S< z(&KPX8<AV#!%dqUV**GDg_MOUO*0z<nvtuR(bUgVPYROePN3lZwu?(Wr%V{s36;$5 z%QalDgI#saN{Hd&qm#PYRH;nn_wx2V;O}afwP78l0UZkHT1@-PmoO^`$@V~}`;$+D zaW0yc%WaZ0{xK5Y_;JmUtVE*v-TT`1I+Nl3SIPz{(7nMvQ1;&N)GK>nCR;>v5Q2MJ z?`&`JhiS>fMl9zbt5cG*U;NHtk629zb_s74#q~#|5DFIyF{|p$T<K*(c6vs_aT=It zTBiJRSHUdk>BSW(ap9z1InSOUF8nhwDE3;UF@+O=y@fA8R0ZUB&-v^=a!KOo>C|Yn zoxi6&uhEG(vGj@p=W~Agb{(0&uMa9h1U!97WP%)j2Ffw&z*;e0E=c=);(7mtEAe~? zmn7R+Tb*5zfeV_d5NT5V>=Xw&4aS$?U98msqUOG68Yb3$h-+?^R-;QjfcWXr=+pJ6 zlcNuT31pZH%;*(?o^)x)I7H@#E$_l70x7S#)ogn3-!l1d74k?*gF>rV1)zdwV0R4p zTK9z;9lfhO2F*I{KgUmM^rAlR<Q=lMY83d?mq-kc4_`1>v(~9?^59`$GtU~2jZ>A~ zbMNTrTwKgL*YQiY3VxJ_<DO>m0rlEAT^%yW_=qcvO5b70uX7HN9rt|N?S4OhE|yZp zHN4#9%WwSJAu<0Q<dzXB$3~=5&3N;H0xO$OnW$&~JjuBoK0MqMM9}toqg>rXc8@AU zqw^E{*Xx0W(PiX^&nm5KNv&rAqV8C5&J8Z^>UGIYr2~uinZ^<uy|9~>er(72fH&>- z_7tI1?zQrE=61!Z^dBf$cvJ~gY&jEY1iQ=92Nr+O;E6B({I&PAx>eU{2|jI{lSJ{T z5(do$zki6vj3zVd8L)06@SsVn;Po~6sg;t!t(|E|r8O+#1O<tFB1Y=JQCNS$?Xgy| zMzXdC(Le6VIZi+nxk_;T92AcEPa1Tc3E*RjNJj(!s+y72!i@Uw(j?w^G2i+qz77rv zFFx$l(GFHlbIXzC?tjq0p?)hX1$-?F`NZTJgxIgcH)-JHvIBou4~|J(q+*@)`Uj}C z!kT$*{Ra?i7`x^9^Yb_9(iq;(pw$<0A9enX!oKvjz$OT=Rf%I==A!uHr^eq=X)O%z zV$F@nMFdxja<K@%lh|(gp{<;)Vr)OYMc1kCc>W-<fs~8uAo+srOV2v&|3wsAM8jS7 zM;WL16w%fertDqKjWM6lcwxfC15i2AxcarTOL_DKlfOfi9_B#+uNEj&q3c_`P;)vS zFZR$jsSu$IUEjEOoCa?JJ|TtCy^D!3gpW@jwUOvEq$xPslU|L^@EeSY&383^RC1zG zFQbPHS(zf6oBFg|q@-VCEN69~j;K+l>TFDLl<gNgEq<_NRr|T*{vg~P>U+Pixraf4 z@}Uo(Fl>t%kx%k$4d&&A5#U+wBVBBx%5rRJ;4U%=l&S%&qUX%AOga$;tzCy|E!5ew z<&zgAX(Jn`;S)C!csxh4`CndpAN(I;NNT*KC;hPFOxywwB#T|uLTQ&31SOQ|k~6Ge zr^ZZ*!T3E7<~G%FsnSswqOT!EG~t|-ln6(Ub)RlM4*<0Y2anSe<hozX4@VkO)v14t zH0U0{OKCDz`brR$*IuPtg24fx1RYlI-H)Q|??q=c=;P=~Et)hw?Uq+raVfK2`%nmp zSOv5)H~tPVhI`zxCU5v>t`SA6dRqKA2<CfMo$$PTmz`ohesK6jS`XW7aj!)fvkD^h zaY#ECi+dr4l%;`OuL?iE`_ujLk(FwV4AuTSRV;js@Mh&)@B;e8%0jkXqC&<qD9+e{ zQEoH<ATyBlq5AKyl`?;`wj5zC%vo@mCGR4l7>P~G#sF1OA>5|00n72fRR4@^4jql8 z-EFx(rbas5ngSI`tM_9SS;yfeyk99n+)?>5KJcF%8nhpvtvSA;l30)w3swZ&Dey&K z!Oj+8RBknon7g4!Y<>LENc!xdB}-Lm4rfJEoT<0=Ty<zFD^Z&{N@lYu)Hy&=Bx7{5 z=O2Is*6Zz9$!*(AJ$j;S-UaI%6<*aD<<&TA-0)DZF9^X=ye@lnkLLEF@$vdoT0rUR zCL~V@*+0N9j?}N|9K#<K&3h~0!*2{3?i#a|{C3nfHCNev37BIEq~d1t-U%9d68WJ4 z!(XO%E}Y!?-=D}6V`+gAxE!ln${#RX@XJC=NumKGjI;B5iW+xI8S@<>aqd61SP&-M zajs4xgMT^^z5pA;swwZ3{DddYL5uOjBi=S>UCN+Yl4eOs>Fg+;&?0OH@WsaA+<Q%z zWNosg<ZYhhzIg5#M;(tzoe_cGU%^oaWXuma_x#09_8O32D2$V2xH@e^<uUfcm|l)2 zZEwMDL^XJB=xjQx<%LS75IELSZfJ%BWi`xDjV9AAo1GHi$q~ws@}45SEpZr9pT3l+ zAkjyjNbR)*^b4x#*oO=@A=2f18PenfaR+N-eh)BdMq|Ik$wckjM?HU(A-i5v=A>Hj zq1qfHNP!0L6TPGc%9#8EXxr9AmzXnoELjT%(CLKpZn-XM=H1*_pcv%*PX>4UyvUVF zxs=a{q_n3?D)c4YQV$sj4({Fs*tJXXW$pK}=R)9|Z8!72L^39(9h|25;HQ!<dy~~{ zG=gnTJ(CTbtkpd#C)B}Yw}6o%zD~wxncV0ar6Q7g%DA4Pjjd^i6n2&6KyMW}$%iZd z&}6?!!41b{fBWn`Qz>NidO}?=*N>yE>*uxJOcW^ZYDRPP{raN`j|+xE;d^&ukuQ`` z)l`<gbXDm{UIVB$UgD@%Omn+{>WqP<i)$%~jHuJM0QQQUVy((?Hff<+1Th5=p92;^ zrO)7FKFwe*W?b+6T+q!f&yrtDY*6dI3Wf?pv(o%|Do;s9TgZ`Q&y;=JwWZG0ZcG+J zK2Z0s<EEV(hBhGq4eFpT!V7Ju``Lxgi#q~JpVx7C=!Ceme+Bj#NJi-YmHdLODvG)^ z6^)1MP}*}ToS`^|`yE?#RW+9~;9Y)Ag!oG~*L!NAxerm$S1Ig=mb#<5&6AJAZCf%K zj$B4uRwR0+m@vIXUCOxEDXfC-=&#ibS_IZp+JqnTr;g}4h!lA>5Z}(Xh0ze8NFv$8 zgaut%GfvWCZwRlq3R^)sC==gI(`k+RdG>l(MI6vjt8{(2kzcV@+xg1e?%bCc{13ox z=V)5mAOh#nAfL7L)5VMyi<<?IP;<vU3HI()t%_T7P*UveNto0cwh%gH^3<lXq`soe zTiiu*=}ULP8@i<AlXHa1Ymp!op6afFQNe6{FRPr5W&V{>NCzlQG=Y&>CZ~Tjk)_0` z_8k_t)Mw+llfUQu4JJMYukm@K8_33oLcb$x4R#!oIpSi4X8B5;@`LGG2riZcSFVNT zCZ?}vR#UYYZiAvqu6PuO197PC6)z`Ed}F+Hen$-2RIWXK42Zi;+j%G{HQrS(eW`Y} z4y6^FyTRPp&8?K8N?D9}nGWXGhN7@Zw(u6Kd}OJHyJ3kIH<Y8sC>=1HMKAF~)8>ek z5?XPSl{TuxG?b?BV?&#*;e)<_8inyI)q(YwWX5BPH@B<r<)wXwP3_NDpa^XgmLE}; zn`*dfCCf1m$BvBlG*s}8^9Z^#rbAeyXED=+=A3V)MVPLY15enyDbBOBMCe2(jLq3g z#M7TpN)!#Nnv+ZcYtA?Jn!(@lO+Q?~hiQ!&8NNSSAIwRuCWfZyn}hQWjYFcjf66E< z<L>0J)QSQflrBC-47&*tc=BF;O9)!~Gco(SS^f0I2c`-=dB{<FbT&2dJ-<fOXOsO6 zt<=bWfR7np;YgI!95teUgY`{6CE|0PY+jHm{nyoF{+GFSLfAn@Q?y@Noy~7d0s|BP z&JNs|d81~pm-6L!$1OW2+@IAbs>@v$?fQ2cpqANqNO19)FYY{G8Z^(sFoe#7Vqdp} z`C9AsNljf+^hFb!R;FI^7&XIVW7A+<${b43g=w*pY{rY010&U5y{YEOmjUAM;xzb- zN*vA<<=XT!1zyvRrqB^uCW|BV+0XT-TUP8H9TPvR$rSD6R4y(ljV>Ik1~?z7DFgdm zC<VLX0*P^7q?7xlUWutIYlX!%BNI96+5{zyP&CBzT4pc04*?)8DcohRm0BN*{idUc z?=EMuxZnR`^e;b$Y<;n+7rQn62OxeW^LIKh!!N#;J`m=UpXK|dtAN56zUy|t_1nYR zv1I-|M+j3>pGP<!+<-z>{=XWrMFIOQf)mPmCUwOy%43wwAKu1cx|=$PdNH&%`ZGl* zq?>Fjht61vx8Tz?{LKj16@oBzeE2*`>8v>NCH4H<wt&I!+A%uo4CU{hCFX7INjL5x zl7qhVJC^upn{moaubjtuF#V>n;^;PDU1|(mr^f1MVu9-S5>#Q*aWs359z&W^%RM@r z3JiNbkp~h+5Pz2F{O*b$oBk(AvCNA{<Cmj;ax~v082x|szdvFm`k{kkoJyyQV#`@N zfehFZ(5PQdKQ=F#@|(63>oDVga}-is5cf9x<xmU0qCH4&RF)JmP!ov>P^J}gqw?6U zt}Y~%2E0tty9YmW=Q-T#9a$NaiAx9YeDq2VXfXe}d&gs%$u)RprSJX^0383?zdN|u zXtvlZy;!?I)pz1giAerX+HP{>X%$ki%&2FO^QSFZa?<}yZ)l<Z2v+YQknyT2qb%<f zMLvW%EydANIVWP2VTtB9yUdLSGz9UeX-_1YUe1~I!gW6)auiu~`z8mA^9BW}aV*xJ zz>VJzqUpkzOKWpD*VJ(}=Sy*3YzCJHF~{l(^3awa`U5)ox^fQ05y35nd}f_>pykYN zfBzMdjMBt$%C*RsJKNDF>C*QH-%r*{_5L;-+h@;?7*qW2ZTCC4&B9x!m3`ZK$7yfB zjWzbPW5`GXRR*B#RX@j4{}HFt7xU}{!Ze(jc<EoICSur!&VYihjID=W9F+(zFiKcg z(VL}o_&!p+{j}&-B9j~>#_F<0j^TrX?A_>*Q*usKm-Wcs6`mKPDoe<~tY!6ym4^Xu zRZ<SD@aQBMR}pCTuteZ~RDMmEX3KN=e4F0<H)vgZZy-KYc^tKd<&+4xc{dge;*QL_ zWCh@16za^x(P6=k0-0)z*PrcCzNQ56EZGYWjT@BfR0`yF7$SmuRq`w;f(w`HQ1>3d zmBr6xF)KA0if^lJGWl;h$4pfc{sFkEOiSVdbI#JoDSQcpc_;Dwa}2qGM(3c#h8YfE zL55F?9Ga1>$Fo_Dsv(M$hTPQTyqoImx*|>uh#1LVDRbG!#NwJ4d;P_lMwW@`l|HCK zCrh{MV};p-HT0LgfSR_;nbDfcvU`?HoMl8DNmig|r?0sN#C?&9z48vG$Fl%feS$YR z|K2!&ye{0{F~ry_$Ei3|^_|V}pz|V5_(_Ub2yGwNpjVS>eo6XY3pz#+!}kzGk~4N7 zgMDbh|G{{|hEI9nBk!~wPf)IMFlQfK<M4cf9c7)?jq^BtQ>_3>I=19gpvr5o>Rb1h zVXt3ojP_PHbQ8w9*i>T5MaWFlCVd=uOmaBs)0wK^7oW6|@Z{IRxhCE&oS!IT@9q{> zOH$Ia8Q=M5^LF@~C*UixK{7}Zxu>Rv<S|Fz`AF;i`Fn9U-V}N*q?RlHN<nm#Mo?g! zYK3Q|?>t^T>>3ER*G<w02R(zJ4SEaQbx;YZH_Aa^*l)pB3g#klOgMn!<gkRKf-RmM zfAP(PnNEUcm2Y%FqYdN@ivs+s`&)Z)Vk_T;sZWl-<2Mhed^$)cbgW;dRd_g9JALUM zApfsH^-bbz*%#+J8317Ya{rY1V{Sj_5NmJ4tb-BdGg5*m)8K#<^<@puHxQ|pwlsN@ zXi)5$Zr`va?#F>wHDmi<i;agE4_a4D(wmHB3F8e7ybxca;M+I7G{trSf$b86YGrzu zTX1KHzsk8DSN~0>TL>?)xg&ps2_d_Kzco_vm^C5H^Ka|ohi#7S3OF60vCIt|ZG^te z0@B_?BHW=_lWm*M&Krx=Kl~HRrCbHZnq#yFu-=r|W<`$qwi3jHav_$d2!!q<byYMQ z!d{V0!j@+c%0zN5)pdpur%U7|I2BU&>VyAy;*P+D%^!)ki_#fzb0ac-a+nrxr@noO zi0f$Eht^P%(Ms&cNYT2X{Dz$dhf9RxeipLVJ9P2v#f8vv)E+B){3Z)z1<sbHy*Zf1 zA>Tewdb@FD(KYHFE6mS;TCcxUNRyP#;9kr@uT)f@Cpr72>d?M!W-FspK1MDs1{~k_ zzFbAMYY==4I{T11A*co#UWKE7-&N3M<-s~5z~xZv(%h|Y*4fD30z-pY^`nY2G)4`f zM8^2>$SE(`TN!K}hlQ*&LJ<;WJ?*9*oD7c07?^O=#pcF20ROAcAOWYuF>pL&Gk)&| zeVgMSz>WXg_6VkK@6|pctMM07umftRQOm;~O$?rqO<>ae=;1!r9UoDdiwwHp%IkL| zO{HRTIz3%&=C?zz^r+cpX(Wj<_XP2_ln@{#V@}6*STaM+ODS%bB;BmXT$Ue_vxWjo zj3gLFzwMLvpL3rnAswu}9{$T1CJbYLTi*v%0{4T!+>=Z_i8~v`<#uf!YZr6G-(K>H zqz{;0UH`Isp|{LC4aCq_i-I`{UU$m8?9vmfHlyHVHl;tt7l8-cg>*PCNtC)b_KUJ; zO1nzs?X82Pley#KlnNTU0}h!=KNo8SHU#Zz3Hs*bC*>hi^VseLxsa|OtMeVt#t{jF zMZb+?=Nl1d^uK_-@757~^zLJbVw8nwjYX2lnK4)S3jwE`H-o1?N7wapJY|X@^P02q z-%;hZJ7f8y`rS&Si$o7|O6jRrK@PfO$MX6Z!OR7Ej1YLwOTD`kg9En%;M>VIDs@GA z%HC)z>f_c7mRkjqB*tN0`4wseA5>86bZhvjp~>Ga&d!j@v)`DTsGnE*eWM~;abO`^ zgKc74duNI&y@P9>0{Po0fO0S7u}nP>nP;5hUJ=Y<ft93^!o`t;U0moar<2q>$2Di> z++CyDzgn9mp1tNvaC)q*sGSJ(;N#|r?vw2et?qsGjKS|&rx|_-vv=$;Pv5)H3gyZd zsLoj_(XTph`q1bAZ3wDLopy4c8ZqV7nEtR}EcT&FMjmzf(B?3_%WvL=jSl-jYL-kV z-@Dp)Q4|*{G|rOn@kKxRK}{<>!79-8x#B(wvAcLXdA}HbNXMt)&zN<o-8OVoa{9U~ z3D4e~w9omplw^hU8w@x)D-2RHUVi<fL&8Js#Z87ni}B5a8S_{Z?+w2dyq6Ql-Pve! zT}YBUgJ;TebHW=@Q0TX7fV%ruudExS`|F!QB`+I^lAN3R&$6jExB-Q_=3bsPX06lO zYtT}M5XMD|#C7`%%_n3e>J|1rjLohoPWFmu9|6%DK%IASYueo0AXjHWH%x#gHn6T} z-%T(e^H?=;@3jwvqetIl)8@r_95jWCW%M`rcCJ8@qeuNEJghSwciOji2?)Cg$#nSB z6|4#Q`8Dbbdq#9C{N*q?Sl`@;*3(1Wb#T5nuNJ`OAW5?HicR|VVZ-$=iRfU7<D?6) zO@W8uhsZ<Xac_-<gjbV>0C@jgH`U~)Fpmeh;ZzpPtF)}z-;|W*AkhKNyrWU-F}6>N zi={#n^;P<BcB{*$t1yudCVo#_hpve!`b)a{I-=5|1W7v%`JAPxtUVcJ1cucaoze0? zjTLDeHLP7f?`Nu?kb4il2(34NhFG1Sg-`#?`?dHtOvLLb=;qDuu9T-ByYWHfilNEZ zW-b~lv$tPgj#i&5<27UEzdUHraGL)$rz!5t<<ondvgJH<PR$T{WWXzb7KfeY(+wxr z0uo;5DN@DpGn%LC?FAi_gvFY$q`ZWFXj6y|&8oV%U-tC4Dr(wBs5CDSZ>ETln`W@& zLE%uk(A1<+ebKM%LUf=f@E#DSBCUc}vOy>5Q{7AgM|GK?G8ioLF?;rAY8yei?^P0m zgG7C6{0GpnG;c}y_8?@~!|S{66pBQm^9)!Cj1zE9J_9>fc+sy<8E5<(4G(0@_r#3; zOQ;Rjvk463ZKAeP@H>O{>1<TJPMJ^N{R3F*%9W(jpt>I=EIo2AjwJ{pNZ&eS$M>bj z)3C*kiw?y$d5zPtEiu_Us9P4sQme2b2$Hr6cp|_XA+^F&Z@o{S9TDcQn>So1a45!j zRMiD<VAxa~^hTEuyYQ>jCkVW*_4=?%WOfwSaJeY8=ns98@1CqUVd|TG3^<jPYA)J* zbWU>=1>~5dY%TjN-JCG8?5jA8KFtkmD7YgQME~sXbfi$bF|1u1Ccb~Ml6#wX+2uhd zU1$9g-b)Ssv-@g3zb{v`$8GbMDtSgfU+Oa1+`O<vm_$5B{H~~6u2E-)P&AK3CGYp9 zj0|7W3jcn)vrk4p@}0|r0lw1jFYRLD?IurinM%)mu7#Nt>AQk1A||Xj?MTe3^n;<* znI<o&AMCDKkZcWN9XujrX>W1=E2Kga335I8T({^%-540~K*+#QhDA7Se|FBG?}z$} zVlG~GBM9dEI?v@~m=^RJ-^{e!7>-a-6iLvWrv3_c%FsU2mkm{3%#&vpv>>V%`Q71i zbmCM|j^wP#)zZT!ZX6Lr!OQch=<I@<3C77($;%X*1I45SD2|n>N3_T4sP<7(*Q0A7 zE`1SPU_@hJ@ynYWZyrc#R{+lkGZ(?@17d?FhXfwu48_pald*nEiA32On|}bI$j!;3 zqc`_~_etSZt78k(-fM<IWiKiiM($o$M8BPw=}cW5r@n?2H|VR;>6s=$mHqsX0L5ND zZ8w-Lb6no}?WKQtlccI1Vz*=C;)B)d;zK#izp<_iGhH@}a6wngqY4S%F2{S#Jci|k zH?S9=5Vt;nJ~k>-OMVa{U>+luB+ktjwiXkdukWx$gHV|$PB#H43Z>JS;zHTLwm3gF z<@It<_MQjjHER17PK*wbX8!>1V~|Zo)P<bT_vfJIXhjw%DV8zxRJZ6V^Z0w#U-rsf z3!Msz-DhZOon5#A!(*<&0Z-oo(xg0oh0k<pGRSZMm<WV&gSfc#f9xFF1G^-Z69!G1 zjJdScxS(#L$oF(+>VJm0YQW{9*(DAxh`8)hEYUyI$6s)cJo)<l%`54b(^DATB_c2z zBKHt;F^dOjGWlI@eEJm4loJBG7a2ors!;%>_CG*`dIa4IcSs%2QAtg)sNae*l5i@S zH*&a14%KfzwQKrOSnzs3n%Uy)#@neHj_lZWZ6*+P6wOihS~ay79IK}gqhR}11Tzk{ zDT*?Y)L0<W8*|z2P8_W@-VP$Dmn;K&-PqS5lKIpuS@;N$Gkj!R5YfXp5?AJ-B_(4i z$1W`tpO!MGK|>%#NOV*H^~tKCGn<ROz%-q(3ca8j?(gwZIoYT@!lDr!jT7AlWnYD1 z<pyP0m5q@*(ABAy2Ad)sUaC#&5Ackl+JkNF&b~EjeKyW);r54_rsL}~jiAx08AO~+ z@_h8^Y*&{SU7MW?3oF~FRVSsaX0na=<rh8kZSKYcBde!syn<gdVqmtUm~YG<mons0 z!5XCUK<BT=Yu^ywGc!r;w{exxH2c-Tv}%sI-%`%echSf)&cJ#ULWR*j+L<igC&nX+ zSR{7MCO1BMYMe@edaR24?Kp0stfo?Ux+;$XgnPE%es0_EzrAW4HBw*Z9pEKydT<jG z(crxn*=wg^4L*st*`Br|KP5Omy6ZwW$x&#a*PSMZtI#}*S_U+ezJ?@|s(LN&?I|jS z2MLWc@i?=#vL*^-(hv`)F018NJwRQkg(REIp}W`$Z4CGcYx=eS0B_cG?!O%EJK$I^ zw1%eF3D<toXhxc$>?+vkh-#<UQX+rzB6vuJ8ZPL_ab}qeD=^-EDXsq)wup=Rn4un5 zmSnmh2}`ArdP`lW_}@!cR^ve3zCVjZhV5MCESVW5MkB_~9O_AyuQU1_z$OCh3ifZ% z!id2f3(e80Y%4pa6xHH{G<=l<2mlo*vajM$@v1@ZGxX!&Zr7ni2SJC>*(=8lLw9R_ z-amxT`r3aK$@6#8$yXDpRZ6Z(PN?Xz+`LuB1!23ElWUuWtudahBzJ_w0P8f#<#fD0 zd0qdhX0P?AMalOL)id3lVy?~g<=MarFW>Zqi&9>qpBcoFMO)cjPdG)pYl?)x@(z`6 zPzPJbB03swTD65VEXP1ZFlmc8=;=5fcj(UqLyCtsc#z_dpL`_Z_cUpB3kz>Iw(QfL z5zGFWgQQ=bNl=|dxS0jEqnXcAYHU}NQBCz2zbap>>!k1qM-SfXu(7%P?_CX>+|&;w zSN0o!sPgu2d!5MkSc3}SCUKt)Sd-bS=L1E{(fxglw_KzSxahhSU!>lS$7Djjeohwu z?3Tfny!t{|ugaJY<agTodH;*2x`m$+G$RxzETyhG8~+b$tB;|MJ9!*|w9AvCs$Fre z1&^hDI!aVV+B+L$K%3DAW>%SNbTDL&L(9#VL{#<t7hcxOK5H<Pe;n7et?0DiJ`_;6 z7#$lXjplHTEl<H?h%zDsfeQ~BtDg84$F}eIImf-r%`BGMnfPBaP{ue<TLV+a_J&T4 zJL9-``-4<fNb|-SJ~d&mK6of+Kk;4xWBh$!R{1{b6c-GpLrX}?c;@Z?Y45Aq+_h?_ znGb^WxWB@4m?HH0Mj?BqGTF3#9dSFsIFzW3y+qmzX_LK$I(ycayoNTa*0%||9&@#+ zf22k~A0hIdrfz%;3aVbEFAg~JTrrh*(HsqX)+_Ls9I==AraA6qX*wisJLI>%WG`3M zEiZe&B1(3{#y0kOzRvAOG&E;Z&zOJ-!^@`LrMmEZgq~E~_H&MdE&to=3!u2ch9O&@ z-Q4m&fQDb)XDNFFYnGNtGY@2|f%dCbipv4%Kg9yjq(jY}og^Qq8gr`bakU2Ph+DgW z)>pSldWxh)-0v>Hs5>kb{{~@7_i5r4!atHM*&<<ssr9nO_x*gi3o9i;)s#|#j7!!W zD3&jwY;AQ%@+)U&_ThQ5ZJhpebkD~j=YN|lwjm)wk5B!C(yD@K>!&d$UNgoe1sPYT z)S9A~`pse4b$(^R)qjrhho%2K8@mCn9lp(tTb5zM*EQJHxHT^{+rBq3f+>}us_Bhr z0iaKolfS@bZacj?Z~Nw(D$CM(2u;w{T}Q`bl1zSlHQv(Ri7IfzG4zK5tSSu6%ICSN zMPL^0s-}9Ywe91NGXB*?#C;GMEy0`loOkX>34x{-)ioF3e_z-L{{gT)YIeNZB#bE! zX)LCANKnnU)s~WnkOq^gxV_%3)h0>rckOzVuLn)`o0ZHNTXbG=yjtX)`C_HJ*S;6h zGvs+yzgS=3R3^>Q(5oVh3h|uug7Lk)Mrvw?-<!iqxX5X8KF?{SkKEKr$69xHskcIy zN9W5>@xMFoQG1qLKj@PS8LS5vP=3Y@9luR1wiH?T0y7hRaM_!T@$!XMMO}yDD1BtI zn}8#1W|0mxE2fnRwf<ePG#;s5RNAFJ62aJ^6*u)-bK169xt9CE1mp{4(G_%K`(`3< z$93zZJ6lJmc{?t}gLhI-+dU03IRZ*d&7mDu+`xQc0ti7H3HcAngt16d@J7=#<+qn9 zP2BRTH#jaDSybjwif@}N@NVj*WP1shs^gNC&x_o3q69<Vj0_~ZlN{A6M>B{9S-dPV z`JDJC?qq=sLm^GE9dZ=MjzpgZj2N}d%Jbs|2KN%wY&1x9Tzo_*#pe*$EuzkAbx9}& z;U|4x)Ex(V&s_dX_e0br-I8F}`t!mJ8!jm-`kD{3GVR8I0!cF#4XkJPQe%RhT{3#} z>vatqXIWH!JX!9mGQ*t5EjY8QzX(mLIcGUrYk%+dCXM#WXw&P+@zsY#e)4hs5nOE5 z<byxP-&?5<dsNSx#%)dm5=nV(Z~O77AHYL>0~-SYISEy6j7}*6sa%dR6yFl<v}TbC z+7R#tRe(^T80J9<A8=_lDLA_*eS}nv-YH;#MQY*Xm5{;BU_HQ_h7QckSoTFzhW*>z zH{HE5`&8*L@$Hlc*B5HEX9vE86PG>GDdv};_EC=h58}K#%_YQ+H@m~5tYw?cHPmd; zq!mzwOlr0-r(gB<!W$(`={v_?Lld&NH>;%^B1>=Fz5@5t!Ixrx>h7VJLTY&(UeQ)` z+LZcvKSK@K*ZZwO0(VRQ05<oGeG4*5zd$ivfm?yTNNIS)!l9h^i1NMBr{-w_8&z5Z z>IHr*HBM|IadAu{X{V!%<+>?dZtuh-8}1>2{eYI>-Yd0VQx~?woV!OhO}FiV{32ak z`rkLCM=_6^I0Z)`ZX@+U^h>mZ{u1S0Bcc3`pB)=NP=!dnw#ygaYT<LyRMIgLauu@? z1k@W$N0e(cJdUJa$cn`rP>0U-Ck#0KbP&G1k8f+3uY&SnWk6M;p>Y#xEOFDfN_hyo z!Vv4v^|G}^_f-d-R@Eum3VEm2j6>TFqdF?7Oeu?>@RMeil3Xp@NR48~LEnSVK9PoU z#<yjYo@mbLfYO%~0nSh)xD+X(Z{z%di*EE3UpmFGx>&NiAbg(oykv=DR-bq2=#XI{ z)>UCrq^Rt?*V&9aXqij}XsU>W17Ac|*R!*!G6<e@u>8DX9=?zjOc=QQ7@TVLx+-~E zUX=6qO<CiSOuG=-rv5rrdO&wY9~l-_d+c(mbc#Q00tOlu$&{~V+$X(Wan3kbqg#d< zF6ZxQfLN}NO9RRFAi)L4c|>2Xmdh7{;u)%v`cnI%-<5tGtI{vsV*oh@k!HP`rIvC- zc?^A%Ds8ee;!Z+yliN4txtxd|l5kskO-Xu;*FXoSkQ-ut^`2Did*mC;lg0enjsVL4 z69hW@#f8lPr>?H2YKpWcGlH^(iKGF74e_ZeRTY>PX7@4btJC}6#?N)xb`^H3ZDLW- zNTY2l4#aIdFqT%3n5J+X8D22NB5@e&c=JVD8TRngiq)$#cQopDU}qtKZ$FZaf!p)3 zuRA`sd`YDKey<%US&k-Evm*O(wKhp>54ld@S^d`{w`6^;7SlOC+Q_F;lw+?=NdfGN z?C!=*M#oG<fEGXHP@#MhN!ZaIad;)OFC}AY%<&eXXYG<A;VjAD3wslVR#t^z6IlX7 z8vVt1NOJ0^<lcm4KUh6G4AH`)QP7g_B`kcM!Bo9uQ$P#05mcd(nSxWYR5VVMKZtFT z`XTD9kqIVYq^`cul`;c})Uzs5Nfg^70KVYjH40vJdUcfzZ1rdPt*)l?bCF6${2YS9 z*jffrS9?IM39tmjGIo{@HXC3PJc#uv8lhO4ZOJ39!g=s~qOs7|ldnI}=xbxtW2&6< zjADA7&MHA2{6ui_E66d~C})c0MpyG3@y#sK2^f<ihnhkZsUIK+#eSx7nPf>M$TPU@ z9o>#JRQ@~gJAgXg_4_B!g2D9)hPrKStXR@aLu#!;`_xM^v1_$a&vl}PNq1*=aiEFc zAehjSy(+yhyjiJqMvKkbh&SmSHyn}4ED<tx?DR?(w6e&tl^Iq=mQb81k-*$rJ}H}$ ziK8U!WokCsQg(Zit$5zYvl@UEJ=r9%(iT(hA8$?AgcHXZ&PiXF%QbkyRr@McLPiRx ziU{LXUfqG(8b0lO0!EY(uSmulsoZw*eVlg?+q6;$&YTGgGO8~7dsW-GsV7J0&X^BQ z?@g<v*QaxPHe;S9J{!zrfxT?#6xh(v1Z%)MQD10Umu#~{=wylG0o9iz5Z_Qdo`2u1 zb?KeXp5sGSX(x(whOr5FWJepC7`?1ioE#O#NjS#t0qP~!?oY`$<!bz+k6Drif!ayy z_V?G7O9R^^du8o9Ka&rg0{%yAQ>XoG;C)N3xQ<z0ACAmAtTtkkVy(XK@WE2ynY+7~ z6q98NCY$kA2i~Ibm@}MvA&s&<#hNEo?i=l88$<qGjd(iP&>g#Nk?Nm0haSDfbGG7) zx_fiGM=X0&qQ|g;r^n)UKt4S0!QZQmHEy?F?e9q@>T2p%F9l3zY=v8H(8GZuqkDGe za8e4g_1jrqZn|=|D~&utw%sW)*3v#$E-~{HAF+=h;1z6g4?Q~FQu<B9XCs>_!zZkr z$KH<<b)VTsW3njTjJ=wy#kNt5v1>!O$kGwZI(eDm)?XQ!r!FM-I<MbBC79$V%QSYQ zoy36m20B&n-atFkCDQ)@sSZQM+6-qVX)?Hv2rRKC#Z}M);ee484}zh!vI!s$O%8Q9 zA6dBT`x8@{ig(86uvvy;11II6RQKz+_jyu$X}gW=7Yp{Bo;rJ`>f3(csaqep&~byf zjIm#;N;jTx>~YXV&raywc}q2Qq9|zY&mYgHWe~ia0%MV5G0U-swHg=g_&W&$6?wzB zUIM;KD|YTjW>lIfAy@?PByuB$?_Ef{e&DCLvv!SuzrS5r{Sf_A@vo|m35fb_>V{q# zonGnA09fhmyRv9=t|f_~Bx&~U=&3$`ieu`<737j<y?+$3<%iwRQ!{2hUHQik%5?t# zJ!YJXAg(8KT74B+BHomb?g0V3pE@dZRGCf#l4hZNHzea3i~N6?@r!lxyl0SNtxHQK zi^@@Tp0xF?K*=O*vm!C=ZGu0igKMZQ(ckIeovK;UkccM)6@;CH5`zH7SYk!}*aU;Q zdR<!Spt4fd*(q%lw#&7?aaf(S%=IlE;Oey!mX=QJF2X&`VPeV-#ltUEoUf1D;rWQ8 zxP}?vovXPWy_2GyD~~_rWgj}&K1ulW9_qglhc}(#+^RKGV;NT`jd$2S<xGoGw9qgh z?M19@Ikp){XnRO?k$~287wVRe)u++i9Z2Of*MqD5D$T{hI-p>$4M=h~-Z{UJ@<^hA znIld^P7*Ff6m_GIHA^TfQ>{c)?%rW}U0n7cM=?iXBvGLw@-N=Le1q|>;z0S>-=w9+ zmZslPJQF6_=_ZnzWgMwE9I3~-9b0m?2r9sjzee_I_-<5G)vz7W)I=mk#t8&+Fh*3V z1QjF^f({5HsC5_@2TJ9se+5Nn+0X=bw!l6Q#{B;GuS4d`wUDrg(75km+vB{JEw(>B z+de=V*1yx%)2!U%j`5l><Ffe(ap`!vMkHXZBF3m(BB>;l5=O^<Hb4XO*bFCHI!wsI z9IQ3W5XA~KoyJBns;j!G@}a*bF2sYsE$2e2TYY6@am@`p(jzkrq)y5<fHGJDJe(2d zIRs}XqI+%L-ENX}s;H-mMMNM;Q50@hJOx)E=aApr5IE0M4qJ$RLB;Y^vGW$Z)#m$H zWv-U1wb;~3i7YaS5=aXsrBWCbB>w<FXrrhQU!g8%i(9{XofvZKK5m*%_Vj9GMW<;> z&`Fky%!<Yu%;*>=W1aRZ8<n~B2h-eqa@EChDyYvNwkIHoP&p=5M$zmf9@4={?a<I8 z)pPws@D4}KKKH2jI}2lk<+)XC9QnnjBcoF*4jNpMpe%(*Ww-V*$zfxVlOZJUW5K?G z6&Bjpr>=qNY3698j-sL`jTUru0aUbP7AVJJgZq=iZapr2S!!F=oog+%DPL6pnh9v> zr;14)l32#(krrj!9`!|$@<>NKrZ+FY(Ju>{<?QEBni%R}>4~w}Io6?y&QBm@RlhN` zz|QGf7LEl7%AVnJ6p#tBJho~B#B5N#EvV*_%x4yf8x^xZaADle7@pSzKau$8Z^xzo z0HYkUr~NtUhY`kB<yeXG74Wgklc{B5?M$9QJ?oOEU<hOqHnbT2E8C)~0hDC=zr^#8 zQF5H43YBOk>&CAXOj%Uvm@18L#<Vsy;Q2f0sAsCV-KwfZ9YKmYioknPZZScXi~iPF z$%4%10PZ-z1Fl*+hLXvr?bL5_qVBW_C1Eub>Wei|S!dd&bGNcs#~LU&?TnV^C5f}< z&Lto*`&NjEPaqCVWfeq`E{H}14K{!PAK$^<^y1b;avKJnWHiPqb-OKva#<G5FKsFm z8u$L?pk`SQwkwMt{{Se9((0XK>E6hPkx+~W(=66eyV!XA55ohmk2={0(I2GUK;k*> zVI?Yg5t5obUE6TQMdX<&cFR&0zidS!taZ#&NkmI3x(jz|!`dX0CsOZ0=x!ALvHE{B zw6zEqQBjib-=|es7DXew9W_A!sVWC-tb~Z2;a6#6q5MQ$?3G@G`k7Bla-n_tQsAnw z$xl3t@hULHQdGdK+n3zJK+%9?Smk9AwIcmN-I#T^sfJw4Jc0iJ+RT@BUZi!Pg7FNi z?!^^3B&dK5MX01J-5$x=Z3=7ObtGt=>wVvkllvO}pTB{!Jp6hqeL_`b>t|C$xR`Jf z4q1}#!&Q>3@X#gH`%z0xBC6`ZiFXugpt%Hh0!W$)v9yY=&!TnXWbAp-{g3Ogc63LN zkpBSIqBE<tS!Hf@CG$Ik{mhG<N%wqD@f*Bu_>Y~O_kH`i#ablK`cq2;Wf{}9JQXpO z!L`z31}wvjkNaOQ1KI4yQtU2bYV$!nZX<YRNfn768aV?dj`#AQ>tOtA`kROPd(VF` z#$#j>tHmloW(MvjLc}T<Dc^x6wtfI&Ndw6BO_b<VR$lQV09WPMeEAyy_}7ot&dEQm zSx?fIYAYD##78I=F-{c}Gk3HPAeJ{^M|WzMc-Kc-k9yf2k-GktIW2;r6y^s2oPN}k zXawUt1_LBxhU3ZWYyKNAa?@V!R$7TvrNWXrif~&wsiP#gRUiOfo=0<xoInTqJzU65 z6gCk;l#(VZ-RJV{KalM{2f+mFgTDX|Rkzk$QhnRr`(EheZpng$W=8~p@%~YwbWZel z{+_JAHb0wVqm9A$l0McUw0lZ6$ocW02^#bG?EcMo_2&_97c`9wkL?yL3hzpP<^*fu zyZ}6buYh-;eH$*y*pqlxWh=?@0msvjF@gvg;{@k{s5OissAqOf=oGNP2kd#=2*;d* zk2o0VP_c`e2-?I5XRLg>g&om?NchlT2a&e$Ljnmq^|a!=w=2Z13;2md(2@TD6GB&i zW@Y#Q1!Q=n1wlquI&Bgd4IE0mn%*du5LdMuf*u<P#y3&twUt?X6QScrUJs7oVb^ju zav3u{O8ELwNWw`eLTn>Q2FU_R9&|=yvZw8tL0~%wAO{Cy;!)z7G-zFLOORE@233@M zfj>r5!TJM()^M+{q_#^1Rk~V~tL|XKPZ1JCSf8026S0xF4GG<x@TGD(YOZoFA=bQW zVx?n=<nhMKRvT^^noiJLwajYHtU(1?Rmj-^`$-GzSo@qmGnB*T<P-)zDP)pqyTnn% zfS0gHQp}#$bApO{!hpNj9<}DGSOX!By)8_pIiO~qJ<`eIEXdFHvbaZZ^(K-sD_gH( zXk`XCr!=lj_98ROxmP6Ti6G=L!~EK{j8+cS%CQ(uW*UC<#(OcZ_-1KQVf%-KM-=C< zh@&L0sFZfz%RM|3V%+@645xIovNI@IAjtQ`%a+3sRP1q-VtRvq)e%b+7YeFg<?a~g z_!_!JD@k%#Vr}$w>ZG+DT1XRuHZe&fSB_QemYe9?Da$cdxux7@QzJu<a!UoWc-XAV z9Xx}jvu=Z1tgY<JHCB^wJ0W-)N&U70Sv&57rIsX;wOJ)BLa)BW`6p46=Y9v~$PbT? z14MeOpQp?M;qbV;U>&bvp`ONo3|aKYxlWt+`viahKN|I8=RI47aJ;@{+#=)f)6AoK zB&=@A2a->7MxX)Ume9~Xm3q!~t+>q5Ak4C?XcrrqV+#~zh#4)w#(BmUCmmmCnnt(U zA&O}m<wDa;So>C4*v~f6oFWEhAPxsO`ks)#t8S~NSE@GgFo8X8J5eYQNh$ZXKjQ5= zf%zRImEmKd`M|@^{K^XQ`%NT<I*~gwGj09=C(m}+Ysdb3`5PxUCyvT;enXeb-^jd| zvUv+vtyn4eyp|=BS353yQ9%hBfI^~xJSZNu@59loG)E@M8u)2d0W5S*$OrZDtuXoW zK7AQmXy~Y@Y3W&mG*HGE;mF^GG7*3?mo1EaGl9oStJQVJ<#DU3j55b9Lzvew<U|hG zK`eZT!h_F|^gUr*<e6!arH+MV8zEc(e1$)d0F3}QqE5z#jUATPhH+b*qv<;>naW+W z4Gectv$w0`(!+|9WNUD_0iJ4X3{KwISpC*M;>X;puze_b3<uhQ6gkI{S%_s#sMfrV ze1Hk_-~vHA-;Ymk2CXJL=*v2=+<D?W)0Q-bMqcKYP8I<w0MSJZRdxy9_vh4m<0N#{ zMwp_xx>1EHjvH^ga3%QVc;xaA7&z;$mJ>;ExvgILBU=^0nHorncRoo|gu@arPEJPy zIr3|szD=R29c7VBFhmtZl9xTIJa{L1*7i@p00a8<ubU(o6<J~19S_U$1AagoBje|O zHNRd>#-a97X1g*ppsT2GcfXB(z|kL%f%CtCtO@v(OW4ugVl)Y2``34mJ)=PAf;Fv; z4?6?Vq9rjT88Au7I~a`Q5<tKQo_@K;KK&{ulHybY0AQg&CU96TKo|r90MCwp<FC~A z6+*LFgm2&pe2;<f3D)=h2^v2G;k5cQw$0U>>lYxYiB{oRb&@LyQfV`YGI$sMQ(dh{ zjTG_7v1Zy@f=fQe9(Z0cR$DSgmCMIfoRqsDK+HGWt@fR4Y=SmM_q}L>JbG_Fif}fn z^>ZU4<DkzV;TfLkEX!6Vz@ufj_!)}A(n#}Ik^3-h9Zzb=t(vhbN%oOhibjv5X#n*r zMU|Dy8`U-fC0KLqZULkDzyZr+<?Y-j`2t)iJzAEvy*Ae2se-5E6~UO#Oz1F!3`IsG z#&NXZzU&CTX^*9Yu1`+ST6&izHz|8DNx5D~*2!e$mRt7FKE2B?Vj9x3h75qb5w~I} z)_`I$h#2Pz#Dn`M<3rEM{r<m0N8^5d7rvp+MSAJgHq077%?B>Xi=!AP`ww)o75i_x z{{V>W_WOIi@5{Tp{#|+x`-i$FW#7igJKvB_!9N;OK7Y2feMi>8hOpIOfB@8Y)gx(- zYc0;JBewEN$%DqmmPStvBayhv(K}9&(uc`9ddR`I2FjatCOyFsUpdBA5FC;e1A!x~ zS%1uQJpRA8jqmn64e!%4^!3aa1<jxf-mpyoTSZCHRGKgx_$~OE8?>kz?mO7<7HGRx zAWLt?!P=kE9(E6ruRqlOiPxj&(}odqoc<`bzSr2=4o-*+>JlJ8*N<?6vG5dl*z~i% zM+}ymiKrhWQ$G9-P81Q(-#8-~=jrD<8`62|;MaO#X)+~};~Ks>J*JJsykvVjEg*1C zHj|On$<xSIy95?se{w0KLa(wz1{>KYM`QP)ut@pv0<HBQg=sNn)KIgJELlGwi{0E+ zyH1>g@O81Ud%YewWos32i$ce7dW=yCAM&Zt03RfH@P9+b{YN^pUP*Y(+0v=H^<a6X ztD@3PSy%jKiGCVr<7Q<ZOK2*N?RrOAW%xU#J4Sbfm?${|a+U#dd~^N2zd}0xf!<;j zcFL7-a?rL$GC>Lnz{fmz$Dba0sN0c_=@jc>oDbK4bbNX8e`A06_&%ahhkK0ipUM^t zM*Qd!Keqf2k2)Va^i|~)s7_|cVW#)1$O}9`c04WcLobFJ16p9dKP>`Jp)M*_T%3t9 z1GpaTo$K^JTKEIV*Prj{{{Tke;C29l6D3H)4hB?$eDU<iKYVl6Jk+tIH!3JtuF0xc zB<SN~DECI{!-IlB=bUFF86A4^iql?Ca)i=U%R>_yQ$;gZHtNizPf4Xhi}@*KF_J?t zDs~v}_0r^iL71mUDIxpJb>lR#Oc`X7G7xvjSnSCQ)>M<YZ2O6?`M1BfLMdbRCPsKW znV^{nF9Wb1Mz!Pn6QT#k{QB2n6ZvN-#pUIi8g}Uu73OjyT!klARh$yaB$z1(7xI0G z+eMI%UZj;;ig*X_*&&IrODk>4puo<?<YW0+v&cCV6_&Zy^-Xb2-MM6j3hI6RxTiwM zqIO1QK~YLVOB{faRZj_th>-|;D(O2Q%0v_bkg_W4L}^G-+CVxa4}tjT4~>sf@2XC# zP~!YbhZbwXz0N?;nIT32{{WUzB5C`GBSGywSimE0req30eG7V5la41A)s^98S)+fI z>~#zqe>x*tBgy#J$D{-5C#iFFBdOV(lr~yyRw<JUhaJSl_PF!y%{)WE?X_&!onKpY zc=kw8eJ$;l56$nuN($4_A)Z#p^RCs3GEavx64*Xf9|Jxw^=7PHF{UJF!pT{3kWxVz zWBVJLiBCBwkqx;A8|6WflhJb<%IRBU>`S#^z`np|A055B8XMNZ^2B%^eSNo+oFfQW zAtY^p(-3vA2=o1kUz4Hzy(##7ov2(0<#ixv5a(;Mb)oP8*0uWw`+1+^xej5Q+8^U7 zU^I4h{q{5f^W>cq{ePjX)-g{Z?Xt3fKY!bTc|3n%o=^4ZKToSxvK2`pc;sS6=*B=9 zUjar(^v^l^ob*~YBJAwnbnf4;`F#FITmGajzxX?IYTf%Eqw5fNXwS{LKXUBoxBD$x zFbEn4Wr*4EKlDOqT9Dg~ts#fM!QTFN@v?M4J0ScIBj?jml*vq=O%-5Cdsbd?$bpZ} z*ncIUoBn`yZGW!*d}~AZia^HQ9|Q}HD--_!>)bz1J-_+>+!g7%?Sl6(B}uZ@pad0f zafD|F9^<(A^NjP-v<oaz8At%AU>Q!o%yqBW_|~*Ft?ypFrdZu&kr{hdzi?5qJIL0R zK=I`K{0*P!>tCI;utksUU`xcR%e@_J_#<Hd08b=-{1OL@cq{-VV8km{ATMvpZrua% zq7ToFYvaeFAsZDEX90;Lj&Ke*{{U`J)P4EL3&@Kr#g%QL2|KVsC>RWV04JV&jAPGO z)q7b?On`SY6*?X}o4Ap!A3i+jl6UFQ`bDpWrPS#Syv5p9XX2S?sMn(`sUB9ywTqSL zWCSowvQv(fR`P2c%?x7xI*fv2EOA3RTO*j0xUXw($xs7qk)lZW@I34OzrRi|(Yxh; z-nLL<4nNyG11Q;l<{xwJe=Tg^8!`U?E;auE0d(bmm6!XU@IJ%uwf_JU_Wqe(lB-GT zW{3-s{y9$DdvZ;kppte+EDh}dux{W~A2HTdR*BZ$t4fi2U#`QbC-m)T54=WHG5|2m z6wB@;jBLw(z{tcprX6-#F!htEabvNU@bs6KWJP>#Q6`H0G!g=0vmEnTl^nNpnv!`N zwbb`(KnUJYjK94NkVp6V2gb(t<H`R3O?ohO{#S0d^*7$j*W?cTw<q72trcisynDeD zMHM+DtcnNtU9h{UC`loS9)gn+Tz-f@)c%LT*U!(#!Pov0HHxq5?NS1S15w`afJCZ; z^D2?BEjzpl000Gv-XJ)QnJ`U$kdqomOSVfN)7CIfWMaqi>Qj<sRgsDEOQ>>rW!;g~ zjvGO$l>qm#U2Fb_<Y<Gx9{_w4`vLU&IpUTITIH4_Q3Uz=xHw(I_jkpP$X}bV>;fCy ztQhZLg#+a3z3KNCiZQd^=(-*^+;ySn{{Zak)!cNmiNnBpb<)m0P(_iN)ER8VV?pD| zX0bP}`!k(w<gWzK)+LDl0J7~Lj{PS!?O>v`ib%*3+-5O=3ChC~cco#EpeTvR<hc3{ zwZGxQ%J+R?CF()^KAxa=Rv>`YmEXd^hd<6DdWlDq^1hrNk5v>$G=z5kXU?_<^*a3x z?|=LLy$m|LoUzBSa^&j}B(7MLWd&wN?F}1u0tjzE>GD2K%HexvAe{m5+5r3w4G-)8 z0ACs-UV^=Ds@C|W)g8opWd4rE$L+td-^kG(eEcS@iyW5<Ws{U?V^;+EQouG&JQdGA zazW$ix2}xoK~m8YBv_cC9z%sB5sY)5M~}BvvD7PAdzWnHxcdJ9FOusui<DzMkfhR{ z=|oW`kVagcpdG4qR1|FheQ>XmcOQv=pFTDZl01K1>ty)z;DfnOHF<IO+AXq>gcU!v z8}NQVM`Qg-AJoC=Tn{?AIzJ^t6Y_lj08jNGkKf0w6>q2QQYcbgN&u|Qxwh>X1-Q-t zz$BB$jC0l%)fbeW{9u_D86m3fJZXU*{DSf#3<WQeTm=I?6QLy62;>%2I_~gv2cI7Y zS_8@X{{W|~OBLzdgx9tBkU>d48Ztb0BYgPs#Yxfl(fwo7xUK;w^hcd+_&+1Z{XT#5 z^^;(u{EwCXzDJLp{{ZX%0AKIwBO@!GN}P^YIRt&P!TWge#(6m-S>v7s8&YI++QGfb z>OjC_$@+g^zE2%WY;{I1InwNx6Q?zV<d$rIDX)1wz@rOgWqIu+$^edZj2bAN!PHE% z#lLO4RADE4j68<!*8Gx5C-?K<{{Z#-^i$8)XWx&-g9UFS7ax)q{+d4;)`t9k{R+ZM zUf*$DvbZ20-)n3S@BKgZ`PZwPMI}8vbp|2xCy>t}I3=8gTob_xIl||H&B5K$E7V!* zXSz}>`4o^t@YKkmu@VO?`Q(OjSQ7ki3I{s@>S4z5Q`@u^)WjSX1n)%af6#pE<Hwzi z>)^+S!U&^o=3SB8TVK#@0(|%fe*=Ff<IuYwm&mydUa$^;Ysp_EX#D=Xcpstj`}HuR zk>m>*X=2!5RE-hyqErA^dk6G9@5iVW{{RsqWISj%!tVEd4sninKk4@Vku5(3PYQt) z@~|!QJ8TyRZw-v|kWP6#jD5_a!asCs9661NQVH1Z*TMMF0C?Z;_2}uIxhUv13cO4` z$$5TeNmpbJ)}fC^{{UT`s^}jlx6gLHE;TROkPmh)!m@UfH?lRNN1u(7PoD!?Kd(lv zL3BmYzAU~3kIQnvw%fQW#YF~yIv^<h`18KsjZjxcOq2lNm90W?gW@S$?~D`mU#at+ ziY--0VZAUVLt3WJa-@9vRlv_VAd$%f9Ag~@IW0+hX}CsEi|vqi+*lAuAJ`w0uOR6D zhpjzhWWL!X(kE#I5$E(gf#d*p`~Lu6(I1e@Uee(rUcm0`Wt5OR`{Y6i*0ccdJf8#| zZ)exbb>NWuFvO~{@4bNV;agq9es%gkKR!Qa)r55zg%#J6xbvK-4TIoh<eY*?{{V~+ zPjBB<Jc%*Fc0o`$a`7R@fCdMge_lSlMhRnBcc}n?G)UO<vA-Mre1E2of$6CFM;Plp zT$EeH<gzkW;h9FEB^YLO#N9VBQ9er~VX+FwMp`d>7aPtei|_JOBM>a_6!0x}BMB68 zDh|dv?gK@DI{v%)1dw&`5BK!YeIv@T^>X*y=WAiT{t=W}Aet(2wt6JU;9L1cXR{=7 zLt;5ZdB&2UdnUFP#7!UCOKMoyo{;U6)Xfw-VZpxA9i?EAM$pG4YGjHX%5cD!4y6M^ z9$<*euUFK^>3+C7_7SIRS39LrLq-P%6nQ<vj0M_56p*ZC*e9KSS5CQgEhfW3$K)Kp zBp(e{TC>4G`>K7Y<)<7~U08pKxMNlBatJINVjD4mV6OZ&kN%_o0B`<}qIX_rp)Xqc zktR1OdoxB(Taq>Mc{yg9{j?Gd$!1C6uQ71_(T?~dJ*uECv`EER*|@6;G>-B{k;fXB zcVbl$5mXj;W+7En63jr>}gCs^u&r*fS|d^>@Kg9CJ_Acuitg@X=3g%TnTV?cx` zuF<4^j?*f`+$?r50S*ZN01T$cD<d<EV;FK=9vO+rGrpN%;^bWCjN)t_+H>XU8`1zj z!N$Wn*vQU95W0elRwg?t0FTMnk!jo;oXN=e{{RBdTbQ-1k1ovp*<qz0V-TeSMisT~ zdlN(26ew3Lnku71Y<KYW>%U5BP|k8J$3}5n*DcbUe2Y2LPD|Imi}drTeI(8D+m~=y z4j<I+FVv2E#In3=mgIPS+FarBlGc;?PB%G>#AYR<$|1?&apWE$(9W!Jem&8C9Tp1{ z&3Ts~&GOqAJ~`4ZalrcBmA<s4j^)E0evR^ebYqeVH7r^&*f7fVDqfy6c=7id1qQL8 zlC9%&8<7LYPZ0>m5>!&v$tcJLedJND5Ay|H8;-W;;o{*#YowYds;7o)lvgX1>eEDo z(%YxEQqr>qU{hxMq?sTA4*7p#k{g+21V<<*$_=0U#B0ddAFXfu4f=ueh*Kemo(+Ot zCinpPBST|;H{;{y_vc=l%0Ee;Q5Sk?p617+JzB44k8++3#gmHI>3=$pk!9oN7*2ne zpE-@E{EOX}5yu;X#f{EbSlm^;%DC)o=xrcyjoy%QMCBjs4^iwx(477LJAXBSPPF4W zHwC4co&}vTSj4%I!I*Iip1D@mKOM+92lAdJTIFe1F`vf69FwAcmfLWKYG!&_;@;|F zc@UzXm}3CD?Nue?U^fs>GB_ZeM^mXRbr9CY1=fnCsg=9&)6BIJCLoHSm5LJbNUIdl zyR=H`ppeW8uu3iKU2m}D)*&B=9ot=l+D5?u=o76C5I<x4dI;0C+b96{4gUaNBVRl6 zN9%td1NZ9cvtPp7uU%*69Vj@?SJB)>F0OHouySmDs~ubEo=(OhuSxj7Bjn|5V{x3$ zYuR{tO7Y`LjqC(j8h9xz&2N;+hog+hPYrx@XXx*n^-Gs^n~dgqlhJ;R)#x`6#q(aH za}KEUEUnHFOBtQ#94DukTDXou(oQkeRL@<@a(<xknzWl0Ud}p5ZA&|3Q`somKG77W z*Gb$)R%j#6;6cenVadi07!#AmNZ^78^T%FS(=M~SOG`X*!xdwI@iz70j6^9@&Lu}d zjt&9vK_KcEG)Lrh@&5pz`2GA3kNW<-NY-$}VDDr4k3aM~*#7%JI@bL<SpNXw-;~X8 z{&&l|fAx3tdDTDnf78yUbu#ZB;GCbXl4o<6E|Bo)`QJCA$0g+-PuypY-_K(>)G^-v zS8I?^I=MltO<o&O&d6;$i{Swu8$Ngc0LUl(b^f2R`l6VFxS?G0k%ONA_&6R%{P^nj z#eI%c?TL2)0<a;JuI~eB0P~Ckf;c!lb+)}2u>*ntU>JgbN5`N1NgvpJkCW(gYXxr5 zvu2<SYced+gZX}8w9Bv8_-shti5eR^*%f9CbXX8~vVZjcr{sQ*tt3-@o<dYERx4=8 zMvjS6S|-rY?%u#U8Vq->`r{0eL%VJ_3^p4);~RztJ~;E|lgC{3O${B!mN_Kyq^|Vg zk+JM>*gOVc2*CsrIqOA$tCot<Ng|LZYWMf1hm+xk!2X02urxML#?2DdSaDEW0k+Om zpO7?%d(iN9HaD*)&&RDr86fr-v;un<+&(*rAHN5GJ0pI6eQt7>Q883&ru!V0Cu3dO zD!}XYz4<x=K%b9Qb#pxR5uKY3ae#b)GxqX);PK#`<Dr@=IOu6+Q7cLec~bbw5>#XQ zkL*9&kr8PmSe2LZJ;V}0CtKI(gT0-d{{TV{qF*SR{{YgS8iBM=D9e{0v$I7DZ_d0C z{XRA|v*=M`K?G;=Bs&s(X+vz1cfSMo{l0ws`keB65w}kGDuQ<WgD)Z9p8{%tY;5Rg z`TYV1_w~wH+q*z<k?~`nrWT%K_xkb1I*ms#ST9SvpUVgXBg<A&E=E4*?Z?pldeT{x zJ=&$)#7v!}orN0&5Dxwc@!*XSukSbzgsf(kMs${w&VY{JY%>*5NE$lv<bFE+dL3Gw zKKWGZeWSNu1IY2Pe?Wgz^W*jFdwU}{*hb9kLqZ`3$B5B^D85MmAB_)!G(gz(`dHb! zKxK3N2R=W)(C7aEBOQ6Cs7TFQH@K^1aC{IHVCRA`pa4JvKhvzc{mX}GK^kB3=x_1- zhR?={`Sbn0Js<vyzT*D?SF$hsioRJO;JIMa*p{qk2B#~5Q<thOM(-_&U8BM@bp5L$ zwrY7^LsBbI$hp{^*AXlSP2B*UPj>q>2|GXCwemnD{C>SKKSb7Pf4$iS?5+G`OT%-K z(1wJwMHXK0yM~nGvO*{<328--j{a2=XRi>*<7l{;%^Wr6W%@ccdXJ<XnNSP;DBjNm zU={=cnv{|tU9f>!JB&`)N=jpOS$fXf{X5mD6^g!^tt9}J$z?&#Aar~nJD7$F7u_Ie z!z(KP08#Q}zu(_g25iv&YP`cG_lFnvAItsT<O}xScmDtp*dy%j_rEUw;x;v*20I~d z8+Hth3sz}lDA<DCE0rgbt;%-mO0!n8TEtHr)$CW9BbEu`bdo7iB#nb8B75}+X;AC0 zRBGe#m$4LU^BlA=V{$7pU8)j&GESCeu{bX$bgAx6>|=C00EJQB1CiR}`P^1}9}X(T z>^4r8R-75!788@<C$*2W9lG&l_}3-J<+$%4#z`zHx3IbFUP}*@s%52;qi&?~!6lxv zjgGF>E`8gz73qs^X$dChQc3PIM9-7X=8hugEYYDmdZp2>29nas2pH4WP!|C_TWLl% zt1_-Q8$+;P8<=u?Sm-<%9&7Y>^zFvEpDBv()CUMM^k1r+M~wA9>OY?HOlKs_b3Sp^ zekp&bmp+^Rm42gZ;d+J6BkHC>ve<m>;<Sbiy&R7!v&<-R-XX%Pm-vBodlA$=xpaG{ zoLi7_o6FE{IfCdOW1Do(sl1D+*lgZzr!UWP4CX@>&bc2>dRLaccNHf;v&FKJOU2Dt z@|Y{J*U99nu<We9Py9iCvpBa0=|v6+^#jhjKSs27xomuMpt54VpLlxZA2X1vkGA|L zqIkXo#AWs|m8({-9N1YQj-0YpR28L?7Mauff$Gm&dRK_#-9gUac^@#q@~gSXxq0dL zPO*H__3E}nm8Z}0{3jH~@?K38b34jw@=UH$tI6%Wkh8%FMYZl|WwymB+^Y9Lj6$j3 z+A~urVqEP6L>qWJj{qnHh17nRQ_pSHvC}(8B#`MdEL*3L2o1tII_BD+4i()EW5&@q z3cH!Rk~%9r;`3greI|5|57v*S+Y|j>X6s@4+ld)YR=n$`u;iaB(BJpJz#)$qt;WoC z@jQij{!b%*38Rw8mluXqnjsaTFq<vJvwQ>VH>*ERTo*6J`ELn}kMe3<o0EM+V5suE zUl{499N;qb6F<)R1+0A($zm<^`ixe2c0O(-^j{&G!s9X4Ddy_imlJty$NvBn53Y_y zjlY}8{ZI8(^7rjqy?N>8;l$Jyt=N*p{{U%>#Z{w>##ItX-))b^Q>j9nLORteSFIdz zMwoN2t1htfUU}8NS;+a`LnX<&*@=sebyF|)n2MG;jtMiIQ<02zLOgyxrF!+`&hU;c zlCm+7P-`<`;9D;|Q(8*_sY_2Z(kyYveioz{!qUY`W2Oe7-k*GRQyCU%CO}=(FkR$s z{{Sqv<fyvQqZ+AcP~0d?bx!wMo0y*JR@W3a1%{&4LMhsko{D1}aYzHm&?zzP?<Mw0 z*N>-em-8>AUSDIVyh_JUINwh?n`b4?ZS=SJestpp$sx@1zP5D3s##7o!1-?yz;*MD z@lL7J<F<JI9v&5rp?238yNZ{QVlVQ8%_v)_K8sN2`RwP{pD+4Wn?IQ4HjmO*PdX`M z6VV==bR&{ux}@Jy9V+XG6~pH`Z^CidZk=%MQPaFv)9*IPb8bNfE~gHfUIobcwsS34 z4LP3jhsZvyI<vyK)(?$gQ>0vn6UCsGmlpp3dEZW-PW@-d@yvCM=3A6ai@@hP!P4Gw zoWtS|CFB{r@?vrM8rayf`Fx&68j;((W?5BUW%UczUZZ09RvVjhyq<p>&AP9g>z6Nz z)of%|ITuyA9zI-NUzp`D)S+Jeo7O1Uw*)mB&+@82f02_PkE=FD^pmn}ZeE50z^=tr zEAIBNiZnY}na<p<L1NF3C#=J*DTO|vtW|jAS&K>~0XH~msV7I3BZdgXr5kLE5J<C~ ziWdW}kZr@${$)2P%Y8KRz5~g+HN$?9{{V>3O?tn_x?Kh8JWr1OXmu;;E2uq1>Zee+ zRtD6PN&ezGyH|kO!Soj^i>+4=7ai#aLkA8&94pb6HO+2k5uD;#{MQ%cn9H~>Nyal7 zJO?4d<lvI7OA(vHT$>-2#pEkW7FyS__v_fHZV3Q*XNpMN22<+?r9E8gCq+7m#yX+W zj#-o8T=$4ye1{$6c($(gXjz_>ylteCwb(6VvJ`R{%lGkFTxLq0%w|^=e(p0RQq3Ed zYf0yy<hj0EpU(2mOOcx|$oWQVC&+mQTGzHL<uiG!vt;tQ8y?z67@F<dHY`dbM33B+ zyAcL}lTe|8a6@F{9E@Q{0P;KmgX8J;74~VQh1jF+j06OnhR9}aNWzhaCm{WSAAQVM zH`oTw$H*h#Yx{ro`|@?`4=XTPYtOxVWFG|U&+o|C@vZ*=Z|&<%9g;p+fw8aX561Qg zJ3svT^tB<H4G>7z!8_K0AJ_N#{@#8_^;}^A)Sf}$=gtrQ1~bq6pB))XTk#Z=MVyvg zk3L2}-#q^S&s(I*N{)(-KxF}j{{XJf{dMvWlk$B??PPnZRShBVp|!L4pV<9xVE7*& z)Ovwa@0A41G^_ZMK>Y*y&?Qd4xcz$DH*~8Q?)+7INYNfNJdf%7_&?jmy+oP>-k2B& z*(Za}GCuzR@JHW{dJ#&Qm1f*acK`|rJYcV$GmH=Q;D4`KnJcl#sT^m>e&fH`>?241 zr~3Z@LFd$ykz^no4}-=gEjng$ts)h1*rLr~qrZKpVM+1vuOmbdH@zs1KsGm{{$cwE zLtnA+{{WZv^|a612aEAYziWRWN%D3Lh?tS*$Hu?E>wbNG_+VOujNxi`e_R-5{{Y9= zgY>}r^<0&XiFn*p6GExy!1&5Me{qbTq37;N&5qMcdRHt+K+XaEYeWrv1LsHo0G|hU zK0zj<8~~p61*KwiQ*FM`Jo(TYKkNShNNpPty@|><cA;Uvjc9;=eh<k%_;1!^mX%cY zxcCF{AZ&bjKRYAGpP%dF*Xdvd*V=ucU~|WT_VJU!^XHzvK{@S6cLF47K{?zAEHD8- zZU8)k{@oB{m@W*<wjt|#dieuKz}Eb4UmrgJYtN=*=uNU`s#%G#ryuN|1(+Fszx$te z`DxE_pKtkh+x{PY{M-3ocijH~f&9Dg`+wp@9+UZUN^k}){b_*S&d3KtKp%mxI{k0K zJJT!lDFl3juIq&UO_R)7{{Uh+4V+VB;ki6GjEq)sMr@{3Q<WM#dt#1H5fqZeZXf1( zY;0x~npkYsV0t|SgVb#%RFvC)rzMP!7q<?iZ%AihklRZ3$b9T(jTxjsA6cs79-QjE z*nQe!u|U`)CPq~t?Rg^{ZT;KwpSeSoRb%JzgZc^TchhHAF+WgMxW0bJLB6H&818?@ z`72htkgLLZo%)sI<=r#oc{Rl2>*Dg2a|u3YhsAjVW%zDW859{D)^7*OWT{Q?qf0b# zpe5vWMF4EGG0*^c*(B@W{QP?Ui1Kx>sGQfK-|knNWH|o-IqBYC9Y-IL<NW7<^!u97 zy@jIi%~uDK$8xSMm&9f0!FsJ+lr}Rs+=gDhLakdh@^KpRDphuO_*DH9WpZ)laXm82 z@af~tQ_QR!YoBv@DfZQsW|tDRc{X#BM;0dYsTO00+Pexdj~jY~YZ|7J)nA7VZCQWR zHT3sgEV0#HZ<VN%+%HBzqOvK9$n9mFnaH@pGSWy%S$8`SttNER--xXbMP<{}6*j$8 z%}s2w(ag|W?Ar?pK?5{E-r*ZRg^-0Q7{QMSxgj}v>hMap@VJCw?cq4)J01YnX!enq zk+14qn2irRI`|~D&>m3*emK}GQYyu2TW+f4XqjbjJQ1(?e|z}@`g-W2H~cI9kU0)h zIh4wAZeKi^e0(%&)=qbxhOZx%8uCH>#}Dnywr0C4I#`i2)^nKu0Dc(6R3om_K`)*w z_*Q)(;iKcpOOEqV{EZ}AuKe#bUz9V#BNFGk8a1x*%e!be%JIm}hGmf+s9MSmH{my5 zO=?SYx1Ahjj$(#EaJ}sKi0-LsXCM1`F+*1{lO<J*8=R<<<rhu2@tx__4z8)x5ZJZA zrJjJSaEon#Ot2e#HRz1Lse53pB_WJ~D*-V?1jm_05aO?z5@+#U{i=ppB3*!42qb9h z$vYpiN1r<fYo13s>7KAVwfR5f$3!m3P)R;W?epPx=jVF$)O^3gYw3nX%do_A7_MaH zEn6cWFvuHNO52ZAagoa$v}{*YMvO_Fr<%N0Sqt1}5j;`({{V$|(aoIfIjQ(>Dv~(y z?M`ed=e(V9S1mN&=*(Djy19hNqV5HXre3Zm+G1N4(*=}6wtf;lKv~~?=S7yNT1d_= z7s5N06?Jq9&7C+7-t`R5_#8&)!AllJAk<%u4^As7;561Pdl{#ec^kWI)8i=xEgfTk zQ&hf5inb+8fIEudkfB-mYvq^P&0<-O$U52H_BZ~7`2K^(pWgipRUSqO60^w62Wo=E z?|Se@{=g6P_11L1!gJ{Ud|fPb_%3xVin&C;HetEeNpe{Vt2~n&CCH+NJVIjRjn=En zkHt~UitxtK<<ilX%()PM3J;@7x3f2EaQ;~mWhup0l@ZT56qPvivB4xVU&FN4WW6=J z4(n!Q&)m*X6UK`LC~Fxj$#+TkVArbzAuifH_tptx2H|=DElnLwbYbzQ$pvAgkz6i7 zm5?bYS=DiN{8oB<ak#XzT6L-<Z6q?X+iYziFx1o1xr`k~s&gemGHzw|h9m;2t;y_| z*)hT?CuR5CD+ttrBUS-`W9PZFM*DmNy%IiiOr$D-^P&6x!+)>z{{Y+9R#N`}3I712 zn-73{JYSjEY%;p!JNf2u2P*Eaup?)LFA%O_aBW67$NM&>b}K5&SGKa^P5dQ3jpe7C zs=RZYK|WUFS)q}Oo@LtS8GyV@G5AECX2;m3qBlj8l2POt0<+3q^E$xr`ftM?z7aI9 zchU!BZ!2QH^ZQcS4c7`kPmX>bML-G1d@|%G%svm|pVMfkXsK+P)goq&D8tZM3vM;7 zZ)cGHqHbDrXC-rp%9T(@ZFyP~Z(9U>jd&Z;9zHzl;D4vBj57yK9g(Bs#(*Avba(Ja z$NvC6x}zV$gXq9zD`Y>|o>Xc<j%rfi&rms6@~f7bK_JXom|q#S$f|=1#Zxg?5$&@x zV%l!mMjT(lW9afMZ8{&wdDNC`WY+xom*-hO=2;qxJd+$H$fJod5#t^=O06zG6-P24 zh9*q9S~A&}<o^JLy?la2H+>(Nz9_L53(?QQ8ZO`m^!PsSj)W$0kh1REh2JWZ@oVYm zC{3GM5hq{)`F)RrP%27fexmR`7ESm(Wj2GB+sh;tTd~nwN+e)c`jANY{{TVy*Yy2* z&WhUxL}Chpy8~PBLG!(T=1-IBsgM5ZAEQxkHDx%5E{-g%Nb6Iey!VkBoOZ%PBvNFs z%_Wz+HQD5GYE;kM&SS*!qQOQQrdslTWBwE$M@Voi++UeWaO)!E44m&aKiZ~x>k7%9 z5xiovLn50|jxIRdX2UYdlGW|3yrldq>*CD6o9OTJS|k1;dj9}1E|FRo{{Zpt*lJ~2 z0rS}f%J%?>)qm=%K_8ec8pjL-pc{RYh}w;=3b_44jlvY$j{;w4C37cSs^z9FFm31$ z@`JAf<N|zue~(_di#_-(3}oxuv22YE=rf@wK<|D}jgOD(>#56s!ei*_-8|J}$9cpP z<!zf3GEnopx88B63qr#mgpOpJ9b%#^Pa4U{=yD7FlgwK4IDz5MllW478BKp5W?mJ_ zo`np2l*R<}{!+NRHl9_J`Nf&i`sW5kRehC;u4c9;+h#3`X}e`!k$x6+`@JeH<3|Fr z(+ryX&;-=fR#3;|Po5TPSsD1^Hbz~}Sr!DpiVXw>dmBctB6@ig4}h|cAzC`O&-niU z8IDy5K{;^2fKVLreFeX2M+(Qv_LJktK1doKemwsG{`$=K{A@P6z6m@30K<RV&i?@R z^XsX^{t=%>$&RN?;5p=%D`gWQV>bNrC^C5}i#(GY9oS=vJT!Q>jnbpdj>OQ-3h>6s zm`hDHnRX5QCccbX-eSt|{#=n{>BU-&1?T*X)Z>%K1dzT*4DV&{PjY!3ZA%{^xt_;~ z;*DXU4PzyFJul&ZUCJPJyJ;Vul<vE$?>Pc`I;!ZyV!&`jRFW%zP9-P-Wi0C$ekSxh zeZ)4cT10OuoKRW+0Ft7fow1wn2QNAikTRi(!vGtWs}{ff8YI{oBgh_4o#_6~ym<4X zes$}k58%!8CDeQ$4CLQl9Pf;0aoj61BIyrFadXqPMqX16a18$dC+bl$Q+baO#ecJS z?jeJ@54BT2o8&nsiy(uK3Ym73r{BYK=<}yp3?^!qN-J`l-UIFCp?ja^GaS<$HTvK$ z^Blhn&D^om-1E#k{p@aUh~zLm(ivjJMJ!e1hR%9{^$*qFUB|e849aqdtMP11v}h(1 z2Y};zdX2y37)-ij=d&Ic5k$nbqFAGc6^?2gdJ$f%kl~}At$AZG^zZQHtaSFSv0m$L zx@K4{_XMnxDmwefouf-^cP3cR<rJX5!_257X;L?oG;%S3v2lM;{{Rs>H&N+3?V6I; osiUZ_v`s?{m2@_K!b;P<8oZlt6$MkegqB!c)-$~nOMt)s*(-!4!~g&Q literal 0 HcmV?d00001 diff --git a/static/img/index/right.png b/static/img/index/right.png new file mode 100644 index 0000000000000000000000000000000000000000..638ed266be8a08afcb681befd52e4a9d6ebc588f GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|dOcknLoEE0 zQx*sq1g)6gSjKi{hT-Drot>G5QBk*8Yt|ik*)-EQJuf*S!QlV*_w&8e(w<euv^_9M zxU|IcO-@dZw1lMO*$<LU<~$vSEeQ%r(uWQlNSHQnUY+<&YZb`@M~<XSnl-Cxeg?C~ zCnm#Lj(P`Qwn`eO<)o&hY*Db5()9B<@=UO4qr(Y>E?x!(r+61Zjr$Q@K!-7Sy85}S Ib4q9e0G~fi8vp<R literal 0 HcmV?d00001 diff --git a/static/img/index/scan.png b/static/img/index/scan.png new file mode 100644 index 0000000000000000000000000000000000000000..3cc0234f73487e6f606c4853ba4387063ed80d0a GIT binary patch literal 36570 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 LNkvXXu0mjfjx|C^ literal 0 HcmV?d00001 diff --git a/static/img/index/title_kt.png b/static/img/index/title_kt.png new file mode 100644 index 0000000000000000000000000000000000000000..95fd44f25161373708e4930f668c6af39d229a93 GIT binary patch literal 4199 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 literal 0 HcmV?d00001 diff --git a/static/img/index/title_xz.png b/static/img/index/title_xz.png new file mode 100644 index 0000000000000000000000000000000000000000..b685fc894cbfc801abdc180e83c1e49a2ce8b4c2 GIT binary patch literal 4101 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 D(-G8O literal 0 HcmV?d00001 diff --git a/static/img/index/title_zj.png b/static/img/index/title_zj.png new file mode 100644 index 0000000000000000000000000000000000000000..d5ff5732940f1066f9aca31b31e9d9874125e073 GIT binary patch literal 5800 zcmbVQ2{@E(zqgb|C{&ip8d<W;3=Kwzp$u6QMk-^>qrog@FqROBNYbPjq_VcilAWv- zl|jgw$r>%pBtq6RdV9OR@0{<P?>gUeJ<I*v_wW8MzyESQ*Ar`NeOze!-tAmmTtY~5 zQ+v+Smvc#i_&9gIfyOD$gP&xMqHu8u?ApAzxl+?5xVW~8;T)X(oUKm4F+{uu+Joqh z)d<9sIA|`eBSwKFG{zh2C+m*RbS*){d@vN#U|d8flxU@H;=dmd*VOvauJwsyb- zdt>xG<c$nvj|9Rw0`OQrv}_>Whd_Y`8p!{W3+IeCr#0nee?k1b4djhC70NnW*~%h_ zWUMSq1FVh#gQ2oIdKwUzwl)l`CJO~a!J6R1nh>Zu1O^9d!y!7dzYlqiHnIl*w>LfZ zTNmfeK>oa+9|^9hNu^RXs9G9CvZp3QPft%14Aq1})j0@tN)W*h9jH#A9Qa#;DVBmE z<4AruB0+XjBHEqk?`I&-vGgw&@T5Ot36$S7aR}24M3Xcj8sJTregQo&e{dv!vd=H& z9vDrm4;GIl_)$1m$R8}}JkgIxIZymwME_a-Cj}g8t*rj&_?Nce@qbiM{LBJ4Zu}<X zU!o}vK_slEJ(fcBCu6W?0UR?AY<fe2Bgj~^ACc@pB>MavDBHhHmW4tzpt8zNID!X} zN>TZT4OmmOAJ#yg6E$shu#P$e;sD`<jB_471lEOv!GA%mh#oi~=zl_Ca0nC*hWrzh zlQJG?KlHx^dtl%Ik&H)k9LC|%o>)y1!BbxL4~=jH(T7OpDCStF^^fyN1j3d~1aLl_ z1&aM~6IrAg0tV58!PKD|kYDCnS;3J6iXWPQ!6Ho!<T>PM;BX#r0MOC}>!P9RhXE~j zbtora)zMnoXmt;mwl3CP7ke0h{n>9y#Q1Nfz~B2l{-678$T&_3qJ92jIh%#KnLBWE z9EB6rpx>o)66^bW<b#v_l`e2JX0r?o<T0D^$9l;Bp2q#h4E!aQdLGLW`hVobUoZ+0 z@S~#1SYuBPTmLriG&#m=ZWiocerW!87k{n&LvjBN=X8wC;h$E;dHK`qumnzrBXe5W z7l$w%E-n#Wq^YsP`G}d+pu{s+dUH5Cq`j(wILxx9-qf~~m9P~RRS>oi@jJR(n-Cw@ zYa6!`xlfg_dt5g&{;Qf7>dFYrxU)&f7k$(eW^8s8B!MvDLCPw8w(j~|vrs#Ap<~sz zUFgn<{zuKJM$vg+9<wOz6-Mlp+Eyz5b5{;milp2Y6OgIh3k*6qIMwm-%WD6x5|WbW z&P=m+UWXZi)I7(>kRwM<fFjgdNmRx(WaUCBo0b?=a{$*@BMsCImoOP&ZGu`^XA8$! z^NWi{Ui!#)U;P>J5fKr!y=k%JD#oKzk5kgqC$ePLlnm$Z&*91&w7%i`sfO=z-Q2`{ z-U~`k<(xb)E83IYYAZ(J0xJm!iki2sO$U<F%X*?>Z)`s))o}5{iAm}2ua;&p?JSkm z!X^P`V#<^FAYeb%9l3Y5R8~l$mzb-1<J7Khw_4z19hv}{dcWNj{dVAVz0Hj!o#-ug zf`Iem_K0XP)iO+EU}}rP6=-5L%E?|8RZ*Co5Z^1q>U<?Khaadr{*;mXlW)xDUh$N~ zudT;zhZ~)HvA+12+X{-;Qbw=reX4paUf7;X;A2$2%hUNcdjyA!tkVy>t*?VU<Ef>m zv#%^p2UV|Zv~{hAAC*U1sO8U$ykKcxr9mYFh1Ar4pn&K|2NDRW(32Zm7bdW6bsl!C z&5bM?Mp4W^%|H3<y?lnuEvi#pp84v&qdZ#C?&Eawj_b=;z+TZgKw>Vv9A9Y0R1>iC z;8t@x$Zw|6R?s!0sj{U(=+w4*uH4WDy#uORo|AzP+pX<eRU14n9{dKl$&e9`GM(*2 zM`~!W$z8p64apHU+&3F|kbOKd8qsTgW1%}wjaa=r7gvWFfsdHF-3#p7Rir`UE)r7a zJ*T$3ru&vhjQ1{I82^PSy9IyPH<TNXu8ag<h5=?(I(=wzNbMmGz`E@amd+WL7Detl ziVzJ|e1vR{NP0-KyZP!0B;K#+&ATTR%#c?#M&AazKWu!k$-mGlnOE2kK`L6==}3f% z4B#yLpN7Ajp3(t()~}8JRJz|N=Tj}@!Dw?~Y?WpSNn57!?eJghR$OalhI;~2JafQ_ zx~UC;ZwkmT9bMf@UrxrF+ReWqRD`&L(8_U{I+ZK9OP4NLD%QSaJXMa<@Suuri<o2h zz!G^xMt}ulqzu;%<uCphMH8!67B9j`b@V%@qJ-X?fT$M1we;G(fw|ewuda)Z931)X z9xo*qHL9C1j0Wndi%;q5$=coC>?g!K^xABfC$I?dl!2GPq*;APvoX(8ZL8-Ewi_|^ z3zymJq`7yOI+orZZ@)lM(Kt1(8#|_}VwRDVrL6kWH{D@*W&SN<-%jTIH(rd>nYokr z;c`7aR{zN_0^(|{sEgIyUh1p;p0?*|Z#O>QVUO!gM0vwy-t!P7L$#Dds5MOOxJ=6| zWjK>@HVHe>11xj^jw&!W{%?lJ3f^H@$)oHtR(nH3L%wj+;}W&^w-sLR(9+*85tsQ1 zLtAb;0WA0;-@iW0eioUoM|0$5AKEghfUHmUi=c-d>v~D6`@}MWmSiD7XWSUfj`ZRi zh1w-3U}0!1h(i=gc-RxjYUohR!KFqMDpuOO%9b-Na>#Mn-X@@A-q6eU0NV1!<g|hP z_rwo%@a))fP!T^84v(bMrFNT06gVu$BsiBQ^*9ykO)u{&Sazl<NC&A=5j1pk8n=j@ z$HF{9yf|zv6hkdo>aKhK98)1xN18?OL1VR3tf<%yl+Tm<k2*iw^CcP!>hHp|5)`WS zSz#2=D34niJqR?a#4>d15~FTteKQ<!9PzGg(20|Os-}<Z(uyF`QM+_2KP^51dT$O< zQ&5iQTbvZ|>R*n4TzOXwNcNyc#&k}@juBBUeY>vvW?&g)^)Pe8xXTQ1yghG))Ug~K zWldQw=g>n@U66+QG}ImtO)X)(WL#I;yxULmC!~~r=_TVDxI~UTV*s)(Zg&IBmwCSZ zGyqL2a<_jD$TKq97<rzv9)-ADH!`{_V{z>2%?Azz^FM4_kkhDF$C4fnW3C?xRlIiH zW`E$Cm6LSPyj=_4aQf}mogLwWku$YN_y|x?w^V;P>|u7|<<(C&WE&p2#j}sO?>19P z8JJUG?i`wAseH2_uYC7KI*GS9If*g};#8$5QesW`qgV4o{QE~)D>1@#SfGMF;2CGN zy1!_v1h_<fqP&6tbR_wSkjRkasWg=8=XAAJLc;jcC7>K0RW!gnO^a48jdp!JJj3Gd z*O_YQMOHU5dM%daf6gY;QLdmaL%ZE;o~gO}q%}iI#pA_tN?TS0Q`C*qQ)-}R=N=^+ zEDbu<Pj{zDdtE=X^8W6fi!1rqy!3^y#d>xwL?|38*vBOG&9>HukKSNk_}T)?m>XRj z0vomuStlEFU0O|C9O@Lwbxi>4cp1BPij^_VwM+UcS5TK7)?%`aZm=yn9Gz!ps(^)c z<+wpsSS<aH;C}uQ){6OFZkPqUkJ%Ctz9qM9S{^BMFF7*JEG#=Ab>QK|j@^Km!df$f z!5luRYkiZpZc%Wj_2QxJJWwRXGN+3}fwh2KWwsN208fLZp=v*sRJ0$g5;kL9|B}eQ zHjw{mYNp}PtF6LZNFnO53a*%b6I*7#7YfG@#e0ywnr@$4F4JLtZ%NDeQKlKbc(pK` zDcR=_PjXRzN2qY8e#(_1YR8sLi?FikJFcJ)W?zsyNVv*V!PZ0WlvCR&ccCP(fbDhH za!n(P2(+XOIF0Gnaax-lRVKCgUU5@G?OR5(z)IH~&}t^trENxI_()Wq>be0e@N!X! zyi;7_RV!N?25294q@w{|pxnM|>t*pJQk84ikL4d<>s<=_HpW<{cG^Zr8rw3MAB+a> zsI>;nsfOtFx5$#k<6ON9-AxJxM5?xZ1<FCX?acQ;b>2X{j^~{njrY!Wyt#VUcjUHC z?QE!#gtX+uRDJdI#I{~52J36qrH1XP@vEO02vLNvpa3+I5s9QF{s{Cp+$9eR$u5_w zJkxdgdW9^qFGl!-+UIv_tp`igPRo5BbEo7g;`!zBxt9^372X@!%27py*-&uP0rVwH zFW&J97am(^2@aVYEj$?`e4Be0>umA<#mmC&2l`k*4RgJg-ZK<hK({))_nJ6isB(7R z{$VNY$O(EHb|AgQ@~qMJ;A_F?x0h)dr<11keDtB_b$?#<XuYl8`eC8GVl{X@ve@GM zgdDv^a6dGBRw}0bT+ssSR)r?BdgeZ_>j{AdX?hZuR-{A!Io_M-mod_##T1k%1)p>@ zY1(k$LoTbnWnwY<eZ<EGO@+gXOy$qCu~%fRm8cp{>paa|Q*}$AOSu6~DFaV{018c^ z@X(e_PQ_`J=quFvPF`S4J8P}v*VGR)_!RvW{3FP_&64Q@u2h?S*N(wpumHz07lS57 zUAI?Rn7*N57^=#3y(HVTi!!Dg<}LCSYZKW(f52nza$JM7FW9W>x(Voo?=z2Jcqk~f zY;-iDa%M#OGXADRf_}g#Z|m9BLGQsQd`@S7jAIT`t&_8^2-3gvI;6x|br5adtrS+; z@bmeK`LUzbFDyk^71b$+94Ou<ASe5<;#0<fVsGQ3)?SaJ;p_p%0;B}N@L@0(ZcI$T zsO9t-Pk@r8{K&$~vRZU0i+<~(5w8x?W#_6`KLA)Ve9q3!=o~#xL#Aq%yzkbo&2@Pq zJ;pkoiwh2Y;<!@L*=cv|_;FO3SC7L(hib7(-juN$n6=r&*(<{A>RFZb@`ZdK;I!hJ zLc;iXiCSp4+lE@TB<gnVm$x}-Cg=22Wr|6!?>0A2TCcMf`_f&B?_%C4!tHinmJgLD ze>^FY@Db6vJ;WSYl;ypU@sMxlOg^IUh*+!)_gUu?E)yS|J5rQhQe>*|{XJ*(46QR{ zb@%HQd^6SIQR2FKG0)OP+9V}u{<(4-pI6yn-rj4?D7)QO$U&v3Z;y2oDtwsLKNvs0 z_T4?BdW$b&k6cv1ZS0J2lOju6XYaLr?`Hz^62^Hi-c>}BPjFo0IFQo@P}JfwFVi&I zpH82iC}y3#=Ehzx6(0H?A8a5henVz>Uw-8wJS>g#74B8$Qqkje2J}{EaIts2-tm#I zRhw|0X(QHS@Y$)7jJ4@q?2X3nU)z_Lox+!2Ta?usR8gvjQVC{yXuiQ)BD)f0vJ)5& zK;8oFd|~^JgG(5uuIKGKRn}NxxgQ6T47Eb?vYmF*sH#EBk9gE{O4z46UomLHCJ6hi zz#bLzj#tmG;%oTg#~X#4j=%Z%-R^xk{<O;HPo15e$;j%x4{BB(G6{>-)_HR`brZz> z;didX(%M=d_joZGhK?G^$v(q%q4?X_miYMiXAWJb>~;(B5J7VJ37MblsYXqVc8+&O z4;&vKrfP~eRahQC^tVmv?OqZe0lRt1rC_Py+(lBAg-&*&sM|j@I*G5JPA5~2Pu8v8 zbV^^W<~<koqGGZm#Kk4eM0dNS!}4;|$kAc3OM^Md>KipRj;o_74+q=V9<7}+dA0wL z^+_N~+}$}-<VDq_Yn={vIzKz0CL1@%ky)p0WwdQb=*XoZKEc!h6F2YnTX)U_E!8W= zW;LbG{eGbPQ=ujW#X|Q~9+t*XiW``ByU%MEB+n-|Ha21u>>l?J?xv?d(~3HpV~^VI zli&B1^X)Unp@Y|kx;Vb?TJtrzs2aX)+mgB7d6nraDJc;J<lgr6RY+9UpDo;|JWq8n zc9nWyxm}gO5*VGig}B)L+Tio7aOA5pl5JO<a8q+evzW<Mdgn~Q={8_)?v@31C3IhJ z=yLSq0vBy2PN(h0ZjTAmfI$V+vC=Oe_RJBMm^~dEt#9<jj`>Drm)aVs)W5xwcnl(K zx-1)ffroqSjy~01Bblvy%R=e0u=F&WEpyuse;*q{0c8p|@t!<+QUOV}sd6>cX?^Og zf2Y0RuIFyw!I)R$RZ_K=#UI>i{kT|gre?uLZp&m_8a1e7V5l<q#}`i8_jcu24wtd( zRt&)qNMp3nzIOfTqUVHs2h}#=$V}znzUFKDHqx<@+hi_$+cOgoGM9SToAu^te%O5H z@p_7nkI(qhlJ3{O!j1XJtpbFH4yyW!RoQb2Kg36o8nrbsl2l$#i#H4(o(J6ONcI+1 zP`=O!+Qj5x@7`y?fJ)wx$EStREeiobB-vh###;^PYA<cV8Xr=npc_TNgk4n+K}g-; zOJ!`pRl}{3^iwAUxA|t7T|f~Qp=+kfYFz}NyiDoL#Op7or3=xW1T8bT*Rha`xRJc; zUGry=i_N|#QggF$!C7=>iUL0_)g2JWqBQh<_YbRoW=W<#%UxW#t8GQ(Az09|^H9Sn zoh)hOBI7)@XI#xwi!>|EcHHw3!Z{lGmC{MfzWT6Ee+1(Bk;>Qs)S<(R3g&Wd(!z2e z>!8M1@7A1MeU^fi-RFp4mqPDy{j8=hlFRh1p&i8wH)G>7st3YFNz{V}51!ZFTRQ}q z2}sjw*S&QAJ{pG4OzO$i`g&;F0R$?kC1l4zhqO!wt>p++_oHCZsvocYvUoOnF5e27 z{E-35ke@}md3nWAIt*fd&P{a_DiTd=n81f;RX)sRFmAi)^Qclp12%)-b$gIRgq%l$ z9k-}>h2>DhdyDu41OAz`*gW$KkzlRVw!JMf;d@)8LqKKnTcym3Fx#g4J71hhJynn> zO44Hzyqt@Uh~TXcQhjP-YYt3BXj3Kb-uoEhcf@RL*Na@Mw56`(x)(IXIS}{qip<aQ z;V|0?gD(6bTnsHl`O+<Svj|5q37hoxS7ER!ynuRjX~w+48_Y;Ow_2(AgZ=CVVDn!x N(#+blz{D-`zW`^oZF>L! literal 0 HcmV?d00001 diff --git a/static/img/index/zj_icon.png b/static/img/index/zj_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..59b41b8a18bb1222ada84d8c268c44766f81c19a GIT binary patch literal 1435 zcmV;M1!Ve(P)<h;3K|Lk000e1NJLTq0015U001Ni1^@s6p3#bF00001b5ch_0Itp) z=>Px)RY^oaR9Hv7m<vo(XB5Z(_uc|x$dn=37B$LCKnYr5LqN@lj)$ELn4ud95;L5n zpiX3{=$v5&x}~C4UNIQlWXQv46o-h$`D!8wGJ;CwAwCw}7KSCMPG$Dq<y^glLaD{# zPSW(Y-{be4bN=7?zLo(1MX})OG=N2{z|UX>IC21F09YR2OaS}xncf}vm!GBfm+xYK z6F_5WoHWKk`pmP>SjKc$#&DNaYR142RA$2GoQZdqI=>MxEdT(=vriZ!4l^HXh2=8v zvxctn&4K|2LKlHEWZ<3tPcSE7fKg~o8wc|+ByZ!M<H1nB2>BSmJg!|VnALr)c0TiO zug4TOmq~&E^gT58bKE<3eEs8E7lvlvbYNM_;(8tM{f<-aO~=<Nl`A4=rlzJsE|-Jj zNC^PX^Jr;lL0MTDYHJ(sEQ{V(?duuSJ3`fC1?w;F^?s<|pHf?M*m>T(c_M5yYi(_X zGBN3?*UZ%i7ly3wCf%5&QB<`Jp?+}Vl>5C)U$56)zA(Yv-Q6lwYV7UpMR@q)C*eUF zEqOj>Fw65enLjJz{Mn;3U*+lW6_Mvlmn7WDO6x3iz>u8V;<5g$+An1Kz0}guQky&- z-YG3D4fnde_Ad$9*g3dWZ2>p#4*9I%Xi0EzaIi)2o;`aY2m&^2*kG}}xw#qfv8v{; zR$ZwY6Zp*c9-IGqaC-{Nl0HXTN=gc-t-QS4V!O#?!nCPgeOX`h<c|s5S{A5ozj2-F z$O!B2??*&Lg!uirbLZgd>S_@lFel*os<^hzV*<DTIA22|8EzEbq@*Oo#l?YP7#urx z43(9Y;`4ALQ6?)|vg`&M3<juFD){*LAU8Kx<ZR!*9bH{rC@Cqy<jIrG*mi;2+uL#a z^l7xUwSgkAWy=;MCMKEzs;jG^R;v*h7${b?$jC^{o;@3Ofe#%zgocI&WM*cHla3}f zN&%J5f`S4>M@J($IoUq&)vH%gSXel!dWYwg)V^-rIs^p;*$1XWfF`k~UI{L#ojfP8 z*%fM<w5F!Us(Pu<P_-+SN^vcAgI}dyOKKlCFik}2&CAOZJt3iym!x*m!M1JNMyP#U zJeKlUR8)kfrY3}ig^6G3bUK8FhKl?9jTMkbj~+pxP=Me80RbXMuh(PEnl-4euZN3^ zi+u%ze`1iG{BD}s-``)n+6)W~h%YxcH*r5p;*ls-RaN5t?d$7<qoX67ot?#T9UUFm znfchZl4*Kuko~F+UA;ByOD{T8$%~$z9x;?uNUT{*OpF)}Pft%V@9F6o&)<@LR1vdi z$93@zX6-!I?T?M#s)|{fo}O;TpfZ@BpD*rdOGbM)Cnv{jaYmyNAs>W2{bX@X-n(!6 z{$mw<S>yf9@3%WA$z<jyBGqg<m5v-a0&j0`Gxn`pw~&yKAgbQa&(Ew)tJR|FV0*1H zs<_E2xaX($H54XGMrzK;$Pg!&Jg0+$wp4L(F%~RXFk)C;T^({Z9%xMc^k}11@U_yw zoj3K@CybU$5)EBzNH}D)2)Z6wI*+NnqM`!3^XpHnjIFq66?|UpwW;@y+qT_htxD}h z<E~x1P*;7wGJc7s#SFZBuW2pEz)`v-{IbI%{qOsFkBJi}+A!^_yJTl)qw}}-N+W{T zcMpPj_8FttC&W4kypIGvqn^96MRz=qw#0ZC9g~bPRwMU~|5E~k7dl7$Fu$PvF#g2P zKZIKn%Ihp7Dk=(dr)$2Q>OSYO*$;D?O#D~iscE4j%goRnSfMD&T^UW6x$!W$T+Sy& pUD@OWhi9xH+~thiB~=My_z#H5OR<hIvE~2(002ovPDHLkV1giSy+8l} literal 0 HcmV?d00001 diff --git a/static/img/index/zjgs.png b/static/img/index/zjgs.png new file mode 100644 index 0000000000000000000000000000000000000000..f344deea760067fc6cd348a5a333f98e9091bdb0 GIT binary patch literal 4358 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 A(EtDd literal 0 HcmV?d00001 diff --git a/static/img/index/zjhc.png b/static/img/index/zjhc.png new file mode 100644 index 0000000000000000000000000000000000000000..c6827c71f1d6350f3da53849761db45b5d414e95 GIT binary patch literal 3605 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 bS@B;1wiTXRtW{%<00000NkvXXu0mjfUjEx! literal 0 HcmV?d00001 diff --git a/static/img/user/account.png b/static/img/user/account.png new file mode 100644 index 0000000000000000000000000000000000000000..8e7b4bb16bced11013e8255a4f71dc48ab5daa43 GIT binary patch literal 1437 zcmV;O1!DS%P)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00001b5ch_0Itp) z=>Px)S4l)cRA@uhT5V{YMG$@_mrBs2Ig5xt(wqT>LWM-Ew4h=^!9Nx&C<+QztYERA zVno3Tf)*{L*dLJ=Y_SCe#efn0BUDsSP^_3jtA$o#Vhetu7t><Z<i__&cALGuyZ83) z_BF}=%e}j^^UOXoJ3A9WhC@|5DPBa*X0?sP7Objy0FMFK0^mv`{|5kX0oWxX#h80W z1E8wy03HDFGJwl!DEU+J`||(}h{#L~AW;CQ>dgT502uI;I&c2&@O~cy@VJP4+A4^a z0I2F!0A2uaucyc<W9hfe?{@$gawuh|j{tZ|L`bowMpFQcb3O^+nS?T~b?K~Blw7yg zpHoa#Ige|0cxyP1Yj*l;LJ&0!KvnNEJn!)~c!PjM<V@Pes(J;0SE`&E{e!f!rxv0= zf&r-Ndc*S#p5!mg#$N{QXeOy@e?qB)Rs4qju|-6VhRCZ6KvijJ@|<DO9j8eL5jhmH zQLQ<uY9kj<#vKI56a6_&eoX)lX9p9@Gq&4dIXfgGQ>`d(*P6X(r0rGwgFf?WE&r$r zKvi!6unz#Ej5`=LXqE3{DDKNp)q<hvX73xL6lqlvb}Im=>TL-xM?FiN0PskRxE%uA z7?6?4y;pehn?>Y9S4J5ClLma3Sl{xI*FGt9DAvsx0)TT2t>_=Txm|tw+;qeT@U#h$ zl)+=$C?XS~n`SZ3(CRY)Hf#@y$Zorr58!<Ox0{dTTuTuq79dr9C;_nF$ooJ<Hq`>~ z6@Yc-<GN(x*RhmnMUJXoml*blk#R&suCE2)8^>`5LNAjt<)$arVpbRqZ!J%!^%Jz` zhSAv*bV0c?Rh7<6-8jZX<l6WE#>YFbDz_I}ap9F4U-Te;FKt6#Us<k{c0$<$pz@dI z=s&c2z|h0^qqHd$MD_rtCSNbNypBNHhOn|{48MN60v(<GC<(*y!Ue^X=q@}MfR-UO zLkgWe-H(|P6EHh=Am6*Qs$#{-X92u;;ag6xL092;JrLOfC{DZv$X27oDDu5`rYcmN zcn6TX%$$A5_ikHc0Fx&#<V<aPBUTnZPgQ8@$MsNU3eIr~`QED+8^FH_E%=}Z{+aH= z<!jSRDDw{gmr;5qq1%e31E70#2Sol(Rp^)Fow%rLdMN=|ppeK0bgxc-nf-a@29#!* z+bkIh?PaPxuaAx(U${Hni6`G(2&cLq`NG4COQHGH;aF2)w7qR0B(=ykj8gN8iwvV^ z%MIat@hHw+JcCv3{m{~&6Fz|@bCD6ivQH%}h-I71xn4MzTFh<-WTRJ|K8&0Whola^ zQ#axR%l<**J}I|~$botx?*I61ieD^rail&eS$t(VgM*c~n_8(H#{j3QY?6HJ(2;ei zT~>CrvdXwqiL(s}mQ{jZP>~e34HFvFRtBlh8n+@CTG(Q~&vM2`F_fz(!!%4-8C>Jc ztzX!(lZ^m!&i1(MCsyp9$ck2`Ve%3H`(Cj7vSGr?V1)te6?Y#sFDSQ(K}!q+crF&L zOhu6X$7iRz7b}CL(C&tcyY3O2h6JtRm)FThI#f%=FB4(-%=8;3WB|+esST65{Q-wo zQ5q7CyeJKmU;xg>_m~p6-;iJ*m@T5zhJ<OD@I>$ToS9*pC;DbRCwIR$41kMG<(L%) zpR@E2(xHO0W(|wSTr=0fH-l@~TjrXEJNiula882_2|Ae{HCWlEqVJixJT;cd{e}ro zjXZC<VbT%+FYGSAMz<HOcyqE0?lvTRI{#*R%h4JpQBJ@E(ruJv_ul<y4~E4(u^NL_ rbD>7OKV+_|Ucz}IaxP?kGjsj}rpbs;;4Hjc00000NkvXXu0mjfOV^t$ literal 0 HcmV?d00001 diff --git a/static/img/user/avatar.png b/static/img/user/avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..595da44d9977a6d024ad9a51b21bbc6fec8756b2 GIT binary patch literal 6153 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 literal 0 HcmV?d00001 diff --git a/static/img/user/banner.jpg b/static/img/user/banner.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2a59eb3122fede7201a25ea6875b24f33ae0a0fa GIT binary patch literal 36056 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 OUH<^HTr@u2fB)GA@BmN% literal 0 HcmV?d00001 diff --git a/static/img/user/chat.png b/static/img/user/chat.png new file mode 100644 index 0000000000000000000000000000000000000000..15c0876f144c646262004c9680c83f546a40dbda GIT binary patch literal 1487 zcmV;=1u*)FP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00001b5ch_0Itp) z=>Px)iAh93RA@u(SzU-#RTTcdVIYH`JERa{jYCBv2`MQB1sMq`7!?W?Jv4#QJP4zZ zU}R9F)PoO!Mny&jJs1=T85sx}Bos|JQK6ABGYS!8%uEFp#jN%>?KAB2v+p_Q{>TXJ zmkamoz1I5HUjLi8$QLhi`O!82GwTI#7l7LVtOl?Qz)}F0dFA{L;3oj50es{39RqNJ z<e!~TUdsT?>}Ds}uoG%CfJRMWrU4vw=UycFRn4=h?X>}z*-c^$U~M~$gR=tiOshaD z12D5i0G<P|4S>uhToYcSp8_}oV1nd~HkjEJ0Imcu=*{dtZ)REiuK?Iy*R(1DFth6b zya!-K!1W&hZvog(^2<86VP>~@MF@-(lPvkk29i=prBw>R%x?4AT^vw63ScYAA1ZN) zY%;SOyg9B9ww#w=k^H0z?WO>{@DetOD2f~s{PilttL`zgN1U0-gp%73pLHZpl+lO+ zFf)b2FPw=anpY^?Sl3+21W3O7;Bdr;VEwH~wYuz<3juhCJ?R}!#3cN!Bqys1)XF|H z>j&_ecY2YKPpoR@*f9Vv`xq}Gy3VyEKW{~-x@UZTQ8Y`;X)nngF*-Q_{wl|U16w+6 z^JSpCPaO376zz2Tg^{iZfWoE1o7PdM`3+?ZyYb!K#QX9&Py4uwN_Q6VNdd@cU#7E? z<agZ=u#BdeUF&@yNt(KaoEc{Uyy3FwkSBh~3I9+Tqi%k8P94f~faGIYe)9l)<oP*> z`GttPDR7pycj!_7zryMglJbf96#`I6tWsC&49V*+q_8nw&Y{PH+G#z>k759xcOi49 z|EMx|`vL*5PubtgvYX`7F#w_0s}^~8fdF{GCDcP+mRwW}P3U`9U8?HTT0!#L6o9Gm zWkByf&;|h;gU#+h-#~HIICE|UHai4kFKGMG=zq0P85L+|BI9Y2%VRh2Tj0QpN&Ygw zaeRCc`g%?QRDI-^O<-zvIR*yOt>N_K;{a8d1<QtWAS%){Gj)4Yp4c?Wz8C;YK9Ec& zxao770DC8=MfPmO<x4+E-_+T!!BpR%&!cEuvnI8#22C@I$`+bX9Kf$<@5Ss~51U!s zjK<YzUzwgf32<fR2FB3nUzPeVMzcUERRM%1)JR&wIi^$N4X{Uom7CDa<Oq!FnVXwX zMVe-&a$fmK>paONF%vo!l+P+5($j)Je(c9zfA96$9mOTRJ8|h%#pSj9z_=AO`Nx5% z6IY;VW;gnAjO1?W#2>m^JZbG7bl(1U-efNPKW6r@@4dzRuv6^)u^Z6DVVD27kP=zg zn`r#<Yz)BNDxdwwg<49eu}U@jVH8r?DrZTiZ0bjAjY~RhU}hH9XU&T_F*mc<0BrGs z9CSVKqn#G24n3EJ!{(~cth4=q22N_KwN$namQ4lf2vlcpL2H|jj6i-mZDt<_bI&8r zjP9w!xD$K*#9JesBq8RaPr|3DC)O=!Dv-2tLnPnqgkV)^m{}MRl}ls^g*hiSRtalx zInMw|{dRqsnWCWvT!{jAk=zqwTy?NDOtQEn?jR&DylSpwCZrBg!`@UIS(pd1a+uju zP7AyJ|1XeyvV*ebNSWCtpK%l6)oZLFsbOz)HJgow0CKHdr@#d74o?I$CA^YW=_s=q zfG{U$BK=aQgih#qlTqcW$B$&`fkcz(QsH%=832VbJ=$sIrU9*j@fw%8C}C!wsbv3( zdULNkRaP@6RvVYtW4gtxLixYUuJfwrHFZ`@U+0C+J_;(rZEGc5#{k^n7#*)DXFJ;z z<JCX!DRb5slNf+7k+dUV?5yVg$@^Mmb15Pg12E#>uG$q|@muC0$CS_-6CUg2z_|30 puU!P)KL0;}Zfm%V{zZQS{{ltHC{Gfzkrn^|002ovPDHLkV1oJ+y*2;< literal 0 HcmV?d00001 diff --git a/static/img/user/code.png b/static/img/user/code.png new file mode 100644 index 0000000000000000000000000000000000000000..82157c2f51364673f483293f3b1e8f529329aa0e GIT binary patch literal 805 zcmV+=1KRwFP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00001b5ch_0Itp) z=>Px%-$_J4RA@u(S}|)>K@k4FppZh%F9=#_rJ$XH78bUGf>s7{f`S1{I|U<XXDKKs z7(puo25c=-C}<%>(8?4i`2|ry#gK6)?&J2J@7=7txA*SpEnIQz%<g<MyF0V{J)-4} zEw>*g01?>_;4**{0QLabmKgK~fG&V#0Cz~f8#G@j01=r4aMumiNo=q+`~rYElHZF2 z3juiJpL?%c6AYvO1%Q)9Aj$wd%z1thz-<64B!8r|SVVRJI0)d1o9|RG*8<7&`MLi9 zAR?-~5(r)+c`K#SWn+uTRk!DE2K@&}ehB4@0Ne*~!3!<9O?0{m<Fh&4A@UT!8UOzt z$vF$aM*w@h&@qy)8U}C}z-w>AD#?8ofS<upxs&AAh5_hl_|4m};g<7u3xH$-B+WcA z3~(Y+YE#i3_{RaPubm6_$lw5Qvx{B3AB939GDyEmvQJ)h>x;nWPqWyhX2$qN&R}Z# zi8o}{l_Qq^jN7PN^1(?|(-Knei#(3E9>9cjvM6}(Tl$@R6zYqBTCP!$2`%b=H<9D6 z@Y0{QWsq8gWo<R{e&9`wLQlWn8+uIkdB5HzROI^gwd)X>-|8agF*SYTuObv7TyF&O z6OIAcAs9zsppV8&sAWs2*-TvV7!{F*GjVkQS_d_pi7Vy-ABB<1#J&0`sC(?K884AG z5A^DzjeOk`Z{lFd^Z@Fzv}BwyfW!_qhaX2EbdixsO>Y_kY4605nY1d1ba&#&J8e|} zY45~FCar3G19#%8CP(Q1cc)PJxI3Ae(69pF36C#cF6wbNd|p37@{Kh~%6<_!>Zq=l zevYW;btU#Ld4)yCh@4Gnpf$E*jGhHl(*u$hL!F}6X{A-)2STGnF&)a&uCXNFk|+7z zmeHM-Cu93PLz<C%8MUP9aU`Egww`7UK(v$S&J}MOPW{wCU!aHisezK)`l&%5t(sqo jBf){i?vEn5)&uwjn(_`$o{n6@00000NkvXXu0mjf<2G=> literal 0 HcmV?d00001 diff --git a/static/img/user/img.png b/static/img/user/img.png new file mode 100644 index 0000000000000000000000000000000000000000..b023b41df0504005e7d9987c3fa0e41fcd925839 GIT binary patch literal 2538 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 AVE_OC literal 0 HcmV?d00001 diff --git a/static/img/user/link.png b/static/img/user/link.png new file mode 100644 index 0000000000000000000000000000000000000000..ab38b598f957f7dc96d20163dd21f75b6db2861d GIT binary patch literal 1120 zcmV-m1fTnfP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00001b5ch_0Itp) z=>Px(8c9S!RA@upT1|*eVHkd%vanEQVPWB?DP@FWp=`v0kcE+%Bnyf?Wn|<EF&P;( z_QVuf7@-uRC>v$Lf{apX%z~PjDGLkcb)MUIuJ4|E&%NiKJLmrRHh1p1-}%1x`M&pk z-oGK*e6Y>s$Nvq$%vJ(e4`3mHISsz8-vA~6ydwEAZtt=;U}pOP^aJQ>u+VYay#+8# zawIyT2!NUCq^}34x3!4iQ8AtY*h_LU{GkBA%oYH689DVS051TH1vm1m!JwqF(f}Ri zJa2EwvWet3J@WxP1+dvol>Q9|dVikebb|#i+Rn`81IPh5;@RP2lG_UaoX+3ygzq8w zpqYY4_cpVg07g9!2V@iZ8wh@U17MANX^7;pRuVpx#mp`N=yTu4NUornEq6Mu=RW}~ zYPQ<M9iEwW0QelJw>~vi34kawRG(3jy{&lq-Ywh%(Cxk-5`dF3R`)<gd#c3&TyQ>0 zV&(+kbnw?1C-fPTXR85lSGm#Aoaj!}_JpKVFw;>YZ!G}KOu`;^v=-`ND6o8nZt@CA z(QqasH2^349Z&z_1iTNRH!BeJ05}?W#34`WPaZGJ0fscJDSad_XK@9o0GOFv`4^AA z1mr|`%I<b#Tp>qGojOVCv;3GE-vhF8uxK-1@ndELot=d+CM|&7?zAP0BExl26>%Uw z09fWE?vC~5)8kuVc%1BptREdqFM2duOgc%7I6}3tp4!ZOit8s?*tkg8>4_b%nTN>$ zAcu}61N8zp4q(tNGDz}7xy>W4K1Z^!amoB7Dg>Lv_dnNWLxmJbiCr{gyenc<=(T6e z3JC8eJmtjG(%L{Y21#PXi?eWSsQ~DB%q&WyGG=8+a#}r}@g}3uBq}spEaU*l3;Qe+ zckK!*R#$XnO<<u~bLEoUwP`GrAXk<ORn1+S#zJP6C|9nQyC$ZYDidom;xxH(T<+Q| z0I722a=B|N6{?4Y>dBSM=C0)X0*FSiP`$abnHA@*<h6eQ8W0QBn=3~GyVAHQi6g5g z7BaJafp@*>PAy+Vs>KlNeLrrmG%WO+<gHqS_d;KlY*N1;6Y-Yh1AbX0HdIanAd7d* z?3^nqX!(3u<zP^ayXrQ(PjXil8_(97LDNNr1qF{92LwP}n0M|$^;~wgBuTN3qi5yj zVY$64Ebc1G3=`~jDek@va_+Su0KYz(kMG;6yzb)tM_y|wY;X)zERCd4gIZs4Y9pfY z8ywF_ZqZtPhek2~^GJ(HwN8~kl%!-<E{p*+*U7TUt4C?7WZ4k*$)Zg@^~r8~t)^NJ zEbI9?OgYtjf2`;R{GDAKq$~5wg;@(%X?>l#d{pH&Yjgz{k*d+Xs7a0PuddPkFOrQ2 m`G5bbK{ItG>E3OB1AhT5hpX=>7D%B00000<MNUMnLSTXwXavas literal 0 HcmV?d00001 diff --git a/static/img/user/money_log.png b/static/img/user/money_log.png new file mode 100644 index 0000000000000000000000000000000000000000..9a9acb181364ea5f15afb9a3c478c3e53da8f642 GIT binary patch literal 1782 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 YA3I}o`*^w*SO5S307*qoM6N<$g4&Qm{r~^~ literal 0 HcmV?d00001 diff --git a/static/img/user/noshop.png b/static/img/user/noshop.png new file mode 100644 index 0000000000000000000000000000000000000000..ec6598eb5ffba08783adb5bb4fbacd63a6643132 GIT binary patch literal 24192 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 literal 0 HcmV?d00001 diff --git a/static/img/user/order_all.png b/static/img/user/order_all.png new file mode 100644 index 0000000000000000000000000000000000000000..ccc7beac03ab5439cfd1fd3a21eca3307de1e5b8 GIT binary patch literal 3461 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 literal 0 HcmV?d00001 diff --git a/static/img/user/order_finish.png b/static/img/user/order_finish.png new file mode 100644 index 0000000000000000000000000000000000000000..f925f7426211eea560136048f2ebda8c2de78029 GIT binary patch literal 4310 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 Ef|f>M$N&HU literal 0 HcmV?d00001 diff --git a/static/img/user/order_ing.png b/static/img/user/order_ing.png new file mode 100644 index 0000000000000000000000000000000000000000..2273d0087b3747892ede3abc32e0cf33702be1c1 GIT binary patch literal 4083 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 literal 0 HcmV?d00001 diff --git a/static/img/user/topbg.png b/static/img/user/topbg.png new file mode 100644 index 0000000000000000000000000000000000000000..0a6765ca7dbb5a8f296b6766150bed898baeb156 GIT binary patch literal 111007 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 Q3GvZT)qPd}!Y1PX0Fm?z+W-In literal 0 HcmV?d00001 diff --git a/static/json/city.json b/static/json/city.json new file mode 100644 index 0000000..11f9902 --- /dev/null +++ b/static/json/city.json @@ -0,0 +1,7904 @@ +[{ + "name": "北京", + "city": [{ + "name": "北京市", + "area": [{ + "name": "东城区" + }, { + "name": "西城区" + }, { + "name": "朝阳区" + }, { + "name": "丰台区" + }, { + "name": "石景山区" + }, { + "name": "海淀区" + }, { + "name": "门头沟区" + }, { + "name": "房山区" + }, { + "name": "通州区" + }, { + "name": "顺义区" + }, { + "name": "昌平区" + }, { + "name": "大兴区" + }, { + "name": "怀柔区" + }, { + "name": "平谷区" + }, { + "name": "密云县" + }, { + "name": "延庆县" + }] + }] +}, { + "name": "天津", + "city": [{ + "name": "天津市", + "area": [{ + "name": "和平区" + }, { + "name": "河东区" + }, { + "name": "河西区" + }, { + "name": "南开区" + }, { + "name": "河北区" + }, { + "name": "红桥区" + }, { + "name": "东丽区" + }, { + "name": "西青区" + }, { + "name": "津南区" + }, { + "name": "北辰区" + }, { + "name": "武清区" + }, { + "name": "宝坻区" + }, { + "name": "滨海新区" + }, { + "name": "宁河县" + }, { + "name": "静海县" + }, { + "name": "蓟县" + }] + }] +}, { + "name": "河北省", + "city": [{ + "name": "石家庄市", + "area": [{ + "name": "长安区" + }, { + "name": "桥西区" + }, { + "name": "新华区" + }, { + "name": "井陉矿区" + }, { + "name": "裕华区" + }, { + "name": "藁城区" + }, { + "name": "鹿泉区" + }, { + "name": "栾城区" + }, { + "name": "井陉县" + }, { + "name": "正定县" + }, { + "name": "行唐县" + }, { + "name": "灵寿县" + }, { + "name": "高邑县" + }, { + "name": "深泽县" + }, { + "name": "赞皇县" + }, { + "name": "无极县" + }, { + "name": "平山县" + }, { + "name": "元氏县" + }, { + "name": "赵县" + }, { + "name": "辛集市" + }, { + "name": "晋州市" + }, { + "name": "新乐市" + }] + }, { + "name": "唐山市", + "area": [{ + "name": "路南区" + }, { + "name": "路北区" + }, { + "name": "古冶区" + }, { + "name": "开平区" + }, { + "name": "丰南区" + }, { + "name": "丰润区" + }, { + "name": "曹妃甸区" + }, { + "name": "滦县" + }, { + "name": "滦南县" + }, { + "name": "乐亭县" + }, { + "name": "迁西县" + }, { + "name": "玉田县" + }, { + "name": "遵化市" + }, { + "name": "迁安市" + }] + }, { + "name": "秦皇岛市", + "area": [{ + "name": "海港区" + }, { + "name": "山海关区" + }, { + "name": "北戴河区" + }, { + "name": "青龙满族自治县" + }, { + "name": "昌黎县" + }, { + "name": "抚宁县" + }, { + "name": "卢龙县" + }] + }, { + "name": "邯郸市", + "area": [{ + "name": "邯山区" + }, { + "name": "丛台区" + }, { + "name": "复兴区" + }, { + "name": "峰峰矿区" + }, { + "name": "邯郸县" + }, { + "name": "临漳县" + }, { + "name": "成安县" + }, { + "name": "大名县" + }, { + "name": "涉县" + }, { + "name": "磁县" + }, { + "name": "肥乡县" + }, { + "name": "永年县" + }, { + "name": "邱县" + }, { + "name": "鸡泽县" + }, { + "name": "广平县" + }, { + "name": "馆陶县" + }, { + "name": "魏县" + }, { + "name": "曲周县" + }, { + "name": "武安市" + }] + }, { + "name": "邢台市", + "area": [{ + "name": "桥东区" + }, { + "name": "桥西区" + }, { + "name": "邢台县" + }, { + "name": "临城县" + }, { + "name": "内丘县" + }, { + "name": "柏乡县" + }, { + "name": "隆尧县" + }, { + "name": "任县" + }, { + "name": "南和县" + }, { + "name": "宁晋县" + }, { + "name": "巨鹿县" + }, { + "name": "新河县" + }, { + "name": "广宗县" + }, { + "name": "平乡县" + }, { + "name": "威县" + }, { + "name": "清河县" + }, { + "name": "临西县" + }, { + "name": "南宫市" + }, { + "name": "沙河市" + }] + }, { + "name": "保定市", + "area": [{ + "name": "新市区" + }, { + "name": "北市区" + }, { + "name": "南市区" + }, { + "name": "满城县" + }, { + "name": "清苑县" + }, { + "name": "涞水县" + }, { + "name": "阜平县" + }, { + "name": "徐水县" + }, { + "name": "定兴县" + }, { + "name": "唐县" + }, { + "name": "高阳县" + }, { + "name": "容城县" + }, { + "name": "涞源县" + }, { + "name": "望都县" + }, { + "name": "安新县" + }, { + "name": "易县" + }, { + "name": "曲阳县" + }, { + "name": "蠡县" + }, { + "name": "顺平县" + }, { + "name": "博野县" + }, { + "name": "雄县" + }, { + "name": "涿州市" + }, { + "name": "定州市" + }, { + "name": "安国市" + }, { + "name": "高碑店市" + }] + }, { + "name": "张家口市", + "area": [{ + "name": "桥东区" + }, { + "name": "桥西区" + }, { + "name": "宣化区" + }, { + "name": "下花园区" + }, { + "name": "宣化县" + }, { + "name": "张北县" + }, { + "name": "康保县" + }, { + "name": "沽源县" + }, { + "name": "尚义县" + }, { + "name": "蔚县" + }, { + "name": "阳原县" + }, { + "name": "怀安县" + }, { + "name": "万全县" + }, { + "name": "怀来县" + }, { + "name": "涿鹿县" + }, { + "name": "赤城县" + }, { + "name": "崇礼县" + }] + }, { + "name": "承德市", + "area": [{ + "name": "双桥区" + }, { + "name": "双滦区" + }, { + "name": "鹰手营子矿区" + }, { + "name": "承德县" + }, { + "name": "兴隆县" + }, { + "name": "平泉县" + }, { + "name": "滦平县" + }, { + "name": "隆化县" + }, { + "name": "丰宁满族自治县" + }, { + "name": "宽城满族自治县" + }, { + "name": "围场满族蒙古族自治县" + }] + }, { + "name": "沧州市", + "area": [{ + "name": "新华区" + }, { + "name": "运河区" + }, { + "name": "沧县" + }, { + "name": "青县" + }, { + "name": "东光县" + }, { + "name": "海兴县" + }, { + "name": "盐山县" + }, { + "name": "肃宁县" + }, { + "name": "南皮县" + }, { + "name": "吴桥县" + }, { + "name": "献县" + }, { + "name": "孟村回族自治县" + }, { + "name": "泊头市" + }, { + "name": "任丘市" + }, { + "name": "黄骅市" + }, { + "name": "河间市" + }] + }, { + "name": "廊坊市", + "area": [{ + "name": "安次区" + }, { + "name": "广阳区" + }, { + "name": "固安县" + }, { + "name": "永清县" + }, { + "name": "香河县" + }, { + "name": "大城县" + }, { + "name": "文安县" + }, { + "name": "大厂回族自治县" + }, { + "name": "霸州市" + }, { + "name": "三河市" + }] + }, { + "name": "衡水市", + "area": [{ + "name": "桃城区" + }, { + "name": "枣强县" + }, { + "name": "武邑县" + }, { + "name": "武强县" + }, { + "name": "饶阳县" + }, { + "name": "安平县" + }, { + "name": "故城县" + }, { + "name": "景县" + }, { + "name": "阜城县" + }, { + "name": "冀州市" + }, { + "name": "深州市" + }] + }] +}, { + "name": "山西省", + "city": [{ + "name": "太原市", + "area": [{ + "name": "小店区" + }, { + "name": "迎泽区" + }, { + "name": "杏花岭区" + }, { + "name": "尖草坪区" + }, { + "name": "万柏林区" + }, { + "name": "晋源区" + }, { + "name": "清徐县" + }, { + "name": "阳曲县" + }, { + "name": "娄烦县" + }, { + "name": "古交市" + }] + }, { + "name": "大同市", + "area": [{ + "name": "城区" + }, { + "name": "矿区" + }, { + "name": "南郊区" + }, { + "name": "新荣区" + }, { + "name": "阳高县" + }, { + "name": "天镇县" + }, { + "name": "广灵县" + }, { + "name": "灵丘县" + }, { + "name": "浑源县" + }, { + "name": "左云县" + }, { + "name": "大同县" + }] + }, { + "name": "阳泉市", + "area": [{ + "name": "城区" + }, { + "name": "矿区" + }, { + "name": "郊区" + }, { + "name": "平定县" + }, { + "name": "盂县" + }] + }, { + "name": "长治市", + "area": [{ + "name": "城区" + }, { + "name": "郊区" + }, { + "name": "长治县" + }, { + "name": "襄垣县" + }, { + "name": "屯留县" + }, { + "name": "平顺县" + }, { + "name": "黎城县" + }, { + "name": "壶关县" + }, { + "name": "长子县" + }, { + "name": "武乡县" + }, { + "name": "沁县" + }, { + "name": "沁源县" + }, { + "name": "潞城市" + }] + }, { + "name": "晋城市", + "area": [{ + "name": "城区" + }, { + "name": "沁水县" + }, { + "name": "阳城县" + }, { + "name": "陵川县" + }, { + "name": "泽州县" + }, { + "name": "高平市" + }] + }, { + "name": "朔州市", + "area": [{ + "name": "朔城区" + }, { + "name": "平鲁区" + }, { + "name": "山阴县" + }, { + "name": "应县" + }, { + "name": "右玉县" + }, { + "name": "怀仁县" + }] + }, { + "name": "晋中市", + "area": [{ + "name": "榆次区" + }, { + "name": "榆社县" + }, { + "name": "左权县" + }, { + "name": "和顺县" + }, { + "name": "昔阳县" + }, { + "name": "寿阳县" + }, { + "name": "太谷县" + }, { + "name": "祁县" + }, { + "name": "平遥县" + }, { + "name": "灵石县" + }, { + "name": "介休市" + }] + }, { + "name": "运城市", + "area": [{ + "name": "盐湖区" + }, { + "name": "临猗县" + }, { + "name": "万荣县" + }, { + "name": "闻喜县" + }, { + "name": "稷山县" + }, { + "name": "新绛县" + }, { + "name": "绛县" + }, { + "name": "垣曲县" + }, { + "name": "夏县" + }, { + "name": "平陆县" + }, { + "name": "芮城县" + }, { + "name": "永济市" + }, { + "name": "河津市" + }] + }, { + "name": "忻州市", + "area": [{ + "name": "忻府区" + }, { + "name": "定襄县" + }, { + "name": "五台县" + }, { + "name": "代县" + }, { + "name": "繁峙县" + }, { + "name": "宁武县" + }, { + "name": "静乐县" + }, { + "name": "神池县" + }, { + "name": "五寨县" + }, { + "name": "岢岚县" + }, { + "name": "河曲县" + }, { + "name": "保德县" + }, { + "name": "偏关县" + }, { + "name": "原平市" + }] + }, { + "name": "临汾市", + "area": [{ + "name": "尧都区" + }, { + "name": "曲沃县" + }, { + "name": "翼城县" + }, { + "name": "襄汾县" + }, { + "name": "洪洞县" + }, { + "name": "古县" + }, { + "name": "安泽县" + }, { + "name": "浮山县" + }, { + "name": "吉县" + }, { + "name": "乡宁县" + }, { + "name": "大宁县" + }, { + "name": "隰县" + }, { + "name": "永和县" + }, { + "name": "蒲县" + }, { + "name": "汾西县" + }, { + "name": "侯马市" + }, { + "name": "霍州市" + }] + }, { + "name": "吕梁市", + "area": [{ + "name": "离石区" + }, { + "name": "文水县" + }, { + "name": "交城县" + }, { + "name": "兴县" + }, { + "name": "临县" + }, { + "name": "柳林县" + }, { + "name": "石楼县" + }, { + "name": "岚县" + }, { + "name": "方山县" + }, { + "name": "中阳县" + }, { + "name": "交口县" + }, { + "name": "孝义市" + }, { + "name": "汾阳市" + }] + }] +}, { + "name": "内蒙古自治区", + "city": [{ + "name": "呼和浩特市", + "area": [{ + "name": "新城区" + }, { + "name": "回民区" + }, { + "name": "玉泉区" + }, { + "name": "赛罕区" + }, { + "name": "土默特左旗" + }, { + "name": "托克托县" + }, { + "name": "和林格尔县" + }, { + "name": "清水河县" + }, { + "name": "武川县" + }] + }, { + "name": "包头市", + "area": [{ + "name": "东河区" + }, { + "name": "昆都仑区" + }, { + "name": "青山区" + }, { + "name": "石拐区" + }, { + "name": "白云鄂博矿区" + }, { + "name": "九原区" + }, { + "name": "土默特右旗" + }, { + "name": "固阳县" + }, { + "name": "达尔罕茂明安联合旗" + }] + }, { + "name": "乌海市", + "area": [{ + "name": "海勃湾区" + }, { + "name": "海南区" + }, { + "name": "乌达区" + }] + }, { + "name": "赤峰市", + "area": [{ + "name": "红山区" + }, { + "name": "元宝山区" + }, { + "name": "松山区" + }, { + "name": "阿鲁科尔沁旗" + }, { + "name": "巴林左旗" + }, { + "name": "巴林右旗" + }, { + "name": "林西县" + }, { + "name": "克什克腾旗" + }, { + "name": "翁牛特旗" + }, { + "name": "喀喇沁旗" + }, { + "name": "宁城县" + }, { + "name": "敖汉旗" + }] + }, { + "name": "通辽市", + "area": [{ + "name": "科尔沁区" + }, { + "name": "科尔沁左翼中旗" + }, { + "name": "科尔沁左翼后旗" + }, { + "name": "开鲁县" + }, { + "name": "库伦旗" + }, { + "name": "奈曼旗" + }, { + "name": "扎鲁特旗" + }, { + "name": "霍林郭勒市" + }] + }, { + "name": "鄂尔多斯市", + "area": [{ + "name": "东胜区" + }, { + "name": "达拉特旗" + }, { + "name": "准格尔旗" + }, { + "name": "鄂托克前旗" + }, { + "name": "鄂托克旗" + }, { + "name": "杭锦旗" + }, { + "name": "乌审旗" + }, { + "name": "伊金霍洛旗" + }] + }, { + "name": "呼伦贝尔市", + "area": [{ + "name": "海拉尔区" + }, { + "name": "扎赉诺尔区" + }, { + "name": "阿荣旗" + }, { + "name": "莫力达瓦达斡尔族自治旗" + }, { + "name": "鄂伦春自治旗" + }, { + "name": "鄂温克族自治旗" + }, { + "name": "陈巴尔虎旗" + }, { + "name": "新巴尔虎左旗" + }, { + "name": "新巴尔虎右旗" + }, { + "name": "满洲里市" + }, { + "name": "牙克石市" + }, { + "name": "扎兰屯市" + }, { + "name": "额尔古纳市" + }, { + "name": "根河市" + }] + }, { + "name": "巴彦淖尔市", + "area": [{ + "name": "临河区" + }, { + "name": "五原县" + }, { + "name": "磴口县" + }, { + "name": "乌拉特前旗" + }, { + "name": "乌拉特中旗" + }, { + "name": "乌拉特后旗" + }, { + "name": "杭锦后旗" + }] + }, { + "name": "乌兰察布市", + "area": [{ + "name": "集宁区" + }, { + "name": "卓资县" + }, { + "name": "化德县" + }, { + "name": "商都县" + }, { + "name": "兴和县" + }, { + "name": "凉城县" + }, { + "name": "察哈尔右翼前旗" + }, { + "name": "察哈尔右翼中旗" + }, { + "name": "察哈尔右翼后旗" + }, { + "name": "四子王旗" + }, { + "name": "丰镇市" + }] + }, { + "name": "兴安盟", + "area": [{ + "name": "乌兰浩特市" + }, { + "name": "阿尔山市" + }, { + "name": "科尔沁右翼前旗" + }, { + "name": "科尔沁右翼中旗" + }, { + "name": "扎赉特旗" + }, { + "name": "突泉县" + }] + }, { + "name": "锡林郭勒盟", + "area": [{ + "name": "二连浩特市" + }, { + "name": "锡林浩特市" + }, { + "name": "阿巴嘎旗" + }, { + "name": "苏尼特左旗" + }, { + "name": "苏尼特右旗" + }, { + "name": "东乌珠穆沁旗" + }, { + "name": "西乌珠穆沁旗" + }, { + "name": "太仆寺旗" + }, { + "name": "镶黄旗" + }, { + "name": "正镶白旗" + }, { + "name": "正蓝旗" + }, { + "name": "多伦县" + }] + }, { + "name": "阿拉善盟", + "area": [{ + "name": "阿拉善左旗" + }, { + "name": "阿拉善右旗" + }, { + "name": "额济纳旗" + }] + }] +}, { + "name": "辽宁省", + "city": [{ + "name": "沈阳市", + "area": [{ + "name": "和平区" + }, { + "name": "沈河区" + }, { + "name": "大东区" + }, { + "name": "皇姑区" + }, { + "name": "铁西区" + }, { + "name": "苏家屯区" + }, { + "name": "浑南区" + }, { + "name": "沈北新区" + }, { + "name": "于洪区" + }, { + "name": "辽中县" + }, { + "name": "康平县" + }, { + "name": "法库县" + }, { + "name": "新民市" + }] + }, { + "name": "大连市", + "area": [{ + "name": "中山区" + }, { + "name": "西岗区" + }, { + "name": "沙河口区" + }, { + "name": "甘井子区" + }, { + "name": "旅顺口区" + }, { + "name": "金州区" + }, { + "name": "长海县" + }, { + "name": "瓦房店市" + }, { + "name": "普兰店市" + }, { + "name": "庄河市" + }] + }, { + "name": "鞍山市", + "area": [{ + "name": "铁东区" + }, { + "name": "铁西区" + }, { + "name": "立山区" + }, { + "name": "千山区" + }, { + "name": "台安县" + }, { + "name": "岫岩满族自治县" + }, { + "name": "海城市" + }] + }, { + "name": "抚顺市", + "area": [{ + "name": "新抚区" + }, { + "name": "东洲区" + }, { + "name": "望花区" + }, { + "name": "顺城区" + }, { + "name": "抚顺县" + }, { + "name": "新宾满族自治县" + }, { + "name": "清原满族自治县" + }] + }, { + "name": "本溪市", + "area": [{ + "name": "平山区" + }, { + "name": "溪湖区" + }, { + "name": "明山区" + }, { + "name": "南芬区" + }, { + "name": "本溪满族自治县" + }, { + "name": "桓仁满族自治县" + }] + }, { + "name": "丹东市", + "area": [{ + "name": "元宝区" + }, { + "name": "振兴区" + }, { + "name": "振安区" + }, { + "name": "宽甸满族自治县" + }, { + "name": "东港市" + }, { + "name": "凤城市" + }] + }, { + "name": "锦州市", + "area": [{ + "name": "古塔区" + }, { + "name": "凌河区" + }, { + "name": "太和区" + }, { + "name": "黑山县" + }, { + "name": "义县" + }, { + "name": "凌海市" + }, { + "name": "北镇市" + }] + }, { + "name": "营口市", + "area": [{ + "name": "站前区" + }, { + "name": "西市区" + }, { + "name": "鲅鱼圈区" + }, { + "name": "老边区" + }, { + "name": "盖州市" + }, { + "name": "大石桥市" + }] + }, { + "name": "阜新市", + "area": [{ + "name": "海州区" + }, { + "name": "新邱区" + }, { + "name": "太平区" + }, { + "name": "清河门区" + }, { + "name": "细河区" + }, { + "name": "阜新蒙古族自治县" + }, { + "name": "彰武县" + }] + }, { + "name": "辽阳市", + "area": [{ + "name": "白塔区" + }, { + "name": "文圣区" + }, { + "name": "宏伟区" + }, { + "name": "弓长岭区" + }, { + "name": "太子河区" + }, { + "name": "辽阳县" + }, { + "name": "灯塔市" + }] + }, { + "name": "盘锦市", + "area": [{ + "name": "双台子区" + }, { + "name": "兴隆台区" + }, { + "name": "大洼县" + }, { + "name": "盘山县" + }] + }, { + "name": "铁岭市", + "area": [{ + "name": "银州区" + }, { + "name": "清河区" + }, { + "name": "铁岭县" + }, { + "name": "西丰县" + }, { + "name": "昌图县" + }, { + "name": "调兵山市" + }, { + "name": "开原市" + }] + }, { + "name": "朝阳市", + "area": [{ + "name": "双塔区" + }, { + "name": "龙城区" + }, { + "name": "朝阳县" + }, { + "name": "建平县" + }, { + "name": "喀喇沁左翼蒙古族自治县" + }, { + "name": "北票市" + }, { + "name": "凌源市" + }] + }, { + "name": "葫芦岛市", + "area": [{ + "name": "连山区" + }, { + "name": "龙港区" + }, { + "name": "南票区" + }, { + "name": "绥中县" + }, { + "name": "建昌县" + }, { + "name": "兴城市" + }] + }, { + "name": "金普新区", + "area": [{ + "name": "金州新区" + }, { + "name": "普湾新区" + }, { + "name": "保税区" + }] + }] +}, { + "name": "吉林省", + "city": [{ + "name": "长春市", + "area": [{ + "name": "南关区" + }, { + "name": "宽城区" + }, { + "name": "朝阳区" + }, { + "name": "二道区" + }, { + "name": "绿园区" + }, { + "name": "双阳区" + }, { + "name": "九台区" + }, { + "name": "农安县" + }, { + "name": "榆树市" + }, { + "name": "德惠市" + }] + }, { + "name": "吉林市", + "area": [{ + "name": "昌邑区" + }, { + "name": "龙潭区" + }, { + "name": "船营区" + }, { + "name": "丰满区" + }, { + "name": "永吉县" + }, { + "name": "蛟河市" + }, { + "name": "桦甸市" + }, { + "name": "舒兰市" + }, { + "name": "磐石市" + }] + }, { + "name": "四平市", + "area": [{ + "name": "铁西区" + }, { + "name": "铁东区" + }, { + "name": "梨树县" + }, { + "name": "伊通满族自治县" + }, { + "name": "公主岭市" + }, { + "name": "双辽市" + }] + }, { + "name": "辽源市", + "area": [{ + "name": "龙山区" + }, { + "name": "西安区" + }, { + "name": "东丰县" + }, { + "name": "东辽县" + }] + }, { + "name": "通化市", + "area": [{ + "name": "东昌区" + }, { + "name": "二道江区" + }, { + "name": "通化县" + }, { + "name": "辉南县" + }, { + "name": "柳河县" + }, { + "name": "梅河口市" + }, { + "name": "集安市" + }] + }, { + "name": "白山市", + "area": [{ + "name": "浑江区" + }, { + "name": "江源区" + }, { + "name": "抚松县" + }, { + "name": "靖宇县" + }, { + "name": "长白朝鲜族自治县" + }, { + "name": "临江市" + }] + }, { + "name": "松原市", + "area": [{ + "name": "宁江区" + }, { + "name": "前郭尔罗斯蒙古族自治县" + }, { + "name": "长岭县" + }, { + "name": "乾安县" + }, { + "name": "扶余市" + }] + }, { + "name": "白城市", + "area": [{ + "name": "洮北区" + }, { + "name": "镇赉县" + }, { + "name": "通榆县" + }, { + "name": "洮南市" + }, { + "name": "大安市" + }] + }, { + "name": "延边朝鲜族自治州", + "area": [{ + "name": "延吉市" + }, { + "name": "图们市" + }, { + "name": "敦化市" + }, { + "name": "珲春市" + }, { + "name": "龙井市" + }, { + "name": "和龙市" + }, { + "name": "汪清县" + }, { + "name": "安图县" + }] + }] +}, { + "name": "黑龙江省", + "city": [{ + "name": "哈尔滨市", + "area": [{ + "name": "道里区" + }, { + "name": "南岗区" + }, { + "name": "道外区" + }, { + "name": "平房区" + }, { + "name": "松北区" + }, { + "name": "香坊区" + }, { + "name": "呼兰区" + }, { + "name": "阿城区" + }, { + "name": "双城区" + }, { + "name": "依兰县" + }, { + "name": "方正县" + }, { + "name": "宾县" + }, { + "name": "巴彦县" + }, { + "name": "木兰县" + }, { + "name": "通河县" + }, { + "name": "延寿县" + }, { + "name": "尚志市" + }, { + "name": "五常市" + }] + }, { + "name": "齐齐哈尔市", + "area": [{ + "name": "龙沙区" + }, { + "name": "建华区" + }, { + "name": "铁锋区" + }, { + "name": "昂昂溪区" + }, { + "name": "富拉尔基区" + }, { + "name": "碾子山区" + }, { + "name": "梅里斯达斡尔族区" + }, { + "name": "龙江县" + }, { + "name": "依安县" + }, { + "name": "泰来县" + }, { + "name": "甘南县" + }, { + "name": "富裕县" + }, { + "name": "克山县" + }, { + "name": "克东县" + }, { + "name": "拜泉县" + }, { + "name": "讷河市" + }] + }, { + "name": "鸡西市", + "area": [{ + "name": "鸡冠区" + }, { + "name": "恒山区" + }, { + "name": "滴道区" + }, { + "name": "梨树区" + }, { + "name": "城子河区" + }, { + "name": "麻山区" + }, { + "name": "鸡东县" + }, { + "name": "虎林市" + }, { + "name": "密山市" + }] + }, { + "name": "鹤岗市", + "area": [{ + "name": "向阳区" + }, { + "name": "工农区" + }, { + "name": "南山区" + }, { + "name": "兴安区" + }, { + "name": "东山区" + }, { + "name": "兴山区" + }, { + "name": "萝北县" + }, { + "name": "绥滨县" + }] + }, { + "name": "双鸭山市", + "area": [{ + "name": "尖山区" + }, { + "name": "岭东区" + }, { + "name": "四方台区" + }, { + "name": "宝山区" + }, { + "name": "集贤县" + }, { + "name": "友谊县" + }, { + "name": "宝清县" + }, { + "name": "饶河县" + }] + }, { + "name": "大庆市", + "area": [{ + "name": "萨尔图区" + }, { + "name": "龙凤区" + }, { + "name": "让胡路区" + }, { + "name": "红岗区" + }, { + "name": "大同区" + }, { + "name": "肇州县" + }, { + "name": "肇源县" + }, { + "name": "林甸县" + }, { + "name": "杜尔伯特蒙古族自治县" + }] + }, { + "name": "伊春市", + "area": [{ + "name": "伊春区" + }, { + "name": "南岔区" + }, { + "name": "友好区" + }, { + "name": "西林区" + }, { + "name": "翠峦区" + }, { + "name": "新青区" + }, { + "name": "美溪区" + }, { + "name": "金山屯区" + }, { + "name": "五营区" + }, { + "name": "乌马河区" + }, { + "name": "汤旺河区" + }, { + "name": "带岭区" + }, { + "name": "乌伊岭区" + }, { + "name": "红星区" + }, { + "name": "上甘岭区" + }, { + "name": "嘉荫县" + }, { + "name": "铁力市" + }] + }, { + "name": "佳木斯市", + "area": [{ + "name": "向阳区" + }, { + "name": "前进区" + }, { + "name": "东风区" + }, { + "name": "郊区" + }, { + "name": "桦南县" + }, { + "name": "桦川县" + }, { + "name": "汤原县" + }, { + "name": "抚远县" + }, { + "name": "同江市" + }, { + "name": "富锦市" + }] + }, { + "name": "七台河市", + "area": [{ + "name": "新兴区" + }, { + "name": "桃山区" + }, { + "name": "茄子河区" + }, { + "name": "勃利县" + }] + }, { + "name": "牡丹江市", + "area": [{ + "name": "东安区" + }, { + "name": "阳明区" + }, { + "name": "爱民区" + }, { + "name": "西安区" + }, { + "name": "东宁县" + }, { + "name": "林口县" + }, { + "name": "绥芬河市" + }, { + "name": "海林市" + }, { + "name": "宁安市" + }, { + "name": "穆棱市" + }] + }, { + "name": "黑河市", + "area": [{ + "name": "爱辉区" + }, { + "name": "嫩江县" + }, { + "name": "逊克县" + }, { + "name": "孙吴县" + }, { + "name": "北安市" + }, { + "name": "五大连池市" + }] + }, { + "name": "绥化市", + "area": [{ + "name": "北林区" + }, { + "name": "望奎县" + }, { + "name": "兰西县" + }, { + "name": "青冈县" + }, { + "name": "庆安县" + }, { + "name": "明水县" + }, { + "name": "绥棱县" + }, { + "name": "安达市" + }, { + "name": "肇东市" + }, { + "name": "海伦市" + }] + }, { + "name": "大兴安岭地区", + "area": [{ + "name": "加格达奇区" + }, { + "name": "新林区" + }, { + "name": "松岭区" + }, { + "name": "呼中区" + }, { + "name": "呼玛县" + }, { + "name": "塔河县" + }, { + "name": "漠河县" + }] + }] +}, { + "name": "上海", + "city": [{ + "name": "上海市", + "area": [{ + "name": "黄浦区" + }, { + "name": "徐汇区" + }, { + "name": "长宁区" + }, { + "name": "静安区" + }, { + "name": "普陀区" + }, { + "name": "闸北区" + }, { + "name": "虹口区" + }, { + "name": "杨浦区" + }, { + "name": "闵行区" + }, { + "name": "宝山区" + }, { + "name": "嘉定区" + }, { + "name": "浦东新区" + }, { + "name": "金山区" + }, { + "name": "松江区" + }, { + "name": "青浦区" + }, { + "name": "奉贤区" + }, { + "name": "崇明县" + }] + }] +}, { + "name": "江苏省", + "city": [{ + "name": "南京市", + "area": [{ + "name": "玄武区" + }, { + "name": "秦淮区" + }, { + "name": "建邺区" + }, { + "name": "鼓楼区" + }, { + "name": "浦口区" + }, { + "name": "栖霞区" + }, { + "name": "雨花台区" + }, { + "name": "江宁区" + }, { + "name": "六合区" + }, { + "name": "溧水区" + }, { + "name": "高淳区" + }] + }, { + "name": "无锡市", + "area": [{ + "name": "崇安区" + }, { + "name": "南长区" + }, { + "name": "北塘区" + }, { + "name": "锡山区" + }, { + "name": "惠山区" + }, { + "name": "滨湖区" + }, { + "name": "江阴市" + }, { + "name": "宜兴市" + }] + }, { + "name": "徐州市", + "area": [{ + "name": "鼓楼区" + }, { + "name": "云龙区" + }, { + "name": "贾汪区" + }, { + "name": "泉山区" + }, { + "name": "铜山区" + }, { + "name": "丰县" + }, { + "name": "沛县" + }, { + "name": "睢宁县" + }, { + "name": "新沂市" + }, { + "name": "邳州市" + }] + }, { + "name": "常州市", + "area": [{ + "name": "天宁区" + }, { + "name": "钟楼区" + }, { + "name": "戚墅堰区" + }, { + "name": "新北区" + }, { + "name": "武进区" + }, { + "name": "溧阳市" + }, { + "name": "金坛市" + }] + }, { + "name": "苏州市", + "area": [{ + "name": "虎丘区" + }, { + "name": "吴中区" + }, { + "name": "相城区" + }, { + "name": "姑苏区" + }, { + "name": "吴江区" + }, { + "name": "常熟市" + }, { + "name": "张家港市" + }, { + "name": "昆山市" + }, { + "name": "太仓市" + }] + }, { + "name": "南通市", + "area": [{ + "name": "崇川区" + }, { + "name": "港闸区" + }, { + "name": "通州区" + }, { + "name": "海安县" + }, { + "name": "如东县" + }, { + "name": "启东市" + }, { + "name": "如皋市" + }, { + "name": "海门市" + }] + }, { + "name": "连云港市", + "area": [{ + "name": "连云区" + }, { + "name": "海州区" + }, { + "name": "赣榆区" + }, { + "name": "东海县" + }, { + "name": "灌云县" + }, { + "name": "灌南县" + }] + }, { + "name": "淮安市", + "area": [{ + "name": "清河区" + }, { + "name": "淮安区" + }, { + "name": "淮阴区" + }, { + "name": "清浦区" + }, { + "name": "涟水县" + }, { + "name": "洪泽县" + }, { + "name": "盱眙县" + }, { + "name": "金湖县" + }] + }, { + "name": "盐城市", + "area": [{ + "name": "亭湖区" + }, { + "name": "盐都区" + }, { + "name": "响水县" + }, { + "name": "滨海县" + }, { + "name": "阜宁县" + }, { + "name": "射阳县" + }, { + "name": "建湖县" + }, { + "name": "东台市" + }, { + "name": "大丰市" + }] + }, { + "name": "扬州市", + "area": [{ + "name": "广陵区" + }, { + "name": "邗江区" + }, { + "name": "江都区" + }, { + "name": "宝应县" + }, { + "name": "仪征市" + }, { + "name": "高邮市" + }] + }, { + "name": "镇江市", + "area": [{ + "name": "京口区" + }, { + "name": "润州区" + }, { + "name": "丹徒区" + }, { + "name": "丹阳市" + }, { + "name": "扬中市" + }, { + "name": "句容市" + }] + }, { + "name": "泰州市", + "area": [{ + "name": "海陵区" + }, { + "name": "高港区" + }, { + "name": "姜堰区" + }, { + "name": "兴化市" + }, { + "name": "靖江市" + }, { + "name": "泰兴市" + }] + }, { + "name": "宿迁市", + "area": [{ + "name": "宿城区" + }, { + "name": "宿豫区" + }, { + "name": "沭阳县" + }, { + "name": "泗阳县" + }, { + "name": "泗洪县" + }] + }] +}, { + "name": "浙江省", + "city": [{ + "name": "杭州市", + "area": [{ + "name": "上城区" + }, { + "name": "下城区" + }, { + "name": "江干区" + }, { + "name": "拱墅区" + }, { + "name": "西湖区" + }, { + "name": "滨江区" + }, { + "name": "萧山区" + }, { + "name": "余杭区" + }, { + "name": "桐庐县" + }, { + "name": "淳安县" + }, { + "name": "建德市" + }, { + "name": "富阳区" + }, { + "name": "临安市" + }] + }, { + "name": "宁波市", + "area": [{ + "name": "海曙区" + }, { + "name": "江东区" + }, { + "name": "江北区" + }, { + "name": "北仑区" + }, { + "name": "镇海区" + }, { + "name": "鄞州区" + }, { + "name": "象山县" + }, { + "name": "宁海县" + }, { + "name": "余姚市" + }, { + "name": "慈溪市" + }, { + "name": "奉化市" + }] + }, { + "name": "温州市", + "area": [{ + "name": "鹿城区" + }, { + "name": "龙湾区" + }, { + "name": "瓯海区" + }, { + "name": "洞头县" + }, { + "name": "永嘉县" + }, { + "name": "平阳县" + }, { + "name": "苍南县" + }, { + "name": "文成县" + }, { + "name": "泰顺县" + }, { + "name": "瑞安市" + }, { + "name": "乐清市" + }] + }, { + "name": "嘉兴市", + "area": [{ + "name": "南湖区" + }, { + "name": "秀洲区" + }, { + "name": "嘉善县" + }, { + "name": "海盐县" + }, { + "name": "海宁市" + }, { + "name": "平湖市" + }, { + "name": "桐乡市" + }] + }, { + "name": "湖州市", + "area": [{ + "name": "吴兴区" + }, { + "name": "南浔区" + }, { + "name": "德清县" + }, { + "name": "长兴县" + }, { + "name": "安吉县" + }] + }, { + "name": "绍兴市", + "area": [{ + "name": "越城区" + }, { + "name": "柯桥区" + }, { + "name": "上虞区" + }, { + "name": "新昌县" + }, { + "name": "诸暨市" + }, { + "name": "嵊州市" + }] + }, { + "name": "金华市", + "area": [{ + "name": "婺城区" + }, { + "name": "金东区" + }, { + "name": "武义县" + }, { + "name": "浦江县" + }, { + "name": "磐安县" + }, { + "name": "兰溪市" + }, { + "name": "义乌市" + }, { + "name": "东阳市" + }, { + "name": "永康市" + }] + }, { + "name": "衢州市", + "area": [{ + "name": "柯城区" + }, { + "name": "衢江区" + }, { + "name": "常山县" + }, { + "name": "开化县" + }, { + "name": "龙游县" + }, { + "name": "江山市" + }] + }, { + "name": "舟山市", + "area": [{ + "name": "定海区" + }, { + "name": "普陀区" + }, { + "name": "岱山县" + }, { + "name": "嵊泗县" + }] + }, { + "name": "台州市", + "area": [{ + "name": "椒江区" + }, { + "name": "黄岩区" + }, { + "name": "路桥区" + }, { + "name": "玉环县" + }, { + "name": "三门县" + }, { + "name": "天台县" + }, { + "name": "仙居县" + }, { + "name": "温岭市" + }, { + "name": "临海市" + }] + }, { + "name": "丽水市", + "area": [{ + "name": "莲都区" + }, { + "name": "青田县" + }, { + "name": "缙云县" + }, { + "name": "遂昌县" + }, { + "name": "松阳县" + }, { + "name": "云和县" + }, { + "name": "庆元县" + }, { + "name": "景宁畲族自治县" + }, { + "name": "龙泉市" + }] + }, { + "name": "舟山群岛新区", + "area": [{ + "name": "金塘岛" + }, { + "name": "六横岛" + }, { + "name": "衢山岛" + }, { + "name": "舟山本岛西北部" + }, { + "name": "岱山岛西南部" + }, { + "name": "泗礁岛" + }, { + "name": "朱家尖岛" + }, { + "name": "洋山岛" + }, { + "name": "长涂岛" + }, { + "name": "虾峙岛" + }] + }] +}, { + "name": "安徽省", + "city": [{ + "name": "合肥市", + "area": [{ + "name": "瑶海区" + }, { + "name": "庐阳区" + }, { + "name": "蜀山区" + }, { + "name": "包河区" + }, { + "name": "长丰县" + }, { + "name": "肥东县" + }, { + "name": "肥西县" + }, { + "name": "庐江县" + }, { + "name": "巢湖市" + }] + }, { + "name": "芜湖市", + "area": [{ + "name": "镜湖区" + }, { + "name": "弋江区" + }, { + "name": "鸠江区" + }, { + "name": "三山区" + }, { + "name": "芜湖县" + }, { + "name": "繁昌县" + }, { + "name": "南陵县" + }, { + "name": "无为县" + }] + }, { + "name": "蚌埠市", + "area": [{ + "name": "龙子湖区" + }, { + "name": "蚌山区" + }, { + "name": "禹会区" + }, { + "name": "淮上区" + }, { + "name": "怀远县" + }, { + "name": "五河县" + }, { + "name": "固镇县" + }] + }, { + "name": "淮南市", + "area": [{ + "name": "大通区" + }, { + "name": "田家庵区" + }, { + "name": "谢家集区" + }, { + "name": "八公山区" + }, { + "name": "潘集区" + }, { + "name": "凤台县" + }] + }, { + "name": "马鞍山市", + "area": [{ + "name": "花山区" + }, { + "name": "雨山区" + }, { + "name": "博望区" + }, { + "name": "当涂县" + }, { + "name": "含山县" + }, { + "name": "和县" + }] + }, { + "name": "淮北市", + "area": [{ + "name": "杜集区" + }, { + "name": "相山区" + }, { + "name": "烈山区" + }, { + "name": "濉溪县" + }] + }, { + "name": "铜陵市", + "area": [{ + "name": "铜官山区" + }, { + "name": "狮子山区" + }, { + "name": "郊区" + }, { + "name": "铜陵县" + }] + }, { + "name": "安庆市", + "area": [{ + "name": "迎江区" + }, { + "name": "大观区" + }, { + "name": "宜秀区" + }, { + "name": "怀宁县" + }, { + "name": "枞阳县" + }, { + "name": "潜山县" + }, { + "name": "太湖县" + }, { + "name": "宿松县" + }, { + "name": "望江县" + }, { + "name": "岳西县" + }, { + "name": "桐城市" + }] + }, { + "name": "黄山市", + "area": [{ + "name": "屯溪区" + }, { + "name": "黄山区" + }, { + "name": "徽州区" + }, { + "name": "歙县" + }, { + "name": "休宁县" + }, { + "name": "黟县" + }, { + "name": "祁门县" + }] + }, { + "name": "滁州市", + "area": [{ + "name": "琅琊区" + }, { + "name": "南谯区" + }, { + "name": "来安县" + }, { + "name": "全椒县" + }, { + "name": "定远县" + }, { + "name": "凤阳县" + }, { + "name": "天长市" + }, { + "name": "明光市" + }] + }, { + "name": "阜阳市", + "area": [{ + "name": "颍州区" + }, { + "name": "颍东区" + }, { + "name": "颍泉区" + }, { + "name": "临泉县" + }, { + "name": "太和县" + }, { + "name": "阜南县" + }, { + "name": "颍上县" + }, { + "name": "界首市" + }] + }, { + "name": "宿州市", + "area": [{ + "name": "埇桥区" + }, { + "name": "砀山县" + }, { + "name": "萧县" + }, { + "name": "灵璧县" + }, { + "name": "泗县" + }] + }, { + "name": "六安市", + "area": [{ + "name": "金安区" + }, { + "name": "裕安区" + }, { + "name": "寿县" + }, { + "name": "霍邱县" + }, { + "name": "舒城县" + }, { + "name": "金寨县" + }, { + "name": "霍山县" + }] + }, { + "name": "亳州市", + "area": [{ + "name": "谯城区" + }, { + "name": "涡阳县" + }, { + "name": "蒙城县" + }, { + "name": "利辛县" + }] + }, { + "name": "池州市", + "area": [{ + "name": "贵池区" + }, { + "name": "东至县" + }, { + "name": "石台县" + }, { + "name": "青阳县" + }] + }, { + "name": "宣城市", + "area": [{ + "name": "宣州区" + }, { + "name": "郎溪县" + }, { + "name": "广德县" + }, { + "name": "泾县" + }, { + "name": "绩溪县" + }, { + "name": "旌德县" + }, { + "name": "宁国市" + }] + }] +}, { + "name": "福建省", + "city": [{ + "name": "福州市", + "area": [{ + "name": "鼓楼区" + }, { + "name": "台江区" + }, { + "name": "仓山区" + }, { + "name": "马尾区" + }, { + "name": "晋安区" + }, { + "name": "闽侯县" + }, { + "name": "连江县" + }, { + "name": "罗源县" + }, { + "name": "闽清县" + }, { + "name": "永泰县" + }, { + "name": "平潭县" + }, { + "name": "福清市" + }, { + "name": "长乐市" + }] + }, { + "name": "厦门市", + "area": [{ + "name": "思明区" + }, { + "name": "海沧区" + }, { + "name": "湖里区" + }, { + "name": "集美区" + }, { + "name": "同安区" + }, { + "name": "翔安区" + }] + }, { + "name": "莆田市", + "area": [{ + "name": "城厢区" + }, { + "name": "涵江区" + }, { + "name": "荔城区" + }, { + "name": "秀屿区" + }, { + "name": "仙游县" + }] + }, { + "name": "三明市", + "area": [{ + "name": "梅列区" + }, { + "name": "三元区" + }, { + "name": "明溪县" + }, { + "name": "清流县" + }, { + "name": "宁化县" + }, { + "name": "大田县" + }, { + "name": "尤溪县" + }, { + "name": "沙县" + }, { + "name": "将乐县" + }, { + "name": "泰宁县" + }, { + "name": "建宁县" + }, { + "name": "永安市" + }] + }, { + "name": "泉州市", + "area": [{ + "name": "鲤城区" + }, { + "name": "丰泽区" + }, { + "name": "洛江区" + }, { + "name": "泉港区" + }, { + "name": "惠安县" + }, { + "name": "安溪县" + }, { + "name": "永春县" + }, { + "name": "德化县" + }, { + "name": "金门县" + }, { + "name": "石狮市" + }, { + "name": "晋江市" + }, { + "name": "南安市" + }] + }, { + "name": "漳州市", + "area": [{ + "name": "芗城区" + }, { + "name": "龙文区" + }, { + "name": "云霄县" + }, { + "name": "漳浦县" + }, { + "name": "诏安县" + }, { + "name": "长泰县" + }, { + "name": "东山县" + }, { + "name": "南靖县" + }, { + "name": "平和县" + }, { + "name": "华安县" + }, { + "name": "龙海市" + }] + }, { + "name": "南平市", + "area": [{ + "name": "延平区" + }, { + "name": "建阳区" + }, { + "name": "顺昌县" + }, { + "name": "浦城县" + }, { + "name": "光泽县" + }, { + "name": "松溪县" + }, { + "name": "政和县" + }, { + "name": "邵武市" + }, { + "name": "武夷山市" + }, { + "name": "建瓯市" + }] + }, { + "name": "龙岩市", + "area": [{ + "name": "新罗区" + }, { + "name": "长汀县" + }, { + "name": "永定区" + }, { + "name": "上杭县" + }, { + "name": "武平县" + }, { + "name": "连城县" + }, { + "name": "漳平市" + }] + }, { + "name": "宁德市", + "area": [{ + "name": "蕉城区" + }, { + "name": "霞浦县" + }, { + "name": "古田县" + }, { + "name": "屏南县" + }, { + "name": "寿宁县" + }, { + "name": "周宁县" + }, { + "name": "柘荣县" + }, { + "name": "福安市" + }, { + "name": "福鼎市" + }] + }] +}, { + "name": "江西省", + "city": [{ + "name": "南昌市", + "area": [{ + "name": "东湖区" + }, { + "name": "西湖区" + }, { + "name": "青云谱区" + }, { + "name": "湾里区" + }, { + "name": "青山湖区" + }, { + "name": "南昌县" + }, { + "name": "新建县" + }, { + "name": "安义县" + }, { + "name": "进贤县" + }] + }, { + "name": "景德镇市", + "area": [{ + "name": "昌江区" + }, { + "name": "珠山区" + }, { + "name": "浮梁县" + }, { + "name": "乐平市" + }] + }, { + "name": "萍乡市", + "area": [{ + "name": "安源区" + }, { + "name": "湘东区" + }, { + "name": "莲花县" + }, { + "name": "上栗县" + }, { + "name": "芦溪县" + }] + }, { + "name": "九江市", + "area": [{ + "name": "庐山区" + }, { + "name": "浔阳区" + }, { + "name": "九江县" + }, { + "name": "武宁县" + }, { + "name": "修水县" + }, { + "name": "永修县" + }, { + "name": "德安县" + }, { + "name": "星子县" + }, { + "name": "都昌县" + }, { + "name": "湖口县" + }, { + "name": "彭泽县" + }, { + "name": "瑞昌市" + }, { + "name": "共青城市" + }] + }, { + "name": "新余市", + "area": [{ + "name": "渝水区" + }, { + "name": "分宜县" + }] + }, { + "name": "鹰潭市", + "area": [{ + "name": "月湖区" + }, { + "name": "余江县" + }, { + "name": "贵溪市" + }] + }, { + "name": "赣州市", + "area": [{ + "name": "章贡区" + }, { + "name": "南康区" + }, { + "name": "赣县" + }, { + "name": "信丰县" + }, { + "name": "大余县" + }, { + "name": "上犹县" + }, { + "name": "崇义县" + }, { + "name": "安远县" + }, { + "name": "龙南县" + }, { + "name": "定南县" + }, { + "name": "全南县" + }, { + "name": "宁都县" + }, { + "name": "于都县" + }, { + "name": "兴国县" + }, { + "name": "会昌县" + }, { + "name": "寻乌县" + }, { + "name": "石城县" + }, { + "name": "瑞金市" + }] + }, { + "name": "吉安市", + "area": [{ + "name": "吉州区" + }, { + "name": "青原区" + }, { + "name": "吉安县" + }, { + "name": "吉水县" + }, { + "name": "峡江县" + }, { + "name": "新干县" + }, { + "name": "永丰县" + }, { + "name": "泰和县" + }, { + "name": "遂川县" + }, { + "name": "万安县" + }, { + "name": "安福县" + }, { + "name": "永新县" + }, { + "name": "井冈山市" + }] + }, { + "name": "宜春市", + "area": [{ + "name": "袁州区" + }, { + "name": "奉新县" + }, { + "name": "万载县" + }, { + "name": "上高县" + }, { + "name": "宜丰县" + }, { + "name": "靖安县" + }, { + "name": "铜鼓县" + }, { + "name": "丰城市" + }, { + "name": "樟树市" + }, { + "name": "高安市" + }] + }, { + "name": "抚州市", + "area": [{ + "name": "临川区" + }, { + "name": "南城县" + }, { + "name": "黎川县" + }, { + "name": "南丰县" + }, { + "name": "崇仁县" + }, { + "name": "乐安县" + }, { + "name": "宜黄县" + }, { + "name": "金溪县" + }, { + "name": "资溪县" + }, { + "name": "东乡县" + }, { + "name": "广昌县" + }] + }, { + "name": "上饶市", + "area": [{ + "name": "信州区" + }, { + "name": "上饶县" + }, { + "name": "广丰县" + }, { + "name": "玉山县" + }, { + "name": "铅山县" + }, { + "name": "横峰县" + }, { + "name": "弋阳县" + }, { + "name": "余干县" + }, { + "name": "鄱阳县" + }, { + "name": "万年县" + }, { + "name": "婺源县" + }, { + "name": "德兴市" + }] + }] +}, { + "name": "山东省", + "city": [{ + "name": "济南市", + "area": [{ + "name": "历下区" + }, { + "name": "市中区" + }, { + "name": "槐荫区" + }, { + "name": "天桥区" + }, { + "name": "历城区" + }, { + "name": "长清区" + }, { + "name": "平阴县" + }, { + "name": "济阳县" + }, { + "name": "商河县" + }, { + "name": "章丘市" + }] + }, { + "name": "青岛市", + "area": [{ + "name": "市南区" + }, { + "name": "市北区" + }, { + "name": "黄岛区" + }, { + "name": "崂山区" + }, { + "name": "李沧区" + }, { + "name": "城阳区" + }, { + "name": "胶州市" + }, { + "name": "即墨市" + }, { + "name": "平度市" + }, { + "name": "莱西市" + }, { + "name": "西海岸新区" + }] + }, { + "name": "淄博市", + "area": [{ + "name": "淄川区" + }, { + "name": "张店区" + }, { + "name": "博山区" + }, { + "name": "临淄区" + }, { + "name": "周村区" + }, { + "name": "桓台县" + }, { + "name": "高青县" + }, { + "name": "沂源县" + }] + }, { + "name": "枣庄市", + "area": [{ + "name": "市中区" + }, { + "name": "薛城区" + }, { + "name": "峄城区" + }, { + "name": "台儿庄区" + }, { + "name": "山亭区" + }, { + "name": "滕州市" + }] + }, { + "name": "东营市", + "area": [{ + "name": "东营区" + }, { + "name": "河口区" + }, { + "name": "垦利县" + }, { + "name": "利津县" + }, { + "name": "广饶县" + }] + }, { + "name": "烟台市", + "area": [{ + "name": "芝罘区" + }, { + "name": "福山区" + }, { + "name": "牟平区" + }, { + "name": "莱山区" + }, { + "name": "长岛县" + }, { + "name": "龙口市" + }, { + "name": "莱阳市" + }, { + "name": "莱州市" + }, { + "name": "蓬莱市" + }, { + "name": "招远市" + }, { + "name": "栖霞市" + }, { + "name": "海阳市" + }] + }, { + "name": "潍坊市", + "area": [{ + "name": "潍城区" + }, { + "name": "寒亭区" + }, { + "name": "坊子区" + }, { + "name": "奎文区" + }, { + "name": "临朐县" + }, { + "name": "昌乐县" + }, { + "name": "青州市" + }, { + "name": "诸城市" + }, { + "name": "寿光市" + }, { + "name": "安丘市" + }, { + "name": "高密市" + }, { + "name": "昌邑市" + }] + }, { + "name": "济宁市", + "area": [{ + "name": "任城区" + }, { + "name": "兖州区" + }, { + "name": "微山县" + }, { + "name": "鱼台县" + }, { + "name": "金乡县" + }, { + "name": "嘉祥县" + }, { + "name": "汶上县" + }, { + "name": "泗水县" + }, { + "name": "梁山县" + }, { + "name": "曲阜市" + }, { + "name": "邹城市" + }] + }, { + "name": "泰安市", + "area": [{ + "name": "泰山区" + }, { + "name": "岱岳区" + }, { + "name": "宁阳县" + }, { + "name": "东平县" + }, { + "name": "新泰市" + }, { + "name": "肥城市" + }] + }, { + "name": "威海市", + "area": [{ + "name": "环翠区" + }, { + "name": "文登区" + }, { + "name": "荣成市" + }, { + "name": "乳山市" + }] + }, { + "name": "日照市", + "area": [{ + "name": "东港区" + }, { + "name": "岚山区" + }, { + "name": "五莲县" + }, { + "name": "莒县" + }] + }, { + "name": "莱芜市", + "area": [{ + "name": "莱城区" + }, { + "name": "钢城区" + }] + }, { + "name": "临沂市", + "area": [{ + "name": "兰山区" + }, { + "name": "罗庄区" + }, { + "name": "河东区" + }, { + "name": "沂南县" + }, { + "name": "郯城县" + }, { + "name": "沂水县" + }, { + "name": "兰陵县" + }, { + "name": "费县" + }, { + "name": "平邑县" + }, { + "name": "莒南县" + }, { + "name": "蒙阴县" + }, { + "name": "临沭县" + }] + }, { + "name": "德州市", + "area": [{ + "name": "德城区" + }, { + "name": "陵城区" + }, { + "name": "宁津县" + }, { + "name": "庆云县" + }, { + "name": "临邑县" + }, { + "name": "齐河县" + }, { + "name": "平原县" + }, { + "name": "夏津县" + }, { + "name": "武城县" + }, { + "name": "乐陵市" + }, { + "name": "禹城市" + }] + }, { + "name": "聊城市", + "area": [{ + "name": "东昌府区" + }, { + "name": "阳谷县" + }, { + "name": "莘县" + }, { + "name": "茌平县" + }, { + "name": "东阿县" + }, { + "name": "冠县" + }, { + "name": "高唐县" + }, { + "name": "临清市" + }] + }, { + "name": "滨州市", + "area": [{ + "name": "滨城区" + }, { + "name": "沾化区" + }, { + "name": "惠民县" + }, { + "name": "阳信县" + }, { + "name": "无棣县" + }, { + "name": "博兴县" + }, { + "name": "邹平县" + }, { + "name": "北海新区" + }] + }, { + "name": "菏泽市", + "area": [{ + "name": "牡丹区" + }, { + "name": "曹县" + }, { + "name": "单县" + }, { + "name": "成武县" + }, { + "name": "巨野县" + }, { + "name": "郓城县" + }, { + "name": "鄄城县" + }, { + "name": "定陶县" + }, { + "name": "东明县" + }] + }] +}, { + "name": "河南省", + "city": [{ + "name": "郑州市", + "area": [{ + "name": "中原区" + }, { + "name": "二七区" + }, { + "name": "管城回族区" + }, { + "name": "金水区" + }, { + "name": "上街区" + }, { + "name": "惠济区" + }, { + "name": "中牟县" + }, { + "name": "巩义市" + }, { + "name": "荥阳市" + }, { + "name": "新密市" + }, { + "name": "新郑市" + }, { + "name": "登封市" + }] + }, { + "name": "开封市", + "area": [{ + "name": "龙亭区" + }, { + "name": "顺河回族区" + }, { + "name": "鼓楼区" + }, { + "name": "禹王台区" + }, { + "name": "祥符区" + }, { + "name": "杞县" + }, { + "name": "通许县" + }, { + "name": "尉氏县" + }, { + "name": "兰考县" + }] + }, { + "name": "洛阳市", + "area": [{ + "name": "老城区" + }, { + "name": "西工区" + }, { + "name": "瀍河回族区" + }, { + "name": "涧西区" + }, { + "name": "吉利区" + }, { + "name": "洛龙区" + }, { + "name": "孟津县" + }, { + "name": "新安县" + }, { + "name": "栾川县" + }, { + "name": "嵩县" + }, { + "name": "汝阳县" + }, { + "name": "宜阳县" + }, { + "name": "洛宁县" + }, { + "name": "伊川县" + }, { + "name": "偃师市" + }] + }, { + "name": "平顶山市", + "area": [{ + "name": "新华区" + }, { + "name": "卫东区" + }, { + "name": "石龙区" + }, { + "name": "湛河区" + }, { + "name": "宝丰县" + }, { + "name": "叶县" + }, { + "name": "鲁山县" + }, { + "name": "郏县" + }, { + "name": "舞钢市" + }, { + "name": "汝州市" + }] + }, { + "name": "安阳市", + "area": [{ + "name": "文峰区" + }, { + "name": "北关区" + }, { + "name": "殷都区" + }, { + "name": "龙安区" + }, { + "name": "安阳县" + }, { + "name": "汤阴县" + }, { + "name": "滑县" + }, { + "name": "内黄县" + }, { + "name": "林州市" + }] + }, { + "name": "鹤壁市", + "area": [{ + "name": "鹤山区" + }, { + "name": "山城区" + }, { + "name": "淇滨区" + }, { + "name": "浚县" + }, { + "name": "淇县" + }] + }, { + "name": "新乡市", + "area": [{ + "name": "红旗区" + }, { + "name": "卫滨区" + }, { + "name": "凤泉区" + }, { + "name": "牧野区" + }, { + "name": "新乡县" + }, { + "name": "获嘉县" + }, { + "name": "原阳县" + }, { + "name": "延津县" + }, { + "name": "封丘县" + }, { + "name": "长垣县" + }, { + "name": "卫辉市" + }, { + "name": "辉县市" + }] + }, { + "name": "焦作市", + "area": [{ + "name": "解放区" + }, { + "name": "中站区" + }, { + "name": "马村区" + }, { + "name": "山阳区" + }, { + "name": "修武县" + }, { + "name": "博爱县" + }, { + "name": "武陟县" + }, { + "name": "温县" + }, { + "name": "沁阳市" + }, { + "name": "孟州市" + }] + }, { + "name": "濮阳市", + "area": [{ + "name": "华龙区" + }, { + "name": "清丰县" + }, { + "name": "南乐县" + }, { + "name": "范县" + }, { + "name": "台前县" + }, { + "name": "濮阳县" + }] + }, { + "name": "许昌市", + "area": [{ + "name": "魏都区" + }, { + "name": "许昌县" + }, { + "name": "鄢陵县" + }, { + "name": "襄城县" + }, { + "name": "禹州市" + }, { + "name": "长葛市" + }] + }, { + "name": "漯河市", + "area": [{ + "name": "源汇区" + }, { + "name": "郾城区" + }, { + "name": "召陵区" + }, { + "name": "舞阳县" + }, { + "name": "临颍县" + }] + }, { + "name": "三门峡市", + "area": [{ + "name": "湖滨区" + }, { + "name": "渑池县" + }, { + "name": "陕县" + }, { + "name": "卢氏县" + }, { + "name": "义马市" + }, { + "name": "灵宝市" + }] + }, { + "name": "南阳市", + "area": [{ + "name": "宛城区" + }, { + "name": "卧龙区" + }, { + "name": "南召县" + }, { + "name": "方城县" + }, { + "name": "西峡县" + }, { + "name": "镇平县" + }, { + "name": "内乡县" + }, { + "name": "淅川县" + }, { + "name": "社旗县" + }, { + "name": "唐河县" + }, { + "name": "新野县" + }, { + "name": "桐柏县" + }, { + "name": "邓州市" + }] + }, { + "name": "商丘市", + "area": [{ + "name": "梁园区" + }, { + "name": "睢阳区" + }, { + "name": "民权县" + }, { + "name": "睢县" + }, { + "name": "宁陵县" + }, { + "name": "柘城县" + }, { + "name": "虞城县" + }, { + "name": "夏邑县" + }, { + "name": "永城市" + }] + }, { + "name": "信阳市", + "area": [{ + "name": "浉河区" + }, { + "name": "平桥区" + }, { + "name": "罗山县" + }, { + "name": "光山县" + }, { + "name": "新县" + }, { + "name": "商城县" + }, { + "name": "固始县" + }, { + "name": "潢川县" + }, { + "name": "淮滨县" + }, { + "name": "息县" + }] + }, { + "name": "周口市", + "area": [{ + "name": "川汇区" + }, { + "name": "扶沟县" + }, { + "name": "西华县" + }, { + "name": "商水县" + }, { + "name": "沈丘县" + }, { + "name": "郸城县" + }, { + "name": "淮阳县" + }, { + "name": "太康县" + }, { + "name": "鹿邑县" + }, { + "name": "项城市" + }] + }, { + "name": "驻马店市", + "area": [{ + "name": "驿城区" + }, { + "name": "西平县" + }, { + "name": "上蔡县" + }, { + "name": "平舆县" + }, { + "name": "正阳县" + }, { + "name": "确山县" + }, { + "name": "泌阳县" + }, { + "name": "汝南县" + }, { + "name": "遂平县" + }, { + "name": "新蔡县" + }] + }, { + "name": "直辖县级", + "area": [{ + "name": "济源市" + }] + }] +}, { + "name": "湖北省", + "city": [{ + "name": "武汉市", + "area": [{ + "name": "江岸区" + }, { + "name": "江汉区" + }, { + "name": "硚口区" + }, { + "name": "汉阳区" + }, { + "name": "武昌区" + }, { + "name": "青山区" + }, { + "name": "洪山区" + }, { + "name": "东西湖区" + }, { + "name": "汉南区" + }, { + "name": "蔡甸区" + }, { + "name": "江夏区" + }, { + "name": "黄陂区" + }, { + "name": "新洲区" + }] + }, { + "name": "黄石市", + "area": [{ + "name": "黄石港区" + }, { + "name": "西塞山区" + }, { + "name": "下陆区" + }, { + "name": "铁山区" + }, { + "name": "阳新县" + }, { + "name": "大冶市" + }] + }, { + "name": "十堰市", + "area": [{ + "name": "茅箭区" + }, { + "name": "张湾区" + }, { + "name": "郧阳区" + }, { + "name": "郧西县" + }, { + "name": "竹山县" + }, { + "name": "竹溪县" + }, { + "name": "房县" + }, { + "name": "丹江口市" + }] + }, { + "name": "宜昌市", + "area": [{ + "name": "西陵区" + }, { + "name": "伍家岗区" + }, { + "name": "点军区" + }, { + "name": "猇亭区" + }, { + "name": "夷陵区" + }, { + "name": "远安县" + }, { + "name": "兴山县" + }, { + "name": "秭归县" + }, { + "name": "长阳土家族自治县" + }, { + "name": "五峰土家族自治县" + }, { + "name": "宜都市" + }, { + "name": "当阳市" + }, { + "name": "枝江市" + }] + }, { + "name": "襄阳市", + "area": [{ + "name": "襄城区" + }, { + "name": "樊城区" + }, { + "name": "襄州区" + }, { + "name": "南漳县" + }, { + "name": "谷城县" + }, { + "name": "保康县" + }, { + "name": "老河口市" + }, { + "name": "枣阳市" + }, { + "name": "宜城市" + }] + }, { + "name": "鄂州市", + "area": [{ + "name": "梁子湖区" + }, { + "name": "华容区" + }, { + "name": "鄂城区" + }] + }, { + "name": "荆门市", + "area": [{ + "name": "东宝区" + }, { + "name": "掇刀区" + }, { + "name": "京山县" + }, { + "name": "沙洋县" + }, { + "name": "钟祥市" + }] + }, { + "name": "孝感市", + "area": [{ + "name": "孝南区" + }, { + "name": "孝昌县" + }, { + "name": "大悟县" + }, { + "name": "云梦县" + }, { + "name": "应城市" + }, { + "name": "安陆市" + }, { + "name": "汉川市" + }] + }, { + "name": "荆州市", + "area": [{ + "name": "沙市区" + }, { + "name": "荆州区" + }, { + "name": "公安县" + }, { + "name": "监利县" + }, { + "name": "江陵县" + }, { + "name": "石首市" + }, { + "name": "洪湖市" + }, { + "name": "松滋市" + }] + }, { + "name": "黄冈市", + "area": [{ + "name": "黄州区" + }, { + "name": "团风县" + }, { + "name": "红安县" + }, { + "name": "罗田县" + }, { + "name": "英山县" + }, { + "name": "浠水县" + }, { + "name": "蕲春县" + }, { + "name": "黄梅县" + }, { + "name": "麻城市" + }, { + "name": "武穴市" + }] + }, { + "name": "咸宁市", + "area": [{ + "name": "咸安区" + }, { + "name": "嘉鱼县" + }, { + "name": "通城县" + }, { + "name": "崇阳县" + }, { + "name": "通山县" + }, { + "name": "赤壁市" + }] + }, { + "name": "随州市", + "area": [{ + "name": "曾都区" + }, { + "name": "随县" + }, { + "name": "广水市" + }] + }, { + "name": "恩施土家族苗族自治州", + "area": [{ + "name": "恩施市" + }, { + "name": "利川市" + }, { + "name": "建始县" + }, { + "name": "巴东县" + }, { + "name": "宣恩县" + }, { + "name": "咸丰县" + }, { + "name": "来凤县" + }, { + "name": "鹤峰县" + }] + }, { + "name": "直辖县级", + "area": [{ + "name": "仙桃市" + }, { + "name": "潜江市" + }, { + "name": "天门市" + }, { + "name": "神农架林区" + }] + }] +}, { + "name": "湖南省", + "city": [{ + "name": "长沙市", + "area": [{ + "name": "芙蓉区" + }, { + "name": "天心区" + }, { + "name": "岳麓区" + }, { + "name": "开福区" + }, { + "name": "雨花区" + }, { + "name": "望城区" + }, { + "name": "长沙县" + }, { + "name": "宁乡县" + }, { + "name": "浏阳市" + }] + }, { + "name": "株洲市", + "area": [{ + "name": "荷塘区" + }, { + "name": "芦淞区" + }, { + "name": "石峰区" + }, { + "name": "天元区" + }, { + "name": "株洲县" + }, { + "name": "攸县" + }, { + "name": "茶陵县" + }, { + "name": "炎陵县" + }, { + "name": "醴陵市" + }] + }, { + "name": "湘潭市", + "area": [{ + "name": "雨湖区" + }, { + "name": "岳塘区" + }, { + "name": "湘潭县" + }, { + "name": "湘乡市" + }, { + "name": "韶山市" + }] + }, { + "name": "衡阳市", + "area": [{ + "name": "珠晖区" + }, { + "name": "雁峰区" + }, { + "name": "石鼓区" + }, { + "name": "蒸湘区" + }, { + "name": "南岳区" + }, { + "name": "衡阳县" + }, { + "name": "衡南县" + }, { + "name": "衡山县" + }, { + "name": "衡东县" + }, { + "name": "祁东县" + }, { + "name": "耒阳市" + }, { + "name": "常宁市" + }] + }, { + "name": "邵阳市", + "area": [{ + "name": "双清区" + }, { + "name": "大祥区" + }, { + "name": "北塔区" + }, { + "name": "邵东县" + }, { + "name": "新邵县" + }, { + "name": "邵阳县" + }, { + "name": "隆回县" + }, { + "name": "洞口县" + }, { + "name": "绥宁县" + }, { + "name": "新宁县" + }, { + "name": "城步苗族自治县" + }, { + "name": "武冈市" + }] + }, { + "name": "岳阳市", + "area": [{ + "name": "岳阳楼区" + }, { + "name": "云溪区" + }, { + "name": "君山区" + }, { + "name": "岳阳县" + }, { + "name": "华容县" + }, { + "name": "湘阴县" + }, { + "name": "平江县" + }, { + "name": "汨罗市" + }, { + "name": "临湘市" + }] + }, { + "name": "常德市", + "area": [{ + "name": "武陵区" + }, { + "name": "鼎城区" + }, { + "name": "安乡县" + }, { + "name": "汉寿县" + }, { + "name": "澧县" + }, { + "name": "临澧县" + }, { + "name": "桃源县" + }, { + "name": "石门县" + }, { + "name": "津市市" + }] + }, { + "name": "张家界市", + "area": [{ + "name": "永定区" + }, { + "name": "武陵源区" + }, { + "name": "慈利县" + }, { + "name": "桑植县" + }] + }, { + "name": "益阳市", + "area": [{ + "name": "资阳区" + }, { + "name": "赫山区" + }, { + "name": "南县" + }, { + "name": "桃江县" + }, { + "name": "安化县" + }, { + "name": "沅江市" + }] + }, { + "name": "郴州市", + "area": [{ + "name": "北湖区" + }, { + "name": "苏仙区" + }, { + "name": "桂阳县" + }, { + "name": "宜章县" + }, { + "name": "永兴县" + }, { + "name": "嘉禾县" + }, { + "name": "临武县" + }, { + "name": "汝城县" + }, { + "name": "桂东县" + }, { + "name": "安仁县" + }, { + "name": "资兴市" + }] + }, { + "name": "永州市", + "area": [{ + "name": "零陵区" + }, { + "name": "冷水滩区" + }, { + "name": "祁阳县" + }, { + "name": "东安县" + }, { + "name": "双牌县" + }, { + "name": "道县" + }, { + "name": "江永县" + }, { + "name": "宁远县" + }, { + "name": "蓝山县" + }, { + "name": "新田县" + }, { + "name": "江华瑶族自治县" + }] + }, { + "name": "怀化市", + "area": [{ + "name": "鹤城区" + }, { + "name": "中方县" + }, { + "name": "沅陵县" + }, { + "name": "辰溪县" + }, { + "name": "溆浦县" + }, { + "name": "会同县" + }, { + "name": "麻阳苗族自治县" + }, { + "name": "新晃侗族自治县" + }, { + "name": "芷江侗族自治县" + }, { + "name": "靖州苗族侗族自治县" + }, { + "name": "通道侗族自治县" + }, { + "name": "洪江市" + }] + }, { + "name": "娄底市", + "area": [{ + "name": "娄星区" + }, { + "name": "双峰县" + }, { + "name": "新化县" + }, { + "name": "冷水江市" + }, { + "name": "涟源市" + }] + }, { + "name": "湘西土家族苗族自治州", + "area": [{ + "name": "吉首市" + }, { + "name": "泸溪县" + }, { + "name": "凤凰县" + }, { + "name": "花垣县" + }, { + "name": "保靖县" + }, { + "name": "古丈县" + }, { + "name": "永顺县" + }, { + "name": "龙山县" + }] + }] +}, { + "name": "广东省", + "city": [{ + "name": "广州市", + "area": [{ + "name": "荔湾区" + }, { + "name": "越秀区" + }, { + "name": "海珠区" + }, { + "name": "天河区" + }, { + "name": "白云区" + }, { + "name": "黄埔区" + }, { + "name": "番禺区" + }, { + "name": "花都区" + }, { + "name": "南沙区" + }, { + "name": "从化区" + }, { + "name": "增城区" + }] + }, { + "name": "韶关市", + "area": [{ + "name": "武江区" + }, { + "name": "浈江区" + }, { + "name": "曲江区" + }, { + "name": "始兴县" + }, { + "name": "仁化县" + }, { + "name": "翁源县" + }, { + "name": "乳源瑶族自治县" + }, { + "name": "新丰县" + }, { + "name": "乐昌市" + }, { + "name": "南雄市" + }] + }, { + "name": "深圳市", + "area": [{ + "name": "罗湖区" + }, { + "name": "福田区" + }, { + "name": "南山区" + }, { + "name": "宝安区" + }, { + "name": "龙岗区" + }, { + "name": "盐田区" + }, { + "name": "光明新区" + }, { + "name": "坪山新区" + }, { + "name": "大鹏新区" + }, { + "name": "龙华新区" + }] + }, { + "name": "珠海市", + "area": [{ + "name": "香洲区" + }, { + "name": "斗门区" + }, { + "name": "金湾区" + }] + }, { + "name": "汕头市", + "area": [{ + "name": "龙湖区" + }, { + "name": "金平区" + }, { + "name": "濠江区" + }, { + "name": "潮阳区" + }, { + "name": "潮南区" + }, { + "name": "澄海区" + }, { + "name": "南澳县" + }] + }, { + "name": "佛山市", + "area": [{ + "name": "禅城区" + }, { + "name": "南海区" + }, { + "name": "顺德区" + }, { + "name": "三水区" + }, { + "name": "高明区" + }] + }, { + "name": "江门市", + "area": [{ + "name": "蓬江区" + }, { + "name": "江海区" + }, { + "name": "新会区" + }, { + "name": "台山市" + }, { + "name": "开平市" + }, { + "name": "鹤山市" + }, { + "name": "恩平市" + }] + }, { + "name": "湛江市", + "area": [{ + "name": "赤坎区" + }, { + "name": "霞山区" + }, { + "name": "坡头区" + }, { + "name": "麻章区" + }, { + "name": "遂溪县" + }, { + "name": "徐闻县" + }, { + "name": "廉江市" + }, { + "name": "雷州市" + }, { + "name": "吴川市" + }] + }, { + "name": "茂名市", + "area": [{ + "name": "茂南区" + }, { + "name": "电白区" + }, { + "name": "高州市" + }, { + "name": "化州市" + }, { + "name": "信宜市" + }] + }, { + "name": "肇庆市", + "area": [{ + "name": "端州区" + }, { + "name": "鼎湖区" + }, { + "name": "广宁县" + }, { + "name": "怀集县" + }, { + "name": "封开县" + }, { + "name": "德庆县" + }, { + "name": "高要市" + }, { + "name": "四会市" + }] + }, { + "name": "惠州市", + "area": [{ + "name": "惠城区" + }, { + "name": "惠阳区" + }, { + "name": "博罗县" + }, { + "name": "惠东县" + }, { + "name": "龙门县" + }] + }, { + "name": "梅州市", + "area": [{ + "name": "梅江区" + }, { + "name": "梅县区" + }, { + "name": "大埔县" + }, { + "name": "丰顺县" + }, { + "name": "五华县" + }, { + "name": "平远县" + }, { + "name": "蕉岭县" + }, { + "name": "兴宁市" + }] + }, { + "name": "汕尾市", + "area": [{ + "name": "城区" + }, { + "name": "海丰县" + }, { + "name": "陆河县" + }, { + "name": "陆丰市" + }] + }, { + "name": "河源市", + "area": [{ + "name": "源城区" + }, { + "name": "紫金县" + }, { + "name": "龙川县" + }, { + "name": "连平县" + }, { + "name": "和平县" + }, { + "name": "东源县" + }] + }, { + "name": "阳江市", + "area": [{ + "name": "江城区" + }, { + "name": "阳东区" + }, { + "name": "阳西县" + }, { + "name": "阳春市" + }] + }, { + "name": "清远市", + "area": [{ + "name": "清城区" + }, { + "name": "清新区" + }, { + "name": "佛冈县" + }, { + "name": "阳山县" + }, { + "name": "连山壮族瑶族自治县" + }, { + "name": "连南瑶族自治县" + }, { + "name": "英德市" + }, { + "name": "连州市" + }] + }, { + "name": "东莞市", + "area": [{ + "name": "莞城区" + }, { + "name": "南城区" + }, { + "name": "万江区" + }, { + "name": "石碣镇" + }, { + "name": "石龙镇" + }, { + "name": "茶山镇" + }, { + "name": "石排镇" + }, { + "name": "企石镇" + }, { + "name": "横沥镇" + }, { + "name": "桥头镇" + }, { + "name": "谢岗镇" + }, { + "name": "东坑镇" + }, { + "name": "常平镇" + }, { + "name": "寮步镇" + }, { + "name": "大朗镇" + }, { + "name": "麻涌镇" + }, { + "name": "中堂镇" + }, { + "name": "高埗镇" + }, { + "name": "樟木头镇" + }, { + "name": "大岭山镇" + }, { + "name": "望牛墩镇" + }, { + "name": "黄江镇" + }, { + "name": "洪梅镇" + }, { + "name": "清溪镇" + }, { + "name": "沙田镇" + }, { + "name": "道滘镇" + }, { + "name": "塘厦镇" + }, { + "name": "虎门镇" + }, { + "name": "厚街镇" + }, { + "name": "凤岗镇" + }, { + "name": "长安镇" + }] + }, { + "name": "中山市", + "area": [{ + "name": "石岐区" + }, { + "name": "南区" + }, { + "name": "五桂山区" + }, { + "name": "火炬开发区" + }, { + "name": "黄圃镇" + }, { + "name": "南头镇" + }, { + "name": "东凤镇" + }, { + "name": "阜沙镇" + }, { + "name": "小榄镇" + }, { + "name": "东升镇" + }, { + "name": "古镇镇" + }, { + "name": "横栏镇" + }, { + "name": "三角镇" + }, { + "name": "民众镇" + }, { + "name": "南朗镇" + }, { + "name": "港口镇" + }, { + "name": "大涌镇" + }, { + "name": "沙溪镇" + }, { + "name": "三乡镇" + }, { + "name": "板芙镇" + }, { + "name": "神湾镇" + }, { + "name": "坦洲镇" + }] + }, { + "name": "潮州市", + "area": [{ + "name": "湘桥区" + }, { + "name": "潮安区" + }, { + "name": "饶平县" + }] + }, { + "name": "揭阳市", + "area": [{ + "name": "榕城区" + }, { + "name": "揭东区" + }, { + "name": "揭西县" + }, { + "name": "惠来县" + }, { + "name": "普宁市" + }] + }, { + "name": "云浮市", + "area": [{ + "name": "云城区" + }, { + "name": "云安区" + }, { + "name": "新兴县" + }, { + "name": "郁南县" + }, { + "name": "罗定市" + }] + }] +}, { + "name": "广西壮族自治区", + "city": [{ + "name": "南宁市", + "area": [{ + "name": "兴宁区" + }, { + "name": "青秀区" + }, { + "name": "江南区" + }, { + "name": "西乡塘区" + }, { + "name": "良庆区" + }, { + "name": "邕宁区" + }, { + "name": "武鸣县" + }, { + "name": "隆安县" + }, { + "name": "马山县" + }, { + "name": "上林县" + }, { + "name": "宾阳县" + }, { + "name": "横县" + }, { + "name": "埌东新区" + }] + }, { + "name": "柳州市", + "area": [{ + "name": "城中区" + }, { + "name": "鱼峰区" + }, { + "name": "柳南区" + }, { + "name": "柳北区" + }, { + "name": "柳江县" + }, { + "name": "柳城县" + }, { + "name": "鹿寨县" + }, { + "name": "融安县" + }, { + "name": "融水苗族自治县" + }, { + "name": "三江侗族自治县" + }, { + "name": "柳东新区" + }] + }, { + "name": "桂林市", + "area": [{ + "name": "秀峰区" + }, { + "name": "叠彩区" + }, { + "name": "象山区" + }, { + "name": "七星区" + }, { + "name": "雁山区" + }, { + "name": "临桂区" + }, { + "name": "阳朔县" + }, { + "name": "灵川县" + }, { + "name": "全州县" + }, { + "name": "兴安县" + }, { + "name": "永福县" + }, { + "name": "灌阳县" + }, { + "name": "龙胜各族自治县" + }, { + "name": "资源县" + }, { + "name": "平乐县" + }, { + "name": "荔浦县" + }, { + "name": "恭城瑶族自治县" + }] + }, { + "name": "梧州市", + "area": [{ + "name": "万秀区" + }, { + "name": "长洲区" + }, { + "name": "龙圩区" + }, { + "name": "苍梧县" + }, { + "name": "藤县" + }, { + "name": "蒙山县" + }, { + "name": "岑溪市" + }] + }, { + "name": "北海市", + "area": [{ + "name": "海城区" + }, { + "name": "银海区" + }, { + "name": "铁山港区" + }, { + "name": "合浦县" + }] + }, { + "name": "防城港市", + "area": [{ + "name": "港口区" + }, { + "name": "防城区" + }, { + "name": "上思县" + }, { + "name": "东兴市" + }] + }, { + "name": "钦州市", + "area": [{ + "name": "钦南区" + }, { + "name": "钦北区" + }, { + "name": "灵山县" + }, { + "name": "浦北县" + }] + }, { + "name": "贵港市", + "area": [{ + "name": "港北区" + }, { + "name": "港南区" + }, { + "name": "覃塘区" + }, { + "name": "平南县" + }, { + "name": "桂平市" + }] + }, { + "name": "玉林市", + "area": [{ + "name": "玉州区" + }, { + "name": "福绵区" + }, { + "name": "玉东新区" + }, { + "name": "容县" + }, { + "name": "陆川县" + }, { + "name": "博白县" + }, { + "name": "兴业县" + }, { + "name": "北流市" + }] + }, { + "name": "百色市", + "area": [{ + "name": "右江区" + }, { + "name": "田阳县" + }, { + "name": "田东县" + }, { + "name": "平果县" + }, { + "name": "德保县" + }, { + "name": "靖西县" + }, { + "name": "那坡县" + }, { + "name": "凌云县" + }, { + "name": "乐业县" + }, { + "name": "田林县" + }, { + "name": "西林县" + }, { + "name": "隆林各族自治县" + }] + }, { + "name": "贺州市", + "area": [{ + "name": "八步区" + }, { + "name": "昭平县" + }, { + "name": "钟山县" + }, { + "name": "富川瑶族自治县" + }, { + "name": "平桂管理区" + }] + }, { + "name": "河池市", + "area": [{ + "name": "金城江区" + }, { + "name": "南丹县" + }, { + "name": "天峨县" + }, { + "name": "凤山县" + }, { + "name": "东兰县" + }, { + "name": "罗城仫佬族自治县" + }, { + "name": "环江毛南族自治县" + }, { + "name": "巴马瑶族自治县" + }, { + "name": "都安瑶族自治县" + }, { + "name": "大化瑶族自治县" + }, { + "name": "宜州市" + }] + }, { + "name": "来宾市", + "area": [{ + "name": "兴宾区" + }, { + "name": "忻城县" + }, { + "name": "象州县" + }, { + "name": "武宣县" + }, { + "name": "金秀瑶族自治县" + }, { + "name": "合山市" + }] + }, { + "name": "崇左市", + "area": [{ + "name": "江州区" + }, { + "name": "扶绥县" + }, { + "name": "宁明县" + }, { + "name": "龙州县" + }, { + "name": "大新县" + }, { + "name": "天等县" + }, { + "name": "凭祥市" + }] + }] +}, { + "name": "海南省", + "city": [{ + "name": "海口市", + "area": [{ + "name": "秀英区" + }, { + "name": "龙华区" + }, { + "name": "琼山区" + }, { + "name": "美兰区" + }] + }, { + "name": "三亚市", + "area": [{ + "name": "海棠区" + }, { + "name": "吉阳区" + }, { + "name": "天涯区" + }, { + "name": "崖州区" + }] + }, { + "name": "三沙市", + "area": [{ + "name": "西沙群岛" + }, { + "name": "南沙群岛" + }, { + "name": "中沙群岛" + }] + }, { + "name": "直辖县级", + "area": [{ + "name": "五指山市" + }, { + "name": "琼海市" + }, { + "name": "儋州市" + }, { + "name": "文昌市" + }, { + "name": "万宁市" + }, { + "name": "东方市" + }, { + "name": "定安县" + }, { + "name": "屯昌县" + }, { + "name": "澄迈县" + }, { + "name": "临高县" + }, { + "name": "白沙黎族自治县" + }, { + "name": "昌江黎族自治县" + }, { + "name": "乐东黎族自治县" + }, { + "name": "陵水黎族自治县" + }, { + "name": "保亭黎族苗族自治县" + }, { + "name": "琼中黎族苗族自治县" + }] + }] +}, { + "name": "重庆", + "city": [{ + "name": "重庆市", + "area": [{ + "name": "万州区" + }, { + "name": "涪陵区" + }, { + "name": "渝中区" + }, { + "name": "大渡口区" + }, { + "name": "江北区" + }, { + "name": "沙坪坝区" + }, { + "name": "九龙坡区" + }, { + "name": "南岸区" + }, { + "name": "北碚区" + }, { + "name": "綦江区" + }, { + "name": "大足区" + }, { + "name": "渝北区" + }, { + "name": "巴南区" + }, { + "name": "黔江区" + }, { + "name": "长寿区" + }, { + "name": "江津区" + }, { + "name": "合川区" + }, { + "name": "永川区" + }, { + "name": "南川区" + }, { + "name": "璧山区" + }, { + "name": "铜梁区" + }, { + "name": "潼南县" + }, { + "name": "荣昌县" + }, { + "name": "梁平县" + }, { + "name": "城口县" + }, { + "name": "丰都县" + }, { + "name": "垫江县" + }, { + "name": "武隆县" + }, { + "name": "忠县" + }, { + "name": "开县" + }, { + "name": "云阳县" + }, { + "name": "奉节县" + }, { + "name": "巫山县" + }, { + "name": "巫溪县" + }, { + "name": "石柱土家族自治县" + }, { + "name": "秀山土家族苗族自治县" + }, { + "name": "酉阳土家族苗族自治县" + }, { + "name": "彭水苗族土家族自治县" + }] + }, { + "name": "两江新区", + "area": [{ + "name": "北部新区" + }, { + "name": "保税港区" + }, { + "name": "工业园区" + }] + }] +}, { + "name": "四川省", + "city": [{ + "name": "成都市", + "area": [{ + "name": "锦江区" + }, { + "name": "青羊区" + }, { + "name": "金牛区" + }, { + "name": "武侯区" + }, { + "name": "成华区" + }, { + "name": "龙泉驿区" + }, { + "name": "青白江区" + }, { + "name": "新都区" + }, { + "name": "温江区" + }, { + "name": "金堂县" + }, { + "name": "双流县" + }, { + "name": "郫县" + }, { + "name": "大邑县" + }, { + "name": "蒲江县" + }, { + "name": "新津县" + }, { + "name": "都江堰市" + }, { + "name": "彭州市" + }, { + "name": "邛崃市" + }, { + "name": "崇州市" + }] + }, { + "name": "自贡市", + "area": [{ + "name": "自流井区" + }, { + "name": "贡井区" + }, { + "name": "大安区" + }, { + "name": "沿滩区" + }, { + "name": "荣县" + }, { + "name": "富顺县" + }] + }, { + "name": "攀枝花市", + "area": [{ + "name": "东区" + }, { + "name": "西区" + }, { + "name": "仁和区" + }, { + "name": "米易县" + }, { + "name": "盐边县" + }] + }, { + "name": "泸州市", + "area": [{ + "name": "江阳区" + }, { + "name": "纳溪区" + }, { + "name": "龙马潭区" + }, { + "name": "泸县" + }, { + "name": "合江县" + }, { + "name": "叙永县" + }, { + "name": "古蔺县" + }] + }, { + "name": "德阳市", + "area": [{ + "name": "旌阳区" + }, { + "name": "中江县" + }, { + "name": "罗江县" + }, { + "name": "广汉市" + }, { + "name": "什邡市" + }, { + "name": "绵竹市" + }] + }, { + "name": "绵阳市", + "area": [{ + "name": "涪城区" + }, { + "name": "游仙区" + }, { + "name": "三台县" + }, { + "name": "盐亭县" + }, { + "name": "安县" + }, { + "name": "梓潼县" + }, { + "name": "北川羌族自治县" + }, { + "name": "平武县" + }, { + "name": "江油市" + }] + }, { + "name": "广元市", + "area": [{ + "name": "利州区" + }, { + "name": "昭化区" + }, { + "name": "朝天区" + }, { + "name": "旺苍县" + }, { + "name": "青川县" + }, { + "name": "剑阁县" + }, { + "name": "苍溪县" + }] + }, { + "name": "遂宁市", + "area": [{ + "name": "船山区" + }, { + "name": "安居区" + }, { + "name": "蓬溪县" + }, { + "name": "射洪县" + }, { + "name": "大英县" + }] + }, { + "name": "内江市", + "area": [{ + "name": "市中区" + }, { + "name": "东兴区" + }, { + "name": "威远县" + }, { + "name": "资中县" + }, { + "name": "隆昌县" + }] + }, { + "name": "乐山市", + "area": [{ + "name": "市中区" + }, { + "name": "沙湾区" + }, { + "name": "五通桥区" + }, { + "name": "金口河区" + }, { + "name": "犍为县" + }, { + "name": "井研县" + }, { + "name": "夹江县" + }, { + "name": "沐川县" + }, { + "name": "峨边彝族自治县" + }, { + "name": "马边彝族自治县" + }, { + "name": "峨眉山市" + }] + }, { + "name": "南充市", + "area": [{ + "name": "顺庆区" + }, { + "name": "高坪区" + }, { + "name": "嘉陵区" + }, { + "name": "南部县" + }, { + "name": "营山县" + }, { + "name": "蓬安县" + }, { + "name": "仪陇县" + }, { + "name": "西充县" + }, { + "name": "阆中市" + }] + }, { + "name": "眉山市", + "area": [{ + "name": "东坡区" + }, { + "name": "彭山区" + }, { + "name": "仁寿县" + }, { + "name": "洪雅县" + }, { + "name": "丹棱县" + }, { + "name": "青神县" + }] + }, { + "name": "宜宾市", + "area": [{ + "name": "翠屏区" + }, { + "name": "南溪区" + }, { + "name": "宜宾县" + }, { + "name": "江安县" + }, { + "name": "长宁县" + }, { + "name": "高县" + }, { + "name": "珙县" + }, { + "name": "筠连县" + }, { + "name": "兴文县" + }, { + "name": "屏山县" + }] + }, { + "name": "广安市", + "area": [{ + "name": "广安区" + }, { + "name": "前锋区" + }, { + "name": "岳池县" + }, { + "name": "武胜县" + }, { + "name": "邻水县" + }, { + "name": "华蓥市" + }] + }, { + "name": "达州市", + "area": [{ + "name": "通川区" + }, { + "name": "达川区" + }, { + "name": "宣汉县" + }, { + "name": "开江县" + }, { + "name": "大竹县" + }, { + "name": "渠县" + }, { + "name": "万源市" + }] + }, { + "name": "雅安市", + "area": [{ + "name": "雨城区" + }, { + "name": "名山区" + }, { + "name": "荥经县" + }, { + "name": "汉源县" + }, { + "name": "石棉县" + }, { + "name": "天全县" + }, { + "name": "芦山县" + }, { + "name": "宝兴县" + }] + }, { + "name": "巴中市", + "area": [{ + "name": "巴州区" + }, { + "name": "恩阳区" + }, { + "name": "通江县" + }, { + "name": "南江县" + }, { + "name": "平昌县" + }] + }, { + "name": "资阳市", + "area": [{ + "name": "雁江区" + }, { + "name": "安岳县" + }, { + "name": "乐至县" + }, { + "name": "简阳市" + }] + }, { + "name": "阿坝藏族羌族自治州", + "area": [{ + "name": "汶川县" + }, { + "name": "理县" + }, { + "name": "茂县" + }, { + "name": "松潘县" + }, { + "name": "九寨沟县" + }, { + "name": "金川县" + }, { + "name": "小金县" + }, { + "name": "黑水县" + }, { + "name": "马尔康县" + }, { + "name": "壤塘县" + }, { + "name": "阿坝县" + }, { + "name": "若尔盖县" + }, { + "name": "红原县" + }] + }, { + "name": "甘孜藏族自治州", + "area": [{ + "name": "康定县" + }, { + "name": "泸定县" + }, { + "name": "丹巴县" + }, { + "name": "九龙县" + }, { + "name": "雅江县" + }, { + "name": "道孚县" + }, { + "name": "炉霍县" + }, { + "name": "甘孜县" + }, { + "name": "新龙县" + }, { + "name": "德格县" + }, { + "name": "白玉县" + }, { + "name": "石渠县" + }, { + "name": "色达县" + }, { + "name": "理塘县" + }, { + "name": "巴塘县" + }, { + "name": "乡城县" + }, { + "name": "稻城县" + }, { + "name": "得荣县" + }] + }, { + "name": "凉山彝族自治州", + "area": [{ + "name": "西昌市" + }, { + "name": "木里藏族自治县" + }, { + "name": "盐源县" + }, { + "name": "德昌县" + }, { + "name": "会理县" + }, { + "name": "会东县" + }, { + "name": "宁南县" + }, { + "name": "普格县" + }, { + "name": "布拖县" + }, { + "name": "金阳县" + }, { + "name": "昭觉县" + }, { + "name": "喜德县" + }, { + "name": "冕宁县" + }, { + "name": "越西县" + }, { + "name": "甘洛县" + }, { + "name": "美姑县" + }, { + "name": "雷波县" + }] + }] +}, { + "name": "贵州省", + "city": [{ + "name": "贵阳市", + "area": [{ + "name": "南明区" + }, { + "name": "云岩区" + }, { + "name": "花溪区" + }, { + "name": "乌当区" + }, { + "name": "白云区" + }, { + "name": "观山湖区" + }, { + "name": "开阳县" + }, { + "name": "息烽县" + }, { + "name": "修文县" + }, { + "name": "清镇市" + }] + }, { + "name": "六盘水市", + "area": [{ + "name": "钟山区" + }, { + "name": "六枝特区" + }, { + "name": "水城县" + }, { + "name": "盘县" + }] + }, { + "name": "遵义市", + "area": [{ + "name": "红花岗区" + }, { + "name": "汇川区" + }, { + "name": "遵义县" + }, { + "name": "桐梓县" + }, { + "name": "绥阳县" + }, { + "name": "正安县" + }, { + "name": "道真仡佬族苗族自治县" + }, { + "name": "务川仡佬族苗族自治县" + }, { + "name": "凤冈县" + }, { + "name": "湄潭县" + }, { + "name": "余庆县" + }, { + "name": "习水县" + }, { + "name": "赤水市" + }, { + "name": "仁怀市" + }] + }, { + "name": "安顺市", + "area": [{ + "name": "西秀区" + }, { + "name": "平坝区" + }, { + "name": "普定县" + }, { + "name": "镇宁布依族苗族自治县" + }, { + "name": "关岭布依族苗族自治县" + }, { + "name": "紫云苗族布依族自治县" + }] + }, { + "name": "毕节市", + "area": [{ + "name": "七星关区" + }, { + "name": "大方县" + }, { + "name": "黔西县" + }, { + "name": "金沙县" + }, { + "name": "织金县" + }, { + "name": "纳雍县" + }, { + "name": "威宁彝族回族苗族自治县" + }, { + "name": "赫章县" + }] + }, { + "name": "铜仁市", + "area": [{ + "name": "碧江区" + }, { + "name": "万山区" + }, { + "name": "江口县" + }, { + "name": "玉屏侗族自治县" + }, { + "name": "石阡县" + }, { + "name": "思南县" + }, { + "name": "印江土家族苗族自治县" + }, { + "name": "德江县" + }, { + "name": "沿河土家族自治县" + }, { + "name": "松桃苗族自治县" + }] + }, { + "name": "黔西南布依族苗族自治州", + "area": [{ + "name": "兴义市 " + }, { + "name": "兴仁县" + }, { + "name": "普安县" + }, { + "name": "晴隆县" + }, { + "name": "贞丰县" + }, { + "name": "望谟县" + }, { + "name": "册亨县" + }, { + "name": "安龙县" + }] + }, { + "name": "黔东南苗族侗族自治州", + "area": [{ + "name": "凯里市" + }, { + "name": "黄平县" + }, { + "name": "施秉县" + }, { + "name": "三穗县" + }, { + "name": "镇远县" + }, { + "name": "岑巩县" + }, { + "name": "天柱县" + }, { + "name": "锦屏县" + }, { + "name": "剑河县" + }, { + "name": "台江县" + }, { + "name": "黎平县" + }, { + "name": "榕江县" + }, { + "name": "从江县" + }, { + "name": "雷山县" + }, { + "name": "麻江县" + }, { + "name": "丹寨县" + }] + }, { + "name": "黔南布依族苗族自治州", + "area": [{ + "name": "都匀市" + }, { + "name": "福泉市" + }, { + "name": "荔波县" + }, { + "name": "贵定县" + }, { + "name": "瓮安县" + }, { + "name": "独山县" + }, { + "name": "平塘县" + }, { + "name": "罗甸县" + }, { + "name": "长顺县" + }, { + "name": "龙里县" + }, { + "name": "惠水县" + }, { + "name": "三都水族自治县" + }] + }] +}, { + "name": "云南省", + "city": [{ + "name": "昆明市", + "area": [{ + "name": "五华区" + }, { + "name": "盘龙区" + }, { + "name": "官渡区" + }, { + "name": "西山区" + }, { + "name": "东川区" + }, { + "name": "呈贡区" + }, { + "name": "晋宁县" + }, { + "name": "富民县" + }, { + "name": "宜良县" + }, { + "name": "石林彝族自治县" + }, { + "name": "嵩明县" + }, { + "name": "禄劝彝族苗族自治县" + }, { + "name": "寻甸回族彝族自治县 " + }, { + "name": "安宁市" + }] + }, { + "name": "曲靖市", + "area": [{ + "name": "麒麟区" + }, { + "name": "马龙县" + }, { + "name": "陆良县" + }, { + "name": "师宗县" + }, { + "name": "罗平县" + }, { + "name": "富源县" + }, { + "name": "会泽县" + }, { + "name": "沾益县" + }, { + "name": "宣威市" + }] + }, { + "name": "玉溪市", + "area": [{ + "name": "红塔区" + }, { + "name": "江川县" + }, { + "name": "澄江县" + }, { + "name": "通海县" + }, { + "name": "华宁县" + }, { + "name": "易门县" + }, { + "name": "峨山彝族自治县" + }, { + "name": "新平彝族傣族自治县" + }, { + "name": "元江哈尼族彝族傣族自治县" + }] + }, { + "name": "保山市", + "area": [{ + "name": "隆阳区" + }, { + "name": "施甸县" + }, { + "name": "腾冲县" + }, { + "name": "龙陵县" + }, { + "name": "昌宁县" + }] + }, { + "name": "昭通市", + "area": [{ + "name": "昭阳区" + }, { + "name": "鲁甸县" + }, { + "name": "巧家县" + }, { + "name": "盐津县" + }, { + "name": "大关县" + }, { + "name": "永善县" + }, { + "name": "绥江县" + }, { + "name": "镇雄县" + }, { + "name": "彝良县" + }, { + "name": "威信县" + }, { + "name": "水富县" + }] + }, { + "name": "丽江市", + "area": [{ + "name": "古城区" + }, { + "name": "玉龙纳西族自治县" + }, { + "name": "永胜县" + }, { + "name": "华坪县" + }, { + "name": "宁蒗彝族自治县" + }] + }, { + "name": "普洱市", + "area": [{ + "name": "思茅区" + }, { + "name": "宁洱哈尼族彝族自治县" + }, { + "name": "墨江哈尼族自治县" + }, { + "name": "景东彝族自治县" + }, { + "name": "景谷傣族彝族自治县" + }, { + "name": "镇沅彝族哈尼族拉祜族自治县" + }, { + "name": "江城哈尼族彝族自治县" + }, { + "name": "孟连傣族拉祜族佤族自治县" + }, { + "name": "澜沧拉祜族自治县" + }, { + "name": "西盟佤族自治县" + }] + }, { + "name": "临沧市", + "area": [{ + "name": "临翔区" + }, { + "name": "凤庆县" + }, { + "name": "云县" + }, { + "name": "永德县" + }, { + "name": "镇康县" + }, { + "name": "双江拉祜族佤族布朗族傣族自治县" + }, { + "name": "耿马傣族佤族自治县" + }, { + "name": "沧源佤族自治县" + }] + }, { + "name": "楚雄彝族自治州", + "area": [{ + "name": "楚雄市" + }, { + "name": "双柏县" + }, { + "name": "牟定县" + }, { + "name": "南华县" + }, { + "name": "姚安县" + }, { + "name": "大姚县" + }, { + "name": "永仁县" + }, { + "name": "元谋县" + }, { + "name": "武定县" + }, { + "name": "禄丰县" + }] + }, { + "name": "红河哈尼族彝族自治州", + "area": [{ + "name": "个旧市" + }, { + "name": "开远市" + }, { + "name": "蒙自市" + }, { + "name": "弥勒市" + }, { + "name": "屏边苗族自治县" + }, { + "name": "建水县" + }, { + "name": "石屏县" + }, { + "name": "泸西县" + }, { + "name": "元阳县" + }, { + "name": "红河县" + }, { + "name": "金平苗族瑶族傣族自治县" + }, { + "name": "绿春县" + }, { + "name": "河口瑶族自治县" + }] + }, { + "name": "文山壮族苗族自治州", + "area": [{ + "name": "文山市" + }, { + "name": "砚山县" + }, { + "name": "西畴县" + }, { + "name": "麻栗坡县" + }, { + "name": "马关县" + }, { + "name": "丘北县" + }, { + "name": "广南县" + }, { + "name": "富宁县" + }] + }, { + "name": "西双版纳傣族自治州", + "area": [{ + "name": "景洪市" + }, { + "name": "勐海县" + }, { + "name": "勐腊县" + }] + }, { + "name": "大理白族自治州", + "area": [{ + "name": "大理市" + }, { + "name": "漾濞彝族自治县" + }, { + "name": "祥云县" + }, { + "name": "宾川县" + }, { + "name": "弥渡县" + }, { + "name": "南涧彝族自治县" + }, { + "name": "巍山彝族回族自治县" + }, { + "name": "永平县" + }, { + "name": "云龙县" + }, { + "name": "洱源县" + }, { + "name": "剑川县" + }, { + "name": "鹤庆县" + }] + }, { + "name": "德宏傣族景颇族自治州", + "area": [{ + "name": "瑞丽市" + }, { + "name": "芒市" + }, { + "name": "梁河县" + }, { + "name": "盈江县" + }, { + "name": "陇川县" + }] + }, { + "name": "怒江傈僳族自治州", + "area": [{ + "name": "泸水县" + }, { + "name": "福贡县" + }, { + "name": "贡山独龙族怒族自治县" + }, { + "name": "兰坪白族普米族自治县" + }] + }, { + "name": "迪庆藏族自治州", + "area": [{ + "name": "香格里拉市" + }, { + "name": "德钦县" + }, { + "name": "维西傈僳族自治县" + }] + }] +}, { + "name": "西藏自治区", + "city": [{ + "name": "拉萨市", + "area": [{ + "name": "城关区" + }, { + "name": "林周县" + }, { + "name": "当雄县" + }, { + "name": "尼木县" + }, { + "name": "曲水县" + }, { + "name": "堆龙德庆县" + }, { + "name": "达孜县" + }, { + "name": "墨竹工卡县" + }] + }, { + "name": "日喀则市", + "area": [{ + "name": "桑珠孜区" + }, { + "name": "南木林县" + }, { + "name": "江孜县" + }, { + "name": "定日县" + }, { + "name": "萨迦县" + }, { + "name": "拉孜县" + }, { + "name": "昂仁县" + }, { + "name": "谢通门县" + }, { + "name": "白朗县" + }, { + "name": "仁布县" + }, { + "name": "康马县" + }, { + "name": "定结县" + }, { + "name": "仲巴县" + }, { + "name": "亚东县" + }, { + "name": "吉隆县" + }, { + "name": "聂拉木县" + }, { + "name": "萨嘎县" + }, { + "name": "岗巴县" + }] + }, { + "name": "昌都市", + "area": [{ + "name": "卡若区" + }, { + "name": "江达县" + }, { + "name": "贡觉县" + }, { + "name": "类乌齐县" + }, { + "name": "丁青县" + }, { + "name": "察雅县" + }, { + "name": "八宿县" + }, { + "name": "左贡县" + }, { + "name": "芒康县" + }, { + "name": "洛隆县" + }, { + "name": "边坝县" + }] + }, { + "name": "山南地区", + "area": [{ + "name": "乃东县" + }, { + "name": "扎囊县" + }, { + "name": "贡嘎县" + }, { + "name": "桑日县" + }, { + "name": "琼结县" + }, { + "name": "曲松县" + }, { + "name": "措美县" + }, { + "name": "洛扎县" + }, { + "name": "加查县" + }, { + "name": "隆子县" + }, { + "name": "错那县" + }, { + "name": "浪卡子县" + }] + }, { + "name": "那曲地区", + "area": [{ + "name": "那曲县" + }, { + "name": "嘉黎县" + }, { + "name": "比如县" + }, { + "name": "聂荣县" + }, { + "name": "安多县" + }, { + "name": "申扎县" + }, { + "name": "索县" + }, { + "name": "班戈县" + }, { + "name": "巴青县" + }, { + "name": "尼玛县" + }, { + "name": "双湖县" + }] + }, { + "name": "阿里地区", + "area": [{ + "name": "普兰县" + }, { + "name": "札达县" + }, { + "name": "噶尔县" + }, { + "name": "日土县" + }, { + "name": "革吉县" + }, { + "name": "改则县" + }, { + "name": "措勤县" + }] + }, { + "name": "林芝地区", + "area": [{ + "name": "林芝县" + }, { + "name": "工布江达县" + }, { + "name": "米林县" + }, { + "name": "墨脱县" + }, { + "name": "波密县" + }, { + "name": "察隅县" + }, { + "name": "朗县" + }] + }] +}, { + "name": "陕西省", + "city": [{ + "name": "西安市", + "area": [{ + "name": "新城区" + }, { + "name": "碑林区" + }, { + "name": "莲湖区" + }, { + "name": "灞桥区" + }, { + "name": "未央区" + }, { + "name": "雁塔区" + }, { + "name": "阎良区" + }, { + "name": "临潼区" + }, { + "name": "长安区" + }, { + "name": "蓝田县" + }, { + "name": "周至县" + }, { + "name": "户县" + }, { + "name": "高陵区" + }] + }, { + "name": "铜川市", + "area": [{ + "name": "王益区" + }, { + "name": "印台区" + }, { + "name": "耀州区" + }, { + "name": "宜君县" + }] + }, { + "name": "宝鸡市", + "area": [{ + "name": "渭滨区" + }, { + "name": "金台区" + }, { + "name": "陈仓区" + }, { + "name": "凤翔县" + }, { + "name": "岐山县" + }, { + "name": "扶风县" + }, { + "name": "眉县" + }, { + "name": "陇县" + }, { + "name": "千阳县" + }, { + "name": "麟游县" + }, { + "name": "凤县" + }, { + "name": "太白县" + }] + }, { + "name": "咸阳市", + "area": [{ + "name": "秦都区" + }, { + "name": "杨陵区" + }, { + "name": "渭城区" + }, { + "name": "三原县" + }, { + "name": "泾阳县" + }, { + "name": "乾县" + }, { + "name": "礼泉县" + }, { + "name": "永寿县" + }, { + "name": "彬县" + }, { + "name": "长武县" + }, { + "name": "旬邑县" + }, { + "name": "淳化县" + }, { + "name": "武功县" + }, { + "name": "兴平市" + }] + }, { + "name": "渭南市", + "area": [{ + "name": "临渭区" + }, { + "name": "华县" + }, { + "name": "潼关县" + }, { + "name": "大荔县" + }, { + "name": "合阳县" + }, { + "name": "澄城县" + }, { + "name": "蒲城县" + }, { + "name": "白水县" + }, { + "name": "富平县" + }, { + "name": "韩城市" + }, { + "name": "华阴市" + }] + }, { + "name": "延安市", + "area": [{ + "name": "宝塔区" + }, { + "name": "延长县" + }, { + "name": "延川县" + }, { + "name": "子长县" + }, { + "name": "安塞县" + }, { + "name": "志丹县" + }, { + "name": "吴起县" + }, { + "name": "甘泉县" + }, { + "name": "富县" + }, { + "name": "洛川县" + }, { + "name": "宜川县" + }, { + "name": "黄龙县" + }, { + "name": "黄陵县" + }] + }, { + "name": "汉中市", + "area": [{ + "name": "汉台区" + }, { + "name": "南郑县" + }, { + "name": "城固县" + }, { + "name": "洋县" + }, { + "name": "西乡县" + }, { + "name": "勉县" + }, { + "name": "宁强县" + }, { + "name": "略阳县" + }, { + "name": "镇巴县" + }, { + "name": "留坝县" + }, { + "name": "佛坪县" + }] + }, { + "name": "榆林市", + "area": [{ + "name": "榆阳区" + }, { + "name": "神木县" + }, { + "name": "府谷县" + }, { + "name": "横山县" + }, { + "name": "靖边县" + }, { + "name": "定边县" + }, { + "name": "绥德县" + }, { + "name": "米脂县" + }, { + "name": "佳县" + }, { + "name": "吴堡县" + }, { + "name": "清涧县" + }, { + "name": "子洲县" + }] + }, { + "name": "安康市", + "area": [{ + "name": "汉滨区" + }, { + "name": "汉阴县" + }, { + "name": "石泉县" + }, { + "name": "宁陕县" + }, { + "name": "紫阳县" + }, { + "name": "岚皋县" + }, { + "name": "平利县" + }, { + "name": "镇坪县" + }, { + "name": "旬阳县" + }, { + "name": "白河县" + }] + }, { + "name": "商洛市", + "area": [{ + "name": "商州区" + }, { + "name": "洛南县" + }, { + "name": "丹凤县" + }, { + "name": "商南县" + }, { + "name": "山阳县" + }, { + "name": "镇安县" + }, { + "name": "柞水县" + }] + }, { + "name": "西咸新区", + "area": [{ + "name": "空港新城" + }, { + "name": "沣东新城" + }, { + "name": "秦汉新城" + }, { + "name": "沣西新城" + }, { + "name": "泾河新城" + }] + }] +}, { + "name": "甘肃省", + "city": [{ + "name": "兰州市", + "area": [{ + "name": "城关区" + }, { + "name": "七里河区" + }, { + "name": "西固区" + }, { + "name": "安宁区" + }, { + "name": "红古区" + }, { + "name": "永登县" + }, { + "name": "皋兰县" + }, { + "name": "榆中县" + }] + }, { + "name": "嘉峪关市", + "area": [{ + "name": "雄关区" + }, { + "name": "长城区" + }, { + "name": "镜铁区" + }] + }, { + "name": "金昌市", + "area": [{ + "name": "金川区" + }, { + "name": "永昌县" + }] + }, { + "name": "白银市", + "area": [{ + "name": "白银区" + }, { + "name": "平川区" + }, { + "name": "靖远县" + }, { + "name": "会宁县" + }, { + "name": "景泰县" + }] + }, { + "name": "天水市", + "area": [{ + "name": "秦州区" + }, { + "name": "麦积区" + }, { + "name": "清水县" + }, { + "name": "秦安县" + }, { + "name": "甘谷县" + }, { + "name": "武山县" + }, { + "name": "张家川回族自治县" + }] + }, { + "name": "武威市", + "area": [{ + "name": "凉州区" + }, { + "name": "民勤县" + }, { + "name": "古浪县" + }, { + "name": "天祝藏族自治县" + }] + }, { + "name": "张掖市", + "area": [{ + "name": "甘州区" + }, { + "name": "肃南裕固族自治县" + }, { + "name": "民乐县" + }, { + "name": "临泽县" + }, { + "name": "高台县" + }, { + "name": "山丹县" + }] + }, { + "name": "平凉市", + "area": [{ + "name": "崆峒区" + }, { + "name": "泾川县" + }, { + "name": "灵台县" + }, { + "name": "崇信县" + }, { + "name": "华亭县" + }, { + "name": "庄浪县" + }, { + "name": "静宁县" + }] + }, { + "name": "酒泉市", + "area": [{ + "name": "肃州区" + }, { + "name": "金塔县" + }, { + "name": "瓜州县" + }, { + "name": "肃北蒙古族自治县" + }, { + "name": "阿克塞哈萨克族自治县" + }, { + "name": "玉门市" + }, { + "name": "敦煌市" + }] + }, { + "name": "庆阳市", + "area": [{ + "name": "西峰区" + }, { + "name": "庆城县" + }, { + "name": "环县" + }, { + "name": "华池县" + }, { + "name": "合水县" + }, { + "name": "正宁县" + }, { + "name": "宁县" + }, { + "name": "镇原县" + }] + }, { + "name": "定西市", + "area": [{ + "name": "安定区" + }, { + "name": "通渭县" + }, { + "name": "陇西县" + }, { + "name": "渭源县" + }, { + "name": "临洮县" + }, { + "name": "漳县" + }, { + "name": "岷县" + }] + }, { + "name": "陇南市", + "area": [{ + "name": "武都区" + }, { + "name": "成县" + }, { + "name": "文县" + }, { + "name": "宕昌县" + }, { + "name": "康县" + }, { + "name": "西和县" + }, { + "name": "礼县" + }, { + "name": "徽县" + }, { + "name": "两当县" + }] + }, { + "name": "临夏回族自治州", + "area": [{ + "name": "临夏市" + }, { + "name": "临夏县" + }, { + "name": "康乐县" + }, { + "name": "永靖县" + }, { + "name": "广河县" + }, { + "name": "和政县" + }, { + "name": "东乡族自治县" + }, { + "name": "积石山保安族东乡族撒拉族自治县" + }] + }, { + "name": "甘南藏族自治州", + "area": [{ + "name": "合作市" + }, { + "name": "临潭县" + }, { + "name": "卓尼县" + }, { + "name": "舟曲县" + }, { + "name": "迭部县" + }, { + "name": "玛曲县" + }, { + "name": "碌曲县" + }, { + "name": "夏河县" + }] + }] +}, { + "name": "青海省", + "city": [{ + "name": "西宁市", + "area": [{ + "name": "城东区" + }, { + "name": "城中区" + }, { + "name": "城西区" + }, { + "name": "城北区" + }, { + "name": "大通回族土族自治县" + }, { + "name": "湟中县" + }, { + "name": "湟源县" + }] + }, { + "name": "海东市", + "area": [{ + "name": "乐都区" + }, { + "name": "平安县" + }, { + "name": "民和回族土族自治县" + }, { + "name": "互助土族自治县" + }, { + "name": "化隆回族自治县" + }, { + "name": "循化撒拉族自治县" + }] + }, { + "name": "海北藏族自治州", + "area": [{ + "name": "门源回族自治县" + }, { + "name": "祁连县" + }, { + "name": "海晏县" + }, { + "name": "刚察县" + }] + }, { + "name": "黄南藏族自治州", + "area": [{ + "name": "同仁县" + }, { + "name": "尖扎县" + }, { + "name": "泽库县" + }, { + "name": "河南蒙古族自治县" + }] + }, { + "name": "海南藏族自治州", + "area": [{ + "name": "共和县" + }, { + "name": "同德县" + }, { + "name": "贵德县" + }, { + "name": "兴海县" + }, { + "name": "贵南县" + }] + }, { + "name": "果洛藏族自治州", + "area": [{ + "name": "玛沁县" + }, { + "name": "班玛县" + }, { + "name": "甘德县" + }, { + "name": "达日县" + }, { + "name": "久治县" + }, { + "name": "玛多县" + }] + }, { + "name": "玉树藏族自治州", + "area": [{ + "name": "玉树市" + }, { + "name": "杂多县" + }, { + "name": "称多县" + }, { + "name": "治多县" + }, { + "name": "囊谦县" + }, { + "name": "曲麻莱县" + }] + }, { + "name": "海西蒙古族藏族自治州", + "area": [{ + "name": "格尔木市" + }, { + "name": "德令哈市" + }, { + "name": "乌兰县" + }, { + "name": "都兰县" + }, { + "name": "天峻县" + }] + }] +}, { + "name": "宁夏回族自治区", + "city": [{ + "name": "银川市", + "area": [{ + "name": "兴庆区" + }, { + "name": "西夏区" + }, { + "name": "金凤区" + }, { + "name": "永宁县" + }, { + "name": "贺兰县" + }, { + "name": "灵武市" + }] + }, { + "name": "石嘴山市", + "area": [{ + "name": "大武口区" + }, { + "name": "惠农区" + }, { + "name": "平罗县" + }] + }, { + "name": "吴忠市", + "area": [{ + "name": "利通区" + }, { + "name": "红寺堡区" + }, { + "name": "盐池县" + }, { + "name": "同心县" + }, { + "name": "青铜峡市" + }] + }, { + "name": "固原市", + "area": [{ + "name": "原州区" + }, { + "name": "西吉县" + }, { + "name": "隆德县" + }, { + "name": "泾源县" + }, { + "name": "彭阳县" + }] + }, { + "name": "中卫市", + "area": [{ + "name": "沙坡头区" + }, { + "name": "中宁县" + }, { + "name": "海原县" + }] + }] +}, { + "name": "新疆维吾尔自治区", + "city": [{ + "name": "乌鲁木齐市", + "area": [{ + "name": "天山区" + }, { + "name": "沙依巴克区" + }, { + "name": "新市区" + }, { + "name": "水磨沟区" + }, { + "name": "头屯河区" + }, { + "name": "达坂城区" + }, { + "name": "米东区" + }, { + "name": "乌鲁木齐县" + }] + }, { + "name": "克拉玛依市", + "area": [{ + "name": "独山子区" + }, { + "name": "克拉玛依区" + }, { + "name": "白碱滩区" + }, { + "name": "乌尔禾区" + }] + }, { + "name": "吐鲁番地区", + "area": [{ + "name": "吐鲁番市" + }, { + "name": "鄯善县" + }, { + "name": "托克逊县" + }] + }, { + "name": "哈密地区", + "area": [{ + "name": "哈密市" + }, { + "name": "巴里坤哈萨克自治县" + }, { + "name": "伊吾县" + }] + }, { + "name": "昌吉回族自治州", + "area": [{ + "name": "昌吉市" + }, { + "name": "阜康市" + }, { + "name": "呼图壁县" + }, { + "name": "玛纳斯县" + }, { + "name": "奇台县" + }, { + "name": "吉木萨尔县" + }, { + "name": "木垒哈萨克自治县" + }] + }, { + "name": "博尔塔拉蒙古自治州", + "area": [{ + "name": "博乐市" + }, { + "name": "阿拉山口市" + }, { + "name": "精河县" + }, { + "name": "温泉县" + }] + }, { + "name": "巴音郭楞蒙古自治州", + "area": [{ + "name": "库尔勒市" + }, { + "name": "轮台县" + }, { + "name": "尉犁县" + }, { + "name": "若羌县" + }, { + "name": "且末县" + }, { + "name": "焉耆回族自治县" + }, { + "name": "和静县" + }, { + "name": "和硕县" + }, { + "name": "博湖县" + }] + }, { + "name": "阿克苏地区", + "area": [{ + "name": "阿克苏市" + }, { + "name": "温宿县" + }, { + "name": "库车县" + }, { + "name": "沙雅县" + }, { + "name": "新和县" + }, { + "name": "拜城县" + }, { + "name": "乌什县" + }, { + "name": "阿瓦提县" + }, { + "name": "柯坪县" + }] + }, { + "name": "克孜勒苏柯尔克孜自治州", + "area": [{ + "name": "阿图什市" + }, { + "name": "阿克陶县" + }, { + "name": "阿合奇县" + }, { + "name": "乌恰县" + }] + }, { + "name": "喀什地区", + "area": [{ + "name": "喀什市" + }, { + "name": "疏附县" + }, { + "name": "疏勒县" + }, { + "name": "英吉沙县" + }, { + "name": "泽普县" + }, { + "name": "莎车县" + }, { + "name": "叶城县" + }, { + "name": "麦盖提县" + }, { + "name": "岳普湖县" + }, { + "name": "伽师县" + }, { + "name": "巴楚县" + }, { + "name": "塔什库尔干塔吉克自治县" + }] + }, { + "name": "和田地区", + "area": [{ + "name": "和田市" + }, { + "name": "和田县" + }, { + "name": "墨玉县" + }, { + "name": "皮山县" + }, { + "name": "洛浦县" + }, { + "name": "策勒县" + }, { + "name": "于田县" + }, { + "name": "民丰县" + }] + }, { + "name": "伊犁哈萨克自治州", + "area": [{ + "name": "伊宁市" + }, { + "name": "奎屯市" + }, { + "name": "霍尔果斯市" + }, { + "name": "伊宁县" + }, { + "name": "察布查尔锡伯自治县" + }, { + "name": "霍城县" + }, { + "name": "巩留县" + }, { + "name": "新源县" + }, { + "name": "昭苏县" + }, { + "name": "特克斯县" + }, { + "name": "尼勒克县" + }] + }, { + "name": "塔城地区", + "area": [{ + "name": "塔城市" + }, { + "name": "乌苏市" + }, { + "name": "额敏县" + }, { + "name": "沙湾县" + }, { + "name": "托里县" + }, { + "name": "裕民县" + }, { + "name": "和布克赛尔蒙古自治县" + }] + }, { + "name": "阿勒泰地区", + "area": [{ + "name": "阿勒泰市" + }, { + "name": "布尔津县" + }, { + "name": "富蕴县" + }, { + "name": "福海县" + }, { + "name": "哈巴河县" + }, { + "name": "青河县" + }, { + "name": "吉木乃县" + }] + }, { + "name": "直辖县级", + "area": [{ + "name": "石河子市" + }, { + "name": "阿拉尔市" + }, { + "name": "图木舒克市" + }, { + "name": "五家渠市" + }, { + "name": "北屯市" + }, { + "name": "铁门关市" + }, { + "name": "双河市" + }] + }] +}, { + "name": "台湾", + "city": [{ + "name": "台北市", + "area": [{ + "name": "松山区" + }, { + "name": "信义区" + }, { + "name": "大安区" + }, { + "name": "中山区" + }, { + "name": "中正区" + }, { + "name": "大同区" + }, { + "name": "万华区" + }, { + "name": "文山区" + }, { + "name": "南港区" + }, { + "name": "内湖区" + }, { + "name": "士林区" + }, { + "name": "北投区" + }] + }, { + "name": "高雄市", + "area": [{ + "name": "盐埕区" + }, { + "name": "鼓山区" + }, { + "name": "左营区" + }, { + "name": "楠梓区" + }, { + "name": "三民区" + }, { + "name": "新兴区" + }, { + "name": "前金区" + }, { + "name": "苓雅区" + }, { + "name": "前镇区" + }, { + "name": "旗津区" + }, { + "name": "小港区" + }, { + "name": "凤山区" + }, { + "name": "林园区" + }, { + "name": "大寮区" + }, { + "name": "大树区" + }, { + "name": "大社区" + }, { + "name": "仁武区" + }, { + "name": "鸟松区" + }, { + "name": "冈山区" + }, { + "name": "桥头区" + }, { + "name": "燕巢区" + }, { + "name": "田寮区" + }, { + "name": "阿莲区" + }, { + "name": "路竹区" + }, { + "name": "湖内区" + }, { + "name": "茄萣区" + }, { + "name": "永安区" + }, { + "name": "弥陀区" + }, { + "name": "梓官区" + }, { + "name": "旗山区" + }, { + "name": "美浓区" + }, { + "name": "六龟区" + }, { + "name": "甲仙区" + }, { + "name": "杉林区" + }, { + "name": "内门区" + }, { + "name": "茂林区" + }, { + "name": "桃源区" + }, { + "name": "那玛夏区" + }] + }, { + "name": "基隆市", + "area": [{ + "name": "中正区" + }, { + "name": "七堵区" + }, { + "name": "暖暖区" + }, { + "name": "仁爱区" + }, { + "name": "中山区" + }, { + "name": "安乐区" + }, { + "name": "信义区" + }] + }, { + "name": "台中市", + "area": [{ + "name": "中区" + }, { + "name": "东区" + }, { + "name": "南区" + }, { + "name": "西区" + }, { + "name": "北区" + }, { + "name": "西屯区" + }, { + "name": "南屯区" + }, { + "name": "北屯区" + }, { + "name": "丰原区" + }, { + "name": "东势区" + }, { + "name": "大甲区" + }, { + "name": "清水区" + }, { + "name": "沙鹿区" + }, { + "name": "梧栖区" + }, { + "name": "后里区" + }, { + "name": "神冈区" + }, { + "name": "潭子区" + }, { + "name": "大雅区" + }, { + "name": "新社区" + }, { + "name": "石冈区" + }, { + "name": "外埔区" + }, { + "name": "大安区" + }, { + "name": "乌日区" + }, { + "name": "大肚区" + }, { + "name": "龙井区" + }, { + "name": "雾峰区" + }, { + "name": "太平区" + }, { + "name": "大里区" + }, { + "name": "和平区" + }] + }, { + "name": "台南市", + "area": [{ + "name": "东区" + }, { + "name": "南区" + }, { + "name": "北区" + }, { + "name": "安南区" + }, { + "name": "安平区" + }, { + "name": "中西区" + }, { + "name": "新营区" + }, { + "name": "盐水区" + }, { + "name": "白河区" + }, { + "name": "柳营区" + }, { + "name": "后壁区" + }, { + "name": "东山区" + }, { + "name": "麻豆区" + }, { + "name": "下营区" + }, { + "name": "六甲区" + }, { + "name": "官田区" + }, { + "name": "大内区" + }, { + "name": "佳里区" + }, { + "name": "学甲区" + }, { + "name": "西港区" + }, { + "name": "七股区" + }, { + "name": "将军区" + }, { + "name": "北门区" + }, { + "name": "新化区" + }, { + "name": "善化区" + }, { + "name": "新市区" + }, { + "name": "安定区" + }, { + "name": "山上区" + }, { + "name": "玉井区" + }, { + "name": "楠西区" + }, { + "name": "南化区" + }, { + "name": "左镇区" + }, { + "name": "仁德区" + }, { + "name": "归仁区" + }, { + "name": "关庙区" + }, { + "name": "龙崎区" + }, { + "name": "永康区" + }] + }, { + "name": "新竹市", + "area": [{ + "name": "东区" + }, { + "name": "北区" + }, { + "name": "香山区" + }] + }, { + "name": "嘉义市", + "area": [{ + "name": "东区" + }, { + "name": "西区" + }] + }, { + "name": "新北市", + "area": [{ + "name": "板桥区" + }, { + "name": "三重区" + }, { + "name": "中和区" + }, { + "name": "永和区" + }, { + "name": "新庄区" + }, { + "name": "新店区" + }, { + "name": "树林区" + }, { + "name": "莺歌区" + }, { + "name": "三峡区" + }, { + "name": "淡水区" + }, { + "name": "汐止区" + }, { + "name": "瑞芳区" + }, { + "name": "土城区" + }, { + "name": "芦洲区" + }, { + "name": "五股区" + }, { + "name": "泰山区" + }, { + "name": "林口区" + }, { + "name": "深坑区" + }, { + "name": "石碇区" + }, { + "name": "坪林区" + }, { + "name": "三芝区" + }, { + "name": "石门区" + }, { + "name": "八里区" + }, { + "name": "平溪区" + }, { + "name": "双溪区" + }, { + "name": "贡寮区" + }, { + "name": "金山区" + }, { + "name": "万里区" + }, { + "name": "乌来区" + }] + }, { + "name": "宜兰县", + "area": [{ + "name": "宜兰市" + }, { + "name": "罗东镇" + }, { + "name": "苏澳镇" + }, { + "name": "头城镇" + }, { + "name": "礁溪乡" + }, { + "name": "壮围乡" + }, { + "name": "员山乡" + }, { + "name": "冬山乡" + }, { + "name": "五结乡" + }, { + "name": "三星乡" + }, { + "name": "大同乡" + }, { + "name": "南澳乡" + }] + }, { + "name": "桃园县", + "area": [{ + "name": "桃园市" + }, { + "name": "中坜市" + }, { + "name": "平镇市" + }, { + "name": "八德市" + }, { + "name": "杨梅市" + }, { + "name": "芦竹市" + }, { + "name": "大溪镇" + }, { + "name": "大园乡" + }, { + "name": "龟山乡" + }, { + "name": "龙潭乡" + }, { + "name": "新屋乡" + }, { + "name": "观音乡" + }, { + "name": "复兴乡" + }] + }, { + "name": "新竹县", + "area": [{ + "name": "竹北市" + }, { + "name": "竹东镇" + }, { + "name": "新埔镇" + }, { + "name": "关西镇" + }, { + "name": "湖口乡" + }, { + "name": "新丰乡" + }, { + "name": "芎林乡" + }, { + "name": "横山乡" + }, { + "name": "北埔乡" + }, { + "name": "宝山乡" + }, { + "name": "峨眉乡" + }, { + "name": "尖石乡" + }, { + "name": "五峰乡" + }] + }, { + "name": "苗栗县", + "area": [{ + "name": "苗栗市" + }, { + "name": "苑里镇" + }, { + "name": "通霄镇" + }, { + "name": "竹南镇" + }, { + "name": "头份镇" + }, { + "name": "后龙镇" + }, { + "name": "卓兰镇" + }, { + "name": "大湖乡" + }, { + "name": "公馆乡" + }, { + "name": "铜锣乡" + }, { + "name": "南庄乡" + }, { + "name": "头屋乡" + }, { + "name": "三义乡" + }, { + "name": "西湖乡" + }, { + "name": "造桥乡" + }, { + "name": "三湾乡" + }, { + "name": "狮潭乡" + }, { + "name": "泰安乡" + }] + }, { + "name": "彰化县", + "area": [{ + "name": "彰化市" + }, { + "name": "鹿港镇" + }, { + "name": "和美镇" + }, { + "name": "线西乡" + }, { + "name": "伸港乡" + }, { + "name": "福兴乡" + }, { + "name": "秀水乡" + }, { + "name": "花坛乡" + }, { + "name": "芬园乡" + }, { + "name": "员林镇" + }, { + "name": "溪湖镇" + }, { + "name": "田中镇" + }, { + "name": "大村乡" + }, { + "name": "埔盐乡" + }, { + "name": "埔心乡" + }, { + "name": "永靖乡" + }, { + "name": "社头乡" + }, { + "name": "二水乡" + }, { + "name": "北斗镇" + }, { + "name": "二林镇" + }, { + "name": "田尾乡" + }, { + "name": "埤头乡" + }, { + "name": "芳苑乡" + }, { + "name": "大城乡" + }, { + "name": "竹塘乡" + }, { + "name": "溪州乡" + }] + }, { + "name": "南投县", + "area": [{ + "name": "南投市" + }, { + "name": "埔里镇" + }, { + "name": "草屯镇" + }, { + "name": "竹山镇" + }, { + "name": "集集镇" + }, { + "name": "名间乡" + }, { + "name": "鹿谷乡" + }, { + "name": "中寮乡" + }, { + "name": "鱼池乡" + }, { + "name": "国姓乡" + }, { + "name": "水里乡" + }, { + "name": "信义乡" + }, { + "name": "仁爱乡" + }] + }, { + "name": "云林县", + "area": [{ + "name": "斗六市" + }, { + "name": "斗南镇" + }, { + "name": "虎尾镇" + }, { + "name": "西螺镇" + }, { + "name": "土库镇" + }, { + "name": "北港镇" + }, { + "name": "古坑乡" + }, { + "name": "大埤乡" + }, { + "name": "莿桐乡" + }, { + "name": "林内乡" + }, { + "name": "二仑乡" + }, { + "name": "仑背乡" + }, { + "name": "麦寮乡" + }, { + "name": "东势乡" + }, { + "name": "褒忠乡" + }, { + "name": "台西乡" + }, { + "name": "元长乡" + }, { + "name": "四湖乡" + }, { + "name": "口湖乡" + }, { + "name": "水林乡" + }] + }, { + "name": "嘉义县", + "area": [{ + "name": "太保市" + }, { + "name": "朴子市" + }, { + "name": "布袋镇" + }, { + "name": "大林镇" + }, { + "name": "民雄乡" + }, { + "name": "溪口乡" + }, { + "name": "新港乡" + }, { + "name": "六脚乡" + }, { + "name": "东石乡" + }, { + "name": "义竹乡" + }, { + "name": "鹿草乡" + }, { + "name": "水上乡" + }, { + "name": "中埔乡" + }, { + "name": "竹崎乡" + }, { + "name": "梅山乡" + }, { + "name": "番路乡" + }, { + "name": "大埔乡" + }, { + "name": "阿里山乡" + }] + }, { + "name": "屏东县", + "area": [{ + "name": "屏东市" + }, { + "name": "潮州镇" + }, { + "name": "东港镇" + }, { + "name": "恒春镇" + }, { + "name": "万丹乡" + }, { + "name": "长治乡" + }, { + "name": "麟洛乡" + }, { + "name": "九如乡" + }, { + "name": "里港乡" + }, { + "name": "盐埔乡" + }, { + "name": "高树乡" + }, { + "name": "万峦乡" + }, { + "name": "内埔乡" + }, { + "name": "竹田乡" + }, { + "name": "新埤乡" + }, { + "name": "枋寮乡" + }, { + "name": "新园乡" + }, { + "name": "崁顶乡" + }, { + "name": "林边乡" + }, { + "name": "南州乡" + }, { + "name": "佳冬乡" + }, { + "name": "琉球乡" + }, { + "name": "车城乡" + }, { + "name": "满州乡" + }, { + "name": "枋山乡" + }, { + "name": "三地门乡" + }, { + "name": "雾台乡" + }, { + "name": "玛家乡" + }, { + "name": "泰武乡" + }, { + "name": "来义乡" + }, { + "name": "春日乡" + }, { + "name": "狮子乡" + }, { + "name": "牡丹乡" + }] + }, { + "name": "台东县", + "area": [{ + "name": "台东市" + }, { + "name": "成功镇" + }, { + "name": "关山镇" + }, { + "name": "卑南乡" + }, { + "name": "鹿野乡" + }, { + "name": "池上乡" + }, { + "name": "东河乡" + }, { + "name": "长滨乡" + }, { + "name": "太麻里乡" + }, { + "name": "大武乡" + }, { + "name": "绿岛乡" + }, { + "name": "海端乡" + }, { + "name": "延平乡" + }, { + "name": "金峰乡" + }, { + "name": "达仁乡" + }, { + "name": "兰屿乡" + }] + }, { + "name": "花莲县", + "area": [{ + "name": "花莲市" + }, { + "name": "凤林镇" + }, { + "name": "玉里镇" + }, { + "name": "新城乡" + }, { + "name": "吉安乡" + }, { + "name": "寿丰乡" + }, { + "name": "光复乡" + }, { + "name": "丰滨乡" + }, { + "name": "瑞穗乡" + }, { + "name": "富里乡" + }, { + "name": "秀林乡" + }, { + "name": "万荣乡" + }, { + "name": "卓溪乡" + }] + }, { + "name": "澎湖县", + "area": [{ + "name": "马公市" + }, { + "name": "湖西乡" + }, { + "name": "白沙乡" + }, { + "name": "西屿乡" + }, { + "name": "望安乡" + }, { + "name": "七美乡" + }] + }, { + "name": "金门县", + "area": [{ + "name": "金城镇" + }, { + "name": "金湖镇" + }, { + "name": "金沙镇" + }, { + "name": "金宁乡" + }, { + "name": "烈屿乡" + }, { + "name": "乌丘乡" + }] + }, { + "name": "连江县", + "area": [{ + "name": "南竿乡" + }, { + "name": "北竿乡" + }, { + "name": "莒光乡" + }, { + "name": "东引乡" + }] + }] +}, { + "name": "香港特别行政区", + "city": [{ + "name": "香港岛", + "area": [{ + "name": "中西区" + }, { + "name": "湾仔区" + }, { + "name": "东区" + }, { + "name": "南区" + }] + }, { + "name": "九龙", + "area": [{ + "name": "油尖旺区" + }, { + "name": "深水埗区" + }, { + "name": "九龙城区" + }, { + "name": "黄大仙区" + }, { + "name": "观塘区" + }] + }, { + "name": "新界", + "area": [{ + "name": "荃湾区" + }, { + "name": "屯门区" + }, { + "name": "元朗区" + }, { + "name": "北区" + }, { + "name": "大埔区" + }, { + "name": "西贡区" + }, { + "name": "沙田区" + }, { + "name": "葵青区" + }, { + "name": "离岛区" + }] + }] +}, { + "name": "澳门特别行政区", + "city": [{ + "name": "澳门半岛", + "area": [{ + "name": "花地玛堂区" + }, { + "name": "圣安多尼堂区" + }, { + "name": "大堂区" + }, { + "name": "望德堂区" + }, { + "name": "风顺堂区" + }] + }, { + "name": "氹仔岛", + "area": [{ + "name": "嘉模堂区" + }] + }, { + "name": "路环岛", + "area": [{ + "name": "圣方济各堂区" + }] + }] +}] \ No newline at end of file diff --git a/static/logo.png b/static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5ef08705ad7d23dd5f9f9dbe97b5d846dfffb156 GIT binary patch literal 16164 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 KiAph(kpBZO{JU!a literal 0 HcmV?d00001 diff --git a/uni.scss b/uni.scss new file mode 100644 index 0000000..7cb2f8b --- /dev/null +++ b/uni.scss @@ -0,0 +1,78 @@ +/** + * 这里是uni-app内置的常用样式变量 + * + * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 + * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App + * + */ +@import '@/uni_modules/uview-ui/theme.scss'; +/** + * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 + * + * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 + */ + +/* 颜色变量 */ + +/* 行为相关颜色 */ +$uni-color-primary: #007aff; +$uni-color-success: #4cd964; +$uni-color-warning: #f0ad4e; +$uni-color-error: #dd524d; + +/* 文字基本颜色 */ +$uni-text-color:#333;//基本色 +$uni-text-color-inverse:#fff;//反色 +$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息 +$uni-text-color-placeholder: #808080; +$uni-text-color-disable:#c0c0c0; + +/* 背景颜色 */ +$uni-bg-color:#ffffff; +$uni-bg-color-grey:#f8f8f8; +$uni-bg-color-hover:#f1f1f1;//点击状态颜色 +$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色 + +/* 边框颜色 */ +$uni-border-color:#c8c7cc; + +/* 尺寸变量 */ + +/* 文字尺寸 */ +$uni-font-size-sm:12px; +$uni-font-size-base:14px; +$uni-font-size-lg:16; + +/* 图片尺寸 */ +$uni-img-size-sm:20px; +$uni-img-size-base:26px; +$uni-img-size-lg:40px; + +/* Border Radius */ +$uni-border-radius-sm: 2px; +$uni-border-radius-base: 3px; +$uni-border-radius-lg: 6px; +$uni-border-radius-circle: 50%; + +/* 水平间距 */ +$uni-spacing-row-sm: 5px; +$uni-spacing-row-base: 10px; +$uni-spacing-row-lg: 15px; + +/* 垂直间距 */ +$uni-spacing-col-sm: 4px; +$uni-spacing-col-base: 8px; +$uni-spacing-col-lg: 12px; + +/* 透明度 */ +$uni-opacity-disabled: 0.3; // 组件禁用态的透明度 + +/* 文章场景相关 */ +$uni-color-title: #2C405A; // 文章标题颜色 +$uni-font-size-title:20px; +$uni-color-subtitle: #555555; // 二级标题颜色 +$uni-font-size-subtitle:26px; +$uni-color-paragraph: #3F536E; // 文章段落颜色 +$uni-font-size-paragraph:15px; + +page{background-color: #F5F6FB;} diff --git a/uni_modules/uview-ui/LICENSE b/uni_modules/uview-ui/LICENSE new file mode 100644 index 0000000..8e39ead --- /dev/null +++ b/uni_modules/uview-ui/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 www.uviewui.com + +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. \ No newline at end of file diff --git a/uni_modules/uview-ui/README.md b/uni_modules/uview-ui/README.md new file mode 100644 index 0000000..c78ff47 --- /dev/null +++ b/uni_modules/uview-ui/README.md @@ -0,0 +1,66 @@ +<p align="center"> + <img alt="logo" src="https://uviewui.com/common/logo.png" width="120" height="120" style="margin-bottom: 10px;"> +</p> +<h3 align="center" style="margin: 30px 0 30px;font-weight: bold;font-size:40px;">uView 2.0</h3> +<h3 align="center">多平台快速开发的UI框架</h3> + +[](https://github.com/umicro/uView2.0) +[](https://github.com/umicro/uView2.0) +[](https://github.com/umicro/uView2.0/issues) +[](https://uviewui.com) +[](https://gitee.com/umicro/uView2.0/releases) +[](https://en.wikipedia.org/wiki/MIT_License) + +## 说明 + +uView UI,是[uni-app](https://uniapp.dcloud.io/)全面兼容nvue的uni-app生态框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水 + +## [官方文档:https://uviewui.com](https://uviewui.com) + + +## 预览 + +您可以通过**微信**扫码,查看最佳的演示效果。 +<br> +<br> +<img src="https://uviewui.com/common/weixin_mini_qrcode.png" width="220" height="220" > + + +## 链接 + +- [官方文档](https://www.uviewui.com/) +- [更新日志](https://www.uviewui.com/components/changelog.html) +- [升级指南](https://www.uviewui.com/components/changeGuide.html) +- [关于我们](https://www.uviewui.com/cooperation/about.html) + +## 交流反馈 + +欢迎加入我们的QQ群交流反馈:[点此跳转](https://www.uviewui.com/components/addQQGroup.html) + +## 关于PR + +> 我们非常乐意接受各位的优质PR,但在此之前我希望您了解uView2.0是一个需要兼容多个平台的(小程序、h5、ios app、android app)包括nvue页面、vue页面。 +> 所以希望在您修复bug并提交之前尽可能的去这些平台测试一下兼容性。最好能携带测试截图以方便审核。非常感谢! + +## 安装 + +#### **uni-app插件市场链接** —— [https://ext.dcloud.net.cn/plugin?id=1593](https://ext.dcloud.net.cn/plugin?id=1593) + +请通过[官网安装文档](https://www.uviewui.com/components/install.html)了解更详细的内容 + +## 快速上手 + +请通过[快速上手](https://uviewui.com/components/quickstart.html)了解更详细的内容 + +## 使用方法 +配置easycom规则后,自动按需引入,无需`import`组件,直接引用即可。 + +```html +<template> + <u-button text="按钮"></u-button> +</template> +``` + +## 版权信息 +uView遵循[MIT](https://en.wikipedia.org/wiki/MIT_License)开源协议,意味着您无需支付任何费用,也无需授权,即可将uView应用到您的产品中。 + diff --git a/uni_modules/uview-ui/changelog.md b/uni_modules/uview-ui/changelog.md new file mode 100644 index 0000000..817d403 --- /dev/null +++ b/uni_modules/uview-ui/changelog.md @@ -0,0 +1,344 @@ +## 2.0.33(2022-06-17) +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 修复`loadmore`组件`lineColor`类型错误问题 +2. 修复`u-parse`组件`imgtap`、`linktap`不生效问题 +## 2.0.32(2022-06-16) +# uView2.0重磅发布,利剑出鞘,一统江湖 +1. `u-loadmore`新增自定义颜色、虚/实线 +2. 修复`u-swiper-action`组件部分平台不能上下滑动的问题 +3. 修复`u-list`回弹问题 +4. 修复`notice-bar`组件动画在低端安卓机可能会抖动的问题 +5. `u-loading-page`添加控制图标大小的属性`iconSize` +6. 修复`u-tooltip`组件`color`参数不生效的问题 +7. 修复`u--input`组件使用`blur`事件输出为`undefined`的bug +8. `u-code-input`组件新增键盘弹起时,是否自动上推页面参数`adjustPosition` +9. 修复`image`组件`load`事件无回调对象问题 +10. 修复`button`组件`loadingSize`设置无效问题 +10. 其他修复 +## 2.0.31(2022-04-19) +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 修复`upload`在`vue`页面上传成功后没有成功标志的问题 +2. 解决演示项目中微信小程序模拟上传图片一直出于上传中问题 +3. 修复`u-code-input`组件在`nvue`页面编译到`app`平台上光标异常问题(`app`去除此功能) +4. 修复`actionSheet`组件标题关闭按钮点击事件名称错误的问题 +5. 其他修复 +## 2.0.30(2022-04-04) +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. `u-rate`增加`readonly`属性 +2. `tabs`滑块支持设置背景图片 +3. 修复`u-subsection` `mode`为`subsection`时,滑块样式不正确的问题 +4. `u-code-input`添加光标效果动画 +5. 修复`popup`的`open`事件不触发 +6. 修复`u-flex-column`无效的问题 +7. 修复`u-datetime-picker`索引在特定场合异常问题 +8. 修复`u-datetime-picker`最小时间字符串模板错误问题 +9. `u-swiper`添加`m3u8`验证 +10. `u-swiper`修改判断image和video逻辑 +11. 修复`swiper`无法使用本地图片问题,增加`type`参数 +12. 修复`u-row-notice`格式错误问题 +13. 修复`u-switch`组件当`unit`为`rpx`时,`nodeStyle`消失的问题 +14. 修复`datetime-picker`组件`showToolbar`与`visibleItemCount`属性无效的问题 +15. 修复`upload`组件条件编译位置判断错误,导致`previewImage`属性设置为`false`时,整个组件都会被隐藏的问题 +16. 修复`u-checkbox-group`设置`shape`属性无效的问题 +17. 修复`u-upload`的`capture`传入字符串的时候不生效的问题 +18. 修复`u-action-sheet`组件,关闭事件逻辑错误的问题 +19. 修复`u-list`触顶事件的触发错误的问题 +20. 修复`u-text`只有手机号可拨打的问题 +21. 修复`u-textarea`不能换行的问题 +22. 其他修复 +## 2.0.29(2022-03-13) +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 修复`u--text`组件设置`decoration`属性未生效的问题 +2. 修复`u-datetime-picker`使用`formatter`后返回值不正确 +3. 修复`u-datetime-picker` `intercept` 可能为undefined +4. 修复已设置单位 uni..config.unit = 'rpx'时,线型指示器 `transform` 的位置翻倍,导致指示器超出宽度 +5. 修复mixin中bem方法生成的类名在支付宝和字节小程序中失效 +6. 修复默认值传值为空的时候,打开`u-datetime-picker`报错,不能选中第一列时间的bug +7. 修复`u-datetime-picker`使用`formatter`后返回值不正确 +8. 修复`u-image`组件`loading`无效果的问题 +9. 修复`config.unit`属性设为`rpx`时,导航栏占用高度不足导致塌陷的问题 +10. 修复`u-datetime-picker`组件`itemHeight`无效问题 +11. 其他修复 +## 2.0.28(2022-02-22) +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. search组件新增searchIconSize属性 +2. 兼容Safari/Webkit中传入时间格式如2022-02-17 12:00:56 +3. 修复text value.js 判断日期出format错误问题 +4. priceFormat格式化金额出现精度错误 +5. priceFormat在部分情况下出现精度损失问题 +6. 优化表单rules提示 +7. 修复avatar组件src为空时,展示状态不对 +8. 其他修复 +## 2.0.27(2022-01-28) +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1.样式修复 +## 2.0.26(2022-01-28) +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1.样式修复 +## 2.0.25(2022-01-27) +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 修复text组件mode=price时,可能会导致精度错误的问题 +2. 添加$u.setConfig()方法,可设置uView内置的config, props, zIndex, color属性,详见:[修改uView内置配置方案](https://uviewui.com/components/setting.html#%E9%BB%98%E8%AE%A4%E5%8D%95%E4%BD%8D%E9%85%8D%E7%BD%AE) +3. 优化form组件在errorType=toast时,如果输入错误页面会有抖动的问题 +4. 修复$u.addUnit()对配置默认单位可能无效的问题 +## 2.0.24(2022-01-25) +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 修复swiper在current指定非0时缩放有误 +2. 修复u-icon添加stop属性的时候报错 +3. 优化遗留的通过正则判断rpx单位的问题 +4. 优化Layout布局 vue使用gutter时,会超出固定区域 +5. 优化search组件高度单位问题(rpx -> px) +6. 修复u-image slot 加载和错误的图片失去了高度 +7. 修复u-index-list中footer插槽与header插槽存在性判断错误 +8. 修复部分机型下u-popup关闭时会闪烁 +9. 修复u-image在nvue-app下失去宽高 +10. 修复u-popup运行报错 +11. 修复u-tooltip报错 +12. 修复box-sizing在app下的警告 +13. 修复u-navbar在小程序中报运行时错误 +14. 其他修复 +## 2.0.23(2022-01-24) +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 修复image组件在hx3.3.9的nvue下可能会显示异常的问题 +2. 修复col组件gutter参数带rpx单位处理不正确的问题 +3. 修复text组件单行时无法显示省略号的问题 +4. navbar添加titleStyle参数 +5. 升级到hx3.3.9可消除nvue下控制台样式警告的问题 +## 2.0.22(2022-01-19) +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. $u.page()方法优化,避免在特殊场景可能报错的问题 +2. picker组件添加immediateChange参数 +3. 新增$u.pages()方法 +## 2.0.21(2022-01-19) +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 优化:form组件在用户设置rules的时候提示用户model必传 +2. 优化遗留的通过正则判断rpx单位的问题 +3. 修复微信小程序环境中tabbar组件开启safeAreaInsetBottom属性后,placeholder高度填充不正确 +4. 修复swiper在current指定非0时缩放有误 +5. 修复u-icon添加stop属性的时候报错 +6. 修复upload组件在accept=all的时候没有作用 +7. 修复在text组件mode为phone时call属性无效的问题 +8. 处理u-form clearValidate方法 +9. 其他修复 +## 2.0.20(2022-01-14) +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 修复calendar默认会选择一个日期,如果直接点确定的话,无法取到值的问题 +2. 修复Slider缺少disabled props 还有注释 +3. 修复u-notice-bar点击事件无法拿到index索引值的问题 +4. 修复u-collapse-item在vue文件下,app端自定义插槽不生效的问题 +5. 优化头像为空时显示默认头像 +6. 修复图片地址赋值后判断加载状态为完成问题 +7. 修复日历滚动到默认日期月份区域 +8. search组件暴露点击左边icon事件 +9. 修复u-form clearValidate方法不生效 +10. upload h5端增加返回文件参数(文件的name参数) +11. 处理upload选择文件后url为blob类型无法预览的问题 +12. u-code-input 修复输入框没有往左移出一半屏幕 +13. 修复Upload上传 disabled为true时,控制台报hoverClass类型错误 +14. 临时处理ios app下grid点击坍塌问题 +15. 其他修复 +## 2.0.19(2021-12-29) +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 优化微信小程序包体积可在微信中预览,请升级HbuilderX3.3.4,同时在“运行->运行到小程序模拟器”中勾选“运行时是否压缩代码” +2. 优化微信小程序setData性能,处理某些方法如$u.route()无法在模板中使用的问题 +3. navbar添加autoBack参数 +4. 允许avatar组件的事件冒泡 +5. 修复cell组件报错问题 +6. 其他修复 +## 2.0.18(2021-12-28) +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 修复app端编译报错问题 +2. 重新处理微信小程序端setData过大的性能问题 +3. 修复边框问题 +4. 修复最大最小月份不大于0则没有数据出现的问题 +5. 修复SwipeAction微信小程序端无法上下滑动问题 +6. 修复input的placeholder在小程序端默认显示为true问题 +7. 修复divider组件click事件无效问题 +8. 修复u-code-input maxlength 属性值为 String 类型时显示异常 +9. 修复当 grid只有 1到2时 在小程序端algin设置无效的问题 +10. 处理form-item的label为top时,取消错误提示的左边距 +11. 其他修复 +## 2.0.17(2021-12-26) +## uView正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583) + +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 解决HBuilderX3.3.3.20211225版本导致的样式问题 +2. calendar日历添加monthNum参数 +3. navbar添加center slot +## 2.0.16(2021-12-25) +## uView正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583) + +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 解决微信小程序setData性能问题 +2. 修复count-down组件change事件不触发问题 +## 2.0.15(2021-12-21) +## uView正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583) + +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 修复Cell单元格titleWidth无效 +2. 修复cheakbox组件ischecked不更新 +3. 修复keyboard是否显示"."按键默认值问题 +4. 修复number-keyboard是否显示键盘的"."符号问题 +5. 修复Input输入框 readonly无效 +6. 修复u-avatar 导致打包app、H5时候报错问题 +7. 修复Upload上传deletable无效 +8. 修复upload当设置maxSize时无效的问题 +9. 修复tabs lineWidth传入带单位的字符串的时候偏移量计算错误问题 +10. 修复rate组件在有padding的view内,显示的星星位置和可触摸区域不匹配,无法正常选中星星 +## 2.0.13(2021-12-14) +## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY) + +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 修复配置默认单位为rpx可能会导致自定义导航栏高度异常的问题 +## 2.0.12(2021-12-14) +## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY) + +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 修复tabs组件在vue环境下划线消失的问题 +2. 修复upload组件在安卓小程序无法选择视频的问题 +3. 添加uni.$u.config.unit配置,用于配置参数默认单位,详见:[默认单位配置](https://www.uviewui.com/components/setting.html#%E9%BB%98%E8%AE%A4%E5%8D%95%E4%BD%8D%E9%85%8D%E7%BD%AE) +4. 修复textarea组件在没绑定v-model时,字符统计不生效问题 +5. 修复nvue下控制是否出现滚动条失效问题 +## 2.0.11(2021-12-13) +## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY) + +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. text组件align参数无效的问题 +2. subsection组件添加keyName参数 +3. upload组件无法判断[Object file]类型的问题 +4. 处理notify层级过低问题 +5. codeInput组件添加disabledDot参数 +6. 处理actionSheet组件round参数无效的问题 +7. calendar组件添加round参数用于控制圆角值 +8. 处理swipeAction组件在vue环境下默认被打开的问题 +9. button组件的throttleTime节流参数无效的问题 +10. 解决u-notify手动关闭方法close()无效的问题 +11. input组件readonly不生效问题 +12. tag组件type参数为info不生效问题 +## 2.0.10(2021-12-08) +## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY) + +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 修复button sendMessagePath属性不生效 +2. 修复DatetimePicker选择器title无效 +3. 修复u-toast设置loading=true不生效 +4. 修复u-text金额模式传0报错 +5. 修复u-toast组件的icon属性配置不生效 +6. button的icon在特殊场景下的颜色优化 +7. IndexList优化,增加# +## 2.0.9(2021-12-01) +## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU) + +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 优化swiper的height支持100%值(仅vue有效),修复嵌入视频时click事件无法触发的问题 +2. 优化tabs组件对list值为空的判断,或者动态变化list时重新计算相关尺寸的问题 +3. 优化datetime-picker组件逻辑,让其后续打开的默认值为上一次的选中值,需要通过v-model绑定值才有效 +4. 修复upload内嵌在其他组件中,选择图片可能不会换行的问题 +## 2.0.8(2021-12-01) +## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU) + +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 修复toast的position参数无效问题 +2. 处理input在ios nvue上无法获得焦点的问题 +3. avatar-group组件添加extraValue参数,让剩余展示数量可手动控制 +4. tabs组件添加keyName参数用于配置从对象中读取的键名 +5. 处理text组件名字脱敏默认配置无效的问题 +6. 处理picker组件item文本太长换行问题 +## 2.0.7(2021-11-30) +## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU) + +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 修复radio和checkbox动态改变v-model无效的问题。 +2. 优化form规则validator在微信小程序用法 +3. 修复backtop组件mode参数在微信小程序无效的问题 +4. 处理Album的previewFullImage属性无效的问题 +5. 处理u-datetime-picker组件mode='time'在选择改变时间时,控制台报错的问题 +## 2.0.6(2021-11-27) +## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU) + +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. 处理tag组件在vue下边框无效的问题。 +2. 处理popup组件圆角参数可能无效的问题。 +3. 处理tabs组件lineColor参数可能无效的问题。 +4. propgress组件在值很小时,显示异常的问题。 +## 2.0.5(2021-11-25) +## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU) + +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. calendar在vue下显示异常问题。 +2. form组件labelPosition和errorType参数无效的问题 +3. input组件inputAlign无效的问题 +4. 其他一些修复 +## 2.0.4(2021-11-23) +## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU) + +# uView2.0重磅发布,利剑出鞘,一统江湖 + +0. input组件缺失@confirm事件,以及subfix和prefix无效问题 +1. component.scss文件样式在vue下干扰全局布局问题 +2. 修复subsection在vue环境下表现异常的问题 +3. tag组件的bgColor等参数无效的问题 +4. upload组件不换行的问题 +5. 其他的一些修复处理 +## 2.0.3(2021-11-16) +## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU) + +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. uView2.0已实现全面兼容nvue +2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升 +3. 目前uView2.0为公测阶段,相关细节可能会有变动 +4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html) +5. 处理modal的confirm回调事件拼写错误问题 +6. 处理input组件@input事件参数错误问题 +7. 其他一些修复 +## 2.0.2(2021-11-16) +## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU) + +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. uView2.0已实现全面兼容nvue +2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升 +3. 目前uView2.0为公测阶段,相关细节可能会有变动 +4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html) +5. 修复input组件formatter参数缺失问题 +6. 优化loading-icon组件的scss写法问题,防止不兼容新版本scss +## 2.0.0(2020-11-15) +## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU) + +# uView2.0重磅发布,利剑出鞘,一统江湖 + +1. uView2.0已实现全面兼容nvue +2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升 +3. 目前uView2.0为公测阶段,相关细节可能会有变动 +4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html) +5. 修复input组件formatter参数缺失问题 + + diff --git a/uni_modules/uview-ui/components/u--form/u--form.vue b/uni_modules/uview-ui/components/u--form/u--form.vue new file mode 100644 index 0000000..fdfc212 --- /dev/null +++ b/uni_modules/uview-ui/components/u--form/u--form.vue @@ -0,0 +1,78 @@ +<template> + <uvForm + ref="uForm" + :model="model" + :rules="rules" + :errorType="errorType" + :borderBottom="borderBottom" + :labelPosition="labelPosition" + :labelWidth="labelWidth" + :labelAlign="labelAlign" + :labelStyle="labelStyle" + :customStyle="customStyle" + > + <slot /> + </uvForm> +</template> + +<script> + /** + * 此组件存在的理由是,在nvue下,u-form被uni-app官方占用了,u-form在nvue中相当于form组件 + * 所以在nvue下,取名为u--form,内部其实还是u-form.vue,只不过做一层中转 + */ + import uvForm from '../u-form/u-form.vue'; + import props from '../u-form/props.js' + export default { + // #ifdef MP-WEIXIN + name: 'u-form', + // #endif + // #ifndef MP-WEIXIN + name: 'u--form', + // #endif + mixins: [uni.$u.mpMixin, props, uni.$u.mixin], + components: { + uvForm + }, + created() { + this.children = [] + }, + methods: { + // 手动设置校验的规则,如果规则中有函数的话,微信小程序中会过滤掉,所以只能手动调用设置规则 + setRules(rules) { + this.$refs.uForm.setRules(rules) + }, + validate() { + /** + * 在微信小程序中,通过this.$parent拿到的父组件是u--form,而不是其内嵌的u-form + * 导致在u-form组件中,拿不到对应的children数组,从而校验无效,所以这里每次调用u-form组件中的 + * 对应方法的时候,在小程序中都先将u--form的children赋值给u-form中的children + */ + // #ifdef MP-WEIXIN + this.setMpData() + // #endif + return this.$refs.uForm.validate() + }, + validateField(value, callback) { + // #ifdef MP-WEIXIN + this.setMpData() + // #endif + return this.$refs.uForm.validateField(value, callback) + }, + resetFields() { + // #ifdef MP-WEIXIN + this.setMpData() + // #endif + return this.$refs.uForm.resetFields() + }, + clearValidate(props) { + // #ifdef MP-WEIXIN + this.setMpData() + // #endif + return this.$refs.uForm.clearValidate(props) + }, + setMpData() { + this.$refs.uForm.children = this.children + } + }, + } +</script> diff --git a/uni_modules/uview-ui/components/u--image/u--image.vue b/uni_modules/uview-ui/components/u--image/u--image.vue new file mode 100644 index 0000000..21b7ab1 --- /dev/null +++ b/uni_modules/uview-ui/components/u--image/u--image.vue @@ -0,0 +1,47 @@ +<template> + <uvImage + :src="src" + :mode="mode" + :width="width" + :height="height" + :shape="shape" + :radius="radius" + :lazyLoad="lazyLoad" + :showMenuByLongpress="showMenuByLongpress" + :loadingIcon="loadingIcon" + :errorIcon="errorIcon" + :showLoading="showLoading" + :showError="showError" + :fade="fade" + :webp="webp" + :duration="duration" + :bgColor="bgColor" + :customStyle="customStyle" + @click="$emit('click')" + @error="$emit('error')" + @load="$emit('load')" + > + <template v-slot:loading> + <slot name="loading"></slot> + </template> + <template v-slot:error> + <slot name="error"></slot> + </template> + </uvImage> +</template> + +<script> + /** + * 此组件存在的理由是,在nvue下,u-image被uni-app官方占用了,u-image在nvue中相当于image组件 + * 所以在nvue下,取名为u--image,内部其实还是u-iamge.vue,只不过做一层中转 + */ + import uvImage from '../u-image/u-image.vue'; + import props from '../u-image/props.js'; + export default { + name: 'u--image', + mixins: [uni.$u.mpMixin, props, uni.$u.mixin], + components: { + uvImage + }, + } +</script> \ No newline at end of file diff --git a/uni_modules/uview-ui/components/u--input/u--input.vue b/uni_modules/uview-ui/components/u--input/u--input.vue new file mode 100644 index 0000000..2fc1213 --- /dev/null +++ b/uni_modules/uview-ui/components/u--input/u--input.vue @@ -0,0 +1,72 @@ +<template> + <uvInput + :value="value" + :type="type" + :fixed="fixed" + :disabled="disabled" + :disabledColor="disabledColor" + :clearable="clearable" + :password="password" + :maxlength="maxlength" + :placeholder="placeholder" + :placeholderClass="placeholderClass" + :placeholderStyle="placeholderStyle" + :showWordLimit="showWordLimit" + :confirmType="confirmType" + :confirmHold="confirmHold" + :holdKeyboard="holdKeyboard" + :focus="focus" + :autoBlur="autoBlur" + :disableDefaultPadding="disableDefaultPadding" + :cursor="cursor" + :cursorSpacing="cursorSpacing" + :selectionStart="selectionStart" + :selectionEnd="selectionEnd" + :adjustPosition="adjustPosition" + :inputAlign="inputAlign" + :fontSize="fontSize" + :color="color" + :prefixIcon="prefixIcon" + :suffixIcon="suffixIcon" + :suffixIconStyle="suffixIconStyle" + :prefixIconStyle="prefixIconStyle" + :border="border" + :readonly="readonly" + :shape="shape" + :customStyle="customStyle" + :formatter="formatter" + @focus="$emit('focus')" + @blur="e => $emit('blur', e)" + @keyboardheightchange="$emit('keyboardheightchange')" + @change="e => $emit('change', e)" + @input="e => $emit('input', e)" + @confirm="e => $emit('confirm', e)" + @clear="$emit('clear')" + @click="$emit('click')" + > + <!-- #ifdef MP --> + <slot name="prefix"></slot> + <slot name="suffix"></slot> + <!-- #endif --> + <!-- #ifndef MP --> + <slot name="prefix" slot="prefix"></slot> + <slot name="suffix" slot="suffix"></slot> + <!-- #endif --> + </uvInput> +</template> + +<script> + /** + * 此组件存在的理由是,在nvue下,u-input被uni-app官方占用了,u-input在nvue中相当于input组件 + * 所以在nvue下,取名为u--input,内部其实还是u-input.vue,只不过做一层中转 + */ + import uvInput from '../u-input/u-input.vue'; + import props from '../u-input/props.js' + export default { + name: 'u--input', + mixins: [uni.$u.mpMixin, props, uni.$u.mixin], + components: { + uvInput + }, + } +</script> \ No newline at end of file diff --git a/uni_modules/uview-ui/components/u--text/u--text.vue b/uni_modules/uview-ui/components/u--text/u--text.vue new file mode 100644 index 0000000..44ee52a --- /dev/null +++ b/uni_modules/uview-ui/components/u--text/u--text.vue @@ -0,0 +1,44 @@ +<template> + <uvText + :type="type" + :show="show" + :text="text" + :prefixIcon="prefixIcon" + :suffixIcon="suffixIcon" + :mode="mode" + :href="href" + :format="format" + :call="call" + :openType="openType" + :bold="bold" + :block="block" + :lines="lines" + :color="color" + :decoration="decoration" + :size="size" + :iconStyle="iconStyle" + :margin="margin" + :lineHeight="lineHeight" + :align="align" + :wordWrap="wordWrap" + :customStyle="customStyle" + @click="$emit('click')" + ></uvText> +</template> + +<script> +/** + * 此组件存在的理由是,在nvue下,u-text被uni-app官方占用了,u-text在nvue中相当于input组件 + * 所以在nvue下,取名为u--input,内部其实还是u-text.vue,只不过做一层中转 + * 不使用v-bind="$attrs",而是分开独立写传参,是因为微信小程序不支持此写法 + */ +import uvText from "../u-text/u-text.vue"; +import props from "../u-text/props.js"; +export default { + name: "u--text", + mixins: [uni.$u.mpMixin, props, uni.$u.mixin], + components: { + uvText, + }, +}; +</script> diff --git a/uni_modules/uview-ui/components/u--textarea/u--textarea.vue b/uni_modules/uview-ui/components/u--textarea/u--textarea.vue new file mode 100644 index 0000000..9dbcfbb --- /dev/null +++ b/uni_modules/uview-ui/components/u--textarea/u--textarea.vue @@ -0,0 +1,47 @@ +<template> + <uvTextarea + :value="value" + :placeholder="placeholder" + :height="height" + :confirmType="confirmType" + :disabled="disabled" + :count="count" + :focus="focus" + :autoHeight="autoHeight" + :fixed="fixed" + :cursorSpacing="cursorSpacing" + :cursor="cursor" + :showConfirmBar="showConfirmBar" + :selectionStart="selectionStart" + :selectionEnd="selectionEnd" + :adjustPosition="adjustPosition" + :disableDefaultPadding="disableDefaultPadding" + :holdKeyboard="holdKeyboard" + :maxlength="maxlength" + :border="border" + :customStyle="customStyle" + :formatter="formatter" + @focus="e => $emit('focus')" + @blur="e => $emit('blur')" + @linechange="e => $emit('linechange', e)" + @confirm="e => $emit('confirm')" + @input="e => $emit('input', e)" + @keyboardheightchange="e => $emit('keyboardheightchange')" + ></uvTextarea> +</template> + +<script> + /** + * 此组件存在的理由是,在nvue下,u--textarea被uni-app官方占用了,u-textarea在nvue中相当于textarea组件 + * 所以在nvue下,取名为u--textarea,内部其实还是u-textarea.vue,只不过做一层中转 + */ + import uvTextarea from '../u-textarea/u-textarea.vue'; + import props from '../u-textarea/props.js' + export default { + name: 'u--textarea', + mixins: [uni.$u.mpMixin, props, uni.$u.mixin], + components: { + uvTextarea + }, + } +</script> diff --git a/uni_modules/uview-ui/components/u-action-sheet/props.js b/uni_modules/uview-ui/components/u-action-sheet/props.js new file mode 100644 index 0000000..e96e04f --- /dev/null +++ b/uni_modules/uview-ui/components/u-action-sheet/props.js @@ -0,0 +1,54 @@ +export default { + props: { + // 操作菜单是否展示 (默认false) + show: { + type: Boolean, + default: uni.$u.props.actionSheet.show + }, + // 标题 + title: { + type: String, + default: uni.$u.props.actionSheet.title + }, + // 选项上方的描述信息 + description: { + type: String, + default: uni.$u.props.actionSheet.description + }, + // 数据 + actions: { + type: Array, + default: uni.$u.props.actionSheet.actions + }, + // 取消按钮的文字,不为空时显示按钮 + cancelText: { + type: String, + default: uni.$u.props.actionSheet.cancelText + }, + // 点击某个菜单项时是否关闭弹窗 + closeOnClickAction: { + type: Boolean, + default: uni.$u.props.actionSheet.closeOnClickAction + }, + // 处理底部安全区(默认true) + safeAreaInsetBottom: { + type: Boolean, + default: uni.$u.props.actionSheet.safeAreaInsetBottom + }, + // 小程序的打开方式 + openType: { + type: String, + default: uni.$u.props.actionSheet.openType + }, + // 点击遮罩是否允许关闭 (默认true) + closeOnClickOverlay: { + type: Boolean, + default: uni.$u.props.actionSheet.closeOnClickOverlay + }, + // 圆角值 + round: { + type: [Boolean, String, Number], + default: uni.$u.props.actionSheet.round + } + } +} diff --git a/uni_modules/uview-ui/components/u-action-sheet/u-action-sheet.vue b/uni_modules/uview-ui/components/u-action-sheet/u-action-sheet.vue new file mode 100644 index 0000000..26d5d8d --- /dev/null +++ b/uni_modules/uview-ui/components/u-action-sheet/u-action-sheet.vue @@ -0,0 +1,278 @@ + +<template> + <u-popup + :show="show" + mode="bottom" + @close="closeHandler" + :safeAreaInsetBottom="safeAreaInsetBottom" + :round="round" + > + <view class="u-action-sheet"> + <view + class="u-action-sheet__header" + v-if="title" + > + <text class="u-action-sheet__header__title u-line-1">{{title}}</text> + <view + class="u-action-sheet__header__icon-wrap" + @tap.stop="cancel" + > + <u-icon + name="close" + size="17" + color="#c8c9cc" + bold + ></u-icon> + </view> + </view> + <text + class="u-action-sheet__description" + :style="[{ + marginTop: `${title && description ? 0 : '18px'}` + }]" + v-if="description" + >{{description}}</text> + <slot> + <u-line v-if="description"></u-line> + <view class="u-action-sheet__item-wrap"> + <template v-for="(item, index) in actions"> + <!-- #ifdef MP --> + <button + :key="index" + class="u-reset-button" + :openType="item.openType" + @getuserinfo="onGetUserInfo" + @contact="onContact" + @getphonenumber="onGetPhoneNumber" + @error="onError" + @launchapp="onLaunchApp" + @opensetting="onOpenSetting" + :lang="lang" + :session-from="sessionFrom" + :send-message-title="sendMessageTitle" + :send-message-path="sendMessagePath" + :send-message-img="sendMessageImg" + :show-message-card="showMessageCard" + :app-parameter="appParameter" + @tap="selectHandler(index)" + :hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''" + > + <!-- #endif --> + <view + class="u-action-sheet__item-wrap__item" + @tap.stop="selectHandler(index)" + :hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''" + :hover-stay-time="150" + > + <template v-if="!item.loading"> + <text + class="u-action-sheet__item-wrap__item__name" + :style="[itemStyle(index)]" + >{{ item.name }}</text> + <text + v-if="item.subname" + class="u-action-sheet__item-wrap__item__subname" + >{{ item.subname }}</text> + </template> + <u-loading-icon + v-else + custom-class="van-action-sheet__loading" + size="18" + mode="circle" + /> + </view> + <!-- #ifdef MP --> + </button> + <!-- #endif --> + <u-line v-if="index !== actions.length - 1"></u-line> + </template> + </view> + </slot> + <u-gap + bgColor="#eaeaec" + height="6" + v-if="cancelText" + ></u-gap> + <view hover-class="u-action-sheet--hover"> + <text + @touchmove.stop.prevent + :hover-stay-time="150" + v-if="cancelText" + class="u-action-sheet__cancel-text" + @tap="cancel" + >{{cancelText}}</text> + </view> + </view> + </u-popup> +</template> + +<script> + import openType from '../../libs/mixin/openType' + import button from '../../libs/mixin/button' + import props from './props.js'; + /** + * ActionSheet 操作菜单 + * @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。 + * @tutorial https://www.uviewui.com/components/actionSheet.html + * + * @property {Boolean} show 操作菜单是否展示 (默认 false ) + * @property {String} title 操作菜单标题 + * @property {String} description 选项上方的描述信息 + * @property {Array<Object>} actions 按钮的文字数组,见官方文档示例 + * @property {String} cancelText 取消按钮的提示文字,不为空时显示按钮 + * @property {Boolean} closeOnClickAction 点击某个菜单项时是否关闭弹窗 (默认 true ) + * @property {Boolean} safeAreaInsetBottom 处理底部安全区 (默认 true ) + * @property {String} openType 小程序的打开方式 (contact | launchApp | getUserInfo | openSetting |getPhoneNumber |error ) + * @property {Boolean} closeOnClickOverlay 点击遮罩是否允许关闭 (默认 true ) + * @property {Number|String} round 圆角值,默认无圆角 (默认 0 ) + * @property {String} lang 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文 + * @property {String} sessionFrom 会话来源,openType="contact"时有效 + * @property {String} sendMessageTitle 会话内消息卡片标题,openType="contact"时有效 + * @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径,openType="contact"时有效 + * @property {String} sendMessageImg 会话内消息卡片图片,openType="contact"时有效 + * @property {Boolean} showMessageCard 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效 (默认 false ) + * @property {String} appParameter 打开 APP 时,向 APP 传递的参数,openType=launchApp 时有效 + * + * @event {Function} select 点击ActionSheet列表项时触发 + * @event {Function} close 点击取消按钮时触发 + * @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,回调的 detail 数据与 wx.getUserInfo 返回的一致,openType="getUserInfo"时有效 + * @event {Function} contact 客服消息回调,openType="contact"时有效 + * @event {Function} getphonenumber 获取用户手机号回调,openType="getPhoneNumber"时有效 + * @event {Function} error 当使用开放能力时,发生错误的回调,openType="error"时有效 + * @event {Function} launchapp 打开 APP 成功的回调,openType="launchApp"时有效 + * @event {Function} opensetting 在打开授权设置页后回调,openType="openSetting"时有效 + * @example <u-action-sheet :actions="list" :title="title" :show="show"></u-action-sheet> + */ + export default { + name: "u-action-sheet", + // 一些props参数和methods方法,通过mixin混入,因为其他文件也会用到 + mixins: [openType, button, uni.$u.mixin, props], + data() { + return { + + } + }, + computed: { + // 操作项目的样式 + itemStyle() { + return (index) => { + let style = {}; + if (this.actions[index].color) style.color = this.actions[index].color + if (this.actions[index].fontSize) style.fontSize = uni.$u.addUnit(this.actions[index].fontSize) + // 选项被禁用的样式 + if (this.actions[index].disabled) style.color = '#c0c4cc' + return style; + } + }, + }, + methods: { + closeHandler() { + // 允许点击遮罩关闭时,才发出close事件 + if(this.closeOnClickOverlay) { + this.$emit('close') + } + }, + // 点击取消按钮 + cancel() { + this.$emit('close') + }, + selectHandler(index) { + const item = this.actions[index] + if (item && !item.disabled && !item.loading) { + this.$emit('select', item) + if (this.closeOnClickAction) { + this.$emit('close') + } + } + }, + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + $u-action-sheet-reset-button-width:100% !default; + $u-action-sheet-title-font-size: 16px !default; + $u-action-sheet-title-padding: 12px 30px !default; + $u-action-sheet-title-color: $u-main-color !default; + $u-action-sheet-header-icon-wrap-right:15px !default; + $u-action-sheet-header-icon-wrap-top:15px !default; + $u-action-sheet-description-font-size:13px !default; + $u-action-sheet-description-color:14px !default; + $u-action-sheet-description-margin: 18px 15px !default; + $u-action-sheet-item-wrap-item-padding:15px !default; + $u-action-sheet-item-wrap-name-font-size:16px !default; + $u-action-sheet-item-wrap-subname-font-size:13px !default; + $u-action-sheet-item-wrap-subname-color: #c0c4cc !default; + $u-action-sheet-item-wrap-subname-margin-top:10px !default; + $u-action-sheet-cancel-text-font-size:16px !default; + $u-action-sheet-cancel-text-color:$u-content-color !default; + $u-action-sheet-cancel-text-font-size:15px !default; + $u-action-sheet-cancel-text-hover-background-color:rgb(242, 243, 245) !default; + + .u-reset-button { + width: $u-action-sheet-reset-button-width; + } + + .u-action-sheet { + text-align: center; + &__header { + position: relative; + padding: $u-action-sheet-title-padding; + &__title { + font-size: $u-action-sheet-title-font-size; + color: $u-action-sheet-title-color; + font-weight: bold; + text-align: center; + } + + &__icon-wrap { + position: absolute; + right: $u-action-sheet-header-icon-wrap-right; + top: $u-action-sheet-header-icon-wrap-top; + } + } + + &__description { + font-size: $u-action-sheet-description-font-size; + color: $u-tips-color; + margin: $u-action-sheet-description-margin; + text-align: center; + } + + &__item-wrap { + + &__item { + padding: $u-action-sheet-item-wrap-item-padding; + @include flex; + align-items: center; + justify-content: center; + flex-direction: column; + + &__name { + font-size: $u-action-sheet-item-wrap-name-font-size; + color: $u-main-color; + text-align: center; + } + + &__subname { + font-size: $u-action-sheet-item-wrap-subname-font-size; + color: $u-action-sheet-item-wrap-subname-color; + margin-top: $u-action-sheet-item-wrap-subname-margin-top; + text-align: center; + } + } + } + + &__cancel-text { + font-size: $u-action-sheet-cancel-text-font-size; + color: $u-action-sheet-cancel-text-color; + text-align: center; + padding: $u-action-sheet-cancel-text-font-size; + } + + &--hover { + background-color: $u-action-sheet-cancel-text-hover-background-color; + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-album/props.js b/uni_modules/uview-ui/components/u-album/props.js new file mode 100644 index 0000000..75cdb37 --- /dev/null +++ b/uni_modules/uview-ui/components/u-album/props.js @@ -0,0 +1,59 @@ +export default { + props: { + // 图片地址,Array<String>|Array<Object>形式 + urls: { + type: Array, + default: uni.$u.props.album.urls + }, + // 指定从数组的对象元素中读取哪个属性作为图片地址 + keyName: { + type: String, + default: uni.$u.props.album.keyName + }, + // 单图时,图片长边的长度 + singleSize: { + type: [String, Number], + default: uni.$u.props.album.singleSize + }, + // 多图时,图片边长 + multipleSize: { + type: [String, Number], + default: uni.$u.props.album.multipleSize + }, + // 多图时,图片水平和垂直之间的间隔 + space: { + type: [String, Number], + default: uni.$u.props.album.space + }, + // 单图时,图片缩放裁剪的模式 + singleMode: { + type: String, + default: uni.$u.props.album.singleMode + }, + // 多图时,图片缩放裁剪的模式 + multipleMode: { + type: String, + default: uni.$u.props.album.multipleMode + }, + // 最多展示的图片数量,超出时最后一个位置将会显示剩余图片数量 + maxCount: { + type: [String, Number], + default: uni.$u.props.album.maxCount + }, + // 是否可以预览图片 + previewFullImage: { + type: Boolean, + default: uni.$u.props.album.previewFullImage + }, + // 每行展示图片数量,如设置,singleSize和multipleSize将会无效 + rowCount: { + type: [String, Number], + default: uni.$u.props.album.rowCount + }, + // 超出maxCount时是否显示查看更多的提示 + showMore: { + type: Boolean, + default: uni.$u.props.album.showMore + } + } +} diff --git a/uni_modules/uview-ui/components/u-album/u-album.vue b/uni_modules/uview-ui/components/u-album/u-album.vue new file mode 100644 index 0000000..687e2d5 --- /dev/null +++ b/uni_modules/uview-ui/components/u-album/u-album.vue @@ -0,0 +1,259 @@ +<template> + <view class="u-album"> + <view + class="u-album__row" + ref="u-album__row" + v-for="(arr, index) in showUrls" + :forComputedUse="albumWidth" + :key="index" + > + <view + class="u-album__row__wrapper" + v-for="(item, index1) in arr" + :key="index1" + :style="[imageStyle(index + 1, index1 + 1)]" + @tap="previewFullImage ? onPreviewTap(getSrc(item)) : ''" + > + <image + :src="getSrc(item)" + :mode=" + urls.length === 1 + ? imageHeight > 0 + ? singleMode + : 'widthFix' + : multipleMode + " + :style="[ + { + width: imageWidth, + height: imageHeight + } + ]" + ></image> + <view + v-if=" + showMore && + urls.length > rowCount * showUrls.length && + index === showUrls.length - 1 && + index1 === showUrls[showUrls.length - 1].length - 1 + " + class="u-album__row__wrapper__text" + > + <u--text + :text="`+${urls.length - maxCount}`" + color="#fff" + :size="multipleSize * 0.3" + align="center" + customStyle="justify-content: center" + ></u--text> + </view> + </view> + </view> + </view> +</template> + +<script> +import props from './props.js' +// #ifdef APP-NVUE +// 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度 +const dom = uni.requireNativePlugin('dom') +// #endif + +/** + * Album 相册 + * @description 本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。减少重复的模板代码 + * @tutorial https://www.uviewui.com/components/album.html + * + * @property {Array} urls 图片地址列表 Array<String>|Array<Object>形式 + * @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址 + * @property {String | Number} singleSize 单图时,图片长边的长度 (默认 180 ) + * @property {String | Number} multipleSize 多图时,图片边长 (默认 70 ) + * @property {String | Number} space 多图时,图片水平和垂直之间的间隔 (默认 6 ) + * @property {String} singleMode 单图时,图片缩放裁剪的模式 (默认 'scaleToFill' ) + * @property {String} multipleMode 多图时,图片缩放裁剪的模式 (默认 'aspectFill' ) + * @property {String | Number} maxCount 取消按钮的提示文字 (默认 9 ) + * @property {Boolean} previewFullImage 是否可以预览图片 (默认 true ) + * @property {String | Number} rowCount 每行展示图片数量,如设置,singleSize和multipleSize将会无效 (默认 3 ) + * @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 (默认 true ) + * + * @event {Function} albumWidth 某些特殊的情况下,需要让文字与相册的宽度相等,这里事件的形式对外发送 (回调参数 width ) + * @example <u-album :urls="urls2" @albumWidth="width => albumWidth = width" multipleSize="68" ></u-album> + */ +export default { + name: 'u-album', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + // 单图的宽度 + singleWidth: 0, + // 单图的高度 + singleHeight: 0, + // 单图时,如果无法获取图片的尺寸信息,让图片宽度默认为容器的一定百分比 + singlePercent: 0.6 + } + }, + watch: { + urls: { + immediate: true, + handler(newVal) { + if (newVal.length === 1) { + this.getImageRect() + } + } + } + }, + computed: { + imageStyle() { + return (index1, index2) => { + const { space, rowCount, multipleSize, urls } = this, + { addUnit, addStyle } = uni.$u, + rowLen = this.showUrls.length, + allLen = this.urls.length + const style = { + marginRight: addUnit(space), + marginBottom: addUnit(space) + } + // 如果为最后一行,则每个图片都无需下边框 + if (index1 === rowLen) style.marginBottom = 0 + // 每行的最右边一张和总长度的最后一张无需右边框 + if ( + index2 === rowCount || + (index1 === rowLen && + index2 === this.showUrls[index1 - 1].length) + ) + style.marginRight = 0 + return style + } + }, + // 将数组划分为二维数组 + showUrls() { + const arr = [] + this.urls.map((item, index) => { + // 限制最大展示数量 + if (index + 1 <= this.maxCount) { + // 计算该元素为第几个素组内 + const itemIndex = Math.floor(index / this.rowCount) + // 判断对应的索引是否存在 + if (!arr[itemIndex]) { + arr[itemIndex] = [] + } + arr[itemIndex].push(item) + } + }) + return arr + }, + imageWidth() { + return uni.$u.addUnit( + this.urls.length === 1 ? this.singleWidth : this.multipleSize + ) + }, + imageHeight() { + return uni.$u.addUnit( + this.urls.length === 1 ? this.singleHeight : this.multipleSize + ) + }, + // 此变量无实际用途,仅仅是为了利用computed特性,让其在urls长度等变化时,重新计算图片的宽度 + // 因为用户在某些特殊的情况下,需要让文字与相册的宽度相等,所以这里事件的形式对外发送 + albumWidth() { + let width = 0 + if (this.urls.length === 1) { + width = this.singleWidth + } else { + width = + this.showUrls[0].length * this.multipleSize + + this.space * (this.showUrls[0].length - 1) + } + this.$emit('albumWidth', width) + return width + } + }, + methods: { + // 预览图片 + onPreviewTap(url) { + const urls = this.urls.map((item) => { + return this.getSrc(item) + }) + uni.previewImage({ + current: url, + urls + }) + }, + // 获取图片的路径 + getSrc(item) { + return uni.$u.test.object(item) + ? (this.keyName && item[this.keyName]) || item.src + : item + }, + // 单图时,获取图片的尺寸 + // 在小程序中,需要将网络图片的的域名添加到小程序的download域名才可能获取尺寸 + // 在没有添加的情况下,让单图宽度默认为盒子的一定宽度(singlePercent) + getImageRect() { + const src = this.getSrc(this.urls[0]) + uni.getImageInfo({ + src, + success: (res) => { + // 判断图片横向还是竖向展示方式 + const isHorizotal = res.width >= res.height + this.singleWidth = isHorizotal + ? this.singleSize + : (res.width / res.height) * this.singleSize + this.singleHeight = !isHorizotal + ? this.singleSize + : (res.height / res.width) * this.singleWidth + }, + fail: () => { + this.getComponentWidth() + } + }) + }, + // 获取组件的宽度 + async getComponentWidth() { + // 延时一定时间,以获取dom尺寸 + await uni.$u.sleep(30) + // #ifndef APP-NVUE + this.$uGetRect('.u-album__row').then((size) => { + this.singleWidth = size.width * this.singlePercent + }) + // #endif + + // #ifdef APP-NVUE + // 这里ref="u-album__row"所在的标签为通过for循环出来,导致this.$refs['u-album__row']是一个数组 + const ref = this.$refs['u-album__row'][0] + ref && + dom.getComponentRect(ref, (res) => { + this.singleWidth = res.size.width * this.singlePercent + }) + // #endif + } + } +} +</script> + +<style lang="scss" scoped> +@import '../../libs/css/components.scss'; + +.u-album { + @include flex(column); + + &__row { + @include flex(row); + flex-wrap: wrap; + + &__wrapper { + position: relative; + + &__text { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.3); + @include flex(row); + justify-content: center; + align-items: center; + } + } + } +} +</style> \ No newline at end of file diff --git a/uni_modules/uview-ui/components/u-alert/props.js b/uni_modules/uview-ui/components/u-alert/props.js new file mode 100644 index 0000000..4297e2c --- /dev/null +++ b/uni_modules/uview-ui/components/u-alert/props.js @@ -0,0 +1,44 @@ +export default { + props: { + // 显示文字 + title: { + type: String, + default: uni.$u.props.alert.title + }, + // 主题,success/warning/info/error + type: { + type: String, + default: uni.$u.props.alert.type + }, + // 辅助性文字 + description: { + type: String, + default: uni.$u.props.alert.description + }, + // 是否可关闭 + closable: { + type: Boolean, + default: uni.$u.props.alert.closable + }, + // 是否显示图标 + showIcon: { + type: Boolean, + default: uni.$u.props.alert.showIcon + }, + // 浅或深色调,light-浅色,dark-深色 + effect: { + type: String, + default: uni.$u.props.alert.effect + }, + // 文字是否居中 + center: { + type: Boolean, + default: uni.$u.props.alert.center + }, + // 字体大小 + fontSize: { + type: [String, Number], + default: uni.$u.props.alert.fontSize + } + } +} diff --git a/uni_modules/uview-ui/components/u-alert/u-alert.vue b/uni_modules/uview-ui/components/u-alert/u-alert.vue new file mode 100644 index 0000000..81f7d43 --- /dev/null +++ b/uni_modules/uview-ui/components/u-alert/u-alert.vue @@ -0,0 +1,243 @@ +<template> + <u-transition + mode="fade" + :show="show" + > + <view + class="u-alert" + :class="[`u-alert--${type}--${effect}`]" + @tap.stop="clickHandler" + :style="[$u.addStyle(customStyle)]" + > + <view + class="u-alert__icon" + v-if="showIcon" + > + <u-icon + :name="iconName" + size="18" + :color="iconColor" + ></u-icon> + </view> + <view + class="u-alert__content" + :style="[{ + paddingRight: closable ? '20px' : 0 + }]" + > + <text + class="u-alert__content__title" + v-if="title" + :style="[{ + fontSize: $u.addUnit(fontSize), + textAlign: center ? 'center' : 'left' + }]" + :class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]" + >{{ title }}</text> + <text + class="u-alert__content__desc" + v-if="description" + :style="[{ + fontSize: $u.addUnit(fontSize), + textAlign: center ? 'center' : 'left' + }]" + :class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]" + >{{ description }}</text> + </view> + <view + class="u-alert__close" + v-if="closable" + @tap.stop="closeHandler" + > + <u-icon + name="close" + :color="iconColor" + size="15" + ></u-icon> + </view> + </view> + </u-transition> +</template> + +<script> + import props from './props.js'; + /** + * Alert 警告提示 + * @description 警告提示,展现需要关注的信息。 + * @tutorial https://www.uviewui.com/components/alertTips.html + * + * @property {String} title 显示的文字 + * @property {String} type 使用预设的颜色 (默认 'warning' ) + * @property {String} description 辅助性文字,颜色比title浅一点,字号也小一点,可选 + * @property {Boolean} closable 关闭按钮(默认为叉号icon图标) (默认 false ) + * @property {Boolean} showIcon 是否显示左边的辅助图标 ( 默认 false ) + * @property {String} effect 多图时,图片缩放裁剪的模式 (默认 'light' ) + * @property {Boolean} center 文字是否居中 (默认 false ) + * @property {String | Number} fontSize 字体大小 (默认 14 ) + * @property {Object} customStyle 定义需要用到的外部样式 + * @event {Function} click 点击组件时触发 + * @example <u-alert :title="title" type = "warning" :closable="closable" :description = "description"></u-alert> + */ + export default { + name: 'u-alert', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + show: true + } + }, + computed: { + iconColor() { + return this.effect === 'light' ? this.type : '#fff' + }, + // 不同主题对应不同的图标 + iconName() { + switch (this.type) { + case 'success': + return 'checkmark-circle-fill'; + break; + case 'error': + return 'close-circle-fill'; + break; + case 'warning': + return 'error-circle-fill'; + break; + case 'info': + return 'info-circle-fill'; + break; + case 'primary': + return 'more-circle-fill'; + break; + default: + return 'error-circle-fill'; + } + } + }, + methods: { + // 点击内容 + clickHandler() { + this.$emit('click') + }, + // 点击关闭按钮 + closeHandler() { + this.show = false + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-alert { + position: relative; + background-color: $u-primary; + padding: 8px 10px; + @include flex(row); + align-items: center; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + + &--primary--dark { + background-color: $u-primary; + } + + &--primary--light { + background-color: #ecf5ff; + } + + &--error--dark { + background-color: $u-error; + } + + &--error--light { + background-color: #FEF0F0; + } + + &--success--dark { + background-color: $u-success; + } + + &--success--light { + background-color: #f5fff0; + } + + &--warning--dark { + background-color: $u-warning; + } + + &--warning--light { + background-color: #FDF6EC; + } + + &--info--dark { + background-color: $u-info; + } + + &--info--light { + background-color: #f4f4f5; + } + + &__icon { + margin-right: 5px; + } + + &__content { + @include flex(column); + flex: 1; + + &__title { + color: $u-main-color; + font-size: 14px; + font-weight: bold; + color: #fff; + margin-bottom: 2px; + } + + &__desc { + color: $u-main-color; + font-size: 14px; + flex-wrap: wrap; + color: #fff; + } + } + + &__title--dark, + &__desc--dark { + color: #FFFFFF; + } + + &__text--primary--light, + &__text--primary--light { + color: $u-primary; + } + + &__text--success--light, + &__text--success--light { + color: $u-success; + } + + &__text--warning--light, + &__text--warning--light { + color: $u-warning; + } + + &__text--error--light, + &__text--error--light { + color: $u-error; + } + + &__text--info--light, + &__text--info--light { + color: $u-info; + } + + &__close { + position: absolute; + top: 11px; + right: 10px; + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-avatar-group/props.js b/uni_modules/uview-ui/components/u-avatar-group/props.js new file mode 100644 index 0000000..58b42ac --- /dev/null +++ b/uni_modules/uview-ui/components/u-avatar-group/props.js @@ -0,0 +1,52 @@ +export default { + props: { + // 头像图片组 + urls: { + type: Array, + default: uni.$u.props.avatarGroup.urls + }, + // 最多展示的头像数量 + maxCount: { + type: [String, Number], + default: uni.$u.props.avatarGroup.maxCount + }, + // 头像形状 + shape: { + type: String, + default: uni.$u.props.avatarGroup.shape + }, + // 图片裁剪模式 + mode: { + type: String, + default: uni.$u.props.avatarGroup.mode + }, + // 超出maxCount时是否显示查看更多的提示 + showMore: { + type: Boolean, + default: uni.$u.props.avatarGroup.showMore + }, + // 头像大小 + size: { + type: [String, Number], + default: uni.$u.props.avatarGroup.size + }, + // 指定从数组的对象元素中读取哪个属性作为图片地址 + keyName: { + type: String, + default: uni.$u.props.avatarGroup.keyName + }, + // 头像之间的遮挡比例 + gap: { + type: [String, Number], + validator(value) { + return value >= 0 && value <= 1 + }, + default: uni.$u.props.avatarGroup.gap + }, + // 需额外显示的值 + extraValue: { + type: [Number, String], + default: uni.$u.props.avatarGroup.extraValue + } + } +} diff --git a/uni_modules/uview-ui/components/u-avatar-group/u-avatar-group.vue b/uni_modules/uview-ui/components/u-avatar-group/u-avatar-group.vue new file mode 100644 index 0000000..7e996d7 --- /dev/null +++ b/uni_modules/uview-ui/components/u-avatar-group/u-avatar-group.vue @@ -0,0 +1,103 @@ +<template> + <view class="u-avatar-group"> + <view + class="u-avatar-group__item" + v-for="(item, index) in showUrl" + :key="index" + :style="{ + marginLeft: index === 0 ? 0 : $u.addUnit(-size * gap) + }" + > + <u-avatar + :size="size" + :shape="shape" + :mode="mode" + :src="$u.test.object(item) ? keyName && item[keyName] || item.url : item" + ></u-avatar> + <view + class="u-avatar-group__item__show-more" + v-if="showMore && index === showUrl.length - 1 && (urls.length > maxCount || extraValue > 0)" + @tap="clickHandler" + > + <u--text + color="#ffffff" + :size="size * 0.4" + :text="`+${extraValue || urls.length - showUrl.length}`" + align="center" + customStyle="justify-content: center" + ></u--text> + </view> + </view> + </view> +</template> + +<script> + import props from './props.js'; + /** + * AvatarGroup 头像组 + * @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。 + * @tutorial https://www.uviewui.com/components/avatar.html + * + * @property {Array} urls 头像图片组 (默认 [] ) + * @property {String | Number} maxCount 最多展示的头像数量 ( 默认 5 ) + * @property {String} shape 头像形状( 'circle' (默认) | 'square' ) + * @property {String} mode 图片裁剪模式(默认 'scaleToFill' ) + * @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 (默认 true ) + * @property {String | Number} size 头像大小 (默认 40 ) + * @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址 + * @property {String | Number} gap 头像之间的遮挡比例(0.4代表遮挡40%) (默认 0.5 ) + * @property {String | Number} extraValue 需额外显示的值 + * @event {Function} showMore 头像组更多点击 + * @example <u-avatar-group:urls="urls" size="35" gap="0.4" ></u-avatar-group:urls=> + */ + export default { + name: 'u-avatar-group', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + + } + }, + computed: { + showUrl() { + return this.urls.slice(0, this.maxCount) + } + }, + methods: { + clickHandler() { + this.$emit('showMore') + } + }, + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-avatar-group { + @include flex; + + &__item { + margin-left: -10px; + position: relative; + + &--no-indent { + // 如果你想质疑作者不会使用:first-child,说明你太年轻,因为nvue不支持 + margin-left: 0; + } + + &__show-more { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background-color: rgba(0, 0, 0, 0.3); + @include flex; + align-items: center; + justify-content: center; + border-radius: 100px; + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-avatar/props.js b/uni_modules/uview-ui/components/u-avatar/props.js new file mode 100644 index 0000000..34ca0f2 --- /dev/null +++ b/uni_modules/uview-ui/components/u-avatar/props.js @@ -0,0 +1,78 @@ +export default { + props: { + // 头像图片路径(不能为相对路径) + src: { + type: String, + default: uni.$u.props.avatar.src + }, + // 头像形状,circle-圆形,square-方形 + shape: { + type: String, + default: uni.$u.props.avatar.shape + }, + // 头像尺寸 + size: { + type: [String, Number], + default: uni.$u.props.avatar.size + }, + // 裁剪模式 + mode: { + type: String, + default: uni.$u.props.avatar.mode + }, + // 显示的文字 + text: { + type: String, + default: uni.$u.props.avatar.text + }, + // 背景色 + bgColor: { + type: String, + default: uni.$u.props.avatar.bgColor + }, + // 文字颜色 + color: { + type: String, + default: uni.$u.props.avatar.color + }, + // 文字大小 + fontSize: { + type: [String, Number], + default: uni.$u.props.avatar.fontSize + }, + // 显示的图标 + icon: { + type: String, + default: uni.$u.props.avatar.icon + }, + // 显示小程序头像,只对百度,微信,QQ小程序有效 + mpAvatar: { + type: Boolean, + default: uni.$u.props.avatar.mpAvatar + }, + // 是否使用随机背景色 + randomBgColor: { + type: Boolean, + default: uni.$u.props.avatar.randomBgColor + }, + // 加载失败的默认头像(组件有内置默认图片) + defaultUrl: { + type: String, + default: uni.$u.props.avatar.defaultUrl + }, + // 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间 + colorIndex: { + type: [String, Number], + // 校验参数规则,索引在0-19之间 + validator(n) { + return uni.$u.test.range(n, [0, 19]) || n === '' + }, + default: uni.$u.props.avatar.colorIndex + }, + // 组件标识符 + name: { + type: String, + default: uni.$u.props.avatar.name + } + } +} diff --git a/uni_modules/uview-ui/components/u-avatar/u-avatar.vue b/uni_modules/uview-ui/components/u-avatar/u-avatar.vue new file mode 100644 index 0000000..3319be5 --- /dev/null +++ b/uni_modules/uview-ui/components/u-avatar/u-avatar.vue @@ -0,0 +1,172 @@ +<template> + <view + class="u-avatar" + :class="[`u-avatar--${shape}`]" + :style="[{ + backgroundColor: (text || icon) ? (randomBgColor ? colors[colorIndex !== '' ? colorIndex : $u.random(0, 19)] : bgColor) : 'transparent', + width: $u.addUnit(size), + height: $u.addUnit(size), + }, $u.addStyle(customStyle)]" + @tap="clickHandler" + > + <slot> + <!-- #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU --> + <open-data + v-if="mpAvatar && allowMp" + type="userAvatarUrl" + :style="[{ + width: $u.addUnit(size), + height: $u.addUnit(size) + }]" + /> + <!-- #endif --> + <!-- #ifndef MP-WEIXIN && MP-QQ && MP-BAIDU --> + <template v-if="mpAvatar && allowMp"></template> + <!-- #endif --> + <u-icon + v-else-if="icon" + :name="icon" + :size="fontSize" + :color="color" + ></u-icon> + <u--text + v-else-if="text" + :text="text" + :size="fontSize" + :color="color" + align="center" + customStyle="justify-content: center" + ></u--text> + <image + class="u-avatar__image" + v-else + :class="[`u-avatar__image--${shape}`]" + :src="avatarUrl || defaultUrl" + :mode="mode" + @error="errorHandler" + :style="[{ + width: $u.addUnit(size), + height: $u.addUnit(size) + }]" + ></image> + </slot> + </view> +</template> + +<script> + import props from './props.js'; + const base64Avatar = + "data:image/jpg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMraHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjMtYzAxMSA2Ni4xNDU2NjEsIDIwMTIvMDIvMDYtMTQ6NTY6MjcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjREMEQwRkY0RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjREMEQwRkY1RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NEQwRDBGRjJGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NEQwRDBGRjNGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAGBAQEBQQGBQUGCQYFBgkLCAYGCAsMCgoLCgoMEAwMDAwMDBAMDg8QDw4MExMUFBMTHBsbGxwfHx8fHx8fHx8fAQcHBw0MDRgQEBgaFREVGh8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx//wAARCADIAMgDAREAAhEBAxEB/8QAcQABAQEAAwEBAAAAAAAAAAAAAAUEAQMGAgcBAQAAAAAAAAAAAAAAAAAAAAAQAAIBAwICBgkDBQAAAAAAAAABAhEDBCEFMVFBYXGREiKBscHRMkJSEyOh4XLxYjNDFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHbHFyZ/Dam+yLA+Z2L0Pjtyj2poD4AAAAAAAAAAAAAAAAAAAAAAAAKWFs9y6lcvvwQeqj8z9wFaziY1n/HbUX9XF97A7QAGXI23EvJ1goyfzR0YEfN269jeZ+a03pNe0DIAAAAAAAAAAAAAAAAAAAACvtO3RcVkXlWutuL9YFYAAAAAOJRjKLjJVi9GmB5/csH/mu1h/in8PU+QGMAAAAAAAAAAAAAAAAAAaMDG/6MmMH8C80+xAelSSVFolwQAAAAAAAHVlWI37ErUulaPk+hgeYnCUJuElSUXRrrQHAAAAAAAAAAAAAAAAABa2Oz4bM7r4zdF2ICmAAAAAAAAAg7zZ8GX41wuJP0rRgYAAAAAAAAAAAAAAAAAD0m2R8ODaXU33tsDSAAAAAAAAAlb9HyWZcnJd9PcBHAAAAAAAAAAAAAAAAAPS7e64Vn+KA0AAAAAAAAAJm+v8Ftf3ewCKAAAAAAAAAAAAAAAAAX9muqeGo9NttP06+0DcAAAAAAAAAjb7dTu2ra+VOT9P8AQCWAAAAAAAAAAAAAAAAAUNmyPt5Ltv4bui/kuAF0AAAAAAADiUlGLlJ0SVW+oDzOXfd/Ind6JPRdS0QHSAAAAAAAAAAAAAAAAAE2nVaNcGB6Lbs6OTao9LsF51z60BrAAAAAABJ3jOVHjW3r/sa9QEgAAAAAAAAAAAAAAAAAAAPu1duWriuW34ZR4MC9hbnZyEoy8l36XwfYBsAAADaSq9EuLAlZ+7xSdrGdW9Hc5dgEdtt1erfFgAAAAAAAAAAAAAAAAADVjbblX6NR8MH80tEBRs7HYivyzlN8lovaBPzduvY0m6eK10TXtAyAarO55lpJK54orolr+4GqO/Xaea1FvqbXvA+Z77kNeW3GPbV+4DJfzcm/pcm3H6Vou5AdAFLC2ed2Pjv1txa8sV8T6wOL+yZEKu1JXFy4MDBOE4ScZxcZLinoB8gAAAAAAAAAAAB242LeyJ+C3GvN9C7QLmJtePYpKS+5c+p8F2IDYAANJqj1T4oCfk7Nj3G5Wn9qXJax7gJ93Z82D8sVNc4v30A6Xg5i42Z+iLfqARwcyT0sz9MWvWBps7LlTf5Grce9/oBTxdtxseklHxT+uWr9AGoAB138ezfj4bsFJdD6V2MCPm7RdtJzs1uW1xXzL3gTgAAAAAAAAADRhYc8q74I6RWs5ckB6GxYtWLat21SK731sDsAAAAAAAAAAAAAAAASt021NO/YjrxuQXT1oCOAAAAAAABzGLlJRSq26JAelwsWONYjbXxcZvmwO8AAAAAAAAAAAAAAAAAAef3TEWPkVivx3NY9T6UBiAAAAAABo2+VmGXblddIJ8eivRUD0oAAAAAAAAAAAAAAAAAAAYt4tKeFKVNYNSXfRgefAAAAAAAAr7VuSSWPedKaW5v1MCsAAAAAAAAAAAAAAAAAAIe6bj96Ts2n+JPzSXzP3ATgAAAAAAAAFbbt1UUrOQ9FpC4/UwK6aaqtU+DAAAAAAAAAAAAAAA4lKMIuUmoxWrb4ARNx3R3q2rLpa4Sl0y/YCcAAAAAAAAAAANmFud7G8r89r6X0dgFvGzLGRGtuWvTF6NAdwAAAAAAAAAAAy5W442PVN+K59EePp5ARMvOv5MvO6QXCC4AZwAAAAAAAAAAAAAcxlKLUotprg1owN+PvORborq+7Hnwl3gUbO74VzRydt8pKn68ANcJwmqwkpLmnUDkAAAAfNy9atqtyagut0AxXt5xIV8Fbj6lRd7Am5G65V6qUvtwfyx94GMAAAAAAAAAAAAAAAAAAAOU2nVOj5gdsc3LiqRvTpyqwOxbnnrhdfpSfrQB7pnv/AGvuS9gHXPMy5/Fem1yq0v0A6W29XqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//Z"; + /** + * Avatar 头像 + * @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。 + * @tutorial https://www.uviewui.com/components/avatar.html + * + * @property {String} src 头像路径,如加载失败,将会显示默认头像(不能为相对路径) + * @property {String} shape 头像形状 ( circle (默认) | square) + * @property {String | Number} size 头像尺寸,可以为指定字符串(large, default, mini),或者数值 (默认 40 ) + * @property {String} mode 头像图片的裁剪类型,与uni的image组件的mode参数一致,如效果达不到需求,可尝试传widthFix值 (默认 'scaleToFill' ) + * @property {String} text 用文字替代图片,级别优先于src + * @property {String} bgColor 背景颜色,一般显示文字时用 (默认 '#c0c4cc' ) + * @property {String} color 文字颜色 (默认 '#ffffff' ) + * @property {String | Number} fontSize 文字大小 (默认 18 ) + * @property {String} icon 显示的图标 + * @property {Boolean} mpAvatar 显示小程序头像,只对百度,微信,QQ小程序有效 (默认 false ) + * @property {Boolean} randomBgColor 是否使用随机背景色 (默认 false ) + * @property {String} defaultUrl 加载失败的默认头像(组件有内置默认图片) + * @property {String | Number} colorIndex 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间 + * @property {String} name 组件标识符 (默认 'level' ) + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @event {Function} click 点击组件时触发 index: 用户传递的标识符 + * @example <u-avatar :src="src" mode="square"></u-avatar> + */ + export default { + name: 'u-avatar', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + // 如果配置randomBgColor参数为true,在图标或者文字的模式下,会随机从中取出一个颜色值当做背景色 + colors: ['#ffb34b', '#f2bba9', '#f7a196', '#f18080', '#88a867', '#bfbf39', '#89c152', '#94d554', '#f19ec2', + '#afaae4', '#e1b0df', '#c38cc1', '#72dcdc', '#9acdcb', '#77b1cc', '#448aca', '#86cefa', '#98d1ee', + '#73d1f1', + '#80a7dc' + ], + avatarUrl: this.src, + allowMp: false + } + }, + watch: { + // 监听头像src的变化,赋值给内部的avatarUrl变量,因为图片加载失败时,需要修改图片的src为默认值 + // 而组件内部不能直接修改props的值,所以需要一个中间变量 + src: { + immediate: true, + handler(newVal) { + this.avatarUrl = newVal + // 如果没有传src,则主动触发error事件,用于显示默认的头像,否则src为''空字符等的时候,会无内容展示 + if(!newVal) { + this.errorHandler() + } + } + } + }, + computed: { + imageStyle() { + const style = {} + return style + } + }, + created() { + this.init() + }, + methods: { + init() { + // 目前只有这几个小程序平台具有open-data标签 + // 其他平台可以通过uni.getUserInfo类似接口获取信息,但是需要弹窗授权(首次),不合符组件逻辑 + // 故目前自动获取小程序头像只支持这几个平台 + // #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU + this.allowMp = true + // #endif + }, + // 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式 + isImg() { + return this.src.indexOf('/') !== -1 + }, + // 图片加载时失败时触发 + errorHandler() { + this.avatarUrl = this.defaultUrl || base64Avatar + }, + clickHandler() { + this.$emit('click', this.name) + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-avatar { + @include flex; + align-items: center; + justify-content: center; + + &--circle { + border-radius: 100px; + } + + &--square { + border-radius: 4px; + } + + &__image { + &--circle { + border-radius: 100px; + } + + &--square { + border-radius: 4px; + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-back-top/props.js b/uni_modules/uview-ui/components/u-back-top/props.js new file mode 100644 index 0000000..6c702c2 --- /dev/null +++ b/uni_modules/uview-ui/components/u-back-top/props.js @@ -0,0 +1,54 @@ +export default { + props: { + // 返回顶部的形状,circle-圆形,square-方形 + mode: { + type: String, + default: uni.$u.props.backtop.mode + }, + // 自定义图标 + icon: { + type: String, + default: uni.$u.props.backtop.icon + }, + // 提示文字 + text: { + type: String, + default: uni.$u.props.backtop.text + }, + // 返回顶部滚动时间 + duration: { + type: [String, Number], + default: uni.$u.props.backtop.duration + }, + // 滚动距离 + scrollTop: { + type: [String, Number], + default: uni.$u.props.backtop.scrollTop + }, + // 距离顶部多少距离显示,单位px + top: { + type: [String, Number], + default: uni.$u.props.backtop.top + }, + // 返回顶部按钮到底部的距离,单位px + bottom: { + type: [String, Number], + default: uni.$u.props.backtop.bottom + }, + // 返回顶部按钮到右边的距离,单位px + right: { + type: [String, Number], + default: uni.$u.props.backtop.right + }, + // 层级 + zIndex: { + type: [String, Number], + default: uni.$u.props.backtop.zIndex + }, + // 图标的样式,对象形式 + iconStyle: { + type: Object, + default: uni.$u.props.backtop.iconStyle + } + } +} diff --git a/uni_modules/uview-ui/components/u-back-top/u-back-top.vue b/uni_modules/uview-ui/components/u-back-top/u-back-top.vue new file mode 100644 index 0000000..2d07566 --- /dev/null +++ b/uni_modules/uview-ui/components/u-back-top/u-back-top.vue @@ -0,0 +1,129 @@ +<template> + <u-transition + mode="fade" + :customStyle="backTopStyle" + :show="show" + > + <view + class="u-back-top" + :style="[contentStyle]" + v-if="!$slots.default && !$slots.$default" + @click="backToTop" + > + <u-icon + :name="icon" + :custom-style="iconStyle" + ></u-icon> + <text + v-if="text" + class="u-back-top__text" + >{{text}}</text> + </view> + <slot v-else /> + </u-transition> +</template> + +<script> + import props from './props.js'; + // #ifdef APP-NVUE + const dom = weex.requireModule('dom') + // #endif + /** + * backTop 返回顶部 + * @description 本组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。 + * @tutorial https://uviewui.com/components/backTop.html + * + * @property {String} mode 返回顶部的形状,circle-圆形,square-方形 (默认 'circle' ) + * @property {String} icon 自定义图标 (默认 'arrow-upward' ) 见官方文档示例 + * @property {String} text 提示文字 + * @property {String | Number} duration 返回顶部滚动时间 (默认 100) + * @property {String | Number} scrollTop 滚动距离 (默认 0 ) + * @property {String | Number} top 距离顶部多少距离显示,单位px (默认 400 ) + * @property {String | Number} bottom 返回顶部按钮到底部的距离,单位px (默认 100 ) + * @property {String | Number} right 返回顶部按钮到右边的距离,单位px (默认 20 ) + * @property {String | Number} zIndex 层级 (默认 9 ) + * @property {Object<Object>} iconStyle 图标的样式,对象形式 (默认 {color: '#909399',fontSize: '19px'}) + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @example <u-back-top :scrollTop="scrollTop"></u-back-top> + */ + export default { + name: 'u-back-top', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + computed: { + backTopStyle() { + // 动画组件样式 + const style = { + bottom: uni.$u.addUnit(this.bottom), + right: uni.$u.addUnit(this.right), + width: '40px', + height: '40px', + position: 'fixed', + zIndex: 10, + } + return style + }, + show() { + return uni.$u.getPx(this.scrollTop) > uni.$u.getPx(this.top) + }, + contentStyle() { + const style = {} + let radius = 0 + // 是否圆形 + if(this.mode === 'circle') { + radius = '100px' + } else { + radius = '4px' + } + // 为了兼容安卓nvue,只能这么分开写 + style.borderTopLeftRadius = radius + style.borderTopRightRadius = radius + style.borderBottomLeftRadius = radius + style.borderBottomRightRadius = radius + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) + } + }, + methods: { + backToTop() { + // #ifdef APP-NVUE + if (!this.$parent.$refs['u-back-top']) { + uni.$u.error(`nvue页面需要给页面最外层元素设置"ref='u-back-top'`) + } + dom.scrollToElement(this.$parent.$refs['u-back-top'], { + offset: 0 + }) + // #endif + + // #ifndef APP-NVUE + uni.pageScrollTo({ + scrollTop: 0, + duration: this.duration + }); + // #endif + this.$emit('click') + } + } + } +</script> + +<style lang="scss" scoped> + @import '../../libs/css/components.scss'; + $u-back-top-flex:1 !default; + $u-back-top-height:100% !default; + $u-back-top-background-color:#E1E1E1 !default; + $u-back-top-tips-font-size:12px !default; + .u-back-top { + @include flex; + flex-direction: column; + align-items: center; + flex:$u-back-top-flex; + height: $u-back-top-height; + justify-content: center; + background-color: $u-back-top-background-color; + + &__tips { + font-size:$u-back-top-tips-font-size; + transform: scale(0.8); + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-badge/props.js b/uni_modules/uview-ui/components/u-badge/props.js new file mode 100644 index 0000000..74c032c --- /dev/null +++ b/uni_modules/uview-ui/components/u-badge/props.js @@ -0,0 +1,72 @@ +export default { + props: { + // 是否显示圆点 + isDot: { + type: Boolean, + default: uni.$u.props.badge.isDot + }, + // 显示的内容 + value: { + type: [Number, String], + default: uni.$u.props.badge.value + }, + // 是否显示 + show: { + type: Boolean, + default: uni.$u.props.badge.show + }, + // 最大值,超过最大值会显示 '{max}+' + max: { + type: [Number, String], + default: uni.$u.props.badge.max + }, + // 主题类型,error|warning|success|primary + type: { + type: String, + default: uni.$u.props.badge.type + }, + // 当数值为 0 时,是否展示 Badge + showZero: { + type: Boolean, + default: uni.$u.props.badge.showZero + }, + // 背景颜色,优先级比type高,如设置,type参数会失效 + bgColor: { + type: [String, null], + default: uni.$u.props.badge.bgColor + }, + // 字体颜色 + color: { + type: [String, null], + default: uni.$u.props.badge.color + }, + // 徽标形状,circle-四角均为圆角,horn-左下角为直角 + shape: { + type: String, + default: uni.$u.props.badge.shape + }, + // 设置数字的显示方式,overflow|ellipsis|limit + // overflow会根据max字段判断,超出显示`${max}+` + // ellipsis会根据max判断,超出显示`${max}...` + // limit会依据1000作为判断条件,超出1000,显示`${value/1000}K`,比如2.2k、3.34w,最多保留2位小数 + numberType: { + type: String, + default: uni.$u.props.badge.numberType + }, + // 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效 + offset: { + type: Array, + default: uni.$u.props.badge.offset + }, + // 是否反转背景和字体颜色 + inverted: { + type: Boolean, + default: uni.$u.props.badge.inverted + }, + // 是否绝对定位 + absolute: { + type: Boolean, + default: uni.$u.props.badge.absolute + } + } +} diff --git a/uni_modules/uview-ui/components/u-badge/u-badge.vue b/uni_modules/uview-ui/components/u-badge/u-badge.vue new file mode 100644 index 0000000..53cfc81 --- /dev/null +++ b/uni_modules/uview-ui/components/u-badge/u-badge.vue @@ -0,0 +1,171 @@ +<template> + <text + v-if="show && ((Number(value) === 0 ? showZero : true) || isDot)" + :class="[isDot ? 'u-badge--dot' : 'u-badge--not-dot', inverted && 'u-badge--inverted', shape === 'horn' && 'u-badge--horn', `u-badge--${type}${inverted ? '--inverted' : ''}`]" + :style="[$u.addStyle(customStyle), badgeStyle]" + class="u-badge" + >{{ isDot ? '' :showValue }}</text> +</template> + +<script> + import props from './props.js'; + /** + * badge 徽标数 + * @description 该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。 + * @tutorial https://uviewui.com/components/badge.html + * + * @property {Boolean} isDot 是否显示圆点 (默认 false ) + * @property {String | Number} value 显示的内容 + * @property {Boolean} show 是否显示 (默认 true ) + * @property {String | Number} max 最大值,超过最大值会显示 '{max}+' (默认999) + * @property {String} type 主题类型,error|warning|success|primary (默认 'error' ) + * @property {Boolean} showZero 当数值为 0 时,是否展示 Badge (默认 false ) + * @property {String} bgColor 背景颜色,优先级比type高,如设置,type参数会失效 + * @property {String} color 字体颜色 (默认 '#ffffff' ) + * @property {String} shape 徽标形状,circle-四角均为圆角,horn-左下角为直角 (默认 'circle' ) + * @property {String} numberType 设置数字的显示方式,overflow|ellipsis|limit (默认 'overflow' ) + * @property {Array}} offset 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效 + * @property {Boolean} inverted 是否反转背景和字体颜色(默认 false ) + * @property {Boolean} absolute 是否绝对定位(默认 false ) + * @property {Object} customStyle 定义需要用到的外部样式 + * @example <u-badge :type="type" :count="count"></u-badge> + */ + export default { + name: 'u-badge', + mixins: [uni.$u.mpMixin, props, uni.$u.mixin], + computed: { + // 是否将badge中心与父组件右上角重合 + boxStyle() { + let style = {}; + return style; + }, + // 整个组件的样式 + badgeStyle() { + const style = {} + if(this.color) { + style.color = this.color + } + if (this.bgColor && !this.inverted) { + style.backgroundColor = this.bgColor + } + if (this.absolute) { + style.position = 'absolute' + // 如果有设置offset参数 + if(this.offset.length) { + // top和right分为为offset的第一个和第二个值,如果没有第二个值,则right等于top + const top = this.offset[0] + const right = this.offset[1] || top + style.top = uni.$u.addUnit(top) + style.right = uni.$u.addUnit(right) + } + } + return style + }, + showValue() { + switch (this.numberType) { + case "overflow": + return Number(this.value) > Number(this.max) ? this.max + "+" : this.value + break; + case "ellipsis": + return Number(this.value) > Number(this.max) ? "..." : this.value + break; + case "limit": + return Number(this.value) > 999 ? Number(this.value) >= 9999 ? + Math.floor(this.value / 1e4 * 100) / 100 + "w" : Math.floor(this.value / + 1e3 * 100) / 100 + "k" : this.value + break; + default: + return Number(this.value) + } + }, + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + $u-badge-primary: $u-primary !default; + $u-badge-error: $u-error !default; + $u-badge-success: $u-success !default; + $u-badge-info: $u-info !default; + $u-badge-warning: $u-warning !default; + $u-badge-dot-radius: 100px !default; + $u-badge-dot-size: 8px !default; + $u-badge-dot-right: 4px !default; + $u-badge-dot-top: 0 !default; + $u-badge-text-font-size: 11px !default; + $u-badge-text-right: 10px !default; + $u-badge-text-padding: 2px 5px !default; + $u-badge-text-align: center !default; + $u-badge-text-color: #FFFFFF !default; + + .u-badge { + border-top-right-radius: $u-badge-dot-radius; + border-top-left-radius: $u-badge-dot-radius; + border-bottom-left-radius: $u-badge-dot-radius; + border-bottom-right-radius: $u-badge-dot-radius; + @include flex; + line-height: $u-badge-text-font-size; + text-align: $u-badge-text-align; + font-size: $u-badge-text-font-size; + color: $u-badge-text-color; + + &--dot { + height: $u-badge-dot-size; + width: $u-badge-dot-size; + } + + &--inverted { + font-size: 13px; + } + + &--not-dot { + padding: $u-badge-text-padding; + } + + &--horn { + border-bottom-left-radius: 0; + } + + &--primary { + background-color: $u-badge-primary; + } + + &--primary--inverted { + color: $u-badge-primary; + } + + &--error { + background-color: $u-badge-error; + } + + &--error--inverted { + color: $u-badge-error; + } + + &--success { + background-color: $u-badge-success; + } + + &--success--inverted { + color: $u-badge-success; + } + + &--info { + background-color: $u-badge-info; + } + + &--info--inverted { + color: $u-badge-info; + } + + &--warning { + background-color: $u-badge-warning; + } + + &--warning--inverted { + color: $u-badge-warning; + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-button/nvue.scss b/uni_modules/uview-ui/components/u-button/nvue.scss new file mode 100644 index 0000000..490db7d --- /dev/null +++ b/uni_modules/uview-ui/components/u-button/nvue.scss @@ -0,0 +1,46 @@ +$u-button-active-opacity:0.75 !default; +$u-button-loading-text-margin-left:4px !default; +$u-button-text-color: #FFFFFF !default; +$u-button-text-plain-error-color:$u-error !default; +$u-button-text-plain-warning-color:$u-warning !default; +$u-button-text-plain-success-color:$u-success !default; +$u-button-text-plain-info-color:$u-info !default; +$u-button-text-plain-primary-color:$u-primary !default; +.u-button { + &--active { + opacity: $u-button-active-opacity; + } + + &--active--plain { + background-color: rgb(217, 217, 217); + } + + &__loading-text { + margin-left:$u-button-loading-text-margin-left; + } + + &__text, + &__loading-text { + color:$u-button-text-color; + } + + &__text--plain--error { + color:$u-button-text-plain-error-color; + } + + &__text--plain--warning { + color:$u-button-text-plain-warning-color; + } + + &__text--plain--success{ + color:$u-button-text-plain-success-color; + } + + &__text--plain--info { + color:$u-button-text-plain-info-color; + } + + &__text--plain--primary { + color:$u-button-text-plain-primary-color; + } +} \ No newline at end of file diff --git a/uni_modules/uview-ui/components/u-button/props.js b/uni_modules/uview-ui/components/u-button/props.js new file mode 100644 index 0000000..07fd844 --- /dev/null +++ b/uni_modules/uview-ui/components/u-button/props.js @@ -0,0 +1,161 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-16 10:04:04 + * @LastAuthor : LQ + * @lastTime : 2021-08-16 10:04:24 + * @FilePath : /u-view2.0/uview-ui/components/u-button/props.js + */ +export default { + props: { + // 是否细边框 + hairline: { + type: Boolean, + default: uni.$u.props.button.hairline + }, + // 按钮的预置样式,info,primary,error,warning,success + type: { + type: String, + default: uni.$u.props.button.type + }, + // 按钮尺寸,large,normal,small,mini + size: { + type: String, + default: uni.$u.props.button.size + }, + // 按钮形状,circle(两边为半圆),square(带圆角) + shape: { + type: String, + default: uni.$u.props.button.shape + }, + // 按钮是否镂空 + plain: { + type: Boolean, + default: uni.$u.props.button.plain + }, + // 是否禁止状态 + disabled: { + type: Boolean, + default: uni.$u.props.button.disabled + }, + // 是否加载中 + loading: { + type: Boolean, + default: uni.$u.props.button.loading + }, + // 加载中提示文字 + loadingText: { + type: [String, Number], + default: uni.$u.props.button.loadingText + }, + // 加载状态图标类型 + loadingMode: { + type: String, + default: uni.$u.props.button.loadingMode + }, + // 加载图标大小 + loadingSize: { + type: [String, Number], + default: uni.$u.props.button.loadingSize + }, + // 开放能力,具体请看uniapp稳定关于button组件部分说明 + // https://uniapp.dcloud.io/component/button + openType: { + type: String, + default: uni.$u.props.button.openType + }, + // 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件 + // 取值为submit(提交表单),reset(重置表单) + formType: { + type: String, + default: uni.$u.props.button.formType + }, + // 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 + // 只微信小程序、QQ小程序有效 + appParameter: { + type: String, + default: uni.$u.props.button.appParameter + }, + // 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效 + hoverStopPropagation: { + type: Boolean, + default: uni.$u.props.button.hoverStopPropagation + }, + // 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效 + lang: { + type: String, + default: uni.$u.props.button.lang + }, + // 会话来源,open-type="contact"时有效。只微信小程序有效 + sessionFrom: { + type: String, + default: uni.$u.props.button.sessionFrom + }, + // 会话内消息卡片标题,open-type="contact"时有效 + // 默认当前标题,只微信小程序有效 + sendMessageTitle: { + type: String, + default: uni.$u.props.button.sendMessageTitle + }, + // 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效 + // 默认当前分享路径,只微信小程序有效 + sendMessagePath: { + type: String, + default: uni.$u.props.button.sendMessagePath + }, + // 会话内消息卡片图片,open-type="contact"时有效 + // 默认当前页面截图,只微信小程序有效 + sendMessageImg: { + type: String, + default: uni.$u.props.button.sendMessageImg + }, + // 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示, + // 用户点击后可以快速发送小程序消息,open-type="contact"时有效 + showMessageCard: { + type: Boolean, + default: uni.$u.props.button.showMessageCard + }, + // 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取 + dataName: { + type: String, + default: uni.$u.props.button.dataName + }, + // 节流,一定时间内只能触发一次 + throttleTime: { + type: [String, Number], + default: uni.$u.props.button.throttleTime + }, + // 按住后多久出现点击态,单位毫秒 + hoverStartTime: { + type: [String, Number], + default: uni.$u.props.button.hoverStartTime + }, + // 手指松开后点击态保留时间,单位毫秒 + hoverStayTime: { + type: [String, Number], + default: uni.$u.props.button.hoverStayTime + }, + // 按钮文字,之所以通过props传入,是因为slot传入的话 + // nvue中无法控制文字的样式 + text: { + type: [String, Number], + default: uni.$u.props.button.text + }, + // 按钮图标 + icon: { + type: String, + default: uni.$u.props.button.icon + }, + // 按钮图标 + iconColor: { + type: String, + default: uni.$u.props.button.icon + }, + // 按钮颜色,支持传入linear-gradient渐变色 + color: { + type: String, + default: uni.$u.props.button.color + } + } +} diff --git a/uni_modules/uview-ui/components/u-button/u-button.vue b/uni_modules/uview-ui/components/u-button/u-button.vue new file mode 100644 index 0000000..5494351 --- /dev/null +++ b/uni_modules/uview-ui/components/u-button/u-button.vue @@ -0,0 +1,490 @@ +<template> + <!-- #ifndef APP-NVUE --> + <button + :hover-start-time="Number(hoverStartTime)" + :hover-stay-time="Number(hoverStayTime)" + :form-type="formType" + :open-type="openType" + :app-parameter="appParameter" + :hover-stop-propagation="hoverStopPropagation" + :send-message-title="sendMessageTitle" + :send-message-path="sendMessagePath" + :lang="lang" + :data-name="dataName" + :session-from="sessionFrom" + :send-message-img="sendMessageImg" + :show-message-card="showMessageCard" + @getphonenumber="getphonenumber" + @getuserinfo="getuserinfo" + @error="error" + @opensetting="opensetting" + @launchapp="launchapp" + :hover-class="!disabled && !loading ? 'u-button--active' : ''" + class="u-button u-reset-button" + :style="[baseColor, $u.addStyle(customStyle)]" + @tap="clickHandler" + :class="bemClass" + > + <template v-if="loading"> + <u-loading-icon + :mode="loadingMode" + :size="loadingSize * 1.15" + :color="loadingColor" + ></u-loading-icon> + <text + class="u-button__loading-text" + :style="[{ fontSize: textSize + 'px' }]" + >{{ loadingText || text }}</text + > + </template> + <template v-else> + <u-icon + v-if="icon" + :name="icon" + :color="iconColorCom" + :size="textSize * 1.35" + :customStyle="{ marginRight: '2px' }" + ></u-icon> + <slot> + <text + class="u-button__text" + :style="[{ fontSize: textSize + 'px' }]" + >{{ text }}</text + > + </slot> + </template> + </button> + <!-- #endif --> + + <!-- #ifdef APP-NVUE --> + <view + :hover-start-time="Number(hoverStartTime)" + :hover-stay-time="Number(hoverStayTime)" + class="u-button" + :hover-class=" + !disabled && !loading && !color && (plain || type === 'info') + ? 'u-button--active--plain' + : !disabled && !loading && !plain + ? 'u-button--active' + : '' + " + @tap="clickHandler" + :class="bemClass" + :style="[baseColor, $u.addStyle(customStyle)]" + > + <template v-if="loading"> + <u-loading-icon + :mode="loadingMode" + :size="loadingSize * 1.15" + :color="loadingColor" + ></u-loading-icon> + <text + class="u-button__loading-text" + :style="[nvueTextStyle]" + :class="[plain && `u-button__text--plain--${type}`]" + >{{ loadingText || text }}</text + > + </template> + <template v-else> + <u-icon + v-if="icon" + :name="icon" + :color="iconColorCom" + :size="textSize * 1.35" + ></u-icon> + <text + class="u-button__text" + :style="[ + { + marginLeft: icon ? '2px' : 0, + }, + nvueTextStyle, + ]" + :class="[plain && `u-button__text--plain--${type}`]" + >{{ text }}</text + > + </template> + </view> + <!-- #endif --> +</template> + +<script> +import button from "../../libs/mixin/button.js"; +import openType from "../../libs/mixin/openType.js"; +import props from "./props.js"; +/** + * button 按钮 + * @description Button 按钮 + * @tutorial https://www.uviewui.com/components/button.html + * + * @property {Boolean} hairline 是否显示按钮的细边框 (默认 true ) + * @property {String} type 按钮的预置样式,info,primary,error,warning,success (默认 'info' ) + * @property {String} size 按钮尺寸,large,normal,mini (默认 normal) + * @property {String} shape 按钮形状,circle(两边为半圆),square(带圆角) (默认 'square' ) + * @property {Boolean} plain 按钮是否镂空,背景色透明 (默认 false) + * @property {Boolean} disabled 是否禁用 (默认 false) + * @property {Boolean} loading 按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈) (默认 false) + * @property {String | Number} loadingText 加载中提示文字 + * @property {String} loadingMode 加载状态图标类型 (默认 'spinner' ) + * @property {String | Number} loadingSize 加载图标大小 (默认 15 ) + * @property {String} openType 开放能力,具体请看uniapp稳定关于button组件部分说明 + * @property {String} formType 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件 + * @property {String} appParameter 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 (注:只微信小程序、QQ小程序有效) + * @property {Boolean} hoverStopPropagation 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效(默认 true ) + * @property {String} lang 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文(默认 en ) + * @property {String} sessionFrom 会话来源,openType="contact"时有效 + * @property {String} sendMessageTitle 会话内消息卡片标题,openType="contact"时有效 + * @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径,openType="contact"时有效 + * @property {String} sendMessageImg 会话内消息卡片图片,openType="contact"时有效 + * @property {Boolean} showMessageCard 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效(默认false) + * @property {String} dataName 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取 + * @property {String | Number} throttleTime 节流,一定时间内只能触发一次 (默认 0 ) + * @property {String | Number} hoverStartTime 按住后多久出现点击态,单位毫秒 (默认 0 ) + * @property {String | Number} hoverStayTime 手指松开后点击态保留时间,单位毫秒 (默认 200 ) + * @property {String | Number} text 按钮文字,之所以通过props传入,是因为slot传入的话(注:nvue中无法控制文字的样式) + * @property {String} icon 按钮图标 + * @property {String} iconColor 按钮图标颜色 + * @property {String} color 按钮颜色,支持传入linear-gradient渐变色 + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @event {Function} click 非禁止并且非加载中,才能点击 + * @event {Function} getphonenumber open-type="getPhoneNumber"时有效 + * @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,从返回参数的detail中获取到的值同uni.getUserInfo + * @event {Function} error 当使用开放能力时,发生错误的回调 + * @event {Function} opensetting 在打开授权设置页并关闭后回调 + * @event {Function} launchapp 打开 APP 成功的回调 + * @example <u-button>月落</u-button> + */ +export default { + name: "u-button", + // #ifdef MP + mixins: [uni.$u.mpMixin, uni.$u.mixin, button, openType, props], + // #endif + // #ifndef MP + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + // #endif + data() { + return {}; + }, + computed: { + // 生成bem风格的类名 + bemClass() { + // this.bem为一个computed变量,在mixin中 + if (!this.color) { + return this.bem( + "button", + ["type", "shape", "size"], + ["disabled", "plain", "hairline"] + ); + } else { + // 由于nvue的原因,在有color参数时,不需要传入type,否则会生成type相关的类型,影响最终的样式 + return this.bem( + "button", + ["shape", "size"], + ["disabled", "plain", "hairline"] + ); + } + }, + loadingColor() { + if (this.plain) { + // 如果有设置color值,则用color值,否则使用type主题颜色 + return this.color + ? this.color + : uni.$u.config.color[`u-${this.type}`]; + } + if (this.type === "info") { + return "#c9c9c9"; + } + return "rgb(200, 200, 200)"; + }, + iconColorCom() { + // 如果是镂空状态,设置了color就用color值,否则使用主题颜色, + // u-icon的color能接受一个主题颜色的值 + if (this.iconColor) return this.iconColor; + if (this.plain) { + return this.color ? this.color : this.type; + } else { + return this.type === "info" ? "#000000" : "#ffffff"; + } + }, + baseColor() { + let style = {}; + if (this.color) { + // 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色 + style.color = this.plain ? this.color : "white"; + if (!this.plain) { + // 非镂空,背景色使用自定义的颜色 + style["background-color"] = this.color; + } + if (this.color.indexOf("gradient") !== -1) { + // 如果自定义的颜色为渐变色,不显示边框,以及通过backgroundImage设置渐变色 + // weex文档说明可以写borderWidth的形式,为什么这里需要分开写? + // 因为weex是阿里巴巴为了部门业绩考核而做的你懂的东西,所以需要这么写才有效 + style.borderTopWidth = 0; + style.borderRightWidth = 0; + style.borderBottomWidth = 0; + style.borderLeftWidth = 0; + if (!this.plain) { + style.backgroundImage = this.color; + } + } else { + // 非渐变色,则设置边框相关的属性 + style.borderColor = this.color; + style.borderWidth = "1px"; + style.borderStyle = "solid"; + } + } + return style; + }, + // nvue版本按钮的字体不会继承父组件的颜色,需要对每一个text组件进行单独的设置 + nvueTextStyle() { + let style = {}; + // 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色 + if (this.type === "info") { + style.color = "#323233"; + } + if (this.color) { + style.color = this.plain ? this.color : "white"; + } + style.fontSize = this.textSize + "px"; + return style; + }, + // 字体大小 + textSize() { + let fontSize = 14, + { size } = this; + if (size === "large") fontSize = 16; + if (size === "normal") fontSize = 14; + if (size === "small") fontSize = 12; + if (size === "mini") fontSize = 10; + return fontSize; + }, + }, + methods: { + clickHandler() { + // 非禁止并且非加载中,才能点击 + if (!this.disabled && !this.loading) { + // 进行节流控制,每this.throttle毫秒内,只在开始处执行 + uni.$u.throttle(() => { + this.$emit("click"); + }, this.throttleTime); + } + }, + // 下面为对接uniapp官方按钮开放能力事件回调的对接 + getphonenumber(res) { + this.$emit("getphonenumber", res); + }, + getuserinfo(res) { + this.$emit("getuserinfo", res); + }, + error(res) { + this.$emit("error", res); + }, + opensetting(res) { + this.$emit("opensetting", res); + }, + launchapp(res) { + this.$emit("launchapp", res); + }, + }, +}; +</script> + +<style lang="scss" scoped> +@import "../../libs/css/components.scss"; + +/* #ifndef APP-NVUE */ +@import "./vue.scss"; +/* #endif */ + +/* #ifdef APP-NVUE */ +@import "./nvue.scss"; +/* #endif */ + +$u-button-u-button-height: 40px !default; +$u-button-text-font-size: 15px !default; +$u-button-loading-text-font-size: 15px !default; +$u-button-loading-text-margin-left: 4px !default; +$u-button-large-width: 100% !default; +$u-button-large-height: 50px !default; +$u-button-normal-padding: 0 12px !default; +$u-button-large-padding: 0 15px !default; +$u-button-normal-font-size: 14px !default; +$u-button-small-min-width: 60px !default; +$u-button-small-height: 30px !default; +$u-button-small-padding: 0px 8px !default; +$u-button-mini-padding: 0px 8px !default; +$u-button-small-font-size: 12px !default; +$u-button-mini-height: 22px !default; +$u-button-mini-font-size: 10px !default; +$u-button-mini-min-width: 50px !default; +$u-button-disabled-opacity: 0.5 !default; +$u-button-info-color: #323233 !default; +$u-button-info-background-color: #fff !default; +$u-button-info-border-color: #ebedf0 !default; +$u-button-info-border-width: 1px !default; +$u-button-info-border-style: solid !default; +$u-button-success-color: #fff !default; +$u-button-success-background-color: $u-success !default; +$u-button-success-border-color: $u-button-success-background-color !default; +$u-button-success-border-width: 1px !default; +$u-button-success-border-style: solid !default; +$u-button-primary-color: #fff !default; +$u-button-primary-background-color: $u-primary !default; +$u-button-primary-border-color: $u-button-primary-background-color !default; +$u-button-primary-border-width: 1px !default; +$u-button-primary-border-style: solid !default; +$u-button-error-color: #fff !default; +$u-button-error-background-color: $u-error !default; +$u-button-error-border-color: $u-button-error-background-color !default; +$u-button-error-border-width: 1px !default; +$u-button-error-border-style: solid !default; +$u-button-warning-color: #fff !default; +$u-button-warning-background-color: $u-warning !default; +$u-button-warning-border-color: $u-button-warning-background-color !default; +$u-button-warning-border-width: 1px !default; +$u-button-warning-border-style: solid !default; +$u-button-block-width: 100% !default; +$u-button-circle-border-top-right-radius: 100px !default; +$u-button-circle-border-top-left-radius: 100px !default; +$u-button-circle-border-bottom-left-radius: 100px !default; +$u-button-circle-border-bottom-right-radius: 100px !default; +$u-button-square-border-top-right-radius: 3px !default; +$u-button-square-border-top-left-radius: 3px !default; +$u-button-square-border-bottom-left-radius: 3px !default; +$u-button-square-border-bottom-right-radius: 3px !default; +$u-button-icon-min-width: 1em !default; +$u-button-plain-background-color: #fff !default; +$u-button-hairline-border-width: 0.5px !default; + +.u-button { + height: $u-button-u-button-height; + position: relative; + align-items: center; + justify-content: center; + @include flex; + /* #ifndef APP-NVUE */ + box-sizing: border-box; + /* #endif */ + flex-direction: row; + + &__text { + font-size: $u-button-text-font-size; + } + + &__loading-text { + font-size: $u-button-loading-text-font-size; + margin-left: $u-button-loading-text-margin-left; + } + + &--large { + /* #ifndef APP-NVUE */ + width: $u-button-large-width; + /* #endif */ + height: $u-button-large-height; + padding: $u-button-large-padding; + } + + &--normal { + padding: $u-button-normal-padding; + font-size: $u-button-normal-font-size; + } + + &--small { + /* #ifndef APP-NVUE */ + min-width: $u-button-small-min-width; + /* #endif */ + height: $u-button-small-height; + padding: $u-button-small-padding; + font-size: $u-button-small-font-size; + } + + &--mini { + height: $u-button-mini-height; + font-size: $u-button-mini-font-size; + /* #ifndef APP-NVUE */ + min-width: $u-button-mini-min-width; + /* #endif */ + padding: $u-button-mini-padding; + } + + &--disabled { + opacity: $u-button-disabled-opacity; + } + + &--info { + color: $u-button-info-color; + background-color: $u-button-info-background-color; + border-color: $u-button-info-border-color; + border-width: $u-button-info-border-width; + border-style: $u-button-info-border-style; + } + + &--success { + color: $u-button-success-color; + background-color: $u-button-success-background-color; + border-color: $u-button-success-border-color; + border-width: $u-button-success-border-width; + border-style: $u-button-success-border-style; + } + + &--primary { + color: $u-button-primary-color; + background-color: $u-button-primary-background-color; + border-color: $u-button-primary-border-color; + border-width: $u-button-primary-border-width; + border-style: $u-button-primary-border-style; + } + + &--error { + color: $u-button-error-color; + background-color: $u-button-error-background-color; + border-color: $u-button-error-border-color; + border-width: $u-button-error-border-width; + border-style: $u-button-error-border-style; + } + + &--warning { + color: $u-button-warning-color; + background-color: $u-button-warning-background-color; + border-color: $u-button-warning-border-color; + border-width: $u-button-warning-border-width; + border-style: $u-button-warning-border-style; + } + + &--block { + @include flex; + width: $u-button-block-width; + } + + &--circle { + border-top-right-radius: $u-button-circle-border-top-right-radius; + border-top-left-radius: $u-button-circle-border-top-left-radius; + border-bottom-left-radius: $u-button-circle-border-bottom-left-radius; + border-bottom-right-radius: $u-button-circle-border-bottom-right-radius; + } + + &--square { + border-bottom-left-radius: $u-button-square-border-top-right-radius; + border-bottom-right-radius: $u-button-square-border-top-left-radius; + border-top-left-radius: $u-button-square-border-bottom-left-radius; + border-top-right-radius: $u-button-square-border-bottom-right-radius; + } + + &__icon { + /* #ifndef APP-NVUE */ + min-width: $u-button-icon-min-width; + line-height: inherit !important; + vertical-align: top; + /* #endif */ + } + + &--plain { + background-color: $u-button-plain-background-color; + } + + &--hairline { + border-width: $u-button-hairline-border-width !important; + } +} +</style> diff --git a/uni_modules/uview-ui/components/u-button/vue.scss b/uni_modules/uview-ui/components/u-button/vue.scss new file mode 100644 index 0000000..32019b2 --- /dev/null +++ b/uni_modules/uview-ui/components/u-button/vue.scss @@ -0,0 +1,80 @@ +// nvue下hover-class无效 +$u-button-before-top:50% !default; +$u-button-before-left:50% !default; +$u-button-before-width:100% !default; +$u-button-before-height:100% !default; +$u-button-before-transform:translate(-50%, -50%) !default; +$u-button-before-opacity:0 !default; +$u-button-before-background-color:#000 !default; +$u-button-before-border-color:#000 !default; +$u-button-active-before-opacity:.15 !default; +$u-button-icon-margin-left:4px !default; +$u-button-plain-u-button-info-color:$u-info; +$u-button-plain-u-button-success-color:$u-success; +$u-button-plain-u-button-error-color:$u-error; +$u-button-plain-u-button-warning-color:$u-error; + +.u-button { + width: 100%; + + &__text { + white-space: nowrap; + line-height: 1; + } + + &:before { + position: absolute; + top:$u-button-before-top; + left:$u-button-before-left; + width:$u-button-before-width; + height:$u-button-before-height; + border: inherit; + border-radius: inherit; + transform:$u-button-before-transform; + opacity:$u-button-before-opacity; + content: " "; + background-color:$u-button-before-background-color; + border-color:$u-button-before-border-color; + } + + &--active { + &:before { + opacity: .15 + } + } + + &__icon+&__text:not(:empty), + &__loading-text { + margin-left:$u-button-icon-margin-left; + } + + &--plain { + &.u-button--primary { + color: $u-primary; + } + } + + &--plain { + &.u-button--info { + color:$u-button-plain-u-button-info-color; + } + } + + &--plain { + &.u-button--success { + color:$u-button-plain-u-button-success-color; + } + } + + &--plain { + &.u-button--error { + color:$u-button-plain-u-button-error-color; + } + } + + &--plain { + &.u-button--warning { + color:$u-button-plain-u-button-warning-color; + } + } +} diff --git a/uni_modules/uview-ui/components/u-calendar/header.vue b/uni_modules/uview-ui/components/u-calendar/header.vue new file mode 100644 index 0000000..dc4f7d0 --- /dev/null +++ b/uni_modules/uview-ui/components/u-calendar/header.vue @@ -0,0 +1,99 @@ +<template> + <view class="u-calendar-header u-border-bottom"> + <text + class="u-calendar-header__title" + v-if="showTitle" + >{{ title }}</text> + <text + class="u-calendar-header__subtitle" + v-if="showSubtitle" + >{{ subtitle }}</text> + <view class="u-calendar-header__weekdays"> + <text class="u-calendar-header__weekdays__weekday">一</text> + <text class="u-calendar-header__weekdays__weekday">二</text> + <text class="u-calendar-header__weekdays__weekday">三</text> + <text class="u-calendar-header__weekdays__weekday">四</text> + <text class="u-calendar-header__weekdays__weekday">五</text> + <text class="u-calendar-header__weekdays__weekday">六</text> + <text class="u-calendar-header__weekdays__weekday">日</text> + </view> + </view> +</template> + +<script> + export default { + name: 'u-calendar-header', + mixins: [uni.$u.mpMixin, uni.$u.mixin], + props: { + // 标题 + title: { + type: String, + default: '' + }, + // 副标题 + subtitle: { + type: String, + default: '' + }, + // 是否显示标题 + showTitle: { + type: Boolean, + default: true + }, + // 是否显示副标题 + showSubtitle: { + type: Boolean, + default: true + }, + }, + data() { + return { + + } + }, + methods: { + name() { + + } + }, + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-calendar-header { + padding-bottom: 4px; + + &__title { + font-size: 16px; + color: $u-main-color; + text-align: center; + height: 42px; + line-height: 42px; + font-weight: bold; + } + + &__subtitle { + font-size: 14px; + color: $u-main-color; + height: 40px; + text-align: center; + line-height: 40px; + font-weight: bold; + } + + &__weekdays { + @include flex; + justify-content: space-between; + + &__weekday { + font-size: 13px; + color: $u-main-color; + line-height: 30px; + flex: 1; + text-align: center; + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-calendar/month.vue b/uni_modules/uview-ui/components/u-calendar/month.vue new file mode 100644 index 0000000..c20937f --- /dev/null +++ b/uni_modules/uview-ui/components/u-calendar/month.vue @@ -0,0 +1,579 @@ +<template> + <view class="u-calendar-month-wrapper" ref="u-calendar-month-wrapper"> + <view v-for="(item, index) in months" :key="index" :class="[`u-calendar-month-${index}`]" + :ref="`u-calendar-month-${index}`" :id="`month-${index}`"> + <text v-if="index !== 0" class="u-calendar-month__title">{{ item.year }}年{{ item.month }}月</text> + <view class="u-calendar-month__days"> + <view v-if="showMark" class="u-calendar-month__days__month-mark-wrapper"> + <text class="u-calendar-month__days__month-mark-wrapper__text">{{ item.month }}</text> + </view> + <view class="u-calendar-month__days__day" v-for="(item1, index1) in item.date" :key="index1" + :style="[dayStyle(index, index1, item1)]" @tap="clickHandler(index, index1, item1)" + :class="[item1.selected && 'u-calendar-month__days__day__select--selected']"> + <view class="u-calendar-month__days__day__select" :style="[daySelectStyle(index, index1, item1)]"> + <text class="u-calendar-month__days__day__select__info" + :class="[item1.disabled && 'u-calendar-month__days__day__select__info--disabled']" + :style="[textStyle(item1)]">{{ item1.day }}</text> + <text v-if="getBottomInfo(index, index1, item1)" + class="u-calendar-month__days__day__select__buttom-info" + :class="[item1.disabled && 'u-calendar-month__days__day__select__buttom-info--disabled']" + :style="[textStyle(item1)]">{{ getBottomInfo(index, index1, item1) }}</text> + <text v-if="item1.dot" class="u-calendar-month__days__day__select__dot"></text> + </view> + </view> + </view> + </view> + </view> +</template> + +<script> + // #ifdef APP-NVUE + // 由于nvue不支持百分比单位,需要查询宽度来计算每个日期的宽度 + const dom = uni.requireNativePlugin('dom') + // #endif + import dayjs from '../../libs/util/dayjs.js'; + export default { + name: 'u-calendar-month', + mixins: [uni.$u.mpMixin, uni.$u.mixin], + props: { + // 是否显示月份背景色 + showMark: { + type: Boolean, + default: true + }, + // 主题色,对底部按钮和选中日期有效 + color: { + type: String, + default: '#3c9cff' + }, + // 月份数据 + months: { + type: Array, + default: () => [] + }, + // 日期选择类型 + mode: { + type: String, + default: 'single' + }, + // 日期行高 + rowHeight: { + type: [String, Number], + default: 58 + }, + // mode=multiple时,最多可选多少个日期 + maxCount: { + type: [String, Number], + default: Infinity + }, + // mode=range时,第一个日期底部的提示文字 + startText: { + type: String, + default: '开始' + }, + // mode=range时,最后一个日期底部的提示文字 + endText: { + type: String, + default: '结束' + }, + // 默认选中的日期,mode为multiple或range是必须为数组格式 + defaultDate: { + type: [Array, String, Date], + default: null + }, + // 最小的可选日期 + minDate: { + type: [String, Number], + default: 0 + }, + // 最大可选日期 + maxDate: { + type: [String, Number], + default: 0 + }, + // 如果没有设置maxDate,则往后推多少个月 + maxMonth: { + type: [String, Number], + default: 2 + }, + // 是否为只读状态,只读状态下禁止选择日期 + readonly: { + type: Boolean, + default: uni.$u.props.calendar.readonly + }, + // 日期区间最多可选天数,默认无限制,mode = range时有效 + maxRange: { + type: [Number, String], + default: Infinity + }, + // 范围选择超过最多可选天数时的提示文案,mode = range时有效 + rangePrompt: { + type: String, + default: '' + }, + // 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效 + showRangePrompt: { + type: Boolean, + default: true + }, + // 是否允许日期范围的起止时间为同一天,mode = range时有效 + allowSameDay: { + type: Boolean, + default: false + } + }, + data() { + return { + // 每个日期的宽度 + width: 0, + // 当前选中的日期item + item: {}, + selected: [] + } + }, + watch: { + selectedChange: { + immediate: true, + handler(n) { + this.setDefaultDate() + } + } + }, + computed: { + // 多个条件的变化,会引起选中日期的变化,这里统一管理监听 + selectedChange() { + return [this.minDate, this.maxDate, this.defaultDate] + }, + dayStyle(index1, index2, item) { + return (index1, index2, item) => { + const style = {} + let week = item.week + // 不进行四舍五入的形式保留2位小数 + const dayWidth = Number(parseFloat(this.width / 7).toFixed(3).slice(0, -1)) + // 得出每个日期的宽度 + // #ifdef APP-NVUE + style.width = uni.$u.addUnit(dayWidth) + // #endif + style.height = uni.$u.addUnit(this.rowHeight) + if (index2 === 0) { + // 获取当前为星期几,如果为0,则为星期天,减一为每月第一天时,需要向左偏移的item个数 + week = (week === 0 ? 7 : week) - 1 + style.marginLeft = uni.$u.addUnit(week * dayWidth) + } + if (this.mode === 'range') { + // 之所以需要这么写,是因为DCloud公司的iOS客户端的开发者能力有限导致的bug + style.paddingLeft = 0 + style.paddingRight = 0 + style.paddingBottom = 0 + style.paddingTop = 0 + } + return style + } + }, + daySelectStyle() { + return (index1, index2, item) => { + let date = dayjs(item.date).format("YYYY-MM-DD"), + style = {} + // 判断date是否在selected数组中,因为月份可能会需要补0,所以使用dateSame判断,而不用数组的includes判断 + if (this.selected.some(item => this.dateSame(item, date))) { + style.backgroundColor = this.color + } + if (this.mode === 'single') { + if (date === this.selected[0]) { + // 因为需要对nvue的兼容,只能这么写,无法缩写,也无法通过类名控制等等 + style.borderTopLeftRadius = '3px' + style.borderBottomLeftRadius = '3px' + style.borderTopRightRadius = '3px' + style.borderBottomRightRadius = '3px' + } + } else if (this.mode === 'range') { + if (this.selected.length >= 2) { + const len = this.selected.length - 1 + // 第一个日期设置左上角和左下角的圆角 + if (this.dateSame(date, this.selected[0])) { + style.borderTopLeftRadius = '3px' + style.borderBottomLeftRadius = '3px' + } + // 最后一个日期设置右上角和右下角的圆角 + if (this.dateSame(date, this.selected[len])) { + style.borderTopRightRadius = '3px' + style.borderBottomRightRadius = '3px' + } + // 处于第一和最后一个之间的日期,背景色设置为浅色,通过将对应颜色进行等分,再取其尾部的颜色值 + if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this + .selected[len]))) { + style.backgroundColor = uni.$u.colorGradient(this.color, '#ffffff', 100)[90] + // 增加一个透明度,让范围区间的背景色也能看到底部的mark水印字符 + style.opacity = 0.7 + } + } else if (this.selected.length === 1) { + // 之所以需要这么写,是因为DCloud公司的iOS客户端的开发者能力有限导致的bug + // 进行还原操作,否则在nvue的iOS,uni-app有bug,会导致诡异的表现 + style.borderTopLeftRadius = '3px' + style.borderBottomLeftRadius = '3px' + } + } else { + if (this.selected.some(item => this.dateSame(item, date))) { + style.borderTopLeftRadius = '3px' + style.borderBottomLeftRadius = '3px' + style.borderTopRightRadius = '3px' + style.borderBottomRightRadius = '3px' + } + } + return style + } + }, + // 某个日期是否被选中 + textStyle() { + return (item) => { + const date = dayjs(item.date).format("YYYY-MM-DD"), + style = {} + // 选中的日期,提示文字设置白色 + if (this.selected.some(item => this.dateSame(item, date))) { + style.color = '#ffffff' + } + if (this.mode === 'range') { + const len = this.selected.length - 1 + // 如果是范围选择模式,第一个和最后一个之间的日期,文字颜色设置为高亮的主题色 + if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this + .selected[len]))) { + style.color = this.color + } + } + return style + } + }, + // 获取底部的提示文字 + getBottomInfo() { + return (index1, index2, item) => { + const date = dayjs(item.date).format("YYYY-MM-DD") + const bottomInfo = item.bottomInfo + // 当为日期范围模式时,且选择的日期个数大于0时 + if (this.mode === 'range' && this.selected.length > 0) { + if (this.selected.length === 1) { + // 选择了一个日期时,如果当前日期为数组中的第一个日期,则显示底部文字为“开始” + if (this.dateSame(date, this.selected[0])) return this.startText + else return bottomInfo + } else { + const len = this.selected.length - 1 + // 如果数组中的日期大于2个时,第一个和最后一个显示为开始和结束日期 + if (this.dateSame(date, this.selected[0]) && this.dateSame(date, this.selected[1]) && + len === 1) { + // 如果长度为2,且第一个等于第二个日期,则提示语放在同一个item中 + return `${this.startText}/${this.endText}` + } else if (this.dateSame(date, this.selected[0])) { + return this.startText + } else if (this.dateSame(date, this.selected[len])) { + return this.endText + } else { + return bottomInfo + } + } + } else { + return bottomInfo + } + } + } + }, + mounted() { + this.init() + }, + methods: { + init() { + // 初始化默认选中 + this.$emit('monthSelected', this.selected) + this.$nextTick(() => { + // 这里需要另一个延时,因为获取宽度后,会进行月份数据渲染,只有渲染完成之后,才有真正的高度 + // 因为nvue下,$nextTick并不是100%可靠的 + uni.$u.sleep(10).then(() => { + this.getWrapperWidth() + this.getMonthRect() + }) + }) + }, + // 判断两个日期是否相等 + dateSame(date1, date2) { + return dayjs(date1).isSame(dayjs(date2)) + }, + // 获取月份数据区域的宽度,因为nvue不支持百分比,所以无法通过css设置每个日期item的宽度 + getWrapperWidth() { + // #ifdef APP-NVUE + dom.getComponentRect(this.$refs['u-calendar-month-wrapper'], res => { + this.width = res.size.width + }) + // #endif + // #ifndef APP-NVUE + this.$uGetRect('.u-calendar-month-wrapper').then(size => { + this.width = size.width + }) + // #endif + }, + getMonthRect() { + // 获取每个月份数据的尺寸,用于父组件在scroll-view滚动事件中,监听当前滚动到了第几个月份 + const promiseAllArr = this.months.map((item, index) => this.getMonthRectByPromise( + `u-calendar-month-${index}`)) + // 一次性返回 + Promise.all(promiseAllArr).then( + sizes => { + let height = 1 + const topArr = [] + for (let i = 0; i < this.months.length; i++) { + // 添加到months数组中,供scroll-view滚动事件中,判断当前滚动到哪个月份 + topArr[i] = height + height += sizes[i].height + } + // 由于微信下,无法通过this.months[i].top的形式(引用类型)去修改父组件的month的top值,所以使用事件形式对外发出 + this.$emit('updateMonthTop', topArr) + }) + }, + // 获取每个月份区域的尺寸 + getMonthRectByPromise(el) { + // #ifndef APP-NVUE + // $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://www.uviewui.com/js/getRect.html + // 组件内部一般用this.$uGetRect,对外的为uni.$u.getRect,二者功能一致,名称不同 + return new Promise(resolve => { + this.$uGetRect(`.${el}`).then(size => { + resolve(size) + }) + }) + // #endif + + // #ifdef APP-NVUE + // nvue下,使用dom模块查询元素高度 + // 返回一个promise,让调用此方法的主体能使用then回调 + return new Promise(resolve => { + dom.getComponentRect(this.$refs[el][0], res => { + resolve(res.size) + }) + }) + // #endif + }, + // 点击某一个日期 + clickHandler(index1, index2, item) { + if (this.readonly) { + return; + } + this.item = item + const date = dayjs(item.date).format("YYYY-MM-DD") + if (item.disabled) return + // 对上一次选择的日期数组进行深度克隆 + let selected = uni.$u.deepClone(this.selected) + if (this.mode === 'single') { + // 单选情况下,让数组中的元素为当前点击的日期 + selected = [date] + } else if (this.mode === 'multiple') { + if (selected.some(item => this.dateSame(item, date))) { + // 如果点击的日期已在数组中,则进行移除操作,也就是达到反选的效果 + const itemIndex = selected.findIndex(item => item === date) + selected.splice(itemIndex, 1) + } else { + // 如果点击的日期不在数组中,且已有的长度小于总可选长度时,则添加到数组中去 + if (selected.length < this.maxCount) selected.push(date) + } + } else { + // 选择区间形式 + if (selected.length === 0 || selected.length >= 2) { + // 如果原来就为0或者大于2的长度,则当前点击的日期,就是开始日期 + selected = [date] + } else if (selected.length === 1) { + // 如果已经选择了开始日期 + const existsDate = selected[0] + // 如果当前选择的日期小于上一次选择的日期,则当前的日期定为开始日期 + if (dayjs(date).isBefore(existsDate)) { + selected = [date] + } else if (dayjs(date).isAfter(existsDate)) { + // 当前日期减去最大可选的日期天数,如果大于起始时间,则进行提示 + if(dayjs(dayjs(date).subtract(this.maxRange, 'day')).isAfter(dayjs(selected[0])) && this.showRangePrompt) { + if(this.rangePrompt) { + uni.$u.toast(this.rangePrompt) + } else { + uni.$u.toast(`选择天数不能超过 ${this.maxRange} 天`) + } + return + } + // 如果当前日期大于已有日期,将当前的添加到数组尾部 + selected.push(date) + const startDate = selected[0] + const endDate = selected[1] + const arr = [] + let i = 0 + do { + // 将开始和结束日期之间的日期添加到数组中 + arr.push(dayjs(startDate).add(i, 'day').format("YYYY-MM-DD")) + i++ + // 累加的日期小于结束日期时,继续下一次的循环 + } while (dayjs(startDate).add(i, 'day').isBefore(dayjs(endDate))) + // 为了一次性修改数组,避免computed中多次触发,这里才用arr变量一次性赋值的方式,同时将最后一个日期添加近来 + arr.push(endDate) + selected = arr + } else { + // 选择区间时,只有一个日期的情况下,且不允许选择起止为同一天的话,不允许选择自己 + if (selected[0] === date && !this.allowSameDay) return + selected.push(date) + } + } + } + this.setSelected(selected) + }, + // 设置默认日期 + setDefaultDate() { + if (!this.defaultDate) { + // 如果没有设置默认日期,则将当天日期设置为默认选中的日期 + const selected = [dayjs().format("YYYY-MM-DD")] + return this.setSelected(selected, false) + } + let defaultDate = [] + const minDate = this.minDate || dayjs().format("YYYY-MM-DD") + const maxDate = this.maxDate || dayjs(minDate).add(this.maxMonth - 1, 'month').format("YYYY-MM-DD") + if (this.mode === 'single') { + // 单选模式,可以是字符串或数组,Date对象等 + if (!uni.$u.test.array(this.defaultDate)) { + defaultDate = [dayjs(this.defaultDate).format("YYYY-MM-DD")] + } else { + defaultDate = [this.defaultDate[0]] + } + } else { + // 如果为非数组,则不执行 + if (!uni.$u.test.array(this.defaultDate)) return + defaultDate = this.defaultDate + } + // 过滤用户传递的默认数组,取出只在可允许最大值与最小值之间的元素 + defaultDate = defaultDate.filter(item => { + return dayjs(item).isAfter(dayjs(minDate).subtract(1, 'day')) && dayjs(item).isBefore(dayjs( + maxDate).add(1, 'day')) + }) + this.setSelected(defaultDate, false) + }, + setSelected(selected, event = true) { + this.selected = selected + event && this.$emit('monthSelected', this.selected) + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-calendar-month-wrapper { + margin-top: 4px; + } + + .u-calendar-month { + + &__title { + font-size: 14px; + line-height: 42px; + height: 42px; + color: $u-main-color; + text-align: center; + font-weight: bold; + } + + &__days { + position: relative; + @include flex; + flex-wrap: wrap; + + &__month-mark-wrapper { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + @include flex; + justify-content: center; + align-items: center; + + &__text { + font-size: 155px; + color: rgba(231, 232, 234, 0.83); + } + } + + &__day { + @include flex; + padding: 2px; + /* #ifndef APP-NVUE */ + // vue下使用css进行宽度计算,因为某些安卓机会无法进行js获取父元素宽度进行计算得出,会有偏移 + width: calc(100% / 7); + box-sizing: border-box; + /* #endif */ + + &__select { + flex: 1; + @include flex; + align-items: center; + justify-content: center; + position: relative; + + &__dot { + width: 7px; + height: 7px; + border-radius: 100px; + background-color: $u-error; + position: absolute; + top: 12px; + right: 7px; + } + + &__buttom-info { + color: $u-content-color; + text-align: center; + position: absolute; + bottom: 5px; + font-size: 10px; + text-align: center; + left: 0; + right: 0; + + &--selected { + color: #ffffff; + } + + &--disabled { + color: #cacbcd; + } + } + + &__info { + text-align: center; + font-size: 16px; + + &--selected { + color: #ffffff; + } + + &--disabled { + color: #cacbcd; + } + } + + &--selected { + background-color: $u-primary; + @include flex; + justify-content: center; + align-items: center; + flex: 1; + border-radius: 3px; + } + + &--range-selected { + opacity: 0.3; + border-radius: 0; + } + + &--range-start-selected { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + &--range-end-selected { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + } + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-calendar/props.js b/uni_modules/uview-ui/components/u-calendar/props.js new file mode 100644 index 0000000..2ad7bc7 --- /dev/null +++ b/uni_modules/uview-ui/components/u-calendar/props.js @@ -0,0 +1,144 @@ +export default { + props: { + // 日历顶部标题 + title: { + type: String, + default: uni.$u.props.calendar.title + }, + // 是否显示标题 + showTitle: { + type: Boolean, + default: uni.$u.props.calendar.showTitle + }, + // 是否显示副标题 + showSubtitle: { + type: Boolean, + default: uni.$u.props.calendar.showSubtitle + }, + // 日期类型选择,single-选择单个日期,multiple-可以选择多个日期,range-选择日期范围 + mode: { + type: String, + default: uni.$u.props.calendar.mode + }, + // mode=range时,第一个日期底部的提示文字 + startText: { + type: String, + default: uni.$u.props.calendar.startText + }, + // mode=range时,最后一个日期底部的提示文字 + endText: { + type: String, + default: uni.$u.props.calendar.endText + }, + // 自定义列表 + customList: { + type: Array, + default: uni.$u.props.calendar.customList + }, + // 主题色,对底部按钮和选中日期有效 + color: { + type: String, + default: uni.$u.props.calendar.color + }, + // 最小的可选日期 + minDate: { + type: [String, Number], + default: uni.$u.props.calendar.minDate + }, + // 最大可选日期 + maxDate: { + type: [String, Number], + default: uni.$u.props.calendar.maxDate + }, + // 默认选中的日期,mode为multiple或range是必须为数组格式 + defaultDate: { + type: [Array, String, Date, null], + default: uni.$u.props.calendar.defaultDate + }, + // mode=multiple时,最多可选多少个日期 + maxCount: { + type: [String, Number], + default: uni.$u.props.calendar.maxCount + }, + // 日期行高 + rowHeight: { + type: [String, Number], + default: uni.$u.props.calendar.rowHeight + }, + // 日期格式化函数 + formatter: { + type: [Function, null], + default: uni.$u.props.calendar.formatter + }, + // 是否显示农历 + showLunar: { + type: Boolean, + default: uni.$u.props.calendar.showLunar + }, + // 是否显示月份背景色 + showMark: { + type: Boolean, + default: uni.$u.props.calendar.showMark + }, + // 确定按钮的文字 + confirmText: { + type: String, + default: uni.$u.props.calendar.confirmText + }, + // 确认按钮处于禁用状态时的文字 + confirmDisabledText: { + type: String, + default: uni.$u.props.calendar.confirmDisabledText + }, + // 是否显示日历弹窗 + show: { + type: Boolean, + default: uni.$u.props.calendar.show + }, + // 是否允许点击遮罩关闭日历 + closeOnClickOverlay: { + type: Boolean, + default: uni.$u.props.calendar.closeOnClickOverlay + }, + // 是否为只读状态,只读状态下禁止选择日期 + readonly: { + type: Boolean, + default: uni.$u.props.calendar.readonly + }, + // 是否展示确认按钮 + showConfirm: { + type: Boolean, + default: uni.$u.props.calendar.showConfirm + }, + // 日期区间最多可选天数,默认无限制,mode = range时有效 + maxRange: { + type: [Number, String], + default: uni.$u.props.calendar.maxRange + }, + // 范围选择超过最多可选天数时的提示文案,mode = range时有效 + rangePrompt: { + type: String, + default: uni.$u.props.calendar.rangePrompt + }, + // 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效 + showRangePrompt: { + type: Boolean, + default: uni.$u.props.calendar.showRangePrompt + }, + // 是否允许日期范围的起止时间为同一天,mode = range时有效 + allowSameDay: { + type: Boolean, + default: uni.$u.props.calendar.allowSameDay + }, + // 圆角值 + round: { + type: [Boolean, String, Number], + default: uni.$u.props.calendar.round + }, + // 最多展示月份数量 + monthNum: { + type: [Number, String], + default: 3 + } + } +} diff --git a/uni_modules/uview-ui/components/u-calendar/u-calendar.vue b/uni_modules/uview-ui/components/u-calendar/u-calendar.vue new file mode 100644 index 0000000..ad892ff --- /dev/null +++ b/uni_modules/uview-ui/components/u-calendar/u-calendar.vue @@ -0,0 +1,383 @@ +<template> + <u-popup + :show="show" + mode="bottom" + closeable + @close="close" + :round="round" + :closeOnClickOverlay="closeOnClickOverlay" + > + <view class="u-calendar"> + <uHeader + :title="title" + :subtitle="subtitle" + :showSubtitle="showSubtitle" + :showTitle="showTitle" + ></uHeader> + <scroll-view + :style="{ + height: $u.addUnit(listHeight) + }" + scroll-y + @scroll="onScroll" + :scroll-top="scrollTop" + :scrollIntoView="scrollIntoView" + > + <uMonth + :color="color" + :rowHeight="rowHeight" + :showMark="showMark" + :months="months" + :mode="mode" + :maxCount="maxCount" + :startText="startText" + :endText="endText" + :defaultDate="defaultDate" + :minDate="innerMinDate" + :maxDate="innerMaxDate" + :maxMonth="monthNum" + :readonly="readonly" + :maxRange="maxRange" + :rangePrompt="rangePrompt" + :showRangePrompt="showRangePrompt" + :allowSameDay="allowSameDay" + ref="month" + @monthSelected="monthSelected" + @updateMonthTop="updateMonthTop" + ></uMonth> + </scroll-view> + <slot name="footer" v-if="showConfirm"> + <view class="u-calendar__confirm"> + <u-button + shape="circle" + :text=" + buttonDisabled ? confirmDisabledText : confirmText + " + :color="color" + @click="confirm" + :disabled="buttonDisabled" + ></u-button> + </view> + </slot> + </view> + </u-popup> +</template> + +<script> +import uHeader from './header.vue' +import uMonth from './month.vue' +import props from './props.js' +import util from './util.js' +import dayjs from '../../libs/util/dayjs.js' +import Calendar from '../../libs/util/calendar.js' +/** + * Calendar 日历 + * @description 此组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中. + * @tutorial https://www.uviewui.com/components/calendar.html + * + * @property {String} title 标题内容 (默认 日期选择 ) + * @property {Boolean} showTitle 是否显示标题 (默认 true ) + * @property {Boolean} showSubtitle 是否显示副标题 (默认 true ) + * @property {String} mode 日期类型选择 single-选择单个日期,multiple-可以选择多个日期,range-选择日期范围 ( 默认 'single' ) + * @property {String} startText mode=range时,第一个日期底部的提示文字 (默认 '开始' ) + * @property {String} endText mode=range时,最后一个日期底部的提示文字 (默认 '结束' ) + * @property {Array} customList 自定义列表 + * @property {String} color 主题色,对底部按钮和选中日期有效 (默认 ‘#3c9cff' ) + * @property {String | Number} minDate 最小的可选日期 (默认 0 ) + * @property {String | Number} maxDate 最大可选日期 (默认 0 ) + * @property {Array | String| Date} defaultDate 默认选中的日期,mode为multiple或range是必须为数组格式 + * @property {String | Number} maxCount mode=multiple时,最多可选多少个日期 (默认 Number.MAX_SAFE_INTEGER ) + * @property {String | Number} rowHeight 日期行高 (默认 56 ) + * @property {Function} formatter 日期格式化函数 + * @property {Boolean} showLunar 是否显示农历 (默认 false ) + * @property {Boolean} showMark 是否显示月份背景色 (默认 true ) + * @property {String} confirmText 确定按钮的文字 (默认 '确定' ) + * @property {String} confirmDisabledText 确认按钮处于禁用状态时的文字 (默认 '确定' ) + * @property {Boolean} show 是否显示日历弹窗 (默认 false ) + * @property {Boolean} closeOnClickOverlay 是否允许点击遮罩关闭日历 (默认 false ) + * @property {Boolean} readonly 是否为只读状态,只读状态下禁止选择日期 (默认 false ) + * @property {String | Number} maxRange 日期区间最多可选天数,默认无限制,mode = range时有效 + * @property {String} rangePrompt 范围选择超过最多可选天数时的提示文案,mode = range时有效 + * @property {Boolean} showRangePrompt 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效 (默认 true ) + * @property {Boolean} allowSameDay 是否允许日期范围的起止时间为同一天,mode = range时有效 (默认 false ) + * @property {Number|String} round 圆角值,默认无圆角 (默认 0 ) + * @property {Number|String} monthNum 最多展示的月份数量 (默认 3 ) + * + * @event {Function()} confirm 点击确定按钮时触发 选择日期相关的返回参数 + * @event {Function()} close 日历关闭时触发 可定义页面关闭时的回调事件 + * @example <u-calendar :defaultDate="defaultDateMultiple" :show="show" mode="multiple" @confirm="confirm"> + </u-calendar> + * */ +export default { + name: 'u-calendar', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + components: { + uHeader, + uMonth + }, + data() { + return { + // 需要显示的月份的数组 + months: [], + // 在月份滚动区域中,当前视图中月份的index索引 + monthIndex: 0, + // 月份滚动区域的高度 + listHeight: 0, + // month组件中选择的日期数组 + selected: [], + scrollIntoView: '', + scrollTop:0, + // 过滤处理方法 + innerFormatter: (value) => value + } + }, + watch: { + selectedChange: { + immediate: true, + handler(n) { + this.setMonth() + } + }, + // 打开弹窗时,设置月份数据 + show: { + immediate: true, + handler(n) { + this.setMonth() + } + } + }, + computed: { + // 由于maxDate和minDate可以为字符串(2021-10-10),或者数值(时间戳),但是dayjs如果接受字符串形式的时间戳会有问题,这里进行处理 + innerMaxDate() { + return uni.$u.test.number(this.maxDate) + ? Number(this.maxDate) + : this.maxDate + }, + innerMinDate() { + return uni.$u.test.number(this.minDate) + ? Number(this.minDate) + : this.minDate + }, + // 多个条件的变化,会引起选中日期的变化,这里统一管理监听 + selectedChange() { + return [this.innerMinDate, this.innerMaxDate, this.defaultDate] + }, + subtitle() { + // 初始化时,this.months为空数组,所以需要特别判断处理 + if (this.months.length) { + return `${this.months[this.monthIndex].year}年${ + this.months[this.monthIndex].month + }月` + } else { + return '' + } + }, + buttonDisabled() { + // 如果为range类型,且选择的日期个数不足1个时,让底部的按钮出于disabled状态 + if (this.mode === 'range') { + if (this.selected.length <= 1) { + return true + } else { + return false + } + } else { + return false + } + } + }, + mounted() { + this.start = Date.now() + this.init() + }, + methods: { + // 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用 + setFormatter(e) { + this.innerFormatter = e + }, + // month组件内部选择日期后,通过事件通知给父组件 + monthSelected(e) { + this.selected = e + if (!this.showConfirm) { + // 在不需要确认按钮的情况下,如果为单选,或者范围多选且已选长度大于2,则直接进行返还 + if ( + this.mode === 'multiple' || + this.mode === 'single' || + (this.mode === 'range' && this.selected.length >= 2) + ) { + this.$emit('confirm', this.selected) + } + } + }, + init() { + // 校验maxDate,不能小于当前时间 + if ( + this.innerMaxDate && + new Date(this.innerMaxDate).getTime() <= Date.now() + ) { + return uni.$u.error('maxDate不能小于当前时间') + } + // 滚动区域的高度 + this.listHeight = this.rowHeight * 5 + 30 + this.setMonth() + }, + close() { + this.$emit('close') + }, + // 点击确定按钮 + confirm() { + if (!this.buttonDisabled) { + this.$emit('confirm', this.selected) + } + }, + // 获得两个日期之间的月份数 + getMonths(minDate, maxDate) { + const minYear = dayjs(minDate).year() + const minMonth = dayjs(minDate).month() + 1 + const maxYear = dayjs(maxDate).year() + const maxMonth = dayjs(maxDate).month() + 1 + return (maxYear - minYear) * 12 + (maxMonth - minMonth) + 1 + }, + // 设置月份数据 + setMonth() { + // 最小日期的毫秒数 + const minDate = this.innerMinDate || dayjs().valueOf() + // 如果没有指定最大日期,则往后推3个月 + const maxDate = + this.innerMaxDate || + dayjs(minDate) + .add(this.monthNum - 1, 'month') + .valueOf() + // 最大最小月份之间的共有多少个月份, + const months = uni.$u.range( + 1, + this.monthNum, + this.getMonths(minDate, maxDate) + ) + // 先清空数组 + this.months = [] + for (let i = 0; i < months; i++) { + this.months.push({ + date: new Array( + dayjs(minDate).add(i, 'month').daysInMonth() + ) + .fill(1) + .map((item, index) => { + // 日期,取值1-31 + let day = index + 1 + // 星期,0-6,0为周日 + const week = dayjs(minDate) + .add(i, 'month') + .date(day) + .day() + const date = dayjs(minDate) + .add(i, 'month') + .date(day) + .format('YYYY-MM-DD') + let bottomInfo = '' + if (this.showLunar) { + // 将日期转为农历格式 + const lunar = Calendar.solar2lunar( + dayjs(date).year(), + dayjs(date).month() + 1, + dayjs(date).date() + ) + bottomInfo = lunar.IDayCn + } + let config = { + day, + week, + // 小于最小允许的日期,或者大于最大的日期,则设置为disabled状态 + disabled: + dayjs(date).isBefore( + dayjs(minDate).format('YYYY-MM-DD') + ) || + dayjs(date).isAfter( + dayjs(maxDate).format('YYYY-MM-DD') + ), + // 返回一个日期对象,供外部的formatter获取当前日期的年月日等信息,进行加工处理 + date: new Date(date), + bottomInfo, + dot: false, + month: + dayjs(minDate).add(i, 'month').month() + 1 + } + const formatter = + this.formatter || this.innerFormatter + return formatter(config) + }), + // 当前所属的月份 + month: dayjs(minDate).add(i, 'month').month() + 1, + // 当前年份 + year: dayjs(minDate).add(i, 'month').year() + }) + } + + }, + // 滚动到默认设置的月份 + scrollIntoDefaultMonth(selected) { + // 查询默认日期在可选列表的下标 + const _index = this.months.findIndex(({ + year, + month + }) => { + month = uni.$u.padZero(month) + return `${year}-${month}` === selected + }) + if (_index !== -1) { + // #ifndef MP-WEIXIN + this.$nextTick(() => { + this.scrollIntoView = `month-${_index}` + }) + // #endif + // #ifdef MP-WEIXIN + this.scrollTop = this.months[_index].top || 0; + // #endif + } + }, + // scroll-view滚动监听 + onScroll(event) { + // 不允许小于0的滚动值,如果scroll-view到顶了,继续下拉,会出现负数值 + const scrollTop = Math.max(0, event.detail.scrollTop) + // 将当前滚动条数值,除以滚动区域的高度,可以得出当前滚动到了哪一个月份的索引 + for (let i = 0; i < this.months.length; i++) { + if (scrollTop >= (this.months[i].top || this.listHeight)) { + this.monthIndex = i + } + } + }, + // 更新月份的top值 + updateMonthTop(topArr = []) { + // 设置对应月份的top值,用于onScroll方法更新月份 + topArr.map((item, index) => { + this.months[index].top = item + }) + + // 获取默认日期的下标 + if (!this.defaultDate) { + // 如果没有设置默认日期,则将当天日期设置为默认选中的日期 + const selected = dayjs().format("YYYY-MM") + this.scrollIntoDefaultMonth(selected) + return + } + let selected = dayjs().format("YYYY-MM"); + // 单选模式,可以是字符串或数组,Date对象等 + if (!uni.$u.test.array(this.defaultDate)) { + selected = dayjs(this.defaultDate).format("YYYY-MM") + } else { + selected = dayjs(this.defaultDate[0]).format("YYYY-MM"); + } + this.scrollIntoDefaultMonth(selected) + } + } +} +</script> + +<style lang="scss" scoped> +@import '../../libs/css/components.scss'; + +.u-calendar { + &__confirm { + padding: 7px 18px; + } +} +</style> diff --git a/uni_modules/uview-ui/components/u-calendar/util.js b/uni_modules/uview-ui/components/u-calendar/util.js new file mode 100644 index 0000000..ca4736b --- /dev/null +++ b/uni_modules/uview-ui/components/u-calendar/util.js @@ -0,0 +1,85 @@ +export default { + methods: { + // 设置月份数据 + setMonth() { + // 月初是周几 + const day = dayjs(this.date).date(1).day() + const start = day == 0 ? 6 : day - 1 + + // 本月天数 + const days = dayjs(this.date).endOf('month').format('D') + + // 上个月天数 + const prevDays = dayjs(this.date).endOf('month').subtract(1, 'month').format('D') + + // 日期数据 + const arr = [] + // 清空表格 + this.month = [] + + // 添加上月数据 + arr.push( + ...new Array(start).fill(1).map((e, i) => { + const day = prevDays - start + i + 1 + + return { + value: day, + disabled: true, + date: dayjs(this.date).subtract(1, 'month').date(day).format('YYYY-MM-DD') + } + }) + ) + + // 添加本月数据 + arr.push( + ...new Array(days - 0).fill(1).map((e, i) => { + const day = i + 1 + + return { + value: day, + date: dayjs(this.date).date(day).format('YYYY-MM-DD') + } + }) + ) + + // 添加下个月 + arr.push( + ...new Array(42 - days - start).fill(1).map((e, i) => { + const day = i + 1 + + return { + value: day, + disabled: true, + date: dayjs(this.date).add(1, 'month').date(day).format('YYYY-MM-DD') + } + }) + ) + + // 分割数组 + for (let n = 0; n < arr.length; n += 7) { + this.month.push( + arr.slice(n, n + 7).map((e, i) => { + e.index = i + n + + // 自定义信息 + const custom = this.customList.find((c) => c.date == e.date) + + // 农历 + if (this.lunar) { + const { + IDayCn, + IMonthCn + } = this.getLunar(e.date) + e.lunar = IDayCn == '初一' ? IMonthCn : IDayCn + } + + return { + ...e, + ...custom + } + }) + ) + } + } + } +} diff --git a/uni_modules/uview-ui/components/u-car-keyboard/props.js b/uni_modules/uview-ui/components/u-car-keyboard/props.js new file mode 100644 index 0000000..3553647 --- /dev/null +++ b/uni_modules/uview-ui/components/u-car-keyboard/props.js @@ -0,0 +1,14 @@ +export default { + props: { + // 是否打乱键盘按键的顺序 + random: { + type: Boolean, + default: false + }, + // 输入一个中文后,是否自动切换到英文 + autoChange: { + type: Boolean, + default: false + } + } +} diff --git a/uni_modules/uview-ui/components/u-car-keyboard/u-car-keyboard.vue b/uni_modules/uview-ui/components/u-car-keyboard/u-car-keyboard.vue new file mode 100644 index 0000000..51175b5 --- /dev/null +++ b/uni_modules/uview-ui/components/u-car-keyboard/u-car-keyboard.vue @@ -0,0 +1,311 @@ +<template> + <view + class="u-keyboard" + @touchmove.stop.prevent="noop" + > + <view + v-for="(group, i) in abc ? engKeyBoardList : areaList" + :key="i" + class="u-keyboard__button" + :index="i" + :class="[i + 1 === 4 && 'u-keyboard__button--center']" + > + <view + v-if="i === 3" + class="u-keyboard__button__inner-wrapper" + > + <view + class="u-keyboard__button__inner-wrapper__left" + hover-class="u-hover-class" + :hover-stay-time="200" + @tap="changeCarInputMode" + > + <text + class="u-keyboard__button__inner-wrapper__left__lang" + :class="[!abc && 'u-keyboard__button__inner-wrapper__left__lang--active']" + >中</text> + <text class="u-keyboard__button__inner-wrapper__left__line">/</text> + <text + class="u-keyboard__button__inner-wrapper__left__lang" + :class="[abc && 'u-keyboard__button__inner-wrapper__left__lang--active']" + >英</text> + </view> + </view> + <view + class="u-keyboard__button__inner-wrapper" + v-for="(item, j) in group" + :key="j" + > + <view + class="u-keyboard__button__inner-wrapper__inner" + :hover-stay-time="200" + @tap="carInputClick(i, j)" + hover-class="u-hover-class" + > + <text class="u-keyboard__button__inner-wrapper__inner__text">{{ item }}</text> + </view> + </view> + <view + v-if="i === 3" + @touchstart="backspaceClick" + @touchend="clearTimer" + class="u-keyboard__button__inner-wrapper" + > + <view + class="u-keyboard__button__inner-wrapper__right" + hover-class="u-hover-class" + :hover-stay-time="200" + > + <u-icon + size="28" + name="backspace" + color="#303133" + ></u-icon> + </view> + </view> + </view> + </view> +</template> + +<script> + import props from './props.js'; + /** + * keyboard 键盘组件 + * @description 此为uView自定义的键盘面板,内含了数字键盘,车牌号键,身份证号键盘3种模式,都有可以打乱按键顺序的选项。 + * @tutorial https://uviewui.com/components/keyboard.html + * @property {Boolean} random 是否打乱键盘的顺序 + * @event {Function} change 点击键盘触发 + * @event {Function} backspace 点击退格键触发 + * @example <u-keyboard ref="uKeyboard" mode="car" v-model="show"></u-keyboard> + */ + export default { + name: "u-keyboard", + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + // 车牌输入时,abc=true为输入车牌号码,bac=false为输入省份中文简称 + abc: false + }; + }, + computed: { + areaList() { + let data = [ + '京', + '沪', + '粤', + '津', + '冀', + '豫', + '云', + '辽', + '黑', + '湘', + '皖', + '鲁', + '苏', + '浙', + '赣', + '鄂', + '桂', + '甘', + '晋', + '陕', + '蒙', + '吉', + '闽', + '贵', + '渝', + '川', + '青', + '琼', + '宁', + '挂', + '藏', + '港', + '澳', + '新', + '使', + '学' + ]; + let tmp = []; + // 打乱顺序 + if (this.random) data = uni.$u.randomArray(data); + // 切割成二维数组 + tmp[0] = data.slice(0, 10); + tmp[1] = data.slice(10, 20); + tmp[2] = data.slice(20, 30); + tmp[3] = data.slice(30, 36); + return tmp; + }, + engKeyBoardList() { + let data = [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 0, + 'Q', + 'W', + 'E', + 'R', + 'T', + 'Y', + 'U', + 'I', + 'O', + 'P', + 'A', + 'S', + 'D', + 'F', + 'G', + 'H', + 'J', + 'K', + 'L', + 'Z', + 'X', + 'C', + 'V', + 'B', + 'N', + 'M' + ]; + let tmp = []; + if (this.random) data = uni.$u.randomArray(data); + tmp[0] = data.slice(0, 10); + tmp[1] = data.slice(10, 20); + tmp[2] = data.slice(20, 30); + tmp[3] = data.slice(30, 36); + return tmp; + } + }, + methods: { + // 点击键盘按钮 + carInputClick(i, j) { + let value = ''; + // 不同模式,获取不同数组的值 + if (this.abc) value = this.engKeyBoardList[i][j]; + else value = this.areaList[i][j]; + // 如果允许自动切换,则将中文状态切换为英文 + if (!this.abc && this.autoChange) uni.$u.sleep(200).then(() => this.abc = true) + this.$emit('change', value); + }, + // 修改汽车牌键盘的输入模式,中文|英文 + changeCarInputMode() { + this.abc = !this.abc; + }, + // 点击退格键 + backspaceClick() { + this.$emit('backspace'); + clearInterval(this.timer); //再次清空定时器,防止重复注册定时器 + this.timer = null; + this.timer = setInterval(() => { + this.$emit('backspace'); + }, 250); + }, + clearTimer() { + clearInterval(this.timer); + this.timer = null; + }, + } + }; +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + $u-car-keyboard-background-color: rgb(224, 228, 230) !default; + $u-car-keyboard-padding:6px 0 6px !default; + $u-car-keyboard-button-inner-width:64rpx !default; + $u-car-keyboard-button-inner-background-color:#FFFFFF !default; + $u-car-keyboard-button-height:80rpx !default; + $u-car-keyboard-button-inner-box-shadow:0 1px 0px #999992 !default; + $u-car-keyboard-button-border-radius:4px !default; + $u-car-keyboard-button-inner-margin:8rpx 5rpx !default; + $u-car-keyboard-button-text-font-size:16px !default; + $u-car-keyboard-button-text-color:$u-main-color !default; + $u-car-keyboard-center-inner-margin: 0 4rpx !default; + $u-car-keyboard-special-button-width:134rpx !default; + $u-car-keyboard-lang-font-size:16px !default; + $u-car-keyboard-lang-color:$u-main-color !default; + $u-car-keyboard-active-color:$u-primary !default; + $u-car-keyboard-line-font-size:15px !default; + $u-car-keyboard-line-color:$u-main-color !default; + $u-car-keyboard-line-margin:0 1px !default; + $u-car-keyboard-u-hover-class-background-color:#BBBCC6 !default; + + .u-keyboard { + @include flex(column); + justify-content: space-around; + background-color: $u-car-keyboard-background-color; + align-items: stretch; + padding: $u-car-keyboard-padding; + + &__button { + @include flex; + justify-content: center; + flex: 1; + /* #ifndef APP-NVUE */ + /* #endif */ + + &__inner-wrapper { + box-shadow: $u-car-keyboard-button-inner-box-shadow; + margin: $u-car-keyboard-button-inner-margin; + border-radius: $u-car-keyboard-button-border-radius; + + &__inner { + @include flex; + justify-content: center; + align-items: center; + width: $u-car-keyboard-button-inner-width; + background-color: $u-car-keyboard-button-inner-background-color; + height: $u-car-keyboard-button-height; + border-radius: $u-car-keyboard-button-border-radius; + + &__text { + font-size: $u-car-keyboard-button-text-font-size; + color: $u-car-keyboard-button-text-color; + } + } + + &__left, + &__right { + border-radius: $u-car-keyboard-button-border-radius; + width: $u-car-keyboard-special-button-width; + height: $u-car-keyboard-button-height; + background-color: $u-car-keyboard-u-hover-class-background-color; + @include flex; + justify-content: center; + align-items: center; + box-shadow: $u-car-keyboard-button-inner-box-shadow; + } + + &__left { + &__line { + font-size: $u-car-keyboard-line-font-size; + color: $u-car-keyboard-line-color; + margin: $u-car-keyboard-line-margin; + } + + &__lang { + font-size: $u-car-keyboard-lang-font-size; + color: $u-car-keyboard-lang-color; + + &--active { + color: $u-car-keyboard-active-color; + } + } + } + } + } + } + + .u-hover-class { + background-color: $u-car-keyboard-u-hover-class-background-color; + } +</style> diff --git a/uni_modules/uview-ui/components/u-cell-group/props.js b/uni_modules/uview-ui/components/u-cell-group/props.js new file mode 100644 index 0000000..350ef40 --- /dev/null +++ b/uni_modules/uview-ui/components/u-cell-group/props.js @@ -0,0 +1,14 @@ +export default { + props: { + // 分组标题 + title: { + type: String, + default: uni.$u.props.cellGroup.title + }, + // 是否显示外边框 + border: { + type: Boolean, + default: uni.$u.props.cellGroup.border + } + } +} diff --git a/uni_modules/uview-ui/components/u-cell-group/u-cell-group.vue b/uni_modules/uview-ui/components/u-cell-group/u-cell-group.vue new file mode 100644 index 0000000..a9508c0 --- /dev/null +++ b/uni_modules/uview-ui/components/u-cell-group/u-cell-group.vue @@ -0,0 +1,61 @@ +<template> + <view :style="[$u.addStyle(customStyle)]" :class="[customClass]" class="u-cell-group"> + <view v-if="title" class="u-cell-group__title"> + <slot name="title"> + <text class="u-cell-group__title__text">{{ title }}</text> + </slot> + </view> + <view class="u-cell-group__wrapper"> + <u-line v-if="border"></u-line> + <slot /> + </view> + </view> +</template> + +<script> + import props from './props.js'; + /** + * cellGroup 单元格 + * @description cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。 + * @tutorial https://uviewui.com/components/cell.html + * + * @property {String} title 分组标题 + * @property {Boolean} border 是否显示外边框 (默认 true ) + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @event {Function} click 点击cell列表时触发 + * @example <u-cell-group title="设置喜好"> + */ + export default { + name: 'u-cell-group', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + $u-cell-group-title-padding: 16px 16px 8px !default; + $u-cell-group-title-font-size: 15px !default; + $u-cell-group-title-line-height: 16px !default; + $u-cell-group-title-color: $u-main-color !default; + + .u-cell-group { + flex: 1; + + &__title { + padding: $u-cell-group-title-padding; + + &__text { + font-size: $u-cell-group-title-font-size; + line-height: $u-cell-group-title-line-height; + color: $u-cell-group-title-color; + } + } + + &__wrapper { + position: relative; + } + } +</style> + diff --git a/uni_modules/uview-ui/components/u-cell/props.js b/uni_modules/uview-ui/components/u-cell/props.js new file mode 100644 index 0000000..da03330 --- /dev/null +++ b/uni_modules/uview-ui/components/u-cell/props.js @@ -0,0 +1,110 @@ +export default { + props: { + // 标题 + title: { + type: [String, Number], + default: uni.$u.props.cell.title + }, + // 标题下方的描述信息 + label: { + type: [String, Number], + default: uni.$u.props.cell.label + }, + // 右侧的内容 + value: { + type: [String, Number], + default: uni.$u.props.cell.value + }, + // 左侧图标名称,或者图片链接(本地文件建议使用绝对地址) + icon: { + type: String, + default: uni.$u.props.cell.icon + }, + // 是否禁用cell + disabled: { + type: Boolean, + default: uni.$u.props.cell.disabled + }, + // 是否显示下边框 + border: { + type: Boolean, + default: uni.$u.props.cell.border + }, + // 内容是否垂直居中(主要是针对右侧的value部分) + center: { + type: Boolean, + default: uni.$u.props.cell.center + }, + // 点击后跳转的URL地址 + url: { + type: String, + default: uni.$u.props.cell.url + }, + // 链接跳转的方式,内部使用的是uView封装的route方法,可能会进行拦截操作 + linkType: { + type: String, + default: uni.$u.props.cell.linkType + }, + // 是否开启点击反馈(表现为点击时加上灰色背景) + clickable: { + type: Boolean, + default: uni.$u.props.cell.clickable + }, + // 是否展示右侧箭头并开启点击反馈 + isLink: { + type: Boolean, + default: uni.$u.props.cell.isLink + }, + // 是否显示表单状态下的必填星号(此组件可能会内嵌入input组件) + required: { + type: Boolean, + default: uni.$u.props.cell.required + }, + // 右侧的图标箭头 + rightIcon: { + type: String, + default: uni.$u.props.cell.rightIcon + }, + // 右侧箭头的方向,可选值为:left,up,down + arrowDirection: { + type: String, + default: uni.$u.props.cell.arrowDirection + }, + // 左侧图标样式 + iconStyle: { + type: [Object, String], + default: () => { + return uni.$u.props.cell.iconStyle + } + }, + // 右侧箭头图标的样式 + rightIconStyle: { + type: [Object, String], + default: () => { + return uni.$u.props.cell.rightIconStyle + } + }, + // 标题的样式 + titleStyle: { + type: [Object, String], + default: () => { + return uni.$u.props.cell.titleStyle + } + }, + // 单位元的大小,可选值为large + size: { + type: String, + default: uni.$u.props.cell.size + }, + // 点击cell是否阻止事件传播 + stop: { + type: Boolean, + default: uni.$u.props.cell.stop + }, + // 标识符,cell被点击时返回 + name: { + type: [Number, String], + default: uni.$u.props.cell.name + } + } +} diff --git a/uni_modules/uview-ui/components/u-cell/u-cell.vue b/uni_modules/uview-ui/components/u-cell/u-cell.vue new file mode 100644 index 0000000..b099c90 --- /dev/null +++ b/uni_modules/uview-ui/components/u-cell/u-cell.vue @@ -0,0 +1,229 @@ +<template> + <view class="u-cell" :class="[customClass]" :style="[$u.addStyle(customStyle)]" + :hover-class="(!disabled && (clickable || isLink)) ? 'u-cell--clickable' : ''" :hover-stay-time="250" + @tap="clickHandler"> + <view class="u-cell__body" :class="[ center && 'u-cell--center', size === 'large' && 'u-cell__body--large']"> + <view class="u-cell__body__content"> + <view class="u-cell__left-icon-wrap" v-if="$slots.icon || icon"> + <slot name="icon" v-if="$slots.icon"> + </slot> + <u-icon v-else :name="icon" :custom-style="iconStyle" :size="size === 'large' ? 22 : 18"></u-icon> + </view> + <view class="u-cell__title"> + <slot name="title"> + <text v-if="title" class="u-cell__title-text" :style="[titleTextStyle]" + :class="[disabled && 'u-cell--disabled', size === 'large' && 'u-cell__title-text--large']">{{ title }}</text> + </slot> + <slot name="label"> + <text class="u-cell__label" v-if="label" + :class="[disabled && 'u-cell--disabled', size === 'large' && 'u-cell__label--large']">{{ label }}</text> + </slot> + </view> + </view> + <slot name="value"> + <text class="u-cell__value" + :class="[disabled && 'u-cell--disabled', size === 'large' && 'u-cell__value--large']" + v-if="!$u.test.empty(value)">{{ value }}</text> + </slot> + <view class="u-cell__right-icon-wrap" v-if="$slots['right-icon'] || isLink" + :class="[`u-cell__right-icon-wrap--${arrowDirection}`]"> + <slot name="right-icon" v-if="$slots['right-icon']"> + </slot> + <u-icon v-else :name="rightIcon" :custom-style="rightIconStyle" :color="disabled ? '#c8c9cc' : 'info'" + :size="size === 'large' ? 18 : 16"></u-icon> + </view> + </view> + <u-line v-if="border"></u-line> + </view> +</template> + +<script> + import props from './props.js'; + /** + * cell 单元格 + * @description cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。 + * @tutorial https://uviewui.com/components/cell.html + * @property {String | Number} title 标题 + * @property {String | Number} label 标题下方的描述信息 + * @property {String | Number} value 右侧的内容 + * @property {String} icon 左侧图标名称,或者图片链接(本地文件建议使用绝对地址) + * @property {Boolean} disabled 是否禁用cell + * @property {Boolean} border 是否显示下边框 (默认 true ) + * @property {Boolean} center 内容是否垂直居中(主要是针对右侧的value部分) (默认 false ) + * @property {String} url 点击后跳转的URL地址 + * @property {String} linkType 链接跳转的方式,内部使用的是uView封装的route方法,可能会进行拦截操作 (默认 'navigateTo' ) + * @property {Boolean} clickable 是否开启点击反馈(表现为点击时加上灰色背景) (默认 false ) + * @property {Boolean} isLink 是否展示右侧箭头并开启点击反馈 (默认 false ) + * @property {Boolean} required 是否显示表单状态下的必填星号(此组件可能会内嵌入input组件) (默认 false ) + * @property {String} rightIcon 右侧的图标箭头 (默认 'arrow-right') + * @property {String} arrowDirection 右侧箭头的方向,可选值为:left,up,down + * @property {Object | String} rightIconStyle 右侧箭头图标的样式 + * @property {Object | String} titleStyle 标题的样式 + * @property {Object | String} iconStyle 左侧图标样式 + * @property {String} size 单位元的大小,可选值为 large,normal,mini + * @property {Boolean} stop 点击cell是否阻止事件传播 (默认 true ) + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @event {Function} click 点击cell列表时触发 + * @example 该组件需要搭配cell-group组件使用,见官方文档示例 + */ + export default { + name: 'u-cell', + data() { + return { + + } + }, + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + computed: { + titleTextStyle() { + return uni.$u.addStyle(this.titleStyle) + } + }, + methods: { + // 点击cell + clickHandler(e) { + if (this.disabled) return + this.$emit('click', { + name: this.name + }) + // 如果配置了url(此props参数通过mixin引入)参数,跳转页面 + this.openPage() + // 是否阻止事件传播 + this.stop && this.preventEvent(e) + }, + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + $u-cell-padding: 10px 15px !default; + $u-cell-font-size: 15px !default; + $u-cell-line-height: 24px !default; + $u-cell-color: $u-main-color !default; + $u-cell-icon-size: 16px !default; + $u-cell-title-font-size: 15px !default; + $u-cell-title-line-height: 22px !default; + $u-cell-title-color: $u-main-color !default; + $u-cell-label-font-size: 12px !default; + $u-cell-label-color: $u-tips-color !default; + $u-cell-label-line-height: 18px !default; + $u-cell-value-font-size: 14px !default; + $u-cell-value-color: $u-content-color !default; + $u-cell-clickable-color: $u-bg-color !default; + $u-cell-disabled-color: #c8c9cc !default; + $u-cell-padding-top-large: 13px !default; + $u-cell-padding-bottom-large: 13px !default; + $u-cell-value-font-size-large: 15px !default; + $u-cell-label-font-size-large: 14px !default; + $u-cell-title-font-size-large: 16px !default; + $u-cell-left-icon-wrap-margin-right: 4px !default; + $u-cell-right-icon-wrap-margin-left: 4px !default; + $u-cell-title-flex:1 !default; + $u-cell-label-margin-top:5px !default; + + + .u-cell { + &__body { + @include flex(); + /* #ifndef APP-NVUE */ + box-sizing: border-box; + /* #endif */ + padding: $u-cell-padding; + font-size: $u-cell-font-size; + color: $u-cell-color; + // line-height: $u-cell-line-height; + align-items: center; + + &__content { + @include flex(row); + align-items: center; + flex: 1; + } + + &--large { + padding-top: $u-cell-padding-top-large; + padding-bottom: $u-cell-padding-bottom-large; + } + } + + &__left-icon-wrap, + &__right-icon-wrap { + @include flex(); + align-items: center; + // height: $u-cell-line-height; + font-size: $u-cell-icon-size; + } + + &__left-icon-wrap { + margin-right: $u-cell-left-icon-wrap-margin-right; + } + + &__right-icon-wrap { + margin-left: $u-cell-right-icon-wrap-margin-left; + transition: transform 0.3s; + + &--up { + transform: rotate(-90deg); + } + + &--down { + transform: rotate(90deg); + } + } + + &__title { + flex: $u-cell-title-flex; + + &-text { + font-size: $u-cell-title-font-size; + line-height: $u-cell-title-line-height; + color: $u-cell-title-color; + + &--large { + font-size: $u-cell-title-font-size-large; + } + } + + } + + &__label { + margin-top: $u-cell-label-margin-top; + font-size: $u-cell-label-font-size; + color: $u-cell-label-color; + line-height: $u-cell-label-line-height; + + &--large { + font-size: $u-cell-label-font-size-large; + } + } + + &__value { + text-align: right; + font-size: $u-cell-value-font-size; + line-height: $u-cell-line-height; + color: $u-cell-value-color; + + &--large { + font-size: $u-cell-value-font-size-large; + } + } + + &--clickable { + background-color: $u-cell-clickable-color; + } + + &--disabled { + color: $u-cell-disabled-color; + /* #ifndef APP-NVUE */ + cursor: not-allowed; + /* #endif */ + } + + &--center { + align-items: center; + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-checkbox-group/props.js b/uni_modules/uview-ui/components/u-checkbox-group/props.js new file mode 100644 index 0000000..2f818a1 --- /dev/null +++ b/uni_modules/uview-ui/components/u-checkbox-group/props.js @@ -0,0 +1,82 @@ +export default { + props: { + // 标识符 + name: { + type: String, + default: uni.$u.props.checkboxGroup.name + }, + // 绑定的值 + value: { + type: Array, + default: uni.$u.props.checkboxGroup.value + }, + // 形状,circle-圆形,square-方形 + shape: { + type: String, + default: uni.$u.props.checkboxGroup.shape + }, + // 是否禁用全部checkbox + disabled: { + type: Boolean, + default: uni.$u.props.checkboxGroup.disabled + }, + + // 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值 + activeColor: { + type: String, + default: uni.$u.props.checkboxGroup.activeColor + }, + // 未选中的颜色 + inactiveColor: { + type: String, + default: uni.$u.props.checkboxGroup.inactiveColor + }, + + // 整个组件的尺寸,默认px + size: { + type: [String, Number], + default: uni.$u.props.checkboxGroup.size + }, + // 布局方式,row-横向,column-纵向 + placement: { + type: String, + default: uni.$u.props.checkboxGroup.placement + }, + // label的字体大小,px单位 + labelSize: { + type: [String, Number], + default: uni.$u.props.checkboxGroup.labelSize + }, + // label的字体颜色 + labelColor: { + type: [String], + default: uni.$u.props.checkboxGroup.labelColor + }, + // 是否禁止点击文本操作 + labelDisabled: { + type: Boolean, + default: uni.$u.props.checkboxGroup.labelDisabled + }, + // 图标颜色 + iconColor: { + type: String, + default: uni.$u.props.checkboxGroup.iconColor + }, + // 图标的大小,单位px + iconSize: { + type: [String, Number], + default: uni.$u.props.checkboxGroup.iconSize + }, + // 勾选图标的对齐方式,left-左边,right-右边 + iconPlacement: { + type: String, + default: uni.$u.props.checkboxGroup.iconPlacement + }, + // 竖向配列时,是否显示下划线 + borderBottom: { + type: Boolean, + default: uni.$u.props.checkboxGroup.borderBottom + } + + } +} diff --git a/uni_modules/uview-ui/components/u-checkbox-group/u-checkbox-group.vue b/uni_modules/uview-ui/components/u-checkbox-group/u-checkbox-group.vue new file mode 100644 index 0000000..7a6b4fa --- /dev/null +++ b/uni_modules/uview-ui/components/u-checkbox-group/u-checkbox-group.vue @@ -0,0 +1,103 @@ +<template> + <view + class="u-checkbox-group" + :class="bemClass" + > + <slot></slot> + </view> +</template> + +<script> + import props from './props.js'; + /** + * checkboxGroup 复选框组 + * @description 复选框组件一般用于需要多个选择的场景,该组件功能完整,使用方便 + * @tutorial https://www.uviewui.com/components/checkbox.html + * @property {String} name 标识符 + * @property {Array} value 绑定的值 + * @property {String} shape 形状,circle-圆形,square-方形 (默认 'square' ) + * @property {Boolean} disabled 是否禁用全部checkbox (默认 false ) + * @property {String} activeColor 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值 (默认 '#2979ff' ) + * @property {String} inactiveColor 未选中的颜色 (默认 '#c8c9cc' ) + * @property {String | Number} size 整个组件的尺寸 单位px (默认 18 ) + * @property {String} placement 布局方式,row-横向,column-纵向 (默认 'row' ) + * @property {String | Number} labelSize label的字体大小,px单位 (默认 14 ) + * @property {String} labelColor label的字体颜色 (默认 '#303133' ) + * @property {Boolean} labelDisabled 是否禁止点击文本操作 (默认 false ) + * @property {String} iconColor 图标颜色 (默认 '#ffffff' ) + * @property {String | Number} iconSize 图标的大小,单位px (默认 12 ) + * @property {String} iconPlacement 勾选图标的对齐方式,left-左边,right-右边 (默认 'left' ) + * @property {Boolean} borderBottom placement为row时,是否显示下边框 (默认 false ) + * @event {Function} change 任一个checkbox状态发生变化时触发,回调为一个对象 + * @event {Function} input 修改通过v-model绑定的值时触发,回调为一个对象 + * @example <u-checkbox-group></u-checkbox-group> + */ + export default { + name: 'u-checkbox-group', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + computed: { + // 这里computed的变量,都是子组件u-checkbox需要用到的,由于头条小程序的兼容性差异,子组件无法实时监听父组件参数的变化 + // 所以需要手动通知子组件,这里返回一个parentData变量,供watch监听,在其中去通知每一个子组件重新从父组件(u-checkbox-group) + // 拉取父组件新的变化后的参数 + parentData() { + return [this.value, this.disabled, this.inactiveColor, this.activeColor, this.size, this.labelDisabled, this.shape, + this.iconSize, this.borderBottom, this.placement + ] + }, + bemClass() { + // this.bem为一个computed变量,在mixin中 + return this.bem('checkbox-group', ['placement']) + }, + }, + watch: { + // 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件 + parentData() { + if (this.children.length) { + this.children.map(child => { + // 判断子组件(u-checkbox)如果有init方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值) + typeof(child.init) === 'function' && child.init() + }) + } + }, + }, + data() { + return { + + } + }, + created() { + this.children = [] + }, + methods: { + // 将其他的checkbox设置为未选中的状态 + unCheckedOther(childInstance) { + const values = [] + this.children.map(child => { + // 将被选中的checkbox,放到数组中返回 + if (child.isChecked) { + values.push(child.name) + } + }) + // 发出事件 + this.$emit('change', values) + // 修改通过v-model绑定的值 + this.$emit('input', values) + }, + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-checkbox-group { + + &--row { + @include flex; + } + + &--column { + @include flex(column); + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-checkbox/props.js b/uni_modules/uview-ui/components/u-checkbox/props.js new file mode 100644 index 0000000..93f4fd9 --- /dev/null +++ b/uni_modules/uview-ui/components/u-checkbox/props.js @@ -0,0 +1,69 @@ +export default { + props: { + // checkbox的名称 + name: { + type: [String, Number, Boolean], + default: uni.$u.props.checkbox.name + }, + // 形状,square为方形,circle为圆型 + shape: { + type: String, + default: uni.$u.props.checkbox.shape + }, + // 整体的大小 + size: { + type: [String, Number], + default: uni.$u.props.checkbox.size + }, + // 是否默认选中 + checked: { + type: Boolean, + default: uni.$u.props.checkbox.checked + }, + // 是否禁用 + disabled: { + type: [String, Boolean], + default: uni.$u.props.checkbox.disabled + }, + // 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值 + activeColor: { + type: String, + default: uni.$u.props.checkbox.activeColor + }, + // 未选中的颜色 + inactiveColor: { + type: String, + default: uni.$u.props.checkbox.inactiveColor + }, + // 图标的大小,单位px + iconSize: { + type: [String, Number], + default: uni.$u.props.checkbox.iconSize + }, + // 图标颜色 + iconColor: { + type: String, + default: uni.$u.props.checkbox.iconColor + }, + // label提示文字,因为nvue下,直接slot进来的文字,由于特殊的结构,无法修改样式 + label: { + type: [String, Number], + default: uni.$u.props.checkbox.label + }, + // label的字体大小,px单位 + labelSize: { + type: [String, Number], + default: uni.$u.props.checkbox.labelSize + }, + // label的颜色 + labelColor: { + type: String, + default: uni.$u.props.checkbox.labelColor + }, + // 是否禁止点击提示语选中复选框 + labelDisabled: { + type: [String, Boolean], + default: uni.$u.props.checkbox.labelDisabled + } + } +} diff --git a/uni_modules/uview-ui/components/u-checkbox/u-checkbox.vue b/uni_modules/uview-ui/components/u-checkbox/u-checkbox.vue new file mode 100644 index 0000000..6429cca --- /dev/null +++ b/uni_modules/uview-ui/components/u-checkbox/u-checkbox.vue @@ -0,0 +1,344 @@ +<template> + <view + class="u-checkbox" + :style="[checkboxStyle]" + @tap.stop="wrapperClickHandler" + :class="[`u-checkbox-label--${parentData.iconPlacement}`, parentData.borderBottom && parentData.placement === 'column' && 'u-border-bottom']" + > + <view + class="u-checkbox__icon-wrap" + @tap.stop="iconClickHandler" + :class="iconClasses" + :style="[iconWrapStyle]" + > + <slot name="icon"> + <u-icon + class="u-checkbox__icon-wrap__icon" + name="checkbox-mark" + :size="elIconSize" + :color="elIconColor" + /> + </slot> + </view> + <text + @tap.stop="labelClickHandler" + :style="{ + color: elDisabled ? elInactiveColor : elLabelColor, + fontSize: elLabelSize, + lineHeight: elLabelSize + }" + >{{label}}</text> + </view> +</template> + +<script> + import props from './props.js'; + /** + * checkbox 复选框 + * @description 复选框组件一般用于需要多个选择的场景,该组件功能完整,使用方便 + * @tutorial https://uviewui.com/components/checkbox.html + * @property {String | Number | Boolean} name checkbox组件的标示符 + * @property {String} shape 形状,square为方形,circle为圆型 + * @property {String | Number} size 整体的大小 + * @property {Boolean} checked 是否默认选中 + * @property {String | Boolean} disabled 是否禁用 + * @property {String} activeColor 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值 + * @property {String} inactiveColor 未选中的颜色 + * @property {String | Number} iconSize 图标的大小,单位px + * @property {String} iconColor 图标颜色 + * @property {String | Number} label label提示文字,因为nvue下,直接slot进来的文字,由于特殊的结构,无法修改样式 + * @property {String} labelColor label的颜色 + * @property {String | Number} labelSize label的字体大小,px单位 + * @property {String | Boolean} labelDisabled 是否禁止点击提示语选中复选框 + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @event {Function} change 任一个checkbox状态发生变化时触发,回调为一个对象 + * @example <u-checkbox v-model="checked" :disabled="false">天涯</u-checkbox> + */ + export default { + name: "u-checkbox", + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + isChecked: false, + // 父组件的默认值,因为头条小程序不支持在computed中使用this.parent.shape的形式 + // 故只能使用如此方法 + parentData: { + iconSize: 12, + labelDisabled: null, + disabled: null, + shape: 'square', + activeColor: null, + inactiveColor: null, + size: 18, + value: null, + iconColor: null, + placement: 'row', + borderBottom: false, + iconPlacement: 'left' + } + } + }, + computed: { + // 是否禁用,如果父组件u-raios-group禁用的话,将会忽略子组件的配置 + elDisabled() { + return this.disabled !== '' ? this.disabled : this.parentData.disabled !== null ? this.parentData.disabled : false; + }, + // 是否禁用label点击 + elLabelDisabled() { + return this.labelDisabled !== '' ? this.labelDisabled : this.parentData.labelDisabled !== null ? this.parentData.labelDisabled : + false; + }, + // 组件尺寸,对应size的值,默认值为21px + elSize() { + return this.size ? this.size : (this.parentData.size ? this.parentData.size : 21); + }, + // 组件的勾选图标的尺寸,默认12px + elIconSize() { + return this.iconSize ? this.iconSize : (this.parentData.iconSize ? this.parentData.iconSize : 12); + }, + // 组件选中激活时的颜色 + elActiveColor() { + return this.activeColor ? this.activeColor : (this.parentData.activeColor ? this.parentData.activeColor : '#2979ff'); + }, + // 组件选未中激活时的颜色 + elInactiveColor() { + return this.inactiveColor ? this.inactiveColor : (this.parentData.inactiveColor ? this.parentData.inactiveColor : + '#c8c9cc'); + }, + // label的颜色 + elLabelColor() { + return this.labelColor ? this.labelColor : (this.parentData.labelColor ? this.parentData.labelColor : '#606266') + }, + // 组件的形状 + elShape() { + return this.shape ? this.shape : (this.parentData.shape ? this.parentData.shape : 'circle'); + }, + // label大小 + elLabelSize() { + return uni.$u.addUnit(this.labelSize ? this.labelSize : (this.parentData.labelSize ? this.parentData.labelSize : + '15')) + }, + elIconColor() { + const iconColor = this.iconColor ? this.iconColor : (this.parentData.iconColor ? this.parentData.iconColor : + '#ffffff'); + // 图标的颜色 + if (this.elDisabled) { + // disabled状态下,已勾选的checkbox图标改为elInactiveColor + return this.isChecked ? this.elInactiveColor : 'transparent' + } else { + return this.isChecked ? iconColor : 'transparent' + } + }, + iconClasses() { + let classes = [] + // 组件的形状 + classes.push('u-checkbox__icon-wrap--' + this.elShape) + if (this.elDisabled) { + classes.push('u-checkbox__icon-wrap--disabled') + } + if (this.isChecked && this.elDisabled) { + classes.push('u-checkbox__icon-wrap--disabled--checked') + } + // 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效 + // #ifdef MP-ALIPAY || MP-TOUTIAO + classes = classes.join(' ') + // #endif + return classes + }, + iconWrapStyle() { + // checkbox的整体样式 + const style = {} + style.backgroundColor = this.isChecked && !this.elDisabled ? this.elActiveColor : '#ffffff' + style.borderColor = this.isChecked && !this.elDisabled ? this.elActiveColor : this.elInactiveColor + style.width = uni.$u.addUnit(this.elSize) + style.height = uni.$u.addUnit(this.elSize) + // 如果是图标在右边的话,移除它的右边距 + if (this.parentData.iconPlacement === 'right') { + style.marginRight = 0 + } + return style + }, + checkboxStyle() { + const style = {} + if (this.parentData.borderBottom && this.parentData.placement === 'row') { + uni.$u.error('检测到您将borderBottom设置为true,需要同时将u-checkbox-group的placement设置为column才有效') + } + // 当父组件设置了显示下边框并且排列形式为纵向时,给内容和边框之间加上一定间隔 + if (this.parentData.borderBottom && this.parentData.placement === 'column') { + style.paddingBottom = '8px' + } + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) + } + }, + mounted() { + this.init() + }, + methods: { + init() { + // 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用 + this.updateParentData() + if (!this.parent) { + uni.$u.error('u-checkbox必须搭配u-checkbox-group组件使用') + } + // 设置初始化时,是否默认选中的状态,父组件u-checkbox-group的value可能是array,所以额外判断 + if (this.checked) { + this.isChecked = true + } else if (uni.$u.test.array(this.parentData.value)) { + // 查找数组是是否存在this.name元素值 + this.isChecked = this.parentData.value.some(item => { + return item === this.name + }) + } + }, + updateParentData() { + this.getParentData('u-checkbox-group') + }, + // 横向两端排列时,点击组件即可触发选中事件 + wrapperClickHandler(e) { + this.parentData.iconPlacement === 'right' && this.iconClickHandler(e) + }, + // 点击图标 + iconClickHandler(e) { + this.preventEvent(e) + // 如果整体被禁用,不允许被点击 + if (!this.elDisabled) { + this.setRadioCheckedStatus() + } + }, + // 点击label + labelClickHandler(e) { + this.preventEvent(e) + // 如果按钮整体被禁用或者label被禁用,则不允许点击文字修改状态 + if (!this.elLabelDisabled && !this.elDisabled) { + this.setRadioCheckedStatus() + } + }, + emitEvent() { + this.$emit('change', this.isChecked) + // 尝试调用u-form的验证方法,进行一定延迟,否则微信小程序更新可能会不及时 + this.$nextTick(() => { + uni.$u.formValidate(this, 'change') + }) + }, + // 改变组件选中状态 + // 这里的改变的依据是,更改本组件的checked值为true,同时通过父组件遍历所有u-checkbox实例 + // 将本组件外的其他u-checkbox的checked都设置为false(都被取消选中状态),因而只剩下一个为选中状态 + setRadioCheckedStatus() { + // 将本组件标记为与原来相反的状态 + this.isChecked = !this.isChecked + this.emitEvent() + typeof this.parent.unCheckedOther === 'function' && this.parent.unCheckedOther(this) + } + }, + watch:{ + checked(){ + this.isChecked = this.checked + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + $u-checkbox-icon-wrap-margin-right:6px !default; + $u-checkbox-icon-wrap-font-size:6px !default; + $u-checkbox-icon-wrap-border-width:1px !default; + $u-checkbox-icon-wrap-border-color:#c8c9cc !default; + $u-checkbox-icon-wrap-icon-line-height:0 !default; + $u-checkbox-icon-wrap-circle-border-radius:100% !default; + $u-checkbox-icon-wrap-square-border-radius:3px !default; + $u-checkbox-icon-wrap-checked-color:#fff !default; + $u-checkbox-icon-wrap-checked-background-color:red !default; + $u-checkbox-icon-wrap-checked-border-color:#2979ff !default; + $u-checkbox-icon-wrap-disabled-background-color:#ebedf0 !default; + $u-checkbox-icon-wrap-disabled-checked-color:#c8c9cc !default; + $u-checkbox-label-margin-left:5px !default; + $u-checkbox-label-margin-right:12px !default; + $u-checkbox-label-color:$u-content-color !default; + $u-checkbox-label-font-size:15px !default; + $u-checkbox-label-disabled-color:#c8c9cc !default; + + .u-checkbox { + /* #ifndef APP-NVUE */ + @include flex(row); + /* #endif */ + overflow: hidden; + flex-direction: row; + align-items: center; + + &-label--left { + flex-direction: row + } + + &-label--right { + flex-direction: row-reverse; + justify-content: space-between + } + + &__icon-wrap { + /* #ifndef APP-NVUE */ + box-sizing: border-box; + // nvue下,border-color过渡有问题 + transition-property: border-color, background-color, color; + transition-duration: 0.2s; + /* #endif */ + color: $u-content-color; + @include flex; + align-items: center; + justify-content: center; + color: transparent; + text-align: center; + margin-right: $u-checkbox-icon-wrap-margin-right; + + font-size: $u-checkbox-icon-wrap-font-size; + border-width: $u-checkbox-icon-wrap-border-width; + border-color: $u-checkbox-icon-wrap-border-color; + border-style: solid; + + /* #ifdef MP-TOUTIAO */ + // 头条小程序兼容性问题,需要设置行高为0,否则图标偏下 + &__icon { + line-height: $u-checkbox-icon-wrap-icon-line-height; + } + + /* #endif */ + + &--circle { + border-radius: $u-checkbox-icon-wrap-circle-border-radius; + } + + &--square { + border-radius: $u-checkbox-icon-wrap-square-border-radius; + } + + &--checked { + color: $u-checkbox-icon-wrap-checked-color; + background-color: $u-checkbox-icon-wrap-checked-background-color; + border-color: $u-checkbox-icon-wrap-checked-border-color; + } + + &--disabled { + background-color: $u-checkbox-icon-wrap-disabled-background-color !important; + } + + &--disabled--checked { + color: $u-checkbox-icon-wrap-disabled-checked-color !important; + } + } + + &__label { + /* #ifndef APP-NVUE */ + word-wrap: break-word; + /* #endif */ + margin-left: $u-checkbox-label-margin-left; + margin-right: $u-checkbox-label-margin-right; + color: $u-checkbox-label-color; + font-size: $u-checkbox-label-font-size; + + &--disabled { + color: $u-checkbox-label-disabled-color; + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-circle-progress/props.js b/uni_modules/uview-ui/components/u-circle-progress/props.js new file mode 100644 index 0000000..d776cfb --- /dev/null +++ b/uni_modules/uview-ui/components/u-circle-progress/props.js @@ -0,0 +1,8 @@ +export default { + props: { + percentage: { + type: [String, Number], + default: uni.$u.props.circleProgress.percentage + } + } +} diff --git a/uni_modules/uview-ui/components/u-circle-progress/u-circle-progress.vue b/uni_modules/uview-ui/components/u-circle-progress/u-circle-progress.vue new file mode 100644 index 0000000..d1ee286 --- /dev/null +++ b/uni_modules/uview-ui/components/u-circle-progress/u-circle-progress.vue @@ -0,0 +1,198 @@ +<template> + <view class="u-circle-progress"> + <view class="u-circle-progress__left"> + <view + class="u-circle-progress__left__circle" + :style="[leftSyle]" + ref="left-circle" + > + + </view> + </view> + <view + class="u-circle-progress__right" + > + <view + class="u-circle-progress__right__circle" + ref="right-circle" + :style="[rightSyle]" + > + + </view> + </view> + <view class="u-circle-progress__circle"> + + </view> + </view> +</template> + +<script> + import props from './props.js'; + // #ifdef APP-NVUE + const animation = uni.requireNativePlugin('animation') + // #endif + /** + * CircleProgress 圆形进度条 TODO: 待完善 + * @description 展示操作或任务的当前进度,比如上传文件,是一个圆形的进度环。 + * @tutorial https://www.uviewui.com/components/circleProgress.html + * @property {String | Number} percentage 圆环进度百分比值,为数值类型,0-100 (默认 30 ) + * @example + */ + export default { + name: 'u-circle-progress', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + leftBorderColor: 'rgb(200, 200, 200)', + rightBorderColor: 'rgb(200, 200, 200)', + } + }, + computed: { + leftSyle() { + const style = {} + style.borderTopColor = this.leftBorderColor + style.borderRightColor = this.leftBorderColor + return style + }, + rightSyle() { + const style = {} + style.borderLeftColor = this.rightBorderColor + style.borderBottomColor = this.rightBorderColor + return style + } + }, + mounted() { + uni.$u.sleep().then(() => { + this.rightBorderColor = 'rgb(66, 185, 131)' + // this.init() + }) + }, + methods: { + init() { + animation.transition(this.$refs['right-circle'].ref, { + styles: { + transform: 'rotate(45deg)', + transformOrigin: 'center center' + }, + }, () => { + this.rightBorderColor = 'rgb(66, 185, 131)' + // animation.transition(this.$refs['right-circle'].ref, { + // styles: { + // transform: 'rotate(225deg)', + // transformOrigin: 'center center' + // }, + // duration: 3000, + // }, () => { + // animation.transition(this.$refs['left-circle'].ref, { + // styles: { + // transform: 'rotate(45deg)', + // transformOrigin: 'center center' + // }, + // }, () => { + // this.leftBorderColor = 'rgb(66, 185, 131)' + // animation.transition(this.$refs['left-circle'].ref, { + // styles: { + // transform: 'rotate(225deg)', + // transformOrigin: 'center center' + // }, + // duration: 1500, + // }, () => { + + // }) + // }) + // }) + }) + + } + }, + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-circle-progress { + @include flex(row); + position: relative; + border-radius: 100px; + height: 100px; + width: 100px; + // transform: rotate(0deg); + // background-color: rgb(66, 185, 131); + background-color: rgb(200, 200, 200); + overflow: hidden; + justify-content: space-between; + + &__circle { + border-radius: 100px; + height: 90px; + width: 90px; + transform: translate(-50%, -50%); + background-color: rgb(255, 255, 255); + left: 50px; + top: 50px; + position: absolute; + } + + &__left { + position: absolute; + left: 0; + width: 50px; + height: 100px; + overflow: hidden; + box-sizing: border-box; + // background-color: rgb(66, 185, 131); + // background-color: rgb(200, 200, 200); + // transform-origin: left center; + + &__circle { + box-sizing: border-box; + // background-color: red; + border-left-color: transparent; + border-bottom-color: transparent; + border-top-left-radius: 50px; + border-top-right-radius: 50px; + border-bottom-right-radius: 50px; + // border-left-color: rgb(66, 185, 131); + // border-bottom-color: rgb(66, 185, 131); + border-top-color: rgb(66, 185, 131); + border-right-color: rgb(66, 185, 131); + border-width: 5px; + width: 100px; + height: 100px; + transform: rotate(225deg); + // border-radius: 100px; + } + } + + &__right { + position: absolute; + right: 0; + width: 50px; + height: 100px; + overflow: hidden; + + &__circle { + position: absolute; + right: 0; + box-sizing: border-box; + // background-color: red; + border-top-color: transparent; + border-right-color: transparent; + border-top-left-radius: 50px; + border-bottom-left-radius: 50px; + border-bottom-right-radius: 50px; + // border-left-color: rgb(66, 185, 131); + // border-bottom-color: rgb(66, 185, 131); + border-left-color: rgb(200, 200, 200); + border-bottom-color: rgb(200, 200, 200); + border-width: 5px; + width: 100px; + height: 100px; + transform: rotate(45deg); + transform-origin: center center; + // border-radius: 100px; + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-code-input/props.js b/uni_modules/uview-ui/components/u-code-input/props.js new file mode 100644 index 0000000..0f016ee --- /dev/null +++ b/uni_modules/uview-ui/components/u-code-input/props.js @@ -0,0 +1,79 @@ +export default { + props: { + // 键盘弹起时,是否自动上推页面 + adjustPosition: { + type: Boolean, + default: uni.$u.props.codeInput.adjustPosition + }, + // 最大输入长度 + maxlength: { + type: [String, Number], + default: uni.$u.props.codeInput.maxlength + }, + // 是否用圆点填充 + dot: { + type: Boolean, + default: uni.$u.props.codeInput.dot + }, + // 显示模式,box-盒子模式,line-底部横线模式 + mode: { + type: String, + default: uni.$u.props.codeInput.mode + }, + // 是否细边框 + hairline: { + type: Boolean, + default: uni.$u.props.codeInput.hairline + }, + // 字符间的距离 + space: { + type: [String, Number], + default: uni.$u.props.codeInput.space + }, + // 预置值 + value: { + type: [String, Number], + default: uni.$u.props.codeInput.value + }, + // 是否自动获取焦点 + focus: { + type: Boolean, + default: uni.$u.props.codeInput.focus + }, + // 字体是否加粗 + bold: { + type: Boolean, + default: uni.$u.props.codeInput.bold + }, + // 字体颜色 + color: { + type: String, + default: uni.$u.props.codeInput.color + }, + // 字体大小 + fontSize: { + type: [String, Number], + default: uni.$u.props.codeInput.fontSize + }, + // 输入框的大小,宽等于高 + size: { + type: [String, Number], + default: uni.$u.props.codeInput.size + }, + // 是否隐藏原生键盘,如果想用自定义键盘的话,需设置此参数为true + disabledKeyboard: { + type: Boolean, + default: uni.$u.props.codeInput.disabledKeyboard + }, + // 边框和线条颜色 + borderColor: { + type: String, + default: uni.$u.props.codeInput.borderColor + }, + // 是否禁止输入"."符号 + disabledDot: { + type: Boolean, + default: uni.$u.props.codeInput.disabledDot + } + } +} diff --git a/uni_modules/uview-ui/components/u-code-input/u-code-input.vue b/uni_modules/uview-ui/components/u-code-input/u-code-input.vue new file mode 100644 index 0000000..96241cf --- /dev/null +++ b/uni_modules/uview-ui/components/u-code-input/u-code-input.vue @@ -0,0 +1,252 @@ +<template> + <view class="u-code-input"> + <view + class="u-code-input__item" + :style="[itemStyle(index)]" + v-for="(item, index) in codeLength" + :key="index" + > + <view + class="u-code-input__item__dot" + v-if="dot && codeArray.length > index" + ></view> + <text + v-else + :style="{ + fontSize: $u.addUnit(fontSize), + fontWeight: bold ? 'bold' : 'normal', + color: color + }" + >{{codeArray[index]}}</text> + <view + class="u-code-input__item__line" + v-if="mode === 'line'" + :style="[lineStyle]" + ></view> + <!-- #ifndef APP-PLUS --> + <view v-if="isFocus && codeArray.length === index" :style="{backgroundColor: color}" class="u-code-input__item__cursor"></view> + <!-- #endif --> + </view> + <input + :disabled="disabledKeyboard" + type="number" + :focus="focus" + :value="inputValue" + :maxlength="maxlength" + :adjustPosition="adjustPosition" + class="u-code-input__input" + @input="inputHandler" + :style="{ + height: $u.addUnit(size) + }" + @focus="isFocus = true" + @blur="isFocus = false" + /> + </view> +</template> + +<script> + import props from './props.js'; + /** + * CodeInput 验证码输入 + * @description 该组件一般用于验证用户短信验证码的场景,也可以结合uView的键盘组件使用 + * @tutorial https://www.uviewui.com/components/codeInput.html + * @property {String | Number} maxlength 最大输入长度 (默认 6 ) + * @property {Boolean} dot 是否用圆点填充 (默认 false ) + * @property {String} mode 显示模式,box-盒子模式,line-底部横线模式 (默认 'box' ) + * @property {Boolean} hairline 是否细边框 (默认 false ) + * @property {String | Number} space 字符间的距离 (默认 10 ) + * @property {String | Number} value 预置值 + * @property {Boolean} focus 是否自动获取焦点 (默认 false ) + * @property {Boolean} bold 字体和输入横线是否加粗 (默认 false ) + * @property {String} color 字体颜色 (默认 '#606266' ) + * @property {String | Number} fontSize 字体大小,单位px (默认 18 ) + * @property {String | Number} size 输入框的大小,宽等于高 (默认 35 ) + * @property {Boolean} disabledKeyboard 是否隐藏原生键盘,如果想用自定义键盘的话,需设置此参数为true (默认 false ) + * @property {String} borderColor 边框和线条颜色 (默认 '#c9cacc' ) + * @property {Boolean} disabledDot 是否禁止输入"."符号 (默认 true ) + * + * @event {Function} change 输入内容发生改变时触发,具体见上方说明 value:当前输入的值 + * @event {Function} finish 输入字符个数达maxlength值时触发,见上方说明 value:当前输入的值 + * @example <u-code-input v-model="value4" :focus="true"></u-code-input> + */ + export default { + name: 'u-code-input', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + inputValue: '', + isFocus: this.focus + } + }, + watch: { + value: { + immediate: true, + handler(val) { + // 转为字符串,超出部分截掉 + this.inputValue = String(val).substring(0, this.maxlength) + } + }, + }, + computed: { + // 根据长度,循环输入框的个数,因为头条小程序数值不能用于v-for + codeLength() { + return new Array(Number(this.maxlength)) + }, + // 循环item的样式 + itemStyle() { + return index => { + const addUnit = uni.$u.addUnit + const style = { + width: addUnit(this.size), + height: addUnit(this.size) + } + // 盒子模式下,需要额外进行处理 + if (this.mode === 'box') { + // 设置盒子的边框,如果是细边框,则设置为0.5px宽度 + style.border = `${this.hairline ? 0.5 : 1}px solid ${this.borderColor}` + // 如果盒子间距为0的话 + if (uni.$u.getPx(this.space) === 0) { + // 给第一和最后一个盒子设置圆角 + if (index === 0) { + style.borderTopLeftRadius = '3px' + style.borderBottomLeftRadius = '3px' + } + if (index === this.codeLength.length - 1) { + style.borderTopRightRadius = '3px' + style.borderBottomRightRadius = '3px' + } + // 最后一个盒子的右边框需要保留 + if (index !== this.codeLength.length - 1) { + style.borderRight = 'none' + } + } + } + if (index !== this.codeLength.length - 1) { + // 设置验证码字符之间的距离,通过margin-right设置,最后一个字符,无需右边框 + style.marginRight = addUnit(this.space) + } else { + // 最后一个盒子的有边框需要保留 + style.marginRight = 0 + } + + return style + } + }, + // 将输入的值,转为数组,给item历遍时,根据当前的索引显示数组的元素 + codeArray() { + return String(this.inputValue).split('') + }, + // 下划线模式下,横线的样式 + lineStyle() { + const style = {} + style.height = this.hairline ? '2px' : '4px' + style.width = uni.$u.addUnit(this.size) + // 线条模式下,背景色即为边框颜色 + style.backgroundColor = this.borderColor + return style + } + }, + methods: { + // 监听输入框的值发生变化 + inputHandler(e) { + const value = e.detail.value + this.inputValue = value + // 是否允许输入“.”符号 + if(this.disabledDot) { + this.$nextTick(() => { + this.inputValue = value.replace('.', '') + }) + } + // 未达到maxlength之前,发送change事件,达到后发送finish事件 + this.$emit('change', value) + // 修改通过v-model双向绑定的值 + this.$emit('input', value) + // 达到用户指定输入长度时,发出完成事件 + if (String(value).length >= Number(this.maxlength)) { + this.$emit('finish', value) + } + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + $u-code-input-cursor-width: 1px; + $u-code-input-cursor-height: 40%; + $u-code-input-cursor-animation-duration: 1s; + $u-code-input-cursor-animation-name: u-cursor-flicker; + + .u-code-input { + @include flex; + position: relative; + overflow: hidden; + + &__item { + @include flex; + justify-content: center; + align-items: center; + position: relative; + + &__text { + font-size: 15px; + color: $u-content-color; + } + + &__dot { + width: 7px; + height: 7px; + border-radius: 100px; + background-color: $u-content-color; + } + + &__line { + position: absolute; + bottom: 0; + height: 4px; + border-radius: 100px; + width: 40px; + background-color: $u-content-color; + } + /* #ifndef APP-PLUS */ + &__cursor { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%,-50%); + width: $u-code-input-cursor-width; + height: $u-code-input-cursor-height; + animation: $u-code-input-cursor-animation-duration u-cursor-flicker infinite; + } + /* #endif */ + + } + + &__input { + // 之所以需要input输入框,是因为有它才能唤起键盘 + // 这里将它设置为两倍的屏幕宽度,再将左边的一半移出屏幕,为了不让用户看到输入的内容 + position: absolute; + left: -750rpx; + width: 1500rpx; + top: 0; + background-color: transparent; + text-align: left; + } + } + + /* #ifndef APP-PLUS */ + @keyframes u-cursor-flicker { + 0% { + opacity: 0; + } + 50% { + opacity: 1; + } + 100% { + opacity: 0; + } + } + /* #endif */ + +</style> diff --git a/uni_modules/uview-ui/components/u-code/props.js b/uni_modules/uview-ui/components/u-code/props.js new file mode 100644 index 0000000..eaf80d0 --- /dev/null +++ b/uni_modules/uview-ui/components/u-code/props.js @@ -0,0 +1,34 @@ +export default { + props: { + // 倒计时总秒数 + seconds: { + type: [String, Number], + default: uni.$u.props.code.seconds + }, + // 尚未开始时提示 + startText: { + type: String, + default: uni.$u.props.code.startText + }, + // 正在倒计时中的提示 + changeText: { + type: String, + default: uni.$u.props.code.changeText + }, + // 倒计时结束时的提示 + endText: { + type: String, + default: uni.$u.props.code.endText + }, + // 是否在H5刷新或各端返回再进入时继续倒计时 + keepRunning: { + type: Boolean, + default: uni.$u.props.code.keepRunning + }, + // 为了区分多个页面,或者一个页面多个倒计时组件本地存储的继续倒计时变了 + uniqueKey: { + type: String, + default: uni.$u.props.code.uniqueKey + } + } +} diff --git a/uni_modules/uview-ui/components/u-code/u-code.vue b/uni_modules/uview-ui/components/u-code/u-code.vue new file mode 100644 index 0000000..cdf9f82 --- /dev/null +++ b/uni_modules/uview-ui/components/u-code/u-code.vue @@ -0,0 +1,129 @@ +<template> + <view class="u-code"> + <!-- 此组件功能由js完成,无需写html逻辑 --> + </view> +</template> + +<script> + import props from './props.js'; + /** + * Code 验证码输入框 + * @description 考虑到用户实际发送验证码的场景,可能是一个按钮,也可能是一段文字,提示语各有不同,所以本组件 不提供界面显示,只提供提示语,由用户将提示语嵌入到具体的场景 + * @tutorial https://www.uviewui.com/components/code.html + * @property {String | Number} seconds 倒计时所需的秒数(默认 60 ) + * @property {String} startText 开始前的提示语,见官网说明(默认 '获取验证码' ) + * @property {String} changeText 倒计时期间的提示语,必须带有字母"x",见官网说明(默认 'X秒重新获取' ) + * @property {String} endText 倒计结束的提示语,见官网说明(默认 '重新获取' ) + * @property {Boolean} keepRunning 是否在H5刷新或各端返回再进入时继续倒计时( 默认false ) + * @property {String} uniqueKey 为了区分多个页面,或者一个页面多个倒计时组件本地存储的继续倒计时变了 + * + * @event {Function} change 倒计时期间,每秒触发一次 + * @event {Function} start 开始倒计时触发 + * @event {Function} end 结束倒计时触发 + * @example <u-code ref="uCode" @change="codeChange" seconds="20"></u-code> + */ + export default { + name: "u-code", + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + secNum: this.seconds, + timer: null, + canGetCode: true, // 是否可以执行验证码操作 + } + }, + mounted() { + this.checkKeepRunning() + }, + watch: { + seconds: { + immediate: true, + handler(n) { + this.secNum = n + } + } + }, + methods: { + checkKeepRunning() { + // 获取上一次退出页面(H5还包括刷新)时的时间戳,如果没有上次的保存,此值可能为空 + let lastTimestamp = Number(uni.getStorageSync(this.uniqueKey + '_$uCountDownTimestamp')) + if(!lastTimestamp) return this.changeEvent(this.startText) + // 当前秒的时间戳 + let nowTimestamp = Math.floor((+ new Date()) / 1000) + // 判断当前的时间戳,是否小于上一次的本该按设定结束,却提前结束的时间戳 + if(this.keepRunning && lastTimestamp && lastTimestamp > nowTimestamp) { + // 剩余尚未执行完的倒计秒数 + this.secNum = lastTimestamp - nowTimestamp + // 清除本地保存的变量 + uni.removeStorageSync(this.uniqueKey + '_$uCountDownTimestamp') + // 开始倒计时 + this.start() + } else { + // 如果不存在需要继续上一次的倒计时,执行正常的逻辑 + this.changeEvent(this.startText) + } + }, + // 开始倒计时 + start() { + // 防止快速点击获取验证码的按钮而导致内部产生多个定时器导致混乱 + if(this.timer) { + clearInterval(this.timer) + this.timer = null + } + this.$emit('start') + this.canGetCode = false + // 这里放这句,是为了一开始时就提示,否则要等setInterval的1秒后才会有提示 + this.changeEvent(this.changeText.replace(/x|X/, this.secNum)) + this.setTimeToStorage() + this.timer = setInterval(() => { + if (--this.secNum) { + // 用当前倒计时的秒数替换提示字符串中的"x"字母 + this.changeEvent(this.changeText.replace(/x|X/, this.secNum)) + } else { + clearInterval(this.timer) + this.timer = null + this.changeEvent(this.endText) + this.secNum = this.seconds + this.$emit('end') + this.canGetCode = true + } + }, 1000) + }, + // 重置,可以让用户再次获取验证码 + reset() { + this.canGetCode = true + clearInterval(this.timer) + this.secNum = this.seconds + this.changeEvent(this.endText) + }, + changeEvent(text) { + this.$emit('change', text) + }, + // 保存时间戳,为了防止倒计时尚未结束,H5刷新或者各端的右上角返回上一页再进来 + setTimeToStorage() { + if(!this.keepRunning || !this.timer) return + // 记录当前的时间戳,为了下次进入页面,如果还在倒计时内的话,继续倒计时 + // 倒计时尚未结束,结果大于0;倒计时已经开始,就会小于初始值,如果等于初始值,说明没有开始倒计时,无需处理 + if(this.secNum > 0 && this.secNum <= this.seconds) { + // 获取当前时间戳(+ new Date()为特殊写法),除以1000变成秒,再去除小数部分 + let nowTimestamp = Math.floor((+ new Date()) / 1000) + // 将本该结束时候的时间戳保存起来 => 当前时间戳 + 剩余的秒数 + uni.setStorage({ + key: this.uniqueKey + '_$uCountDownTimestamp', + data: nowTimestamp + Number(this.secNum) + }) + } + } + }, + // 组件销毁的时候,清除定时器,否则定时器会继续存在,系统不会自动清除 + beforeDestroy() { + this.setTimeToStorage() + clearTimeout(this.timer) + this.timer = null + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; +</style> diff --git a/uni_modules/uview-ui/components/u-col/props.js b/uni_modules/uview-ui/components/u-col/props.js new file mode 100644 index 0000000..0622251 --- /dev/null +++ b/uni_modules/uview-ui/components/u-col/props.js @@ -0,0 +1,29 @@ +export default { + props: { + // 占父容器宽度的多少等分,总分为12份 + span: { + type: [String, Number], + default: uni.$u.props.col.span + }, + // 指定栅格左侧的间隔数(总12栏) + offset: { + type: [String, Number], + default: uni.$u.props.col.offset + }, + // 水平排列方式,可选值为`start`(或`flex-start`)、`end`(或`flex-end`)、`center`、`around`(或`space-around`)、`between`(或`space-between`) + justify: { + type: String, + default: uni.$u.props.col.justify + }, + // 垂直对齐方式,可选值为top、center、bottom、stretch + align: { + type: String, + default: uni.$u.props.col.align + }, + // 文字对齐方式 + textAlign: { + type: String, + default: uni.$u.props.col.textAlign + } + } +} diff --git a/uni_modules/uview-ui/components/u-col/u-col.vue b/uni_modules/uview-ui/components/u-col/u-col.vue new file mode 100644 index 0000000..8be1517 --- /dev/null +++ b/uni_modules/uview-ui/components/u-col/u-col.vue @@ -0,0 +1,162 @@ +<template> + <view + class="u-col" + ref="u-col" + :class="[ + 'u-col-' + span + ]" + :style="[colStyle]" + @tap="clickHandler" + > + <slot></slot> + </view> +</template> + +<script> + import props from './props.js'; + /** + * CodeInput 栅格系统的列 + * @description 该组件一般用于Layout 布局 通过基础的 12 分栏,迅速简便地创建布局 + * @tutorial https://www.uviewui.com/components/Layout.html + * @property {String | Number} span 栅格占据的列数,总12等份 (默认 12 ) + * @property {String | Number} offset 分栏左边偏移,计算方式与span相同 (默认 0 ) + * @property {String} justify 水平排列方式,可选值为`start`(或`flex-start`)、`end`(或`flex-end`)、`center`、`around`(或`space-around`)、`between`(或`space-between`) (默认 'start' ) + * @property {String} align 垂直对齐方式,可选值为top、center、bottom、stretch (默认 'stretch' ) + * @property {String} textAlign 文字水平对齐方式 (默认 'left' ) + * @property {Object} customStyle 定义需要用到的外部样式 + * @event {Function} click col被点击,会阻止事件冒泡到row + * @example <u-col span="3" offset="3" > <view class="demo-layout bg-purple"></view> </u-col> + */ + export default { + name: 'u-col', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + width: 0, + parentData: { + gutter: 0 + }, + gridNum: 12 + } + }, + computed: { + uJustify() { + if (this.justify == 'end' || this.justify == 'start') return 'flex-' + this.justify + else if (this.justify == 'around' || this.justify == 'between') return 'space-' + this.justify + else return this.justify + }, + uAlignItem() { + if (this.align == 'top') return 'flex-start' + if (this.align == 'bottom') return 'flex-end' + else return this.align + }, + colStyle() { + const style = { + // 这里写成"padding: 0 10px"的形式是因为nvue的需要 + paddingLeft: uni.$u.addUnit(uni.$u.getPx(this.parentData.gutter)/2), + paddingRight: uni.$u.addUnit(uni.$u.getPx(this.parentData.gutter)/2), + alignItems: this.uAlignItem, + justifyContent: this.uJustify, + textAlign: this.textAlign, + // #ifndef APP-NVUE + // 在非nvue上,使用百分比形式 + flex: `0 0 ${100 / this.gridNum * this.span}%`, + marginLeft: 100 / 12 * this.offset + '%', + // #endif + // #ifdef APP-NVUE + // 在nvue上,由于无法使用百分比单位,这里需要获取父组件的宽度,再计算得出该有对应的百分比尺寸 + width: uni.$u.addUnit(Math.floor(this.width / this.gridNum * Number(this.span))), + marginLeft: uni.$u.addUnit(Math.floor(this.width / this.gridNum * Number(this.offset))), + // #endif + } + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) + } + }, + mounted() { + this.init() + }, + methods: { + async init() { + // 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用 + this.updateParentData() + this.width = await this.parent.getComponentWidth() + }, + updateParentData() { + this.getParentData('u-row') + }, + clickHandler(e) { + this.$emit('click'); + } + }, + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-col { + padding: 0; + /* #ifndef APP-NVUE */ + box-sizing:border-box; + /* #endif */ + /* #ifdef MP */ + display: block; + /* #endif */ + } + + // nvue下百分比无效 + /* #ifndef APP-NVUE */ + .u-col-0 { + width: 0; + } + + .u-col-1 { + width: calc(100%/12); + } + + .u-col-2 { + width: calc(100%/12 * 2); + } + + .u-col-3 { + width: calc(100%/12 * 3); + } + + .u-col-4 { + width: calc(100%/12 * 4); + } + + .u-col-5 { + width: calc(100%/12 * 5); + } + + .u-col-6 { + width: calc(100%/12 * 6); + } + + .u-col-7 { + width: calc(100%/12 * 7); + } + + .u-col-8 { + width: calc(100%/12 * 8); + } + + .u-col-9 { + width: calc(100%/12 * 9); + } + + .u-col-10 { + width: calc(100%/12 * 10); + } + + .u-col-11 { + width: calc(100%/12 * 11); + } + + .u-col-12 { + width: calc(100%/12 * 12); + } + + /* #endif */ +</style> diff --git a/uni_modules/uview-ui/components/u-collapse-item/props.js b/uni_modules/uview-ui/components/u-collapse-item/props.js new file mode 100644 index 0000000..bd5749b --- /dev/null +++ b/uni_modules/uview-ui/components/u-collapse-item/props.js @@ -0,0 +1,59 @@ +export default { + props: { + // 标题 + title: { + type: String, + default: uni.$u.props.collapseItem.title + }, + // 标题右侧内容 + value: { + type: String, + default: uni.$u.props.collapseItem.value + }, + // 标题下方的描述信息 + label: { + type: String, + default: uni.$u.props.collapseItem.label + }, + // 是否禁用折叠面板 + disabled: { + type: Boolean, + default: uni.$u.props.collapseItem.disabled + }, + // 是否展示右侧箭头并开启点击反馈 + isLink: { + type: Boolean, + default: uni.$u.props.collapseItem.isLink + }, + // 是否开启点击反馈 + clickable: { + type: Boolean, + default: uni.$u.props.collapseItem.clickable + }, + // 是否显示内边框 + border: { + type: Boolean, + default: uni.$u.props.collapseItem.border + }, + // 标题的对齐方式 + align: { + type: String, + default: uni.$u.props.collapseItem.align + }, + // 唯一标识符 + name: { + type: [String, Number], + default: uni.$u.props.collapseItem.name + }, + // 标题左侧图片,可为绝对路径的图片或内置图标 + icon: { + type: String, + default: uni.$u.props.collapseItem.icon + }, + // 面板展开收起的过渡时间,单位ms + duration: { + type: Number, + default: uni.$u.props.collapseItem.duration + } + } +} diff --git a/uni_modules/uview-ui/components/u-collapse-item/u-collapse-item.vue b/uni_modules/uview-ui/components/u-collapse-item/u-collapse-item.vue new file mode 100644 index 0000000..0e1b703 --- /dev/null +++ b/uni_modules/uview-ui/components/u-collapse-item/u-collapse-item.vue @@ -0,0 +1,225 @@ +<template> + <view class="u-collapse-item"> + <u-cell + :title="title" + :value="value" + :label="label" + :icon="icon" + :isLink="isLink" + :clickable="clickable" + :border="parentData.border && showBorder" + @click="clickHandler" + :arrowDirection="expanded ? 'up' : 'down'" + :disabled="disabled" + > + <!-- #ifndef MP-WEIXIN --> + <!-- 微信小程序不支持,因为微信中不支持 <slot name="title" slot="title" />的写法 --> + <template slot="title"> + <slot name="title"></slot> + </template> + <template slot="icon"> + <slot name="icon"></slot> + </template> + <template slot="value"> + <slot name="value"></slot> + </template> + <template slot="right-icon"> + <slot name="right-icon"></slot> + </template> + <!-- #endif --> + </u-cell> + <view + class="u-collapse-item__content" + :animation="animationData" + ref="animation" + > + <view + class="u-collapse-item__content__text content-class" + :id="elId" + :ref="elId" + ><slot /></view> + </view> + <u-line v-if="parentData.border"></u-line> + </view> +</template> + +<script> + import props from './props.js'; + // #ifdef APP-NVUE + const animation = uni.requireNativePlugin('animation') + const dom = uni.requireNativePlugin('dom') + // #endif + /** + * collapseItem 折叠面板Item + * @description 通过折叠面板收纳内容区域(搭配u-collapse使用) + * @tutorial https://www.uviewui.com/components/collapse.html + * @property {String} title 标题 + * @property {String} value 标题右侧内容 + * @property {String} label 标题下方的描述信息 + * @property {Boolean} disbled 是否禁用折叠面板 ( 默认 false ) + * @property {Boolean} isLink 是否展示右侧箭头并开启点击反馈 ( 默认 true ) + * @property {Boolean} clickable 是否开启点击反馈 ( 默认 true ) + * @property {Boolean} border 是否显示内边框 ( 默认 true ) + * @property {String} align 标题的对齐方式 ( 默认 'left' ) + * @property {String | Number} name 唯一标识符 + * @property {String} icon 标题左侧图片,可为绝对路径的图片或内置图标 + * @event {Function} change 某个item被打开或者收起时触发 + * @example <u-collapse-item :title="item.head" v-for="(item, index) in itemList" :key="index">{{item.body}}</u-collapse-item> + */ + export default { + name: "u-collapse-item", + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + elId: uni.$u.guid(), + // uni.createAnimation的导出数据 + animationData: {}, + // 是否展开状态 + expanded: false, + // 根据expanded确定是否显示border,为了控制展开时,cell的下划线更好的显示效果,进行一定时间的延时 + showBorder: false, + // 是否动画中,如果是则不允许继续触发点击 + animating: false, + // 父组件u-collapse的参数 + parentData: { + accordion: false, + border: false + } + }; + }, + watch: { + expanded(n) { + clearTimeout(this.timer) + this.timer = null + // 这里根据expanded的值来进行一定的延时,是为了cell的下划线更好的显示效果 + this.timer = setTimeout(() => { + this.showBorder = n + }, n ? 10 : 290) + } + }, + mounted() { + this.init() + }, + methods: { + // 异步获取内容,或者动态修改了内容时,需要重新初始化 + init() { + // 初始化数据 + this.updateParentData() + if (!this.parent) { + return uni.$u.error('u-collapse-item必须要搭配u-collapse组件使用') + } + const { + value, + accordion, + children = [] + } = this.parent + + if (accordion) { + if (uni.$u.test.array(value)) { + return uni.$u.error('手风琴模式下,u-collapse组件的value参数不能为数组') + } + this.expanded = this.name == value + } else { + if (!uni.$u.test.array(value) && value !== null) { + return uni.$u.error('非手风琴模式下,u-collapse组件的value参数必须为数组') + } + this.expanded = (value || []).some(item => item == this.name) + } + // 设置组件的展开或收起状态 + this.$nextTick(function() { + this.setContentAnimate() + }) + }, + updateParentData() { + // 此方法在mixin中 + this.getParentData('u-collapse') + }, + async setContentAnimate() { + // 每次面板打开或者收起时,都查询元素尺寸 + // 好处是,父组件从服务端获取内容后,变更折叠面板后可以获得最新的高度 + const rect = await this.queryRect() + const height = this.expanded ? rect.height : 0 + this.animating = true + // #ifdef APP-NVUE + const ref = this.$refs['animation'].ref + animation.transition(ref, { + styles: { + height: height + 'px' + }, + duration: this.duration, + // 必须设置为true,否则会到面板收起或展开时,页面其他元素不会随之调整它们的布局 + needLayout: true, + timingFunction: 'ease-in-out', + }, () => { + this.animating = false + }) + // #endif + + // #ifndef APP-NVUE + const animation = uni.createAnimation({ + timingFunction: 'ease-in-out', + }); + animation + .height(height) + .step({ + duration: this.duration, + }) + .step() + // 导出动画数据给面板的animationData值 + this.animationData = animation.export() + // 标识动画结束 + uni.$u.sleep(this.duration).then(() => { + this.animating = false + }) + // #endif + }, + // 点击collapsehead头部 + clickHandler() { + if (this.disabled && this.animating) return + // 设置本组件为相反的状态 + this.parent && this.parent.onChange(this) + }, + // 查询内容高度 + queryRect() { + // #ifndef APP-NVUE + // $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://www.uviewui.com/js/getRect.html + // 组件内部一般用this.$uGetRect,对外的为uni.$u.getRect,二者功能一致,名称不同 + return new Promise(resolve => { + this.$uGetRect(`#${this.elId}`).then(size => { + resolve(size) + }) + }) + // #endif + + // #ifdef APP-NVUE + // nvue下,使用dom模块查询元素高度 + // 返回一个promise,让调用此方法的主体能使用then回调 + return new Promise(resolve => { + dom.getComponentRect(this.$refs[this.elId], res => { + resolve(res.size) + }) + }) + // #endif + } + }, + }; +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-collapse-item { + + &__content { + overflow: hidden; + height: 0; + + &__text { + padding: 12px 15px; + color: $u-content-color; + font-size: 14px; + line-height: 18px; + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-collapse/props.js b/uni_modules/uview-ui/components/u-collapse/props.js new file mode 100644 index 0000000..7ee6d31 --- /dev/null +++ b/uni_modules/uview-ui/components/u-collapse/props.js @@ -0,0 +1,19 @@ +export default { + props: { + // 当前展开面板的name,非手风琴模式:[<string | number>],手风琴模式:string | number + value: { + type: [String, Number, Array, null], + default: uni.$u.props.collapse.value + }, + // 是否手风琴模式 + accordion: { + type: Boolean, + default: uni.$u.props.collapse.accordion + }, + // 是否显示外边框 + border: { + type: Boolean, + default: uni.$u.props.collapse.border + } + } +} diff --git a/uni_modules/uview-ui/components/u-collapse/u-collapse.vue b/uni_modules/uview-ui/components/u-collapse/u-collapse.vue new file mode 100644 index 0000000..fc188a2 --- /dev/null +++ b/uni_modules/uview-ui/components/u-collapse/u-collapse.vue @@ -0,0 +1,90 @@ +<template> + <view class="u-collapse"> + <u-line v-if="border"></u-line> + <slot /> + </view> +</template> + +<script> + import props from './props.js'; + /** + * collapse 折叠面板 + * @description 通过折叠面板收纳内容区域 + * @tutorial https://www.uviewui.com/components/collapse.html + * @property {String | Number | Array} value 当前展开面板的name,非手风琴模式:[<string | number>],手风琴模式:string | number + * @property {Boolean} accordion 是否手风琴模式( 默认 false ) + * @property {Boolean} border 是否显示外边框 ( 默认 true ) + * @event {Function} change 当前激活面板展开时触发(如果是手风琴模式,参数activeNames类型为String,否则为Array) + * @example <u-collapse></u-collapse> + */ + export default { + name: "u-collapse", + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + watch: { + needInit() { + this.init() + } + }, + created() { + this.children = [] + }, + computed: { + needInit() { + // 通过computed,同时监听accordion和value值的变化 + // 再通过watch去执行init()方法,进行再一次的初始化 + return [this.accordion, this.value] + } + }, + watch: { + // 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件 + parentData() { + if (this.children.length) { + this.children.map(child => { + // 判断子组件(u-checkbox)如果有updateParentData方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值) + typeof(child.updateParentData) === 'function' && child.updateParentData() + }) + } + }, + }, + methods: { + // 重新初始化一次内部的所有子元素 + init() { + this.children.map(child => { + child.init() + }) + }, + /** + * collapse-item被点击时触发,由collapse统一处理各子组件的状态 + * @param {Object} target 被操作的面板的实例 + */ + onChange(target) { + let changeArr = [] + this.children.map((child, index) => { + // 如果是手风琴模式,将其他的折叠面板收起来 + if (this.accordion) { + child.expanded = child === target ? !target.expanded : false + child.setContentAnimate() + } else { + if(child === target) { + child.expanded = !child.expanded + child.setContentAnimate() + } + } + // 拼接change事件中,数组元素的状态 + changeArr.push({ + // 如果没有定义name属性,则默认返回组件的index索引 + name: child.name || index, + status: child.expanded ? 'open' : 'close' + }) + }) + + this.$emit('change', changeArr) + this.$emit(target.expanded ? 'open' : 'close', target.name) + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; +</style> diff --git a/uni_modules/uview-ui/components/u-column-notice/props.js b/uni_modules/uview-ui/components/u-column-notice/props.js new file mode 100644 index 0000000..4809154 --- /dev/null +++ b/uni_modules/uview-ui/components/u-column-notice/props.js @@ -0,0 +1,55 @@ +export default { + props: { + // 显示的内容,字符串 + text: { + type: [Array], + default: uni.$u.props.columnNotice.text + }, + // 是否显示左侧的音量图标 + icon: { + type: String, + default: uni.$u.props.columnNotice.icon + }, + // 通告模式,link-显示右箭头,closable-显示右侧关闭图标 + mode: { + type: String, + default: uni.$u.props.columnNotice.mode + }, + // 文字颜色,各图标也会使用文字颜色 + color: { + type: String, + default: uni.$u.props.columnNotice.color + }, + // 背景颜色 + bgColor: { + type: String, + default: uni.$u.props.columnNotice.bgColor + }, + // 字体大小,单位px + fontSize: { + type: [String, Number], + default: uni.$u.props.columnNotice.fontSize + }, + // 水平滚动时的滚动速度,即每秒滚动多少px(px),这有利于控制文字无论多少时,都能有一个恒定的速度 + speed: { + type: [String, Number], + default: uni.$u.props.columnNotice.speed + }, + // direction = row时,是否使用步进形式滚动 + step: { + type: Boolean, + default: uni.$u.props.columnNotice.step + }, + // 滚动一个周期的时间长,单位ms + duration: { + type: [String, Number], + default: uni.$u.props.columnNotice.duration + }, + // 是否禁止用手滑动切换 + // 目前HX2.6.11,只支持App 2.5.5+、H5 2.5.5+、支付宝小程序、字节跳动小程序 + disableTouch: { + type: Boolean, + default: uni.$u.props.columnNotice.disableTouch + } + } +} diff --git a/uni_modules/uview-ui/components/u-column-notice/u-column-notice.vue b/uni_modules/uview-ui/components/u-column-notice/u-column-notice.vue new file mode 100644 index 0000000..fc39532 --- /dev/null +++ b/uni_modules/uview-ui/components/u-column-notice/u-column-notice.vue @@ -0,0 +1,160 @@ +<template> + <view + class="u-notice" + @tap="clickHandler" + > + <slot name="icon"> + <view + class="u-notice__left-icon" + v-if="icon" + > + <u-icon + :name="icon" + :color="color" + size="19" + ></u-icon> + </view> + </slot> + <swiper + :disable-touch="disableTouch" + :vertical="step ? false : true" + circular + :interval="duration" + :autoplay="true" + class="u-notice__swiper" + @change="noticeChange" + > + <swiper-item + v-for="(item, index) in text" + :key="index" + class="u-notice__swiper__item" + > + <text + class="u-notice__swiper__item__text u-line-1" + :style="[textStyle]" + >{{ item }}</text> + </swiper-item> + </swiper> + <view + class="u-notice__right-icon" + v-if="['link', 'closable'].includes(mode)" + > + <u-icon + v-if="mode === 'link'" + name="arrow-right" + :size="17" + :color="color" + ></u-icon> + <u-icon + v-if="mode === 'closable'" + name="close" + :size="16" + :color="color" + @click="close" + ></u-icon> + </view> + </view> +</template> + +<script> + import props from './props.js'; + /** + * ColumnNotice 滚动通知中的垂直滚动 内部组件 + * @description 该组件用于滚动通告场景,是其中的垂直滚动方式 + * @tutorial https://www.uviewui.com/components/noticeBar.html + * @property {Array} text 显示的内容,字符串 + * @property {String} icon 是否显示左侧的音量图标 ( 默认 'volume' ) + * @property {String} mode 通告模式,link-显示右箭头,closable-显示右侧关闭图标 + * @property {String} color 文字颜色,各图标也会使用文字颜色 ( 默认 '#f9ae3d' ) + * @property {String} bgColor 背景颜色 ( 默认 '#fdf6ec' ) + * @property {String | Number} fontSize 字体大小,单位px ( 默认 14 ) + * @property {String | Number} speed 水平滚动时的滚动速度,即每秒滚动多少px(rpx),这有利于控制文字无论多少时,都能有一个恒定的速度 ( 默认 80 ) + * @property {Boolean} step direction = row时,是否使用步进形式滚动 ( 默认 false ) + * @property {String | Number} duration 滚动一个周期的时间长,单位ms ( 默认 1500 ) + * @property {Boolean} disableTouch 是否禁止用手滑动切换 目前HX2.6.11,只支持App 2.5.5+、H5 2.5.5+、支付宝小程序、字节跳动小程序 ( 默认 true ) + * @example + */ + export default { + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + watch: { + text: { + immediate: true, + handler(newValue, oldValue) { + if(!uni.$u.test.array(newValue)) { + uni.$u.error('noticebar组件direction为column时,要求text参数为数组形式') + } + } + } + }, + computed: { + // 文字内容的样式 + textStyle() { + let style = {} + style.color = this.color + style.fontSize = uni.$u.addUnit(this.fontSize) + return style + }, + // 垂直或者水平滚动 + vertical() { + if (this.mode == 'horizontal') return false + else return true + }, + }, + data() { + return { + index:0 + } + }, + methods: { + noticeChange(e){ + this.index = e.detail.current + }, + // 点击通告栏 + clickHandler() { + this.$emit('click', this.index) + }, + // 点击关闭按钮 + close() { + this.$emit('close') + } + } + }; +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-notice { + @include flex; + align-items: center; + justify-content: space-between; + + &__left-icon { + align-items: center; + margin-right: 5px; + } + + &__right-icon { + margin-left: 5px; + align-items: center; + } + + &__swiper { + height: 16px; + @include flex; + align-items: center; + flex: 1; + + &__item { + @include flex; + align-items: center; + overflow: hidden; + + &__text { + font-size: 14px; + color: $u-warning; + } + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-count-down/props.js b/uni_modules/uview-ui/components/u-count-down/props.js new file mode 100644 index 0000000..d62f025 --- /dev/null +++ b/uni_modules/uview-ui/components/u-count-down/props.js @@ -0,0 +1,24 @@ +export default { + props: { + // 倒计时时长,单位ms + time: { + type: [String, Number], + default: uni.$u.props.countDown.time + }, + // 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒 + format: { + type: String, + default: uni.$u.props.countDown.format + }, + // 是否自动开始倒计时 + autoStart: { + type: Boolean, + default: uni.$u.props.countDown.autoStart + }, + // 是否展示毫秒倒计时 + millisecond: { + type: Boolean, + default: uni.$u.props.countDown.millisecond + } + } +} diff --git a/uni_modules/uview-ui/components/u-count-down/u-count-down.vue b/uni_modules/uview-ui/components/u-count-down/u-count-down.vue new file mode 100644 index 0000000..b5e85a6 --- /dev/null +++ b/uni_modules/uview-ui/components/u-count-down/u-count-down.vue @@ -0,0 +1,163 @@ +<template> + <view class="u-count-down"> + <slot> + <text class="u-count-down__text">{{ formattedTime }}</text> + </slot> + </view> +</template> + +<script> + import props from './props.js'; + import { + isSameSecond, + parseFormat, + parseTimeData + } from './utils'; + /** + * u-count-down 倒计时 + * @description 该组件一般使用于某个活动的截止时间上,通过数字的变化,给用户明确的时间感受,提示用户进行某一个行为操作。 + * @tutorial https://uviewui.com/components/countDown.html + * @property {String | Number} time 倒计时时长,单位ms (默认 0 ) + * @property {String} format 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒 (默认 'HH:mm:ss' ) + * @property {Boolean} autoStart 是否自动开始倒计时 (默认 true ) + * @property {Boolean} millisecond 是否展示毫秒倒计时 (默认 false ) + * @event {Function} finish 倒计时结束时触发 + * @event {Function} change 倒计时变化时触发 + * @event {Function} start 开始倒计时 + * @event {Function} pause 暂停倒计时 + * @event {Function} reset 重设倒计时,若 auto-start 为 true,重设后会自动开始倒计时 + * @example <u-count-down :time="time"></u-count-down> + */ + export default { + name: 'u-count-down', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + timer: null, + // 各单位(天,时,分等)剩余时间 + timeData: parseTimeData(0), + // 格式化后的时间,如"03:23:21" + formattedTime: '0', + // 倒计时是否正在进行中 + runing: false, + endTime: 0, // 结束的毫秒时间戳 + remainTime: 0, // 剩余的毫秒时间 + } + }, + watch: { + time(n) { + this.reset() + } + }, + mounted() { + this.init() + }, + methods: { + init() { + this.reset() + }, + // 开始倒计时 + start() { + if (this.runing) return + // 标识为进行中 + this.runing = true + // 结束时间戳 = 此刻时间戳 + 剩余的时间 + this.endTime = Date.now() + this.remainTime + this.toTick() + }, + // 根据是否展示毫秒,执行不同操作函数 + toTick() { + if (this.millisecond) { + this.microTick() + } else { + this.macroTick() + } + }, + macroTick() { + this.clearTimeout() + // 每隔一定时间,更新一遍定时器的值 + // 同时此定时器的作用也能带来毫秒级的更新 + this.timer = setTimeout(() => { + // 获取剩余时间 + const remain = this.getRemainTime() + // 重设剩余时间 + if (!isSameSecond(remain, this.remainTime) || remain === 0) { + this.setRemainTime(remain) + } + // 如果剩余时间不为0,则继续检查更新倒计时 + if (this.remainTime !== 0) { + this.macroTick() + } + }, 30) + }, + microTick() { + this.clearTimeout() + this.timer = setTimeout(() => { + this.setRemainTime(this.getRemainTime()) + if (this.remainTime !== 0) { + this.microTick() + } + }, 50) + }, + // 获取剩余的时间 + getRemainTime() { + // 取最大值,防止出现小于0的剩余时间值 + return Math.max(this.endTime - Date.now(), 0) + }, + // 设置剩余的时间 + setRemainTime(remain) { + this.remainTime = remain + // 根据剩余的毫秒时间,得出该有天,小时,分钟等的值,返回一个对象 + const timeData = parseTimeData(remain) + this.$emit('change', timeData) + // 得出格式化后的时间 + this.formattedTime = parseFormat(this.format, timeData) + // 如果时间已到,停止倒计时 + if (remain <= 0) { + this.pause() + this.$emit('finish') + } + }, + // 重置倒计时 + reset() { + this.pause() + this.remainTime = this.time + this.setRemainTime(this.remainTime) + if (this.autoStart) { + this.start() + } + }, + // 暂停倒计时 + pause() { + this.runing = false; + this.clearTimeout() + }, + // 清空定时器 + clearTimeout() { + clearTimeout(this.timer) + this.timer = null + } + }, + beforeDestroy() { + this.clearTimeout() + } + } +</script> + +<style + lang="scss" + scoped +> + @import "../../libs/css/components.scss"; + $u-count-down-text-color:$u-content-color !default; + $u-count-down-text-font-size:15px !default; + $u-count-down-text-line-height:22px !default; + + .u-count-down { + &__text { + color: $u-count-down-text-color; + font-size: $u-count-down-text-font-size; + line-height: $u-count-down-text-line-height; + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-count-down/utils.js b/uni_modules/uview-ui/components/u-count-down/utils.js new file mode 100644 index 0000000..8c75005 --- /dev/null +++ b/uni_modules/uview-ui/components/u-count-down/utils.js @@ -0,0 +1,62 @@ +// 补0,如1 -> 01 +function padZero(num, targetLength = 2) { + let str = `${num}` + while (str.length < targetLength) { + str = `0${str}` + } + return str +} +const SECOND = 1000 +const MINUTE = 60 * SECOND +const HOUR = 60 * MINUTE +const DAY = 24 * HOUR +export function parseTimeData(time) { + const days = Math.floor(time / DAY) + const hours = Math.floor((time % DAY) / HOUR) + const minutes = Math.floor((time % HOUR) / MINUTE) + const seconds = Math.floor((time % MINUTE) / SECOND) + const milliseconds = Math.floor(time % SECOND) + return { + days, + hours, + minutes, + seconds, + milliseconds + } +} +export function parseFormat(format, timeData) { + let { + days, + hours, + minutes, + seconds, + milliseconds + } = timeData + // 如果格式化字符串中不存在DD(天),则将天的时间转为小时中去 + if (format.indexOf('DD') === -1) { + hours += days * 24 + } else { + // 对天补0 + format = format.replace('DD', padZero(days)) + } + // 其他同理于DD的格式化处理方式 + if (format.indexOf('HH') === -1) { + minutes += hours * 60 + } else { + format = format.replace('HH', padZero(hours)) + } + if (format.indexOf('mm') === -1) { + seconds += minutes * 60 + } else { + format = format.replace('mm', padZero(minutes)) + } + if (format.indexOf('ss') === -1) { + milliseconds += seconds * 1000 + } else { + format = format.replace('ss', padZero(seconds)) + } + return format.replace('SSS', padZero(milliseconds, 3)) +} +export function isSameSecond(time1, time2) { + return Math.floor(time1 / 1000) === Math.floor(time2 / 1000) +} diff --git a/uni_modules/uview-ui/components/u-count-to/props.js b/uni_modules/uview-ui/components/u-count-to/props.js new file mode 100644 index 0000000..86873c1 --- /dev/null +++ b/uni_modules/uview-ui/components/u-count-to/props.js @@ -0,0 +1,59 @@ +export default { + props: { + // 开始的数值,默认从0增长到某一个数 + startVal: { + type: [String, Number], + default: uni.$u.props.countTo.startVal + }, + // 要滚动的目标数值,必须 + endVal: { + type: [String, Number], + default: uni.$u.props.countTo.endVal + }, + // 滚动到目标数值的动画持续时间,单位为毫秒(ms) + duration: { + type: [String, Number], + default: uni.$u.props.countTo.duration + }, + // 设置数值后是否自动开始滚动 + autoplay: { + type: Boolean, + default: uni.$u.props.countTo.autoplay + }, + // 要显示的小数位数 + decimals: { + type: [String, Number], + default: uni.$u.props.countTo.decimals + }, + // 是否在即将到达目标数值的时候,使用缓慢滚动的效果 + useEasing: { + type: Boolean, + default: uni.$u.props.countTo.useEasing + }, + // 十进制分割 + decimal: { + type: [String, Number], + default: uni.$u.props.countTo.decimal + }, + // 字体颜色 + color: { + type: String, + default: uni.$u.props.countTo.color + }, + // 字体大小 + fontSize: { + type: [String, Number], + default: uni.$u.props.countTo.fontSize + }, + // 是否加粗字体 + bold: { + type: Boolean, + default: uni.$u.props.countTo.bold + }, + // 千位分隔符,类似金额的分割(¥23,321.05中的",") + separator: { + type: String, + default: uni.$u.props.countTo.separator + } + } +} diff --git a/uni_modules/uview-ui/components/u-count-to/u-count-to.vue b/uni_modules/uview-ui/components/u-count-to/u-count-to.vue new file mode 100644 index 0000000..417b732 --- /dev/null +++ b/uni_modules/uview-ui/components/u-count-to/u-count-to.vue @@ -0,0 +1,184 @@ +<template> + <text + class="u-count-num" + :style="{ + fontSize: $u.addUnit(fontSize), + fontWeight: bold ? 'bold' : 'normal', + color: color + }" + >{{ displayValue }}</text> +</template> + +<script> + import props from './props.js'; +/** + * countTo 数字滚动 + * @description 该组件一般用于需要滚动数字到某一个值的场景,目标要求是一个递增的值。 + * @tutorial https://www.uviewui.com/components/countTo.html + * @property {String | Number} startVal 开始的数值,默认从0增长到某一个数(默认 0 ) + * @property {String | Number} endVal 要滚动的目标数值,必须 (默认 0 ) + * @property {String | Number} duration 滚动到目标数值的动画持续时间,单位为毫秒(ms) (默认 2000 ) + * @property {Boolean} autoplay 设置数值后是否自动开始滚动 (默认 true ) + * @property {String | Number} decimals 要显示的小数位数,见官网说明(默认 0 ) + * @property {Boolean} useEasing 滚动结束时,是否缓动结尾,见官网说明(默认 true ) + * @property {String} decimal 十进制分割 ( 默认 "." ) + * @property {String} color 字体颜色( 默认 '#606266' ) + * @property {String | Number} fontSize 字体大小,单位px( 默认 22 ) + * @property {Boolean} bold 字体是否加粗(默认 false ) + * @property {String} separator 千位分隔符,见官网说明 + * @event {Function} end 数值滚动到目标值时触发 + * @example <u-count-to ref="uCountTo" :end-val="endVal" :autoplay="autoplay"></u-count-to> + */ +export default { + name: 'u-count-to', + data() { + return { + localStartVal: this.startVal, + displayValue: this.formatNumber(this.startVal), + printVal: null, + paused: false, // 是否暂停 + localDuration: Number(this.duration), + startTime: null, // 开始的时间 + timestamp: null, // 时间戳 + remaining: null, // 停留的时间 + rAF: null, + lastTime: 0 // 上一次的时间 + }; + }, + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + computed: { + countDown() { + return this.startVal > this.endVal; + } + }, + watch: { + startVal() { + this.autoplay && this.start(); + }, + endVal() { + this.autoplay && this.start(); + } + }, + mounted() { + this.autoplay && this.start(); + }, + methods: { + easingFn(t, b, c, d) { + return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b; + }, + requestAnimationFrame(callback) { + const currTime = new Date().getTime(); + // 为了使setTimteout的尽可能的接近每秒60帧的效果 + const timeToCall = Math.max(0, 16 - (currTime - this.lastTime)); + const id = setTimeout(() => { + callback(currTime + timeToCall); + }, timeToCall); + this.lastTime = currTime + timeToCall; + return id; + }, + cancelAnimationFrame(id) { + clearTimeout(id); + }, + // 开始滚动数字 + start() { + this.localStartVal = this.startVal; + this.startTime = null; + this.localDuration = this.duration; + this.paused = false; + this.rAF = this.requestAnimationFrame(this.count); + }, + // 暂定状态,重新再开始滚动;或者滚动状态下,暂停 + reStart() { + if (this.paused) { + this.resume(); + this.paused = false; + } else { + this.stop(); + this.paused = true; + } + }, + // 暂停 + stop() { + this.cancelAnimationFrame(this.rAF); + }, + // 重新开始(暂停的情况下) + resume() { + if (!this.remaining) return + this.startTime = 0; + this.localDuration = this.remaining; + this.localStartVal = this.printVal; + this.requestAnimationFrame(this.count); + }, + // 重置 + reset() { + this.startTime = null; + this.cancelAnimationFrame(this.rAF); + this.displayValue = this.formatNumber(this.startVal); + }, + count(timestamp) { + if (!this.startTime) this.startTime = timestamp; + this.timestamp = timestamp; + const progress = timestamp - this.startTime; + this.remaining = this.localDuration - progress; + if (this.useEasing) { + if (this.countDown) { + this.printVal = this.localStartVal - this.easingFn(progress, 0, this.localStartVal - this.endVal, this.localDuration); + } else { + this.printVal = this.easingFn(progress, this.localStartVal, this.endVal - this.localStartVal, this.localDuration); + } + } else { + if (this.countDown) { + this.printVal = this.localStartVal - (this.localStartVal - this.endVal) * (progress / this.localDuration); + } else { + this.printVal = this.localStartVal + (this.endVal - this.localStartVal) * (progress / this.localDuration); + } + } + if (this.countDown) { + this.printVal = this.printVal < this.endVal ? this.endVal : this.printVal; + } else { + this.printVal = this.printVal > this.endVal ? this.endVal : this.printVal; + } + this.displayValue = this.formatNumber(this.printVal) || 0; + if (progress < this.localDuration) { + this.rAF = this.requestAnimationFrame(this.count); + } else { + this.$emit('end'); + } + }, + // 判断是否数字 + isNumber(val) { + return !isNaN(parseFloat(val)); + }, + formatNumber(num) { + // 将num转为Number类型,因为其值可能为字符串数值,调用toFixed会报错 + num = Number(num); + num = num.toFixed(Number(this.decimals)); + num += ''; + const x = num.split('.'); + let x1 = x[0]; + const x2 = x.length > 1 ? this.decimal + x[1] : ''; + const rgx = /(\d+)(\d{3})/; + if (this.separator && !this.isNumber(this.separator)) { + while (rgx.test(x1)) { + x1 = x1.replace(rgx, '$1' + this.separator + '$2'); + } + } + return x1 + x2; + }, + destroyed() { + this.cancelAnimationFrame(this.rAF); + } + } +}; +</script> + +<style lang="scss" scoped> +@import "../../libs/css/components.scss"; + +.u-count-num { + /* #ifndef APP-NVUE */ + display: inline-flex; + /* #endif */ + text-align: center; +} +</style> diff --git a/uni_modules/uview-ui/components/u-datetime-picker/props.js b/uni_modules/uview-ui/components/u-datetime-picker/props.js new file mode 100644 index 0000000..f44c0f9 --- /dev/null +++ b/uni_modules/uview-ui/components/u-datetime-picker/props.js @@ -0,0 +1,116 @@ +export default { + props: { + // 是否打开组件 + show: { + type: Boolean, + default: uni.$u.props.datetimePicker.show + }, + // 是否展示顶部的操作栏 + showToolbar: { + type: Boolean, + default: uni.$u.props.datetimePicker.showToolbar + }, + // 绑定值 + value: { + type: [String, Number], + default: uni.$u.props.datetimePicker.value + }, + // 顶部标题 + title: { + type: String, + default: uni.$u.props.datetimePicker.title + }, + // 展示格式,mode=date为日期选择,mode=time为时间选择,mode=year-month为年月选择,mode=datetime为日期时间选择 + mode: { + type: String, + default: uni.$u.props.datetimePicker.mode + }, + // 可选的最大时间 + maxDate: { + type: Number, + // 最大默认值为后10年 + default: uni.$u.props.datetimePicker.maxDate + }, + // 可选的最小时间 + minDate: { + type: Number, + // 最小默认值为前10年 + default: uni.$u.props.datetimePicker.minDate + }, + // 可选的最小小时,仅mode=time有效 + minHour: { + type: Number, + default: uni.$u.props.datetimePicker.minHour + }, + // 可选的最大小时,仅mode=time有效 + maxHour: { + type: Number, + default: uni.$u.props.datetimePicker.maxHour + }, + // 可选的最小分钟,仅mode=time有效 + minMinute: { + type: Number, + default: uni.$u.props.datetimePicker.minMinute + }, + // 可选的最大分钟,仅mode=time有效 + maxMinute: { + type: Number, + default: uni.$u.props.datetimePicker.maxMinute + }, + // 选项过滤函数 + filter: { + type: [Function, null], + default: uni.$u.props.datetimePicker.filter + }, + // 选项格式化函数 + formatter: { + type: [Function, null], + default: uni.$u.props.datetimePicker.formatter + }, + // 是否显示加载中状态 + loading: { + type: Boolean, + default: uni.$u.props.datetimePicker.loading + }, + // 各列中,单个选项的高度 + itemHeight: { + type: [String, Number], + default: uni.$u.props.datetimePicker.itemHeight + }, + // 取消按钮的文字 + cancelText: { + type: String, + default: uni.$u.props.datetimePicker.cancelText + }, + // 确认按钮的文字 + confirmText: { + type: String, + default: uni.$u.props.datetimePicker.confirmText + }, + // 取消按钮的颜色 + cancelColor: { + type: String, + default: uni.$u.props.datetimePicker.cancelColor + }, + // 确认按钮的颜色 + confirmColor: { + type: String, + default: uni.$u.props.datetimePicker.confirmColor + }, + // 每列中可见选项的数量 + visibleItemCount: { + type: [String, Number], + default: uni.$u.props.datetimePicker.visibleItemCount + }, + // 是否允许点击遮罩关闭选择器 + closeOnClickOverlay: { + type: Boolean, + default: uni.$u.props.datetimePicker.closeOnClickOverlay + }, + // 各列的默认索引 + defaultIndex: { + type: Array, + default: uni.$u.props.datetimePicker.defaultIndex + } + } +} diff --git a/uni_modules/uview-ui/components/u-datetime-picker/u-datetime-picker.vue b/uni_modules/uview-ui/components/u-datetime-picker/u-datetime-picker.vue new file mode 100644 index 0000000..18d8dcc --- /dev/null +++ b/uni_modules/uview-ui/components/u-datetime-picker/u-datetime-picker.vue @@ -0,0 +1,360 @@ +<template> + <u-picker + ref="picker" + :show="show" + :closeOnClickOverlay="closeOnClickOverlay" + :columns="columns" + :title="title" + :itemHeight="itemHeight" + :showToolbar="showToolbar" + :visibleItemCount="visibleItemCount" + :defaultIndex="innerDefaultIndex" + :cancelText="cancelText" + :confirmText="confirmText" + :cancelColor="cancelColor" + :confirmColor="confirmColor" + @close="close" + @cancel="cancel" + @confirm="confirm" + @change="change" + > + </u-picker> +</template> + +<script> + function times(n, iteratee) { + let index = -1 + const result = Array(n < 0 ? 0 : n) + while (++index < n) { + result[index] = iteratee(index) + } + return result + } + import props from './props.js'; + import dayjs from '../../libs/util/dayjs.js'; + /** + * DatetimePicker 时间日期选择器 + * @description 此选择器用于时间日期 + * @tutorial https://www.uviewui.com/components/datetimePicker.html + * @property {Boolean} show 用于控制选择器的弹出与收起 ( 默认 false ) + * @property {Boolean} showToolbar 是否显示顶部的操作栏 ( 默认 true ) + * @property {String | Number} value 绑定值 + * @property {String} title 顶部标题 + * @property {String} mode 展示格式 mode=date为日期选择,mode=time为时间选择,mode=year-month为年月选择,mode=datetime为日期时间选择 ( 默认 ‘datetime ) + * @property {Number} maxDate 可选的最大时间 默认值为后10年 + * @property {Number} minDate 可选的最小时间 默认值为前10年 + * @property {Number} minHour 可选的最小小时,仅mode=time有效 ( 默认 0 ) + * @property {Number} maxHour 可选的最大小时,仅mode=time有效 ( 默认 23 ) + * @property {Number} minMinute 可选的最小分钟,仅mode=time有效 ( 默认 0 ) + * @property {Number} maxMinute 可选的最大分钟,仅mode=time有效 ( 默认 59 ) + * @property {Function} filter 选项过滤函数 + * @property {Function} formatter 选项格式化函数 + * @property {Boolean} loading 是否显示加载中状态 ( 默认 false ) + * @property {String | Number} itemHeight 各列中,单个选项的高度 ( 默认 44 ) + * @property {String} cancelText 取消按钮的文字 ( 默认 '取消' ) + * @property {String} confirmText 确认按钮的文字 ( 默认 '确认' ) + * @property {String} cancelColor 取消按钮的颜色 ( 默认 '#909193' ) + * @property {String} confirmColor 确认按钮的颜色 ( 默认 '#3c9cff' ) + * @property {String | Number} visibleItemCount 每列中可见选项的数量 ( 默认 5 ) + * @property {Boolean} closeOnClickOverlay 是否允许点击遮罩关闭选择器 ( 默认 false ) + * @property {Array} defaultIndex 各列的默认索引 + * @event {Function} close 关闭选择器时触发 + * @event {Function} confirm 点击确定按钮,返回当前选择的值 + * @event {Function} change 当选择值变化时触发 + * @event {Function} cancel 点击取消按钮 + * @example <u-datetime-picker :show="show" :value="value1" mode="datetime" ></u-datetime-picker> + */ + export default { + name: 'datetime-picker', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + columns: [], + innerDefaultIndex: [], + innerFormatter: (type, value) => value + } + }, + watch: { + show(newValue, oldValue) { + if (newValue) { + this.updateColumnValue(this.innerValue) + } + }, + propsChange() { + this.init() + } + }, + computed: { + // 如果以下这些变量发生了变化,意味着需要重新初始化各列的值 + propsChange() { + return [this.mode, this.maxDate, this.minDate, this.minHour, this.maxHour, this.minMinute, this.maxMinute, this.filter, ] + } + }, + mounted() { + this.init() + }, + methods: { + init() { + this.innerValue = this.correctValue(this.value) + this.updateColumnValue(this.innerValue) + }, + // 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用 + setFormatter(e) { + this.innerFormatter = e + }, + // 关闭选择器 + close() { + if (this.closeOnClickOverlay) { + this.$emit('close') + } + }, + // 点击工具栏的取消按钮 + cancel() { + this.$emit('cancel') + }, + // 点击工具栏的确定按钮 + confirm() { + this.$emit('confirm', { + value: this.innerValue, + mode: this.mode + }) + this.$emit('input', this.innerValue) + }, + //用正则截取输出值,当出现多组数字时,抛出错误 + intercept(e,type){ + let judge = e.match(/\d+/g) + //判断是否掺杂数字 + if(judge.length>1){ + uni.$u.error("请勿在过滤或格式化函数时添加数字") + return 0 + }else if(type&&judge[0].length==4){//判断是否是年份 + return judge[0] + }else if(judge[0].length>2){ + uni.$u.error("请勿在过滤或格式化函数时添加数字") + return 0 + }else{ + return judge[0] + } + }, + // 列发生变化时触发 + change(e) { + const { indexs, values } = e + let selectValue = '' + if(this.mode === 'time') { + // 根据value各列索引,从各列数组中,取出当前时间的选中值 + selectValue = `${this.intercept(values[0][indexs[0]])}:${this.intercept(values[1][indexs[1]])}` + } else { + // 将选择的值转为数值,比如'03'转为数值的3,'2019'转为数值的2019 + const year = parseInt(this.intercept(values[0][indexs[0]],'year')) + const month = parseInt(this.intercept(values[1][indexs[1]])) + let date = parseInt(values[2] ? this.intercept(values[2][indexs[2]]) : 1) + let hour = 0, minute = 0 + // 此月份的最大天数 + const maxDate = dayjs(`${year}-${month}`).daysInMonth() + // year-month模式下,date不会出现在列中,设置为1,为了符合后边需要减1的需求 + if (this.mode === 'year-month') { + date = 1 + } + // 不允许超过maxDate值 + date = Math.min(maxDate, date) + if (this.mode === 'datetime') { + hour = parseInt(this.intercept(values[3][indexs[3]])) + minute = parseInt(this.intercept(values[4][indexs[4]])) + } + // 转为时间模式 + selectValue = Number(new Date(year, month - 1, date, hour, minute)) + } + // 取出准确的合法值,防止超越边界的情况 + selectValue = this.correctValue(selectValue) + this.innerValue = selectValue + this.updateColumnValue(selectValue) + // 发出change时间,value为当前选中的时间戳 + this.$emit('change', { + value: selectValue, + // #ifndef MP-WEIXIN + // 微信小程序不能传递this实例,会因为循环引用而报错 + picker: this.$refs.picker, + // #endif + mode: this.mode + }) + }, + // 更新各列的值,进行补0、格式化等操作 + updateColumnValue(value) { + this.innerValue = value + this.updateColumns() + this.updateIndexs(value) + }, + // 更新索引 + updateIndexs(value) { + let values = [] + const formatter = this.formatter || this.innerFormatter + const padZero = uni.$u.padZero + if (this.mode === 'time') { + // 将time模式的时间用:分隔成数组 + const timeArr = value.split(':') + // 使用formatter格式化方法进行管道处理 + values = [formatter('hour', timeArr[0]), formatter('minute', timeArr[1])] + } else { + const date = new Date(value) + values = [ + formatter('year', `${dayjs(value).year()}`), + // 月份补0 + formatter('month', padZero(dayjs(value).month() + 1)) + ] + if (this.mode === 'date') { + // date模式,需要添加天列 + values.push(formatter('day', padZero(dayjs(value).date()))) + } + if (this.mode === 'datetime') { + // 数组的push方法,可以写入多个参数 + values.push(formatter('day', padZero(dayjs(value).date())), formatter('hour', padZero(dayjs(value).hour())), formatter('minute', padZero(dayjs(value).minute()))) + } + } + + // 根据当前各列的所有值,从各列默认值中找到默认值在各列中的索引 + const indexs = this.columns.map((column, index) => { + // 通过取大值,可以保证不会出现找不到索引的-1情况 + return Math.max(0, column.findIndex(item => item === values[index])) + }) + this.innerDefaultIndex = indexs + }, + // 更新各列的值 + updateColumns() { + const formatter = this.formatter || this.innerFormatter + // 获取各列的值,并且map后,对各列的具体值进行补0操作 + const results = this.getOriginColumns().map((column) => column.values.map((value) => formatter(column.type, value))) + this.columns = results + }, + getOriginColumns() { + // 生成各列的值 + const results = this.getRanges().map(({ type, range }) => { + let values = times(range[1] - range[0] + 1, (index) => { + let value = range[0] + index + value = type === 'year' ? `${value}` : uni.$u.padZero(value) + return value + }) + // 进行过滤 + if (this.filter) { + values = this.filter(type, values) + } + return { type, values } + }) + return results + }, + // 通过最大值和最小值生成数组 + generateArray(start, end) { + return Array.from(new Array(end + 1).keys()).slice(start) + }, + // 得出合法的时间 + correctValue(value) { + const isDateMode = this.mode !== 'time' + if (isDateMode && !uni.$u.test.date(value)) { + // 如果是日期类型,但是又没有设置合法的当前时间的话,使用最小时间为当前时间 + value = this.minDate + } else if (!isDateMode && !value) { + // 如果是时间类型,而又没有默认值的话,就用最小时间 + value = `${uni.$u.padZero(this.minHour)}:${uni.$u.padZero(this.minMinute)}` + } + // 时间类型 + if (!isDateMode) { + if (String(value).indexOf(':') === -1) return uni.$u.error('时间错误,请传递如12:24的格式') + let [hour, minute] = value.split(':') + // 对时间补零,同时控制在最小值和最大值之间 + hour = uni.$u.padZero(uni.$u.range(this.minHour, this.maxHour, Number(hour))) + minute = uni.$u.padZero(uni.$u.range(this.minMinute, this.maxMinute, Number(minute))) + return `${ hour }:${ minute }` + } else { + // 如果是日期格式,控制在最小日期和最大日期之间 + value = dayjs(value).isBefore(dayjs(this.minDate)) ? this.minDate : value + value = dayjs(value).isAfter(dayjs(this.maxDate)) ? this.maxDate : value + return value + } + }, + // 获取每列的最大和最小值 + getRanges() { + if (this.mode === 'time') { + return [ + { + type: 'hour', + range: [this.minHour, this.maxHour], + }, + { + type: 'minute', + range: [this.minMinute, this.maxMinute], + }, + ]; + } + const { maxYear, maxDate, maxMonth, maxHour, maxMinute, } = this.getBoundary('max', this.innerValue); + const { minYear, minDate, minMonth, minHour, minMinute, } = this.getBoundary('min', this.innerValue); + const result = [ + { + type: 'year', + range: [minYear, maxYear], + }, + { + type: 'month', + range: [minMonth, maxMonth], + }, + { + type: 'day', + range: [minDate, maxDate], + }, + { + type: 'hour', + range: [minHour, maxHour], + }, + { + type: 'minute', + range: [minMinute, maxMinute], + }, + ]; + if (this.mode === 'date') + result.splice(3, 2); + if (this.mode === 'year-month') + result.splice(2, 3); + return result; + }, + // 根据minDate、maxDate、minHour、maxHour等边界值,判断各列的开始和结束边界值 + getBoundary(type, innerValue) { + const value = new Date(innerValue) + const boundary = new Date(this[`${type}Date`]) + const year = dayjs(boundary).year() + let month = 1 + let date = 1 + let hour = 0 + let minute = 0 + if (type === 'max') { + month = 12 + // 月份的天数 + date = dayjs(value).daysInMonth() + hour = 23 + minute = 59 + } + // 获取边界值,逻辑是:当年达到了边界值(最大或最小年),就检查月允许的最大和最小值,以此类推 + if (dayjs(value).year() === year) { + month = dayjs(boundary).month() + 1 + if (dayjs(value).month() + 1 === month) { + date = dayjs(boundary).date() + if (dayjs(value).date() === date) { + hour = dayjs(boundary).hour() + if (dayjs(value).hour() === hour) { + minute = dayjs(boundary).minute() + } + } + } + } + return { + [`${type}Year`]: year, + [`${type}Month`]: month, + [`${type}Date`]: date, + [`${type}Hour`]: hour, + [`${type}Minute`]: minute + } + }, + }, + } +</script> + +<style lang="scss" scoped> + @import '../../libs/css/components.scss'; +</style> diff --git a/uni_modules/uview-ui/components/u-divider/props.js b/uni_modules/uview-ui/components/u-divider/props.js new file mode 100644 index 0000000..1fa8359 --- /dev/null +++ b/uni_modules/uview-ui/components/u-divider/props.js @@ -0,0 +1,44 @@ +export default { + props: { + // 是否虚线 + dashed: { + type: Boolean, + default: uni.$u.props.divider.dashed + }, + // 是否细线 + hairline: { + type: Boolean, + default: uni.$u.props.divider.hairline + }, + // 是否以点替代文字,优先于text字段起作用 + dot: { + type: Boolean, + default: uni.$u.props.divider.dot + }, + // 内容文本的位置,left-左边,center-中间,right-右边 + textPosition: { + type: String, + default: uni.$u.props.divider.textPosition + }, + // 文本内容 + text: { + type: [String, Number], + default: uni.$u.props.divider.text + }, + // 文本大小 + textSize: { + type: [String, Number], + default: uni.$u.props.divider.textSize + }, + // 文本颜色 + textColor: { + type: String, + default: uni.$u.props.divider.textColor + }, + // 线条颜色 + lineColor: { + type: String, + default: uni.$u.props.divider.lineColor + } + } +} diff --git a/uni_modules/uview-ui/components/u-divider/u-divider.vue b/uni_modules/uview-ui/components/u-divider/u-divider.vue new file mode 100644 index 0000000..b629da6 --- /dev/null +++ b/uni_modules/uview-ui/components/u-divider/u-divider.vue @@ -0,0 +1,116 @@ +<template> + <view + class="u-divider" + :style="[$u.addStyle(customStyle)]" + @tap="click" + > + <u-line + :color="lineColor" + :customStyle="leftLineStyle" + :hairline="hairline" + :dashed="dashed" + ></u-line> + <text + v-if="dot" + class="u-divider__dot" + >●</text> + <text + v-else-if="text" + class="u-divider__text" + :style="[textStyle]" + >{{text}}</text> + <u-line + :color="lineColor" + :customStyle="rightLineStyle" + :hairline="hairline" + :dashed="dashed" + ></u-line> + </view> +</template> + +<script> + import props from './props.js'; + /** + * divider 分割线 + * @description 区隔内容的分割线,一般用于页面底部"没有更多"的提示。 + * @tutorial https://www.uviewui.com/components/divider.html + * @property {Boolean} dashed 是否虚线 (默认 false ) + * @property {Boolean} hairline 是否细线 (默认 true ) + * @property {Boolean} dot 是否以点替代文字,优先于text字段起作用 (默认 false ) + * @property {String} textPosition 内容文本的位置,left-左边,center-中间,right-右边 (默认 'center' ) + * @property {String | Number} text 文本内容 + * @property {String | Number} textSize 文本大小 (默认 14) + * @property {String} textColor 文本颜色 (默认 '#909399' ) + * @property {String} lineColor 线条颜色 (默认 '#dcdfe6' ) + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @event {Function} click divider组件被点击时触发 + * @example <u-divider :color="color">锦瑟无端五十弦</u-divider> + */ + export default { + name:'u-divider', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + computed: { + textStyle() { + const style = {} + style.fontSize = uni.$u.addUnit(this.textSize) + style.color = this.textColor + return style + }, + // 左边线条的的样式 + leftLineStyle() { + const style = {} + // 如果是在左边,设置左边的宽度为固定值 + if (this.textPosition === 'left') { + style.width = '80rpx' + } else { + style.flex = 1 + } + return style + }, + // 右边线条的的样式 + rightLineStyle() { + const style = {} + // 如果是在右边,设置右边的宽度为固定值 + if (this.textPosition === 'right') { + style.width = '80rpx' + } else { + style.flex = 1 + } + return style + } + }, + methods: { + // divider组件被点击时触发 + click() { + this.$emit('click'); + } + } + } +</script> + +<style lang="scss" scoped> + @import '../../libs/css/components.scss'; + $u-divider-margin:15px 0 !default; + $u-divider-text-margin:0 15px !default; + $u-divider-dot-font-size:12px !default; + $u-divider-dot-margin:0 12px !default; + $u-divider-dot-color: #c0c4cc !default; + + .u-divider { + @include flex; + flex-direction: row; + align-items: center; + margin: $u-divider-margin; + + &__text { + margin: $u-divider-text-margin; + } + + &__dot { + font-size: $u-divider-dot-font-size; + margin: $u-divider-dot-margin; + color: $u-divider-dot-color; + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-dropdown-item/props.js b/uni_modules/uview-ui/components/u-dropdown-item/props.js new file mode 100644 index 0000000..501a1f0 --- /dev/null +++ b/uni_modules/uview-ui/components/u-dropdown-item/props.js @@ -0,0 +1,36 @@ +export default { + props: { + // 当前选中项的value值 + value: { + type: [Number, String, Array], + default: '' + }, + // 菜单项标题 + title: { + type: [String, Number], + default: '' + }, + // 选项数据,如果传入了默认slot,此参数无效 + options: { + type: Array, + default() { + return [] + } + }, + // 是否禁用此菜单项 + disabled: { + type: Boolean, + default: false + }, + // 下拉弹窗的高度 + height: { + type: [Number, String], + default: 'auto' + }, + // 点击遮罩是否可以收起弹窗 + closeOnClickOverlay: { + type: Boolean, + default: true + } + } +} diff --git a/uni_modules/uview-ui/components/u-dropdown-item/u-dropdown-item.vue b/uni_modules/uview-ui/components/u-dropdown-item/u-dropdown-item.vue new file mode 100644 index 0000000..07de583 --- /dev/null +++ b/uni_modules/uview-ui/components/u-dropdown-item/u-dropdown-item.vue @@ -0,0 +1,146 @@ +<template> + <view class="u-drawdown-item"> + <u-overlay + customStyle="top: 126px" + :show="show" + :closeOnClickOverlay="closeOnClickOverlay" + @click="overlayClick" + ></u-overlay> + <view + class="u-drawdown-item__content" + :style="[style]" + :animation="animationData" + ref="animation" + > + <slot /> + </view> + </view> +</template> + +<script> + // #ifdef APP-NVUE + const animation = uni.requireNativePlugin('animation') + const dom = uni.requireNativePlugin('dom') + // #endif + import props from './props.js'; + /** + * Drawdownitem + * @description + * @tutorial url + * @property {String} + * @event {Function} + * @example + */ + export default { + name: 'u-drawdown-item', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + show: false, + top: '126px', + // uni.createAnimation的导出数据 + animationData: {}, + } + }, + mounted() { + this.init() + }, + watch: { + // 发生变化时,需要去更新父组件对应的值 + dataChange(newValue, oldValue) { + this.updateParentData() + } + }, + computed: { + // 监听对应变量的变化 + dataChange() { + return [this.title, this.disabled] + }, + style() { + const style = { + zIndex: 10071, + position: 'fixed', + display: 'flex', + left: 0, + right: 0 + } + style.top = uni.$u.addUnit(this.top) + return style + } + }, + methods: { + init() { + this.updateParentData() + }, + // 更新父组件所需的数据 + updateParentData() { + // 获取父组件u-dropdown + this.getParentData('u-dropdown') + if (!this.parent) uni.$u.error('u-dropdown-item必须配合u-dropdown使用') + // 查找父组件menuList数组中对应的标题数据 + const menuIndex = this.parent.menuList.findIndex(item => item.title === this.title) + const menuContent = { + title: this.title, + disabled: this.disabled + } + if (menuIndex >= 0) { + // 如果能找到,则直接修改 + this.parent.menuList[menuIndex] = menuContent; + } else { + // 如果无法找到,则为第一次添加,直接push即可 + this.parent.menuList.push(menuContent); + } + }, + async setContentAnimate(height) { + this.animating = true + // #ifdef APP-NVUE + const ref = this.$refs['animation'].ref + animation.transition(ref, { + styles: { + height: uni.$u.addUnit(height) + }, + duration: this.duration, + timingFunction: 'ease-in-out', + }, () => { + this.animating = false + }) + // #endif + + // #ifndef APP-NVUE + const animation = uni.createAnimation({ + timingFunction: 'ease-in-out', + }); + animation + .height(height) + .step({ + duration: this.duration, + }) + .step() + // 导出动画数据给面板的animationData值 + this.animationData = animation.export() + // 标识动画结束 + uni.$u.sleep(this.duration).then(() => { + this.animating = false + }) + // #endif + }, + overlayClick() { + this.show = false + this.setContentAnimate(0) + } + }, + } +</script> + +<style lang="scss" scoped> + @import '../../libs/css/components.scss'; + + .u-drawdown-item { + + &__content { + background-color: #FFFFFF; + overflow: hidden; + height: 0; + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-dropdown/props.js b/uni_modules/uview-ui/components/u-dropdown/props.js new file mode 100644 index 0000000..5f8465e --- /dev/null +++ b/uni_modules/uview-ui/components/u-dropdown/props.js @@ -0,0 +1,65 @@ +export default { + props: { + // 标题选中时的样式 + activeStyle: { + type: [String, Object], + default: () => ({ + color: '#2979ff', + fontSize: '14px' + }) + }, + // 标题未选中时的样式 + inactiveStyle: { + type: [String, Object], + default: () => ({ + color: '#606266', + fontSize: '14px' + }) + }, + // 点击遮罩是否关闭菜单 + closeOnClickMask: { + type: Boolean, + default: true + }, + // 点击当前激活项标题是否关闭菜单 + closeOnClickSelf: { + type: Boolean, + default: true + }, + // 过渡时间 + duration: { + type: [Number, String], + default: 300 + }, + // 标题菜单的高度 + height: { + type: [Number, String], + default: 40 + }, + // 是否显示下边框 + borderBottom: { + type: Boolean, + default: false + }, + // 标题的字体大小 + titleSize: { + type: [Number, String], + default: 14 + }, + // 下拉出来的内容部分的圆角值 + borderRadius: { + type: [Number, String], + default: 0 + }, + // 菜单右侧的icon图标 + menuIcon: { + type: String, + default: 'arrow-down' + }, + // 菜单右侧图标的大小 + menuIconSize: { + type: [Number, String], + default: 14 + } + } +} diff --git a/uni_modules/uview-ui/components/u-dropdown/u-dropdown.vue b/uni_modules/uview-ui/components/u-dropdown/u-dropdown.vue new file mode 100644 index 0000000..9c50bfe --- /dev/null +++ b/uni_modules/uview-ui/components/u-dropdown/u-dropdown.vue @@ -0,0 +1,127 @@ +<template> + <view class="u-drawdown"> + <view + class="u-dropdown__menu" + :style="{ + height: $u.addUnit(height) + }" + ref="u-dropdown__menu" + > + <view + class="u-dropdown__menu__item" + v-for="(item, index) in menuList" + :key="index" + @tap.stop="clickHandler(item, index)" + > + <view class="u-dropdown__menu__item__content"> + <text + class="u-dropdown__menu__item__content__text" + :style="[index === current ? activeStyle : inactiveStyle]" + >{{item.title}}</text> + <view + class="u-dropdown__menu__item__content__arrow" + :class="[index === current && 'u-dropdown__menu__item__content__arrow--rotate']" + > + <u-icon + :name="menuIcon" + :size="$u.addUnit(menuIconSize)" + ></u-icon> + </view> + </view> + </view> + </view> + <view class="u-dropdown__content"> + <slot /> + </view> + </view> +</template> + +<script> + import props from './props.js'; + /** + * Dropdown + * @description + * @tutorial url + * @property {String} + * @event {Function} + * @example + */ + export default { + name: 'u-dropdown', + mixins: [uni.$u.mixin, props], + data() { + return { + // �˵����� + menuList: [], + current: 0 + } + }, + computed: { + + }, + created() { + // �������������(u-dropdown-item)��this��������data��������������������С��������ѭ�����ö����� + this.children = []; + }, + methods: { + clickHandler(item, index) { + this.children.map(child => { + if(child.title === item.title) { + // this.queryRect('u-dropdown__menu').then(size => { + child.$emit('click') + child.setContentAnimate(child.show ? 0 : 300) + child.show = !child.show + // }) + } else { + child.show = false + child.setContentAnimate(0) + } + }) + }, + // ��ȡ��ǩ�ijߴ�λ�� + queryRect(el) { + // #ifndef APP-NVUE + // $uGetRectΪuView�Դ��Ľڵ��ѯ����������ĵ����ܣ�https://www.uviewui.com/js/getRect.html + // ����ڲ�һ����this.$uGetRect�������Ϊthis.$u.getRect�����߹���һ�£����Ʋ�ͬ + return new Promise(resolve => { + this.$uGetRect(`.${el}`).then(size => { + resolve(size) + }) + }) + // #endif + + // #ifdef APP-NVUE + // nvue�£�ʹ��domģ���ѯԪ�ظ߶� + // ����һ��promise���õ��ô˷�����������ʹ��then�ص� + return new Promise(resolve => { + dom.getComponentRect(this.$refs[el], res => { + resolve(res.size) + }) + }) + // #endif + }, + }, + } +</script> + +<style lang="scss"> + @import '../../libs/css/components.scss'; + + .u-dropdown { + + &__menu { + @include flex; + + &__item { + flex: 1; + @include flex; + justify-content: center; + + &__content { + @include flex; + align-items: center; + } + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-empty/props.js b/uni_modules/uview-ui/components/u-empty/props.js new file mode 100644 index 0000000..78662f8 --- /dev/null +++ b/uni_modules/uview-ui/components/u-empty/props.js @@ -0,0 +1,59 @@ +export default { + props: { + // 内置图标名称,或图片路径,建议绝对路径 + icon: { + type: String, + default: uni.$u.props.empty.icon + }, + // 提示文字 + text: { + type: String, + default: uni.$u.props.empty.text + }, + // 文字颜色 + textColor: { + type: String, + default: uni.$u.props.empty.textColor + }, + // 文字大小 + textSize: { + type: [String, Number], + default: uni.$u.props.empty.textSize + }, + // 图标的颜色 + iconColor: { + type: String, + default: uni.$u.props.empty.iconColor + }, + // 图标的大小 + iconSize: { + type: [String, Number], + default: uni.$u.props.empty.iconSize + }, + // 选择预置的图标类型 + mode: { + type: String, + default: uni.$u.props.empty.mode + }, + // 图标宽度,单位px + width: { + type: [String, Number], + default: uni.$u.props.empty.width + }, + // 图标高度,单位px + height: { + type: [String, Number], + default: uni.$u.props.empty.height + }, + // 是否显示组件 + show: { + type: Boolean, + default: uni.$u.props.empty.show + }, + // 组件距离上一个元素之间的距离,默认px单位 + marginTop: { + type: [String, Number], + default: uni.$u.props.empty.marginTop + } + } +} diff --git a/uni_modules/uview-ui/components/u-empty/u-empty.vue b/uni_modules/uview-ui/components/u-empty/u-empty.vue new file mode 100644 index 0000000..03d6a27 --- /dev/null +++ b/uni_modules/uview-ui/components/u-empty/u-empty.vue @@ -0,0 +1,128 @@ +<template> + <view + class="u-empty" + :style="[emptyStyle]" + v-if="show" + > + <u-icon + v-if="!isSrc" + :name="mode === 'message' ? 'chat' : `empty-${mode}`" + :size="iconSize" + :color="iconColor" + margin-top="14" + ></u-icon> + <image + v-else + :style="{ + width: $u.addUnit(width), + height: $u.addUnit(height), + }" + :src="icon" + mode="widthFix" + ></image> + <text + class="u-empty__text" + :style="[textStyle]" + >{{text ? text : icons[mode]}}</text> + <view class="u-empty__wrap" v-if="$slots.default || $slots.$default"> + <slot /> + </view> + </view> +</template> + +<script> + import props from './props.js'; + + /** + * empty 内容为空 + * @description 该组件用于需要加载内容,但是加载的第一页数据就为空,提示一个"没有内容"的场景, 我们精心挑选了十几个场景的图标,方便您使用。 + * @tutorial https://www.uviewui.com/components/empty.html + * @property {String} icon 内置图标名称,或图片路径,建议绝对路径 + * @property {String} text 提示文字 + * @property {String} textColor 文字颜色 (默认 '#c0c4cc' ) + * @property {String | Number} textSize 文字大小 (默认 14 ) + * @property {String} iconColor 图标的颜色 (默认 '#c0c4cc' ) + * @property {String | Number} iconSize 图标的大小 (默认 90 ) + * @property {String} mode 选择预置的图标类型 (默认 'data' ) + * @property {String | Number} width 图标宽度,单位px (默认 160 ) + * @property {String | Number} height 图标高度,单位px (默认 160 ) + * @property {Boolean} show 是否显示组件 (默认 true ) + * @property {String | Number} marginTop 组件距离上一个元素之间的距离,默认px单位 (默认 0 ) + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @event {Function} click 点击组件时触发 + * @event {Function} close 点击关闭按钮时触发 + * @example <u-empty text="所谓伊人,在水一方" mode="list"></u-empty> + */ + export default { + name: "u-empty", + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + icons: { + car: '购物车为空', + page: '页面不存在', + search: '没有搜索结果', + address: '没有收货地址', + wifi: '没有WiFi', + order: '订单为空', + coupon: '没有优惠券', + favor: '暂无收藏', + permission: '无权限', + history: '无历史记录', + news: '无新闻列表', + message: '消息列表为空', + list: '列表为空', + data: '数据为空', + comment: '暂无评论', + } + } + }, + computed: { + // 组件样式 + emptyStyle() { + const style = {} + style.marginTop = uni.$u.addUnit(this.marginTop) + // 合并customStyle样式,此参数通过mixin中的props传递 + return uni.$u.deepMerge(uni.$u.addStyle(this.customStyle), style) + }, + // 文本样式 + textStyle() { + const style = {} + style.color = this.textColor + style.fontSize = uni.$u.addUnit(this.textSize) + return style + }, + // 判断icon是否图片路径 + isSrc() { + return this.icon.indexOf('/') >= 0 + } + } + } +</script> + +<style lang="scss" scoped> + @import '../../libs/css/components.scss'; + $u-empty-text-margin-top:20rpx !default; + $u-empty-slot-margin-top:20rpx !default; + + .u-empty { + @include flex; + flex-direction: column; + justify-content: center; + align-items: center; + + &__text { + @include flex; + justify-content: center; + align-items: center; + margin-top: $u-empty-text-margin-top; + } + } + .u-slot-wrap { + @include flex; + justify-content: center; + align-items: center; + margin-top:$u-empty-slot-margin-top; + } +</style> diff --git a/uni_modules/uview-ui/components/u-form-item/props.js b/uni_modules/uview-ui/components/u-form-item/props.js new file mode 100644 index 0000000..53a0191 --- /dev/null +++ b/uni_modules/uview-ui/components/u-form-item/props.js @@ -0,0 +1,43 @@ +export default { + props: { + // input的label提示语 + label: { + type: String, + default: uni.$u.props.formItem.label + }, + // 绑定的值 + prop: { + type: String, + default: uni.$u.props.formItem.prop + }, + // 是否显示表单域的下划线边框 + borderBottom: { + type: [String, Boolean], + default: uni.$u.props.formItem.borderBottom + }, + // label的宽度,单位px + labelWidth: { + type: [String, Number], + default: uni.$u.props.formItem.labelWidth + }, + // 右侧图标 + rightIcon: { + type: String, + default: uni.$u.props.formItem.rightIcon + }, + // 左侧图标 + leftIcon: { + type: String, + default: uni.$u.props.formItem.leftIcon + }, + // 是否显示左边的必填星号,只作显示用,具体校验必填的逻辑,请在rules中配置 + required: { + type: Boolean, + default: uni.$u.props.formItem.required + }, + leftIconStyle: { + type: [String, Object], + default: uni.$u.props.formItem.leftIconStyle, + } + } +} diff --git a/uni_modules/uview-ui/components/u-form-item/u-form-item.vue b/uni_modules/uview-ui/components/u-form-item/u-form-item.vue new file mode 100644 index 0000000..701d7cc --- /dev/null +++ b/uni_modules/uview-ui/components/u-form-item/u-form-item.vue @@ -0,0 +1,235 @@ +<template> + <view class="u-form-item"> + <view + class="u-form-item__body" + @tap="clickHandler" + :style="[$u.addStyle(customStyle), { + flexDirection: parentData.labelPosition === 'left' ? 'row' : 'column' + }]" + > + <!-- 微信小程序中,将一个参数设置空字符串,结果会变成字符串"true" --> + <slot name="label"> + <!-- {{required}} --> + <view + class="u-form-item__body__left" + v-if="required || leftIcon || label" + :style="{ + width: $u.addUnit(labelWidth || parentData.labelWidth), + marginBottom: parentData.labelPosition === 'left' ? 0 : '5px', + }" + > + <!-- 为了块对齐 --> + <view class="u-form-item__body__left__content"> + <!-- nvue不支持伪元素before --> + <text + v-if="required" + class="u-form-item__body__left__content__required" + >*</text> + <view + class="u-form-item__body__left__content__icon" + v-if="leftIcon" + > + <u-icon + :name="leftIcon" + :custom-style="leftIconStyle" + ></u-icon> + </view> + <text + class="u-form-item__body__left__content__label" + :style="[parentData.labelStyle, { + justifyContent: parentData.labelAlign === 'left' ? 'flex-start' : parentData.labelAlign === 'center' ? 'center' : 'flex-end' + }]" + >{{ label }}</text> + </view> + </view> + </slot> + <view class="u-form-item__body__right"> + <view class="u-form-item__body__right__content"> + <view class="u-form-item__body__right__content__slot"> + <slot /> + </view> + <view + class="item__body__right__content__icon" + v-if="$slots.right" + > + <slot name="right" /> + </view> + </view> + </view> + </view> + <slot name="error"> + <text + v-if="!!message && parentData.errorType === 'message'" + class="u-form-item__body__right__message" + :style="{ + marginLeft: $u.addUnit(parentData.labelPosition === 'top' ? 0 : (labelWidth || parentData.labelWidth)) + }" + >{{ message }}</text> + </slot> + <u-line + v-if="borderBottom" + :color="message && parentData.errorType === 'border-bottom' ? $u.color.error : propsLine.color" + :customStyle="`margin-top: ${message && parentData.errorType === 'message' ? '5px' : 0}`" + ></u-line> + </view> +</template> + +<script> + import props from './props.js'; + /** + * Form 表单 + * @description 此组件一般用于表单场景,可以配置Input输入框,Select弹出框,进行表单验证等。 + * @tutorial https://www.uviewui.com/components/form.html + * @property {String} label input的label提示语 + * @property {String} prop 绑定的值 + * @property {String | Boolean} borderBottom 是否显示表单域的下划线边框 + * @property {String | Number} labelWidth label的宽度,单位px + * @property {String} rightIcon 右侧图标 + * @property {String} leftIcon 左侧图标 + * @property {String | Object} leftIconStyle 左侧图标的样式 + * @property {Boolean} required 是否显示左边的必填星号,只作显示用,具体校验必填的逻辑,请在rules中配置 (默认 false ) + * + * @example <u-form-item label="姓名" prop="userInfo.name" borderBottom ref="item1"></u-form-item> + */ + export default { + name: 'u-form-item', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + // 错误提示语 + message: '', + parentData: { + // 提示文本的位置 + labelPosition: 'left', + // 提示文本对齐方式 + labelAlign: 'left', + // 提示文本的样式 + labelStyle: {}, + // 提示文本的宽度 + labelWidth: 45, + // 错误提示方式 + errorType: 'message' + } + } + }, + // 组件创建完成时,将当前实例保存到u-form中 + computed: { + propsLine() { + return uni.$u.props.line + } + }, + mounted() { + this.init() + }, + methods: { + init() { + // 父组件的实例 + this.updateParentData() + if (!this.parent) { + uni.$u.error('u-form-item需要结合u-form组件使用') + } + }, + // 获取父组件的参数 + updateParentData() { + // 此方法写在mixin中 + this.getParentData('u-form'); + }, + // 移除u-form-item的校验结果 + clearValidate() { + this.message = null + }, + // 清空当前的组件的校验结果,并重置为初始值 + resetField() { + // 找到原始值 + const value = uni.$u.getProperty(this.parent.originalModel, this.prop) + // 将u-form的model的prop属性链还原原始值 + uni.$u.setProperty(this.parent.model, this.prop, value) + // 移除校验结果 + this.message = null + }, + // 点击组件 + clickHandler() { + this.$emit('click') + } + }, + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-form-item { + @include flex(column); + font-size: 14px; + color: $u-main-color; + + &__body { + @include flex; + padding: 10px 0; + + &__left { + @include flex; + align-items: center; + + &__content { + position: relative; + @include flex; + align-items: center; + padding-right: 10rpx; + flex: 1; + + &__icon { + margin-right: 8rpx; + } + + &__required { + position: absolute; + left: -9px; + color: $u-error; + line-height: 20px; + font-size: 20px; + top: 3px; + } + + &__label { + @include flex; + align-items: center; + flex: 1; + color: $u-main-color; + font-size: 15px; + } + } + } + + &__right { + flex: 1; + + &__content { + @include flex; + align-items: center; + flex: 1; + + &__slot { + flex: 1; + /* #ifndef MP */ + @include flex; + align-items: center; + /* #endif */ + } + + &__icon { + margin-left: 10rpx; + color: $u-light-color; + font-size: 30rpx; + } + } + + &__message { + font-size: 12px; + line-height: 12px; + color: $u-error; + } + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-form/props.js b/uni_modules/uview-ui/components/u-form/props.js new file mode 100644 index 0000000..f2a629c --- /dev/null +++ b/uni_modules/uview-ui/components/u-form/props.js @@ -0,0 +1,45 @@ +export default { + props: { + // 当前form的需要验证字段的集合 + model: { + type: Object, + default: uni.$u.props.form.model + }, + // 验证规则 + rules: { + type: [Object, Function, Array], + default: uni.$u.props.form.rules + }, + // 有错误时的提示方式,message-提示信息,toast-进行toast提示 + // border-bottom-下边框呈现红色,none-无提示 + errorType: { + type: String, + default: uni.$u.props.form.errorType + }, + // 是否显示表单域的下划线边框 + borderBottom: { + type: Boolean, + default: uni.$u.props.form.borderBottom + }, + // label的位置,left-左边,top-上边 + labelPosition: { + type: String, + default: uni.$u.props.form.labelPosition + }, + // label的宽度,单位px + labelWidth: { + type: [String, Number], + default: uni.$u.props.form.labelWidth + }, + // lable字体的对齐方式 + labelAlign: { + type: String, + default: uni.$u.props.form.labelAlign + }, + // lable的样式,对象形式 + labelStyle: { + type: Object, + default: uni.$u.props.form.labelStyle + } + } +} diff --git a/uni_modules/uview-ui/components/u-form/u-form.vue b/uni_modules/uview-ui/components/u-form/u-form.vue new file mode 100644 index 0000000..fe2dde2 --- /dev/null +++ b/uni_modules/uview-ui/components/u-form/u-form.vue @@ -0,0 +1,214 @@ +<template> + <view class="u-form"> + <slot /> + </view> +</template> + +<script> + import props from "./props.js"; + import Schema from "../../libs/util/async-validator"; + // 去除警告信息 + Schema.warning = function() {}; + /** + * Form 表单 + * @description 此组件一般用于表单场景,可以配置Input输入框,Select弹出框,进行表单验证等。 + * @tutorial https://www.uviewui.com/components/form.html + * @property {Object} model 当前form的需要验证字段的集合 + * @property {Object | Function | Array} rules 验证规则 + * @property {String} errorType 错误的提示方式,见上方说明 ( 默认 message ) + * @property {Boolean} borderBottom 是否显示表单域的下划线边框 ( 默认 true ) + * @property {String} labelPosition 表单域提示文字的位置,left-左侧,top-上方 ( 默认 'left' ) + * @property {String | Number} labelWidth 提示文字的宽度,单位px ( 默认 45 ) + * @property {String} labelAlign lable字体的对齐方式 ( 默认 ‘left' ) + * @property {Object} labelStyle lable的样式,对象形式 + * @example <u--formlabelPosition="left" :model="model1" :rules="rules" ref="form1"></u--form> + */ + export default { + name: "u-form", + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + provide() { + return { + uForm: this, + }; + }, + data() { + return { + formRules: {}, + // 规则校验器 + validator: {}, + // 原始的model快照,用于resetFields方法重置表单时使用 + originalModel: null, + }; + }, + watch: { + // 监听规则的变化 + rules: { + immediate: true, + handler(n) { + this.setRules(n); + }, + }, + // 监听属性的变化,通知子组件u-form-item重新获取信息 + propsChange(n) { + if (this.children?.length) { + this.children.map((child) => { + // 判断子组件(u-form-item)如果有updateParentData方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值) + typeof child.updateParentData == "function" && + child.updateParentData(); + }); + } + }, + // 监听model的初始值作为重置表单的快照 + model: { + immediate: true, + handler(n) { + if (!this.originalModel) { + this.originalModel = uni.$u.deepClone(n); + } + }, + }, + }, + computed: { + propsChange() { + return [ + this.errorType, + this.borderBottom, + this.labelPosition, + this.labelWidth, + this.labelAlign, + this.labelStyle, + ]; + }, + }, + created() { + // 存储当前form下的所有u-form-item的实例 + // 不能定义在data中,否则微信小程序会造成循环引用而报错 + this.children = []; + }, + methods: { + // 手动设置校验的规则,如果规则中有函数的话,微信小程序中会过滤掉,所以只能手动调用设置规则 + setRules(rules) { + // 判断是否有规则 + if (Object.keys(rules).length === 0) return; + if (process.env.NODE_ENV === 'development' && Object.keys(this.model).length === 0) { + uni.$u.error('设置rules,model必须设置!如果已经设置,请刷新页面。'); + return; + }; + this.formRules = rules; + // 重新将规则赋予Validator + this.validator = new Schema(rules); + }, + // 清空所有u-form-item组件的内容,本质上是调用了u-form-item组件中的resetField()方法 + resetFields() { + this.resetModel(); + }, + // 重置model为初始值的快照 + resetModel(obj) { + // 历遍所有u-form-item,根据其prop属性,还原model的原始快照 + this.children.map((child) => { + const prop = child?.prop; + const value = uni.$u.getProperty(this.originalModel, prop); + uni.$u.setProperty(this.model, prop, value); + }); + }, + // 清空校验结果 + clearValidate(props) { + props = [].concat(props); + this.children.map((child) => { + // 如果u-form-item的prop在props数组中,则清除对应的校验结果信息 + if (props[0] === undefined || props.includes(child.prop)) { + child.message = null; + } + }); + }, + // 对部分表单字段进行校验 + async validateField(value, callback, event = null) { + // $nextTick是必须的,否则model的变更,可能会延后于此方法的执行 + this.$nextTick(() => { + // 校验错误信息,返回给回调方法,用于存放所有form-item的错误信息 + const errorsRes = []; + // 如果为字符串,转为数组 + value = [].concat(value); + // 历遍children所有子form-item + this.children.map((child) => { + // 用于存放form-item的错误信息 + const childErrors = []; + if (value.includes(child.prop)) { + // 获取对应的属性,通过类似'a.b.c'的形式 + const propertyVal = uni.$u.getProperty( + this.model, + child.prop + ); + // 属性链数组 + const propertyChain = child.prop.split("."); + const propertyName = + propertyChain[propertyChain.length - 1]; + + const rule = this.formRules[child.prop]; + // 如果不存在对应的规则,直接返回,否则校验器会报错 + if (!rule) return; + // rule规则可为数组形式,也可为对象形式,此处拼接成为数组 + const rules = [].concat(rule); + + // 对rules数组进行校验 + for (let i = 0; i < rules.length; i++) { + const ruleItem = rules[i]; + // 将u-form-item的触发器转为数组形式 + const trigger = [].concat(ruleItem?.trigger); + // 如果是有传入触发事件,但是此form-item却没有配置此触发器的话,不执行校验操作 + if (event && !trigger.includes(event)) continue; + // 实例化校验对象,传入构造规则 + const validator = new Schema({ + [propertyName]: ruleItem, + }); + validator.validate({ + [propertyName]: propertyVal, + }, + (errors, fields) => { + if (uni.$u.test.array(errors)) { + errorsRes.push(...errors); + childErrors.push(...errors); + } + child.message = + childErrors[0]?.message ?? null; + } + ); + } + } + }); + // 执行回调函数 + typeof callback === "function" && callback(errorsRes); + }); + }, + // 校验全部数据 + validate(callback) { + // 开发环境才提示,生产环境不会提示 + if (process.env.NODE_ENV === 'development' && Object.keys(this.formRules).length === 0) { + uni.$u.error('未设置rules,请看文档说明!如果已经设置,请刷新页面。'); + return; + } + return new Promise((resolve, reject) => { + // $nextTick是必须的,否则model的变更,可能会延后于validate方法 + this.$nextTick(() => { + // 获取所有form-item的prop,交给validateField方法进行校验 + const formItemProps = this.children.map( + (item) => item.prop + ); + this.validateField(formItemProps, (errors) => { + if(errors.length) { + // 如果错误提示方式为toast,则进行提示 + this.errorType === 'toast' && uni.$u.toast(errors[0].message) + reject(errors) + } else { + resolve(true) + } + }); + }); + }); + }, + }, + }; +</script> + +<style lang="scss" scoped> +</style> diff --git a/uni_modules/uview-ui/components/u-gap/props.js b/uni_modules/uview-ui/components/u-gap/props.js new file mode 100644 index 0000000..89953e3 --- /dev/null +++ b/uni_modules/uview-ui/components/u-gap/props.js @@ -0,0 +1,24 @@ +export default { + props: { + // 背景颜色(默认transparent) + bgColor: { + type: String, + default: uni.$u.props.gap.bgColor + }, + // 分割槽高度,单位px(默认30) + height: { + type: [String, Number], + default: uni.$u.props.gap.height + }, + // 与上一个组件的距离 + marginTop: { + type: [String, Number], + default: uni.$u.props.gap.marginTop + }, + // 与下一个组件的距离 + marginBottom: { + type: [String, Number], + default: uni.$u.props.gap.marginBottom + } + } +} diff --git a/uni_modules/uview-ui/components/u-gap/u-gap.vue b/uni_modules/uview-ui/components/u-gap/u-gap.vue new file mode 100644 index 0000000..e4429f0 --- /dev/null +++ b/uni_modules/uview-ui/components/u-gap/u-gap.vue @@ -0,0 +1,38 @@ +<template> + <view class="u-gap" :style="[gapStyle]"></view> +</template> + +<script> + import props from './props.js'; + /** + * gap 间隔槽 + * @description 该组件一般用于内容块之间的用一个灰色块隔开的场景,方便用户风格统一,减少工作量 + * @tutorial https://www.uviewui.com/components/gap.html + * @property {String} bgColor 背景颜色 (默认 'transparent' ) + * @property {String | Number} height 分割槽高度,单位px (默认 20 ) + * @property {String | Number} marginTop 与前一个组件的距离,单位px( 默认 0 ) + * @property {String | Number} marginBottom 与后一个组件的距离,单位px (默认 0 ) + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @example <u-gap height="80" bg-color="#bbb"></u-gap> + */ + export default { + name: "u-gap", + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + computed: { + gapStyle() { + const style = { + backgroundColor: this.bgColor, + height: uni.$u.addUnit(this.height), + marginTop: uni.$u.addUnit(this.marginTop), + marginBottom: uni.$u.addUnit(this.marginBottom), + } + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) + } + } + }; +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; +</style> diff --git a/uni_modules/uview-ui/components/u-grid-item/props.js b/uni_modules/uview-ui/components/u-grid-item/props.js new file mode 100644 index 0000000..06c3c66 --- /dev/null +++ b/uni_modules/uview-ui/components/u-grid-item/props.js @@ -0,0 +1,14 @@ +export default { + props: { + // 宫格的name + name: { + type: [String, Number, null], + default: uni.$u.props.gridItem.name + }, + // 背景颜色 + bgColor: { + type: String, + default: uni.$u.props.gridItem.bgColor + } + } +} diff --git a/uni_modules/uview-ui/components/u-grid-item/u-grid-item.vue b/uni_modules/uview-ui/components/u-grid-item/u-grid-item.vue new file mode 100644 index 0000000..fc0c7cf --- /dev/null +++ b/uni_modules/uview-ui/components/u-grid-item/u-grid-item.vue @@ -0,0 +1,209 @@ +<template> + <!-- #ifndef APP-NVUE --> + <view + class="u-grid-item" + hover-class="u-grid-item--hover-class" + :hover-stay-time="200" + @tap="clickHandler" + :class="classes" + :style="[itemStyle]" + > + <slot /> + </view> + <!-- #endif --> + <!-- #ifdef APP-NVUE --> + <view + class="u-grid-item" + :hover-stay-time="200" + @tap="clickHandler" + :class="classes" + :style="[itemStyle]" + > + <slot /> + </view> + <!-- #endif --> +</template> + +<script> + import props from './props.js'; + /** + * gridItem 提示 + * @description 宫格组件一般用于同时展示多个同类项目的场景,可以给宫格的项目设置徽标组件(badge),或者图标等,也可以扩展为左右滑动的轮播形式。搭配u-grid使用 + * @tutorial https://www.uviewui.com/components/grid.html + * @property {String | Number} name 宫格的name ( 默认 null ) + * @property {String} bgColor 宫格的背景颜色 (默认 'transparent' ) + * @property {Object} customStyle 自定义样式,对象形式 + * @event {Function} click 点击宫格触发 + * @example <u-grid-item></u-grid-item> + */ + export default { + name: "u-grid-item", + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + parentData: { + col: 3, // 父组件划分的宫格数 + border: true, // 是否显示边框,根据父组件决定 + }, + // #ifdef APP-NVUE + width: 0, // nvue下才这么计算,vue下放到computed中,否则会因为延时造成闪烁 + // #endif + classes: [], // 类名集合,用于判断是否显示右边和下边框 + }; + }, + mounted() { + this.init() + }, + computed: { + // #ifndef APP-NVUE + // vue下放到computed中,否则会因为延时造成闪烁 + width() { + return 100 / Number(this.parentData.col) + '%' + }, + // #endif + itemStyle() { + const style = { + background: this.bgColor, + width: this.width + } + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) + } + }, + methods: { + init() { + // 用于在父组件u-grid的children中被添加入子组件时, + // 重新计算item的边框 + uni.$on('$uGridItem', () => { + this.gridItemClasses() + }) + // 父组件的实例 + this.updateParentData() + // #ifdef APP-NVUE + // 获取元素该有的长度,nvue下要延时才准确 + this.$nextTick(function(){ + this.getItemWidth() + }) + // #endif + // 发出事件,通知所有的grid-item都重新计算自己的边框 + uni.$emit('$uGridItem') + this.gridItemClasses() + }, + // 获取父组件的参数 + updateParentData() { + // 此方法写在mixin中 + this.getParentData('u-grid'); + }, + clickHandler() { + let name = this.name + // 如果没有设置name属性,历遍父组件的children数组,判断当前的元素是否和本实例this相等,找出当前组件的索引 + const children = this.parent?.children + if(children && this.name === null) { + name = children.findIndex(child => child === this) + } + // 调用父组件方法,发出事件 + this.parent && this.parent.childClick(name) + this.$emit('click', name) + }, + async getItemWidth() { + // 如果是nvue,不能使用百分比,只能使用固定宽度 + let width = 0 + if(this.parent) { + // 获取父组件宽度后,除以栅格数,得出每个item的宽度 + const parentWidth = await this.getParentWidth() + width = parentWidth / Number(this.parentData.col) + 'px' + } + this.width = width + }, + // 获取父元素的尺寸 + getParentWidth() { + // #ifdef APP-NVUE + // 返回一个promise,让调用者可以用await同步获取 + const dom = uni.requireNativePlugin('dom') + return new Promise(resolve => { + // 调用父组件的ref + dom.getComponentRect(this.parent.$refs['u-grid'], res => { + resolve(res.size.width) + }) + }) + // #endif + }, + gridItemClasses() { + if(this.parentData.border) { + const classes = [] + this.parent.children.map((child, index) =>{ + if(this === child) { + const len = this.parent.children.length + // 贴近右边屏幕边沿的child,并且最后一个(比如只有横向2个的时候),无需右边框 + if((index + 1) % this.parentData.col !== 0 && index + 1 !== len) { + classes.push('u-border-right') + } + // 总的宫格数量对列数取余的值 + // 如果取余后,值为0,则意味着要将最后一排的宫格,都不需要下边框 + const lessNum = len % this.parentData.col === 0 ? this.parentData.col : len % this.parentData.col + // 最下面的一排child,无需下边框 + if(index < len - lessNum) { + classes.push('u-border-bottom') + } + } + }) + // 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效 + // #ifdef MP-ALIPAY || MP-TOUTIAO + classes = classes.join(' ') + // #endif + this.classes = classes + } + } + }, + beforeDestroy() { + // 移除事件监听,释放性能 + uni.$off('$uGridItem') + } + }; +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + $u-grid-item-hover-class-opcatiy:.5 !default; + $u-grid-item-margin-top:1rpx !default; + $u-grid-item-border-right-width:0.5px !default; + $u-grid-item-border-bottom-width:0.5px !default; + $u-grid-item-border-right-color:$u-border-color !default; + $u-grid-item-border-bottom-color:$u-border-color !default; + .u-grid-item { + align-items: center; + justify-content: center; + position: relative; + flex-direction: column; + /* #ifndef APP-NVUE */ + box-sizing: border-box; + display: flex; + /* #endif */ + + /* #ifdef MP */ + position: relative; + float: left; + /* #endif */ + + /* #ifdef MP-WEIXIN */ + margin-top:$u-grid-item-margin-top; + /* #endif */ + + &--hover-class { + opacity:$u-grid-item-hover-class-opcatiy; + } + } + + /* #ifdef APP-NVUE */ + // 由于nvue不支持组件内引入app.vue中再引入的样式,所以需要写在这里 + .u-border-right { + border-right-width:$u-grid-item-border-right-width; + border-color: $u-grid-item-border-right-color; + } + + .u-border-bottom { + border-bottom-width:$u-grid-item-border-bottom-width; + border-color:$u-grid-item-border-bottom-color; + } + + /* #endif */ +</style> diff --git a/uni_modules/uview-ui/components/u-grid/props.js b/uni_modules/uview-ui/components/u-grid/props.js new file mode 100644 index 0000000..87b0f6a --- /dev/null +++ b/uni_modules/uview-ui/components/u-grid/props.js @@ -0,0 +1,19 @@ +export default { + props: { + // 分成几列 + col: { + type: [String, Number], + default: uni.$u.props.grid.col + }, + // 是否显示边框 + border: { + type: Boolean, + default: uni.$u.props.grid.border + }, + // 宫格对齐方式,表现为数量少的时候,靠左,居中,还是靠右 + align: { + type: String, + default: uni.$u.props.grid.align + } + } +} diff --git a/uni_modules/uview-ui/components/u-grid/u-grid.vue b/uni_modules/uview-ui/components/u-grid/u-grid.vue new file mode 100644 index 0000000..b43cc27 --- /dev/null +++ b/uni_modules/uview-ui/components/u-grid/u-grid.vue @@ -0,0 +1,97 @@ +<template> + <view + class="u-grid" + ref='u-grid' + :style="[gridStyle]" + > + <slot /> + </view> +</template> + +<script> + import props from './props.js'; + /** + * grid 宫格布局 + * @description 宫格组件一般用于同时展示多个同类项目的场景,可以给宫格的项目设置徽标组件(badge),或者图标等,也可以扩展为左右滑动的轮播形式。 + * @tutorial https://www.uviewui.com/components/grid.html + * @property {String | Number} col 宫格的列数(默认 3 ) + * @property {Boolean} border 是否显示宫格的边框(默认 false ) + * @property {String} align 宫格对齐方式,表现为数量少的时候,靠左,居中,还是靠右 (默认 'left' ) + * @property {Object} customStyle 定义需要用到的外部样式 + * @event {Function} click 点击宫格触发 + * @example <u-grid :col="3" @click="click"></u-grid> + */ + export default { + name: 'u-grid', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + index: 0, + width: 0 + } + }, + watch: { + // 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件 + parentData() { + if (this.children.length) { + this.children.map(child => { + // 判断子组件(u-radio)如果有updateParentData方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值) + typeof(child.updateParentData) == 'function' && child.updateParentData(); + }) + } + }, + }, + created() { + // 如果将children定义在data中,在微信小程序会造成循环引用而报错 + this.children = [] + }, + computed: { + // 计算父组件的值是否发生变化 + parentData() { + return [this.hoverClass, this.col, this.size, this.border]; + }, + // 宫格对齐方式 + gridStyle() { + let style = {}; + switch (this.align) { + case 'left': + style.justifyContent = 'flex-start'; + break; + case 'center': + style.justifyContent = 'center'; + break; + case 'right': + style.justifyContent = 'flex-end'; + break; + default: + style.justifyContent = 'flex-start'; + }; + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)); + } + }, + methods: { + // 此方法由u-grid-item触发,用于在u-grid发出事件 + childClick(name) { + this.$emit('click', name) + } + } + }; +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + $u-grid-width:100% !default; + .u-grid { + /* #ifdef MP */ + width: $u-grid-width; + position: relative; + box-sizing: border-box; + overflow: hidden; + display: block; + /* #endif */ + justify-content: center; + @include flex; + flex-wrap: wrap; + align-items: center; + } +</style> diff --git a/uni_modules/uview-ui/components/u-icon/icons.js b/uni_modules/uview-ui/components/u-icon/icons.js new file mode 100644 index 0000000..f4d0fe2 --- /dev/null +++ b/uni_modules/uview-ui/components/u-icon/icons.js @@ -0,0 +1,214 @@ +export default { + 'uicon-level': '\ue693', + 'uicon-column-line': '\ue68e', + 'uicon-checkbox-mark': '\ue807', + 'uicon-folder': '\ue7f5', + 'uicon-movie': '\ue7f6', + 'uicon-star-fill': '\ue669', + 'uicon-star': '\ue65f', + 'uicon-phone-fill': '\ue64f', + 'uicon-phone': '\ue622', + 'uicon-apple-fill': '\ue881', + 'uicon-chrome-circle-fill': '\ue885', + 'uicon-backspace': '\ue67b', + 'uicon-attach': '\ue632', + 'uicon-cut': '\ue948', + 'uicon-empty-car': '\ue602', + 'uicon-empty-coupon': '\ue682', + 'uicon-empty-address': '\ue646', + 'uicon-empty-favor': '\ue67c', + 'uicon-empty-permission': '\ue686', + 'uicon-empty-news': '\ue687', + 'uicon-empty-search': '\ue664', + 'uicon-github-circle-fill': '\ue887', + 'uicon-rmb': '\ue608', + 'uicon-person-delete-fill': '\ue66a', + 'uicon-reload': '\ue788', + 'uicon-order': '\ue68f', + 'uicon-server-man': '\ue6bc', + 'uicon-search': '\ue62a', + 'uicon-fingerprint': '\ue955', + 'uicon-more-dot-fill': '\ue630', + 'uicon-scan': '\ue662', + 'uicon-share-square': '\ue60b', + 'uicon-map': '\ue61d', + 'uicon-map-fill': '\ue64e', + 'uicon-tags': '\ue629', + 'uicon-tags-fill': '\ue651', + 'uicon-bookmark-fill': '\ue63b', + 'uicon-bookmark': '\ue60a', + 'uicon-eye': '\ue613', + 'uicon-eye-fill': '\ue641', + 'uicon-mic': '\ue64a', + 'uicon-mic-off': '\ue649', + 'uicon-calendar': '\ue66e', + 'uicon-calendar-fill': '\ue634', + 'uicon-trash': '\ue623', + 'uicon-trash-fill': '\ue658', + 'uicon-play-left': '\ue66d', + 'uicon-play-right': '\ue610', + 'uicon-minus': '\ue618', + 'uicon-plus': '\ue62d', + 'uicon-info': '\ue653', + 'uicon-info-circle': '\ue7d2', + 'uicon-info-circle-fill': '\ue64b', + 'uicon-question': '\ue715', + 'uicon-error': '\ue6d3', + 'uicon-close': '\ue685', + 'uicon-checkmark': '\ue6a8', + 'uicon-android-circle-fill': '\ue67e', + 'uicon-android-fill': '\ue67d', + 'uicon-ie': '\ue87b', + 'uicon-IE-circle-fill': '\ue889', + 'uicon-google': '\ue87a', + 'uicon-google-circle-fill': '\ue88a', + 'uicon-setting-fill': '\ue872', + 'uicon-setting': '\ue61f', + 'uicon-minus-square-fill': '\ue855', + 'uicon-plus-square-fill': '\ue856', + 'uicon-heart': '\ue7df', + 'uicon-heart-fill': '\ue851', + 'uicon-camera': '\ue7d7', + 'uicon-camera-fill': '\ue870', + 'uicon-more-circle': '\ue63e', + 'uicon-more-circle-fill': '\ue645', + 'uicon-chat': '\ue620', + 'uicon-chat-fill': '\ue61e', + 'uicon-bag-fill': '\ue617', + 'uicon-bag': '\ue619', + 'uicon-error-circle-fill': '\ue62c', + 'uicon-error-circle': '\ue624', + 'uicon-close-circle': '\ue63f', + 'uicon-close-circle-fill': '\ue637', + 'uicon-checkmark-circle': '\ue63d', + 'uicon-checkmark-circle-fill': '\ue635', + 'uicon-question-circle-fill': '\ue666', + 'uicon-question-circle': '\ue625', + 'uicon-share': '\ue631', + 'uicon-share-fill': '\ue65e', + 'uicon-shopping-cart': '\ue621', + 'uicon-shopping-cart-fill': '\ue65d', + 'uicon-bell': '\ue609', + 'uicon-bell-fill': '\ue640', + 'uicon-list': '\ue650', + 'uicon-list-dot': '\ue616', + 'uicon-zhihu': '\ue6ba', + 'uicon-zhihu-circle-fill': '\ue709', + 'uicon-zhifubao': '\ue6b9', + 'uicon-zhifubao-circle-fill': '\ue6b8', + 'uicon-weixin-circle-fill': '\ue6b1', + 'uicon-weixin-fill': '\ue6b2', + 'uicon-twitter-circle-fill': '\ue6ab', + 'uicon-twitter': '\ue6aa', + 'uicon-taobao-circle-fill': '\ue6a7', + 'uicon-taobao': '\ue6a6', + 'uicon-weibo-circle-fill': '\ue6a5', + 'uicon-weibo': '\ue6a4', + 'uicon-qq-fill': '\ue6a1', + 'uicon-qq-circle-fill': '\ue6a0', + 'uicon-moments-circel-fill': '\ue69a', + 'uicon-moments': '\ue69b', + 'uicon-qzone': '\ue695', + 'uicon-qzone-circle-fill': '\ue696', + 'uicon-baidu-circle-fill': '\ue680', + 'uicon-baidu': '\ue681', + 'uicon-facebook-circle-fill': '\ue68a', + 'uicon-facebook': '\ue689', + 'uicon-car': '\ue60c', + 'uicon-car-fill': '\ue636', + 'uicon-warning-fill': '\ue64d', + 'uicon-warning': '\ue694', + 'uicon-clock-fill': '\ue638', + 'uicon-clock': '\ue60f', + 'uicon-edit-pen': '\ue612', + 'uicon-edit-pen-fill': '\ue66b', + 'uicon-email': '\ue611', + 'uicon-email-fill': '\ue642', + 'uicon-minus-circle': '\ue61b', + 'uicon-minus-circle-fill': '\ue652', + 'uicon-plus-circle': '\ue62e', + 'uicon-plus-circle-fill': '\ue661', + 'uicon-file-text': '\ue663', + 'uicon-file-text-fill': '\ue665', + 'uicon-pushpin': '\ue7e3', + 'uicon-pushpin-fill': '\ue86e', + 'uicon-grid': '\ue673', + 'uicon-grid-fill': '\ue678', + 'uicon-play-circle': '\ue647', + 'uicon-play-circle-fill': '\ue655', + 'uicon-pause-circle-fill': '\ue654', + 'uicon-pause': '\ue8fa', + 'uicon-pause-circle': '\ue643', + 'uicon-eye-off': '\ue648', + 'uicon-eye-off-outline': '\ue62b', + 'uicon-gift-fill': '\ue65c', + 'uicon-gift': '\ue65b', + 'uicon-rmb-circle-fill': '\ue657', + 'uicon-rmb-circle': '\ue677', + 'uicon-kefu-ermai': '\ue656', + 'uicon-server-fill': '\ue751', + 'uicon-coupon-fill': '\ue8c4', + 'uicon-coupon': '\ue8ae', + 'uicon-integral': '\ue704', + 'uicon-integral-fill': '\ue703', + 'uicon-home-fill': '\ue964', + 'uicon-home': '\ue965', + 'uicon-hourglass-half-fill': '\ue966', + 'uicon-hourglass': '\ue967', + 'uicon-account': '\ue628', + 'uicon-plus-people-fill': '\ue626', + 'uicon-minus-people-fill': '\ue615', + 'uicon-account-fill': '\ue614', + 'uicon-thumb-down-fill': '\ue726', + 'uicon-thumb-down': '\ue727', + 'uicon-thumb-up': '\ue733', + 'uicon-thumb-up-fill': '\ue72f', + 'uicon-lock-fill': '\ue979', + 'uicon-lock-open': '\ue973', + 'uicon-lock-opened-fill': '\ue974', + 'uicon-lock': '\ue97a', + 'uicon-red-packet-fill': '\ue690', + 'uicon-photo-fill': '\ue98b', + 'uicon-photo': '\ue98d', + 'uicon-volume-off-fill': '\ue659', + 'uicon-volume-off': '\ue644', + 'uicon-volume-fill': '\ue670', + 'uicon-volume': '\ue633', + 'uicon-red-packet': '\ue691', + 'uicon-download': '\ue63c', + 'uicon-arrow-up-fill': '\ue6b0', + 'uicon-arrow-down-fill': '\ue600', + 'uicon-play-left-fill': '\ue675', + 'uicon-play-right-fill': '\ue676', + 'uicon-rewind-left-fill': '\ue679', + 'uicon-rewind-right-fill': '\ue67a', + 'uicon-arrow-downward': '\ue604', + 'uicon-arrow-leftward': '\ue601', + 'uicon-arrow-rightward': '\ue603', + 'uicon-arrow-upward': '\ue607', + 'uicon-arrow-down': '\ue60d', + 'uicon-arrow-right': '\ue605', + 'uicon-arrow-left': '\ue60e', + 'uicon-arrow-up': '\ue606', + 'uicon-skip-back-left': '\ue674', + 'uicon-skip-forward-right': '\ue672', + 'uicon-rewind-right': '\ue66f', + 'uicon-rewind-left': '\ue671', + 'uicon-arrow-right-double': '\ue68d', + 'uicon-arrow-left-double': '\ue68c', + 'uicon-wifi-off': '\ue668', + 'uicon-wifi': '\ue667', + 'uicon-empty-data': '\ue62f', + 'uicon-empty-history': '\ue684', + 'uicon-empty-list': '\ue68b', + 'uicon-empty-page': '\ue627', + 'uicon-empty-order': '\ue639', + 'uicon-man': '\ue697', + 'uicon-woman': '\ue69c', + 'uicon-man-add': '\ue61c', + 'uicon-man-add-fill': '\ue64c', + 'uicon-man-delete': '\ue61a', + 'uicon-man-delete-fill': '\ue66a', + 'uicon-zh': '\ue70a', + 'uicon-en': '\ue692' +} diff --git a/uni_modules/uview-ui/components/u-icon/props.js b/uni_modules/uview-ui/components/u-icon/props.js new file mode 100644 index 0000000..71845b7 --- /dev/null +++ b/uni_modules/uview-ui/components/u-icon/props.js @@ -0,0 +1,89 @@ +export default { + props: { + // 图标类名 + name: { + type: String, + default: uni.$u.props.icon.name + }, + // 图标颜色,可接受主题色 + color: { + type: String, + default: uni.$u.props.icon.color + }, + // 字体大小,单位px + size: { + type: [String, Number], + default: uni.$u.props.icon.size + }, + // 是否显示粗体 + bold: { + type: Boolean, + default: uni.$u.props.icon.bold + }, + // 点击图标的时候传递事件出去的index(用于区分点击了哪一个) + index: { + type: [String, Number], + default: uni.$u.props.icon.index + }, + // 触摸图标时的类名 + hoverClass: { + type: String, + default: uni.$u.props.icon.hoverClass + }, + // 自定义扩展前缀,方便用户扩展自己的图标库 + customPrefix: { + type: String, + default: uni.$u.props.icon.customPrefix + }, + // 图标右边或者下面的文字 + label: { + type: [String, Number], + default: uni.$u.props.icon.label + }, + // label的位置,只能右边或者下边 + labelPos: { + type: String, + default: uni.$u.props.icon.labelPos + }, + // label的大小 + labelSize: { + type: [String, Number], + default: uni.$u.props.icon.labelSize + }, + // label的颜色 + labelColor: { + type: String, + default: uni.$u.props.icon.labelColor + }, + // label与图标的距离 + space: { + type: [String, Number], + default: uni.$u.props.icon.space + }, + // 图片的mode + imgMode: { + type: String, + default: uni.$u.props.icon.imgMode + }, + // 用于显示图片小图标时,图片的宽度 + width: { + type: [String, Number], + default: uni.$u.props.icon.width + }, + // 用于显示图片小图标时,图片的高度 + height: { + type: [String, Number], + default: uni.$u.props.icon.height + }, + // 用于解决某些情况下,让图标垂直居中的用途 + top: { + type: [String, Number], + default: uni.$u.props.icon.top + }, + // 是否阻止事件传播 + stop: { + type: Boolean, + default: uni.$u.props.icon.stop + } + } +} diff --git a/uni_modules/uview-ui/components/u-icon/u-icon.vue b/uni_modules/uview-ui/components/u-icon/u-icon.vue new file mode 100644 index 0000000..9340328 --- /dev/null +++ b/uni_modules/uview-ui/components/u-icon/u-icon.vue @@ -0,0 +1,234 @@ +<template> + <view + class="u-icon" + @tap="clickHandler" + :class="['u-icon--' + labelPos]" + > + <image + class="u-icon__img" + v-if="isImg" + :src="name" + :mode="imgMode" + :style="[imgStyle, $u.addStyle(customStyle)]" + ></image> + <text + v-else + class="u-icon__icon" + :class="uClasses" + :style="[iconStyle, $u.addStyle(customStyle)]" + :hover-class="hoverClass" + >{{icon}}</text> + <!-- 这里进行空字符串判断,如果仅仅是v-if="label",可能会出现传递0的时候,结果也无法显示 --> + <text + v-if="label !== ''" + class="u-icon__label" + :style="{ + color: labelColor, + fontSize: $u.addUnit(labelSize), + marginLeft: labelPos == 'right' ? $u.addUnit(space) : 0, + marginTop: labelPos == 'bottom' ? $u.addUnit(space) : 0, + marginRight: labelPos == 'left' ? $u.addUnit(space) : 0, + marginBottom: labelPos == 'top' ? $u.addUnit(space) : 0, + }" + >{{ label }}</text> + </view> +</template> + +<script> + // #ifdef APP-NVUE + // nvue通过weex的dom模块引入字体,相关文档地址如下: + // https://weex.apache.org/zh/docs/modules/dom.html#addrule + const fontUrl = 'https://at.alicdn.com/t/font_2225171_8kdcwk4po24.ttf' + const domModule = weex.requireModule('dom') + domModule.addRule('fontFace', { + 'fontFamily': "uicon-iconfont", + 'src': `url('${fontUrl}')` + }) + // #endif + + // 引入图标名称,已经对应的unicode + import icons from './icons' + + import props from './props.js';; + + /** + * icon 图标 + * @description 基于字体的图标集,包含了大多数常见场景的图标。 + * @tutorial https://www.uviewui.com/components/icon.html + * @property {String} name 图标名称,见示例图标集 + * @property {String} color 图标颜色,可接受主题色 (默认 color['u-content-color'] ) + * @property {String | Number} size 图标字体大小,单位px (默认 '16px' ) + * @property {Boolean} bold 是否显示粗体 (默认 false ) + * @property {String | Number} index 点击图标的时候传递事件出去的index(用于区分点击了哪一个) + * @property {String} hoverClass 图标按下去的样式类,用法同uni的view组件的hoverClass参数,详情见官网 + * @property {String} customPrefix 自定义扩展前缀,方便用户扩展自己的图标库 (默认 'uicon' ) + * @property {String | Number} label 图标右侧的label文字 + * @property {String} labelPos label相对于图标的位置,只能right或bottom (默认 'right' ) + * @property {String | Number} labelSize label字体大小,单位px (默认 '15px' ) + * @property {String} labelColor 图标右侧的label文字颜色 ( 默认 color['u-content-color'] ) + * @property {String | Number} space label与图标的距离,单位px (默认 '3px' ) + * @property {String} imgMode 图片的mode + * @property {String | Number} width 显示图片小图标时的宽度 + * @property {String | Number} height 显示图片小图标时的高度 + * @property {String | Number} top 图标在垂直方向上的定位 用于解决某些情况下,让图标垂直居中的用途 (默认 0 ) + * @property {Boolean} stop 是否阻止事件传播 (默认 false ) + * @property {Object} customStyle icon的样式,对象形式 + * @event {Function} click 点击图标时触发 + * @event {Function} touchstart 事件触摸时触发 + * @example <u-icon name="photo" color="#2979ff" size="28"></u-icon> + */ + export default { + name: 'u-icon', + data() { + return { + + } + }, + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + computed: { + uClasses() { + let classes = [] + classes.push(this.customPrefix + '-' + this.name) + // // uView的自定义图标类名为u-iconfont + // if (this.customPrefix == 'uicon') { + // classes.push('u-iconfont') + // } else { + // classes.push(this.customPrefix) + // } + // 主题色,通过类配置 + if (this.color && uni.$u.config.type.includes(this.color)) classes.push('u-icon__icon--' + this.color) + // 阿里,头条,百度小程序通过数组绑定类名时,无法直接使用[a, b, c]的形式,否则无法识别 + // 故需将其拆成一个字符串的形式,通过空格隔开各个类名 + //#ifdef MP-ALIPAY || MP-TOUTIAO || MP-BAIDU + classes = classes.join(' ') + //#endif + return classes + }, + iconStyle() { + let style = {} + style = { + fontSize: uni.$u.addUnit(this.size), + lineHeight: uni.$u.addUnit(this.size), + fontWeight: this.bold ? 'bold' : 'normal', + // 某些特殊情况需要设置一个到顶部的距离,才能更好的垂直居中 + top: uni.$u.addUnit(this.top) + } + // 非主题色值时,才当作颜色值 + if (this.color && !uni.$u.config.type.includes(this.color)) style.color = this.color + + return style + }, + // 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式 + isImg() { + return this.name.indexOf('/') !== -1 + }, + imgStyle() { + let style = {} + // 如果设置width和height属性,则优先使用,否则使用size属性 + style.width = this.width ? uni.$u.addUnit(this.width) : uni.$u.addUnit(this.size) + style.height = this.height ? uni.$u.addUnit(this.height) : uni.$u.addUnit(this.size) + return style + }, + // 通过图标名,查找对应的图标 + icon() { + // 如果内置的图标中找不到对应的图标,就直接返回name值,因为用户可能传入的是unicode代码 + return icons['uicon-' + this.name] || this.name + } + }, + methods: { + clickHandler(e) { + this.$emit('click', this.index) + // 是否阻止事件冒泡 + this.stop && this.preventEvent(e) + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + // 变量定义 + $u-icon-primary: $u-primary !default; + $u-icon-success: $u-success !default; + $u-icon-info: $u-info !default; + $u-icon-warning: $u-warning !default; + $u-icon-error: $u-error !default; + $u-icon-label-line-height:1 !default; + + /* #ifndef APP-NVUE */ + // 非nvue下加载字体 + @font-face { + font-family: 'uicon-iconfont'; + src: url('https://at.alicdn.com/t/font_2225171_8kdcwk4po24.ttf') format('truetype'); + } + + /* #endif */ + + .u-icon { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + align-items: center; + + &--left { + flex-direction: row-reverse; + align-items: center; + } + + &--right { + flex-direction: row; + align-items: center; + } + + &--top { + flex-direction: column-reverse; + justify-content: center; + } + + &--bottom { + flex-direction: column; + justify-content: center; + } + + &__icon { + font-family: uicon-iconfont; + position: relative; + @include flex; + align-items: center; + + &--primary { + color: $u-icon-primary; + } + + &--success { + color: $u-icon-success; + } + + &--error { + color: $u-icon-error; + } + + &--warning { + color: $u-icon-warning; + } + + &--info { + color: $u-icon-info; + } + } + + &__img { + /* #ifndef APP-NVUE */ + height: auto; + will-change: transform; + /* #endif */ + } + + &__label { + /* #ifndef APP-NVUE */ + line-height: $u-icon-label-line-height; + /* #endif */ + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-image/props.js b/uni_modules/uview-ui/components/u-image/props.js new file mode 100644 index 0000000..2eabb74 --- /dev/null +++ b/uni_modules/uview-ui/components/u-image/props.js @@ -0,0 +1,84 @@ +export default { + props: { + // 图片地址 + src: { + type: String, + default: uni.$u.props.image.src + }, + // 裁剪模式 + mode: { + type: String, + default: uni.$u.props.image.mode + }, + // 宽度,单位任意 + width: { + type: [String, Number], + default: uni.$u.props.image.width + }, + // 高度,单位任意 + height: { + type: [String, Number], + default: uni.$u.props.image.height + }, + // 图片形状,circle-圆形,square-方形 + shape: { + type: String, + default: uni.$u.props.image.shape + }, + // 圆角,单位任意 + radius: { + type: [String, Number], + default: uni.$u.props.image.radius + }, + // 是否懒加载,微信小程序、App、百度小程序、字节跳动小程序 + lazyLoad: { + type: Boolean, + default: uni.$u.props.image.lazyLoad + }, + // 开启长按图片显示识别微信小程序码菜单 + showMenuByLongpress: { + type: Boolean, + default: uni.$u.props.image.showMenuByLongpress + }, + // 加载中的图标,或者小图片 + loadingIcon: { + type: String, + default: uni.$u.props.image.loadingIcon + }, + // 加载失败的图标,或者小图片 + errorIcon: { + type: String, + default: uni.$u.props.image.errorIcon + }, + // 是否显示加载中的图标或者自定义的slot + showLoading: { + type: Boolean, + default: uni.$u.props.image.showLoading + }, + // 是否显示加载错误的图标或者自定义的slot + showError: { + type: Boolean, + default: uni.$u.props.image.showError + }, + // 是否需要淡入效果 + fade: { + type: Boolean, + default: uni.$u.props.image.fade + }, + // 只支持网络资源,只对微信小程序有效 + webp: { + type: Boolean, + default: uni.$u.props.image.webp + }, + // 过渡时间,单位ms + duration: { + type: [String, Number], + default: uni.$u.props.image.duration + }, + // 背景颜色,用于深色页面加载图片时,为了和背景色融合 + bgColor: { + type: String, + default: uni.$u.props.image.bgColor + } + } +} diff --git a/uni_modules/uview-ui/components/u-image/u-image.vue b/uni_modules/uview-ui/components/u-image/u-image.vue new file mode 100644 index 0000000..473e35b --- /dev/null +++ b/uni_modules/uview-ui/components/u-image/u-image.vue @@ -0,0 +1,232 @@ +<template> + <u-transition + mode="fade" + :show="show" + :duration="fade ? 1000 : 0" + > + <view + class="u-image" + @tap="onClick" + :style="[wrapStyle, backgroundStyle]" + > + <image + v-if="!isError" + :src="src" + :mode="mode" + @error="onErrorHandler" + @load="onLoadHandler" + :show-menu-by-longpress="showMenuByLongpress" + :lazy-load="lazyLoad" + class="u-image__image" + :style="{ + borderRadius: shape == 'circle' ? '10000px' : $u.addUnit(radius), + width: $u.addUnit(width), + height: $u.addUnit(height) + }" + ></image> + <view + v-if="showLoading && loading" + class="u-image__loading" + :style="{ + borderRadius: shape == 'circle' ? '50%' : $u.addUnit(radius), + backgroundColor: this.bgColor, + width: $u.addUnit(width), + height: $u.addUnit(height) + }" + > + <slot name="loading"> + <u-icon + :name="loadingIcon" + :width="width" + :height="height" + ></u-icon> + </slot> + </view> + <view + v-if="showError && isError && !loading" + class="u-image__error" + :style="{ + borderRadius: shape == 'circle' ? '50%' : $u.addUnit(radius), + width: $u.addUnit(width), + height: $u.addUnit(height) + }" + > + <slot name="error"> + <u-icon + :name="errorIcon" + :width="width" + :height="height" + ></u-icon> + </slot> + </view> + </view> + </u-transition> +</template> + +<script> + import props from './props.js'; + /** + * Image 图片 + * @description 此组件为uni-app的image组件的加强版,在继承了原有功能外,还支持淡入动画、加载中、加载失败提示、圆角值和形状等。 + * @tutorial https://uviewui.com/components/image.html + * @property {String} src 图片地址 + * @property {String} mode 裁剪模式,见官网说明 (默认 'aspectFill' ) + * @property {String | Number} width 宽度,单位任意,如果为数值,则为px单位 (默认 '300' ) + * @property {String | Number} height 高度,单位任意,如果为数值,则为px单位 (默认 '225' ) + * @property {String} shape 图片形状,circle-圆形,square-方形 (默认 'square' ) + * @property {String | Number} radius 圆角值,单位任意,如果为数值,则为px单位 (默认 0 ) + * @property {Boolean} lazyLoad 是否懒加载,仅微信小程序、App、百度小程序、字节跳动小程序有效 (默认 true ) + * @property {Boolean} showMenuByLongpress 是否开启长按图片显示识别小程序码菜单,仅微信小程序有效 (默认 true ) + * @property {String} loadingIcon 加载中的图标,或者小图片 (默认 'photo' ) + * @property {String} errorIcon 加载失败的图标,或者小图片 (默认 'error-circle' ) + * @property {Boolean} showLoading 是否显示加载中的图标或者自定义的slot (默认 true ) + * @property {Boolean} showError 是否显示加载错误的图标或者自定义的slot (默认 true ) + * @property {Boolean} fade 是否需要淡入效果 (默认 true ) + * @property {Boolean} webp 只支持网络资源,只对微信小程序有效 (默认 false ) + * @property {String | Number} duration 搭配fade参数的过渡时间,单位ms (默认 500 ) + * @property {String} bgColor 背景颜色,用于深色页面加载图片时,为了和背景色融合 (默认 '#f3f4f6' ) + * @property {Object} customStyle 定义需要用到的外部样式 + * @event {Function} click 点击图片时触发 + * @event {Function} error 图片加载失败时触发 + * @event {Function} load 图片加载成功时触发 + * @example <u-image width="100%" height="300px" :src="src"></u-image> + */ + export default { + name: 'u-image', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + // 图片是否加载错误,如果是,则显示错误占位图 + isError: false, + // 初始化组件时,默认为加载中状态 + loading: true, + // 不透明度,为了实现淡入淡出的效果 + opacity: 1, + // 过渡时间,因为props的值无法修改,故需要一个中间值 + durationTime: this.duration, + // 图片加载完成时,去掉背景颜色,因为如果是png图片,就会显示灰色的背景 + backgroundStyle: {}, + // 用于fade模式的控制组件显示与否 + show: false + }; + }, + watch: { + src: { + immediate: true, + handler(n) { + if (!n) { + // 如果传入null或者'',或者false,或者undefined,标记为错误状态 + this.isError = true + + } else { + this.isError = false; + this.loading = true; + } + } + } + }, + computed: { + wrapStyle() { + let style = {}; + // 通过调用addUnit()方法,如果有单位,如百分比,px单位等,直接返回,如果是纯粹的数值,则加上rpx单位 + style.width = this.$u.addUnit(this.width); + style.height = this.$u.addUnit(this.height); + // 如果是显示圆形,设置一个很多的半径值即可 + style.borderRadius = this.shape == 'circle' ? '10000px' : uni.$u.addUnit(this.radius) + // 如果设置圆角,必须要有hidden,否则可能圆角无效 + style.overflow = this.borderRadius > 0 ? 'hidden' : 'visible' + // if (this.fade) { + // style.opacity = this.opacity + // // nvue下,这几个属性必须要分开写 + // style.transitionDuration = `${this.durationTime}ms` + // style.transitionTimingFunction = 'ease-in-out' + // style.transitionProperty = 'opacity' + // } + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)); + + } + }, + mounted() { + this.show = true + }, + methods: { + // 点击图片 + onClick() { + this.$emit('click') + }, + // 图片加载失败 + onErrorHandler(err) { + this.loading = false + this.isError = true + this.$emit('error', err) + }, + // 图片加载完成,标记loading结束 + onLoadHandler(event) { + this.loading = false + this.isError = false + this.$emit('load', event) + this.removeBgColor() + // 如果不需要动画效果,就不执行下方代码,同时移除加载时的背景颜色 + // 否则无需fade效果时,png图片依然能看到下方的背景色 + // if (!this.fade) return this.removeBgColor(); + // // 原来opacity为1(不透明,是为了显示占位图),改成0(透明,意味着该元素显示的是背景颜色,默认的灰色),再改成1,是为了获得过渡效果 + // this.opacity = 0; + // // 这里设置为0,是为了图片展示到背景全透明这个过程时间为0,延时之后延时之后重新设置为duration,是为了获得背景透明(灰色) + // // 到图片展示的过程中的淡入效果 + // this.durationTime = 0; + // // 延时50ms,否则在浏览器H5,过渡效果无效 + // setTimeout(() => { + // this.durationTime = this.duration; + // this.opacity = 1; + // setTimeout(() => { + // this.removeBgColor(); + // }, this.durationTime); + // }, 50); + }, + // 移除图片的背景色 + removeBgColor() { + // 淡入动画过渡完成后,将背景设置为透明色,否则png图片会看到灰色的背景 + this.backgroundStyle = { + backgroundColor: 'transparent' + }; + } + } + }; +</script> + +<style lang="scss" scoped> + @import '../../libs/css/components.scss'; + + $u-image-error-top:0px !default; + $u-image-error-left:0px !default; + $u-image-error-width:100% !default; + $u-image-error-hight:100% !default; + $u-image-error-background-color:$u-bg-color !default; + $u-image-error-color:$u-tips-color !default; + $u-image-error-font-size: 46rpx !default; + + .u-image { + position: relative; + transition: opacity 0.5s ease-in-out; + + &__image { + width: 100%; + height: 100%; + } + + &__loading, + &__error { + position: absolute; + top: $u-image-error-top; + left: $u-image-error-left; + width: $u-image-error-width; + height: $u-image-error-hight; + @include flex; + align-items: center; + justify-content: center; + background-color: $u-image-error-background-color; + color: $u-image-error-color; + font-size: $u-image-error-font-size; + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-index-anchor/props.js b/uni_modules/uview-ui/components/u-index-anchor/props.js new file mode 100644 index 0000000..6d8b59a --- /dev/null +++ b/uni_modules/uview-ui/components/u-index-anchor/props.js @@ -0,0 +1,29 @@ +export default { + props: { + // 列表锚点文本内容 + text: { + type: [String, Number], + default: uni.$u.props.indexAnchor.text + }, + // 列表锚点文字颜色 + color: { + type: String, + default: uni.$u.props.indexAnchor.color + }, + // 列表锚点文字大小,单位默认px + size: { + type: [String, Number], + default: uni.$u.props.indexAnchor.size + }, + // 列表锚点背景颜色 + bgColor: { + type: String, + default: uni.$u.props.indexAnchor.bgColor + }, + // 列表锚点高度,单位默认px + height: { + type: [String, Number], + default: uni.$u.props.indexAnchor.height + } + } +} diff --git a/uni_modules/uview-ui/components/u-index-anchor/u-index-anchor.vue b/uni_modules/uview-ui/components/u-index-anchor/u-index-anchor.vue new file mode 100644 index 0000000..b95ddef --- /dev/null +++ b/uni_modules/uview-ui/components/u-index-anchor/u-index-anchor.vue @@ -0,0 +1,91 @@ +<template> + <!-- #ifdef APP-NVUE --> + <header> + <!-- #endif --> + <view + class="u-index-anchor u-border-bottom" + :ref="`u-index-anchor-${text}`" + :style="{ + height: $u.addUnit(height), + backgroundColor: bgColor + }" + > + <text + class="u-index-anchor__text" + :style="{ + fontSize: $u.addUnit(size), + color: color + }" + >{{ text }}</text> + </view> + <!-- #ifdef APP-NVUE --> + </header> + <!-- #endif --> +</template> + +<script> + import props from './props.js'; + // #ifdef APP-NVUE + const dom = uni.requireNativePlugin('dom') + // #endif + /** + * IndexAnchor 列表锚点 + * @description + * @tutorial https://uviewui.com/components/indexList.html + * @property {String | Number} text 列表锚点文本内容 + * @property {String} color 列表锚点文字颜色 ( 默认 '#606266' ) + * @property {String | Number} size 列表锚点文字大小,单位默认px ( 默认 14 ) + * @property {String} bgColor 列表锚点背景颜色 ( 默认 '#dedede' ) + * @property {String | Number} height 列表锚点高度,单位默认px ( 默认 32 ) + * @example <u-index-anchor :text="indexList[index]"></u-index-anchor> + */ + export default { + name: 'u-index-anchor', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + } + }, + mounted() { + this.init() + }, + methods: { + init() { + // 此处会活动父组件实例,并赋值给实例的parent属性 + const indexList = uni.$u.$parent.call(this, 'u-index-list') + if (!indexList) { + return uni.$u.error('u-index-anchor必须要搭配u-index-list组件使用') + } + // 将当前实例放入到u-index-list中 + indexList.anchors.push(this) + const indexListItem = uni.$u.$parent.call(this, 'u-index-item') + // #ifndef APP-NVUE + // 只有在非nvue下,u-index-anchor才是嵌套在u-index-item中的 + if (!indexListItem) { + return uni.$u.error('u-index-anchor必须要搭配u-index-item组件使用') + } + // 设置u-index-item的id为anchor的text标识符,因为非nvue下滚动列表需要依赖scroll-view滚动到元素的特性 + indexListItem.id = this.text.charCodeAt(0) + // #endif + } + }, + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-index-anchor { + position: sticky; + top: 0; + @include flex; + align-items: center; + padding-left: 15px; + z-index: 1; + + &__text { + @include flex; + align-items: center; + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-index-item/props.js b/uni_modules/uview-ui/components/u-index-item/props.js new file mode 100644 index 0000000..7c11331 --- /dev/null +++ b/uni_modules/uview-ui/components/u-index-item/props.js @@ -0,0 +1,5 @@ +export default { + props: { + + } +} diff --git a/uni_modules/uview-ui/components/u-index-item/u-index-item.vue b/uni_modules/uview-ui/components/u-index-item/u-index-item.vue new file mode 100644 index 0000000..0bc7fb3 --- /dev/null +++ b/uni_modules/uview-ui/components/u-index-item/u-index-item.vue @@ -0,0 +1,87 @@ +<template> + <!-- #ifdef APP-NVUE --> + <cell ref="u-index-item"> + <!-- #endif --> + <view + class="u-index-item" + :id="`u-index-item-${id}`" + :class="[`u-index-item-${id}`]" + > + <slot /> + </view> + <!-- #ifdef APP-NVUE --> + </cell> + <!-- #endif --> +</template> + +<script> + import props from './props.js'; + // #ifdef APP-NVUE + // 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度 + const dom = uni.requireNativePlugin('dom') + // #endif + /** + * IndexItem + * @description + * @tutorial https://uviewui.com/components/indexList.html + * @property {String} + * @event {Function} + * @example + */ + export default { + name: 'u-index-item', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + // 本组件到滚动条顶部的距离 + top: 0, + height: 0, + id: '' + } + }, + created() { + // 子组件u-index-anchor的实例 + this.anchor = {} + }, + mounted() { + this.init() + }, + methods: { + init() { + // 此处会活动父组件实例,并赋值给实例的parent属性 + this.getParentData('u-index-list') + if (!this.parent) { + return uni.$u.error('u-index-item必须要搭配u-index-list组件使用') + } + uni.$u.sleep().then(() =>{ + this.getIndexItemRect().then(size => { + // 由于对象的引用特性,此处会同时生效到父组件的children数组的本实例的top属性中,供父组件判断读取 + this.top = Math.ceil(size.top) + this.height = Math.ceil(size.height) + }) + }) + }, + getIndexItemRect() { + return new Promise(resolve => { + // #ifndef APP-NVUE + this.$uGetRect('.u-index-item').then(size => { + resolve(size) + }) + // #endif + + // #ifdef APP-NVUE + const ref = this.$refs['u-index-item'] + dom.getComponentRect(ref, res => { + resolve(res.size) + }) + // #endif + }) + } + }, + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + +</style> diff --git a/uni_modules/uview-ui/components/u-index-list/props.js b/uni_modules/uview-ui/components/u-index-list/props.js new file mode 100644 index 0000000..354d459 --- /dev/null +++ b/uni_modules/uview-ui/components/u-index-list/props.js @@ -0,0 +1,29 @@ +export default { + props: { + // 右边锚点非激活的颜色 + inactiveColor: { + type: String, + default: uni.$u.props.indexList.inactiveColor + }, + // 右边锚点激活的颜色 + activeColor: { + type: String, + default: uni.$u.props.indexList.activeColor + }, + // 索引字符列表,数组形式 + indexList: { + type: Array, + default: uni.$u.props.indexList.indexList + }, + // 是否开启锚点自动吸顶 + sticky: { + type: Boolean, + default: uni.$u.props.indexList.sticky + }, + // 自定义导航栏的高度 + customNavHeight: { + type: [String, Number], + default: uni.$u.props.indexList.customNavHeight + } + } +} diff --git a/uni_modules/uview-ui/components/u-index-list/u-index-list.vue b/uni_modules/uview-ui/components/u-index-list/u-index-list.vue new file mode 100644 index 0000000..d712618 --- /dev/null +++ b/uni_modules/uview-ui/components/u-index-list/u-index-list.vue @@ -0,0 +1,440 @@ +<template> + <view class="u-index-list"> + <!-- #ifdef APP-NVUE --> + <list + :scrollTop="scrollTop" + enable-back-to-top + :offset-accuracy="1" + :style="{ + maxHeight: $u.addUnit(scrollViewHeight) + }" + @scroll="scrollHandler" + ref="uList" + > + <cell + v-if="$slots.header" + ref="header" + > + <slot name="header" /> + </cell> + <slot /> + <cell v-if="$slots.footer"> + <slot name="footer" /> + </cell> + </list> + <!-- #endif --> + <!-- #ifndef APP-NVUE --> + <scroll-view + :scrollTop="scrollTop" + :scrollIntoView="scrollIntoView" + :offset-accuracy="1" + :style="{ + maxHeight: $u.addUnit(scrollViewHeight) + }" + scroll-y + @scroll="scrollHandler" + ref="uList" + > + <view v-if="$slots.header"> + <slot name="header" /> + </view> + <slot /> + <view v-if="$slots.footer"> + <slot name="footer" /> + </view> + </scroll-view> + <!-- #endif --> + <view + class="u-index-list__letter" + ref="u-index-list__letter" + :style="{ top: $u.addUnit(letterInfo.top || 100) }" + @touchstart="touchStart" + @touchmove.stop.prevent="touchMove" + @touchend.stop.prevent="touchEnd" + @touchcancel.stop.prevent="touchEnd" + > + <view + class="u-index-list__letter__item" + v-for="(item, index) in uIndexList" + :key="index" + :style="{ + backgroundColor: activeIndex === index ? activeColor : 'transparent' + }" + > + <text + class="u-index-list__letter__item__index" + :style="{color: activeIndex === index ? '#fff' : inactiveColor}" + >{{ item }}</text> + </view> + </view> + <u-transition + mode="fade" + :show="touching" + :customStyle="{ + position: 'fixed', + right: '50px', + top: $u.addUnit(indicatorTop), + zIndex: 2 + }" + > + <view + class="u-index-list__indicator" + :class="['u-index-list__indicator--show']" + :style="{ + height: $u.addUnit(indicatorHeight), + width: $u.addUnit(indicatorHeight) + }" + > + <text class="u-index-list__indicator__text">{{ uIndexList[activeIndex] }}</text> + </view> + </u-transition> + </view> +</template> + +<script> + const indexList = () => { + const indexList = []; + const charCodeOfA = 'A'.charCodeAt(0); + for (let i = 0; i < 26; i++) { + indexList.push(String.fromCharCode(charCodeOfA + i)); + } + return indexList; + } + import props from './props.js'; + // #ifdef APP-NVUE + // 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度 + const dom = uni.requireNativePlugin('dom') + // #endif + /** + * IndexList 索引列表 + * @description 通过折叠面板收纳内容区域 + * @tutorial https://uviewui.com/components/indexList.html + * @property {String} inactiveColor 右边锚点非激活的颜色 ( 默认 '#606266' ) + * @property {String} activeColor 右边锚点激活的颜色 ( 默认 '#5677fc' ) + * @property {Array} indexList 索引字符列表,数组形式 + * @property {Boolean} sticky 是否开启锚点自动吸顶 ( 默认 true ) + * @property {String | Number} customNavHeight 自定义导航栏的高度 ( 默认 0 ) + * */ + export default { + name: 'u-index-list', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + // #ifdef MP-WEIXIN + // 将自定义节点设置成虚拟的,更加接近Vue组件的表现,能更好的使用flex属性 + options: { + virtualHost: true + }, + // #endif + data() { + return { + // 当前正在被选中的字母索引 + activeIndex: -1, + touchmoveIndex: 1, + // 索引字母的信息 + letterInfo: { + height: 0, + itemHeight: 0, + top: 0 + }, + // 设置字母指示器的高度,后面为了让指示器跟随字母,并将尖角部分指向字母的中部,需要依赖此值 + indicatorHeight: 50, + // 字母放大指示器的top值,为了让其指向当前激活的字母 + // indicatorTop: 0 + // 当前是否正在被触摸状态 + touching: false, + // 滚动条顶部top值 + scrollTop: 0, + // scroll-view的高度 + scrollViewHeight: 0, + // 系统信息 + sys: uni.$u.sys(), + scrolling: false, + scrollIntoView: '', + } + }, + computed: { + // 如果有传入外部的indexList锚点数组则使用,否则使用内部生成A-Z字母 + uIndexList() { + return this.indexList.length ? this.indexList : indexList() + }, + // 字母放大指示器的top值,为了让其指向当前激活的字母 + indicatorTop() { + const { + top, + itemHeight + } = this.letterInfo + return Math.floor(top + itemHeight * this.activeIndex + itemHeight / 2 - this.indicatorHeight / 2) + } + }, + watch: { + // 监听字母索引的变化,重新设置尺寸 + uIndexList: { + immediate: true, + handler() { + uni.$u.sleep().then(() => { + this.setIndexListLetterInfo() + }) + } + } + }, + created() { + this.children = [] + this.anchors = [] + this.init() + }, + mounted() { + this.setIndexListLetterInfo() + }, + methods: { + init() { + // 设置列表的高度为整个屏幕的高度 + //减去this.customNavHeight,并将this.scrollViewHeight设置为maxHeight + //解决当u-index-list组件放在tabbar页面时,scroll-view内容较少时,还能滚动 + this.scrollViewHeight = this.sys.windowHeight - this.customNavHeight + }, + // 索引列表被触摸 + touchStart(e) { + // 获取触摸点信息 + const touchStart = e.changedTouches[0] + if (!touchStart) return + this.touching = true + const { + pageY + } = touchStart + // 根据当前触摸点的坐标,获取当前触摸的为第几个字母 + const currentIndex = this.getIndexListLetter(pageY) + this.setValueForTouch(currentIndex) + }, + // 索引字母列表被触摸滑动中 + touchMove(e) { + // 获取触摸点信息 + let touchMove = e.changedTouches[0] + if (!touchMove) return; + + // 滑动结束后迅速开始第二次滑动时候 touching 为 false 造成不显示 indicator 问题 + if (!this.touching) { + this.touching = true + } + const { + pageY + } = touchMove + const currentIndex = this.getIndexListLetter(pageY) + this.setValueForTouch(currentIndex) + }, + // 触摸结束 + touchEnd(e) { + // 延时一定时间后再隐藏指示器,为了让用户看的更直观,同时也是为了消除快速切换u-transition的show带来的影响 + uni.$u.sleep(300).then(() => { + this.touching = false + }) + }, + // 获取索引列表的尺寸以及单个字符的尺寸信息 + getIndexListLetterRect() { + return new Promise(resolve => { + // 延时一定时间,以获取dom尺寸 + // #ifndef APP-NVUE + this.$uGetRect('.u-index-list__letter').then(size => { + resolve(size) + }) + // #endif + + // #ifdef APP-NVUE + const ref = this.$refs['u-index-list__letter'] + dom.getComponentRect(ref, res => { + resolve(res.size) + }) + // #endif + }) + }, + // 设置indexList索引的尺寸信息 + setIndexListLetterInfo() { + this.getIndexListLetterRect().then(size => { + const { + height + } = size + const sys = uni.$u.sys() + const windowHeight = sys.windowHeight + let customNavHeight = 0 + // 消除各端导航栏非原生和原生导致的差异,让索引列表字母对屏幕垂直居中 + if (this.customNavHeight == 0) { + // #ifdef H5 + customNavHeight = sys.windowTop + // #endif + // #ifndef H5 + // 在非H5中,为原生导航栏,其高度不算在windowHeight内,这里设置为负值,后面相加时变成减去其高度的一半 + customNavHeight = -(sys.statusBarHeight + 44) + // #endif + } else { + customNavHeight = uni.$u.getPx(this.customNavHeight) + } + this.letterInfo = { + height, + // 为了让字母列表对屏幕绝对居中,让其对导航栏进行修正,也即往上偏移导航栏的一半高度 + top: (windowHeight - height) / 2 + customNavHeight / 2, + itemHeight: Math.floor(height / this.uIndexList.length) + } + }) + }, + // 获取当前被触摸的索引字母 + getIndexListLetter(pageY) { + const { + top, + height, + itemHeight + } = this.letterInfo + // 对H5的pageY进行修正,这是由于uni-app自作多情在H5中将触摸点的坐标跟H5的导航栏结合导致的问题 + // #ifdef H5 + pageY += uni.$u.sys().windowTop + // #endif + // 对第一和最后一个字母做边界处理,因为用户可能在字母列表上触摸到两端的尽头后依然继续滑动 + if (pageY < top) { + return 0 + } else if (pageY >= top + height) { + // 如果超出了,取最后一个字母 + return this.uIndexList.length - 1 + } else { + // 将触摸点的Y轴偏移值,减去索引字母的top值,除以每个字母的高度,即可得到当前触摸点落在哪个字母上 + return Math.floor((pageY - top) / itemHeight); + } + }, + // 设置各项由触摸而导致变化的值 + setValueForTouch(currentIndex) { + // 如果偏移量太小,前后得出的会是同一个索引字母,为了防抖,进行返回 + if (currentIndex === this.activeIndex) return + this.activeIndex = currentIndex + // #ifndef APP-NVUE || MP-WEIXIN + // 在非nvue中,由于anchor和item都在u-index-item中,所以需要对index-item进行偏移 + this.scrollIntoView = `u-index-item-${this.uIndexList[currentIndex].charCodeAt(0)}` + // #endif + // #ifdef MP-WEIXIN + // 微信小程序下,scroll-view的scroll-into-view属性无法对slot中的内容的id生效,只能通过设置scrollTop的形式去移动滚动条 + this.scrollTop = this.children[currentIndex].top + // #endif + // #ifdef APP-NVUE + // 在nvue中,由于cell和header为同级元素,所以实际是需要对header(anchor)进行偏移 + const anchor = `u-index-anchor-${this.uIndexList[currentIndex]}` + dom.scrollToElement(this.anchors[currentIndex].$refs[anchor], { + offset: 0, + animated: false + }) + // #endif + }, + getHeaderRect() { + // 获取header slot的高度,因为list组件中获取元素的尺寸是没有top值的 + return new Promise(resolve => { + dom.getComponentRect(this.$refs.header, res => { + resolve(res.size) + }) + }) + }, + // scroll-view的滚动事件 + async scrollHandler(e) { + if (this.touching || this.scrolling) return + // 每过一定时间取样一次,减少资源损耗以及可能带来的卡顿 + this.scrolling = true + uni.$u.sleep(10).then(() => { + this.scrolling = false + }) + let scrollTop = 0 + const len = this.children.length + let children = this.children + const anchors = this.anchors + // #ifdef APP-NVUE + // nvue下获取的滚动条偏移为负数,需要转为正数 + scrollTop = Math.abs(e.contentOffset.y) + // 获取header slot的尺寸信息 + const header = await this.getHeaderRect() + // item的top值,在nvue下,模拟出的anchor的top,类似非nvue下的index-item的top + let top = header.height + // 由于list组件无法获取cell的top值,这里通过header slot和各个item之间的height,模拟出类似非nvue下的位置信息 + children = this.children.map((item, index) => { + const child = { + height: item.height, + top + } + // 进行累加,给下一个item提供计算依据 + top += item.height + anchors[index].height + return child + }) + // #endif + // #ifndef APP-NVUE + // 非nvue通过detail获取滚动条位移 + scrollTop = e.detail.scrollTop + // #endif + for (let i = 0; i < len; i++) { + const item = children[i], + nextItem = children[i + 1] + // 如果滚动条高度小于第一个item的top值,此时无需设置任意字母为高亮 + if (scrollTop <= children[0].top || scrollTop >= children[len - 1].top + children[len - + 1].height) { + this.activeIndex = -1 + break + } else if (!nextItem) { + // 当不存在下一个item时,意味着历遍到了最后一个 + this.activeIndex = len - 1 + break + } else if (scrollTop > item.top && scrollTop < nextItem.top) { + this.activeIndex = i + break + } + } + }, + }, + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-index-list { + + &__letter { + position: fixed; + right: 0; + text-align: center; + z-index: 3; + padding: 0 6px; + + &__item { + width: 16px; + height: 16px; + border-radius: 100px; + margin: 1px 0; + @include flex; + align-items: center; + justify-content: center; + + &--active { + background-color: $u-primary; + } + + &__index { + font-size: 12px; + text-align: center; + line-height: 12px; + } + } + } + + &__indicator { + width: 50px; + height: 50px; + border-radius: 100px 100px 0 100px; + text-align: center; + color: #ffffff; + background-color: #c9c9c9; + transform: rotate(-45deg); + @include flex; + justify-content: center; + align-items: center; + + &__text { + font-size: 28px; + line-height: 28px; + font-weight: bold; + color: #fff; + transform: rotate(45deg); + text-align: center; + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-input/props.js b/uni_modules/uview-ui/components/u-input/props.js new file mode 100644 index 0000000..88917c3 --- /dev/null +++ b/uni_modules/uview-ui/components/u-input/props.js @@ -0,0 +1,182 @@ +export default { + props: { + // 输入的值 + value: { + type: [String, Number], + default: uni.$u.props.input.value + }, + // 输入框类型 + // number-数字输入键盘,app-vue下可以输入浮点数,app-nvue和小程序平台下只能输入整数 + // idcard-身份证输入键盘,微信、支付宝、百度、QQ小程序 + // digit-带小数点的数字键盘,App的nvue页面、微信、支付宝、百度、头条、QQ小程序 + // text-文本输入键盘 + type: { + type: String, + default: uni.$u.props.input.type + }, + // 如果 textarea 是在一个 position:fixed 的区域,需要显示指定属性 fixed 为 true, + // 兼容性:微信小程序、百度小程序、字节跳动小程序、QQ小程序 + fixed: { + type: Boolean, + default: uni.$u.props.input.fixed + }, + // 是否禁用输入框 + disabled: { + type: Boolean, + default: uni.$u.props.input.disabled + }, + // 禁用状态时的背景色 + disabledColor: { + type: String, + default: uni.$u.props.input.disabledColor + }, + // 是否显示清除控件 + clearable: { + type: Boolean, + default: uni.$u.props.input.clearable + }, + // 是否密码类型 + password: { + type: Boolean, + default: uni.$u.props.input.password + }, + // 最大输入长度,设置为 -1 的时候不限制最大长度 + maxlength: { + type: [String, Number], + default: uni.$u.props.input.maxlength + }, + // 输入框为空时的占位符 + placeholder: { + type: String, + default: uni.$u.props.input.placeholder + }, + // 指定placeholder的样式类,注意页面或组件的style中写了scoped时,需要在类名前写/deep/ + placeholderClass: { + type: String, + default: uni.$u.props.input.placeholderClass + }, + // 指定placeholder的样式 + placeholderStyle: { + type: [String, Object], + default: uni.$u.props.input.placeholderStyle + }, + // 是否显示输入字数统计,只在 type ="text"或type ="textarea"时有效 + showWordLimit: { + type: Boolean, + default: uni.$u.props.input.showWordLimit + }, + // 设置右下角按钮的文字,有效值:send|search|next|go|done,兼容性详见uni-app文档 + // https://uniapp.dcloud.io/component/input + // https://uniapp.dcloud.io/component/textarea + confirmType: { + type: String, + default: uni.$u.props.input.confirmType + }, + // 点击键盘右下角按钮时是否保持键盘不收起,H5无效 + confirmHold: { + type: Boolean, + default: uni.$u.props.input.confirmHold + }, + // focus时,点击页面的时候不收起键盘,微信小程序有效 + holdKeyboard: { + type: Boolean, + default: uni.$u.props.input.holdKeyboard + }, + // 自动获取焦点 + // 在 H5 平台能否聚焦以及软键盘是否跟随弹出,取决于当前浏览器本身的实现。nvue 页面不支持,需使用组件的 focus()、blur() 方法控制焦点 + focus: { + type: Boolean, + default: uni.$u.props.input.focus + }, + // 键盘收起时,是否自动失去焦点,目前仅App3.0.0+有效 + autoBlur: { + type: Boolean, + default: uni.$u.props.input.autoBlur + }, + // 是否去掉 iOS 下的默认内边距,仅微信小程序,且type=textarea时有效 + disableDefaultPadding: { + type: Boolean, + default: uni.$u.props.input.disableDefaultPadding + }, + // 指定focus时光标的位置 + cursor: { + type: [String, Number], + default: uni.$u.props.input.cursor + }, + // 输入框聚焦时底部与键盘的距离 + cursorSpacing: { + type: [String, Number], + default: uni.$u.props.input.cursorSpacing + }, + // 光标起始位置,自动聚集时有效,需与selection-end搭配使用 + selectionStart: { + type: [String, Number], + default: uni.$u.props.input.selectionStart + }, + // 光标结束位置,自动聚集时有效,需与selection-start搭配使用 + selectionEnd: { + type: [String, Number], + default: uni.$u.props.input.selectionEnd + }, + // 键盘弹起时,是否自动上推页面 + adjustPosition: { + type: Boolean, + default: uni.$u.props.input.adjustPosition + }, + // 输入框内容对齐方式,可选值为:left|center|right + inputAlign: { + type: String, + default: uni.$u.props.input.inputAlign + }, + // 输入框字体的大小 + fontSize: { + type: [String, Number], + default: uni.$u.props.input.fontSize + }, + // 输入框字体颜色 + color: { + type: String, + default: uni.$u.props.input.color + }, + // 输入框前置图标 + prefixIcon: { + type: String, + default: uni.$u.props.input.prefixIcon + }, + // 前置图标样式,对象或字符串 + prefixIconStyle: { + type: [String, Object], + default: uni.$u.props.input.prefixIconStyle + }, + // 输入框后置图标 + suffixIcon: { + type: String, + default: uni.$u.props.input.suffixIcon + }, + // 后置图标样式,对象或字符串 + suffixIconStyle: { + type: [String, Object], + default: uni.$u.props.input.suffixIconStyle + }, + // 边框类型,surround-四周边框,bottom-底部边框,none-无边框 + border: { + type: String, + default: uni.$u.props.input.border + }, + // 是否只读,与disabled不同之处在于disabled会置灰组件,而readonly则不会 + readonly: { + type: Boolean, + default: uni.$u.props.input.readonly + }, + // 输入框形状,circle-圆形,square-方形 + shape: { + type: String, + default: uni.$u.props.input.shape + }, + // 用于处理或者过滤输入框内容的方法 + formatter: { + type: [Function, null], + default: uni.$u.props.input.formatter + } + } +} diff --git a/uni_modules/uview-ui/components/u-input/u-input.vue b/uni_modules/uview-ui/components/u-input/u-input.vue new file mode 100644 index 0000000..c755390 --- /dev/null +++ b/uni_modules/uview-ui/components/u-input/u-input.vue @@ -0,0 +1,353 @@ +<template> + <view class="u-input" :class="inputClass" :style="[wrapperStyle]"> + <view class="u-input__content"> + <view + class="u-input__content__prefix-icon" + v-if="prefixIcon || $slots.prefix" + > + <slot name="prefix"> + <u-icon + :name="prefixIcon" + size="18" + :customStyle="prefixIconStyle" + ></u-icon> + </slot> + </view> + <view class="u-input__content__field-wrapper" @tap="clickHandler"> + <!-- 根据uni-app的input组件文档,H5和APP中只要声明了password参数(无论true还是false),type均失效,此时 + 为了防止type=number时,又存在password属性,type无效,此时需要设置password为undefined + --> + <input + class="u-input__content__field-wrapper__field" + :style="[inputStyle]" + :type="type" + :focus="focus" + :cursor="cursor" + :value="innerValue" + :auto-blur="autoBlur" + :disabled="disabled || readonly" + :maxlength="maxlength" + :placeholder="placeholder" + :placeholder-style="placeholderStyle" + :placeholder-class="placeholderClass" + :confirm-type="confirmType" + :confirm-hold="confirmHold" + :hold-keyboard="holdKeyboard" + :cursor-spacing="cursorSpacing" + :adjust-position="adjustPosition" + :selection-end="selectionEnd" + :selection-start="selectionStart" + :password="password || type === 'password' || undefined" + @input="onInput" + @blur="onBlur" + @focus="onFocus" + @confirm="onConfirm" + @keyboardheightchange="onkeyboardheightchange" + /> + </view> + <view + class="u-input__content__clear" + v-if="isShowClear" + @tap="onClear" + > + <u-icon + name="close" + size="11" + color="#ffffff" + customStyle="line-height: 12px" + ></u-icon> + </view> + <view + class="u-input__content__subfix-icon" + v-if="suffixIcon || $slots.suffix" + > + <slot name="suffix"> + <u-icon + :name="suffixIcon" + size="18" + :customStyle="suffixIconStyle" + ></u-icon> + </slot> + </view> + </view> + </view> +</template> + +<script> +import props from "./props.js"; +/** + * Input 输入框 + * @description 此组件为一个输入框,默认没有边框和样式,是专门为配合表单组件u-form而设计的,利用它可以快速实现表单验证,输入内容,下拉选择等功能。 + * @tutorial https://uviewui.com/components/input.html + * @property {String | Number} value 输入的值 + * @property {String} type 输入框类型,见上方说明 ( 默认 'text' ) + * @property {Boolean} fixed 如果 textarea 是在一个 position:fixed 的区域,需要显示指定属性 fixed 为 true,兼容性:微信小程序、百度小程序、字节跳动小程序、QQ小程序 ( 默认 false ) + * @property {Boolean} disabled 是否禁用输入框 ( 默认 false ) + * @property {String} disabledColor 禁用状态时的背景色( 默认 '#f5f7fa' ) + * @property {Boolean} clearable 是否显示清除控件 ( 默认 false ) + * @property {Boolean} password 是否密码类型 ( 默认 false ) + * @property {String | Number} maxlength 最大输入长度,设置为 -1 的时候不限制最大长度 ( 默认 -1 ) + * @property {String} placeholder 输入框为空时的占位符 + * @property {String} placeholderClass 指定placeholder的样式类,注意页面或组件的style中写了scoped时,需要在类名前写/deep/ ( 默认 'input-placeholder' ) + * @property {String | Object} placeholderStyle 指定placeholder的样式,字符串/对象形式,如"color: red;" + * @property {Boolean} showWordLimit 是否显示输入字数统计,只在 type ="text"或type ="textarea"时有效 ( 默认 false ) + * @property {String} confirmType 设置右下角按钮的文字,兼容性详见uni-app文档 ( 默认 'done' ) + * @property {Boolean} confirmHold 点击键盘右下角按钮时是否保持键盘不收起,H5无效 ( 默认 false ) + * @property {Boolean} holdKeyboard focus时,点击页面的时候不收起键盘,微信小程序有效 ( 默认 false ) + * @property {Boolean} focus 自动获取焦点,在 H5 平台能否聚焦以及软键盘是否跟随弹出,取决于当前浏览器本身的实现。nvue 页面不支持,需使用组件的 focus()、blur() 方法控制焦点 ( 默认 false ) + * @property {Boolean} autoBlur 键盘收起时,是否自动失去焦点,目前仅App3.0.0+有效 ( 默认 false ) + * @property {Boolean} disableDefaultPadding 是否去掉 iOS 下的默认内边距,仅微信小程序,且type=textarea时有效 ( 默认 false ) + * @property {String | Number} cursor 指定focus时光标的位置( 默认 -1 ) + * @property {String | Number} cursorSpacing 输入框聚焦时底部与键盘的距离 ( 默认 30 ) + * @property {String | Number} selectionStart 光标起始位置,自动聚集时有效,需与selection-end搭配使用 ( 默认 -1 ) + * @property {String | Number} selectionEnd 光标结束位置,自动聚集时有效,需与selection-start搭配使用 ( 默认 -1 ) + * @property {Boolean} adjustPosition 键盘弹起时,是否自动上推页面 ( 默认 true ) + * @property {String} inputAlign 输入框内容对齐方式( 默认 'left' ) + * @property {String | Number} fontSize 输入框字体的大小 ( 默认 '15px' ) + * @property {String} color 输入框字体颜色 ( 默认 '#303133' ) + * @property {Function} formatter 内容式化函数 + * @property {String} prefixIcon 输入框前置图标 + * @property {String | Object} prefixIconStyle 前置图标样式,对象或字符串 + * @property {String} suffixIcon 输入框后置图标 + * @property {String | Object} suffixIconStyle 后置图标样式,对象或字符串 + * @property {String} border 边框类型,surround-四周边框,bottom-底部边框,none-无边框 ( 默认 'surround' ) + * @property {Boolean} readonly 是否只读,与disabled不同之处在于disabled会置灰组件,而readonly则不会 ( 默认 false ) + * @property {String} shape 输入框形状,circle-圆形,square-方形 ( 默认 'square' ) + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @example <u-input v-model="value" :password="true" suffix-icon="lock-fill" /> + */ +export default { + name: "u-input", + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + // 输入框的值 + innerValue: "", + // 是否处于获得焦点状态 + focused: false, + // value是否第一次变化,在watch中,由于加入immediate属性,会在第一次触发,此时不应该认为value发生了变化 + firstChange: true, + // value绑定值的变化是由内部还是外部引起的 + changeFromInner: false, + // 过滤处理方法 + innerFormatter: value => value + }; + }, + watch: { + value: { + immediate: true, + handler(newVal, oldVal) { + this.innerValue = newVal; + /* #ifdef H5 */ + // 在H5中,外部value变化后,修改input中的值,不会触发@input事件,此时手动调用值变化方法 + if ( + this.firstChange === false && + this.changeFromInner === false + ) { + this.valueChange(); + } + /* #endif */ + this.firstChange = false; + // 重置changeFromInner的值为false,标识下一次引起默认为外部引起的 + this.changeFromInner = false; + }, + }, + }, + computed: { + // 是否显示清除控件 + isShowClear() { + const { clearable, readonly, focused, innerValue } = this; + return !!clearable && !readonly && !!focused && innerValue !== ""; + }, + // 组件的类名 + inputClass() { + let classes = [], + { border, disabled, shape } = this; + border === "surround" && + (classes = classes.concat(["u-border", "u-input--radius"])); + classes.push(`u-input--${shape}`); + border === "bottom" && + (classes = classes.concat([ + "u-border-bottom", + "u-input--no-radius", + ])); + return classes.join(" "); + }, + // 组件的样式 + wrapperStyle() { + const style = {}; + // 禁用状态下,被背景色加上对应的样式 + if (this.disabled) { + style.backgroundColor = this.disabledColor; + } + // 无边框时,去除内边距 + if (this.border === "none") { + style.padding = "0"; + } else { + // 由于uni-app的iOS开发者能力有限,导致需要分开写才有效 + style.paddingTop = "6px"; + style.paddingBottom = "6px"; + style.paddingLeft = "9px"; + style.paddingRight = "9px"; + } + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)); + }, + // 输入框的样式 + inputStyle() { + const style = { + color: this.color, + fontSize: uni.$u.addUnit(this.fontSize), + textAlign: this.inputAlign + }; + return style; + }, + }, + methods: { + // 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用 + setFormatter(e) { + this.innerFormatter = e + }, + // 当键盘输入时,触发input事件 + onInput(e) { + let { value = "" } = e.detail || {}; + // 格式化过滤方法 + const formatter = this.formatter || this.innerFormatter + const formatValue = formatter(value) + // 为了避免props的单向数据流特性,需要先将innerValue值设置为当前值,再在$nextTick中重新赋予设置后的值才有效 + this.innerValue = value + this.$nextTick(() => { + this.innerValue = formatValue; + this.valueChange(); + }) + }, + // 输入框失去焦点时触发 + onBlur(event) { + this.$emit("blur", event.detail.value); + // H5端的blur会先于点击清除控件的点击click事件触发,导致focused + // 瞬间为false,从而隐藏了清除控件而无法被点击到 + uni.$u.sleep(50).then(() => { + this.focused = false; + }); + // 尝试调用u-form的验证方法 + uni.$u.formValidate(this, "blur"); + }, + // 输入框聚焦时触发 + onFocus(event) { + this.focused = true; + this.$emit("focus"); + }, + // 点击完成按钮时触发 + onConfirm(event) { + this.$emit("confirm", this.innerValue); + }, + // 键盘高度发生变化的时候触发此事件 + // 兼容性:微信小程序2.7.0+、App 3.1.0+ + onkeyboardheightchange() { + this.$emit("keyboardheightchange"); + }, + // 内容发生变化,进行处理 + valueChange() { + const value = this.innerValue; + this.$nextTick(() => { + this.$emit("input", value); + // 标识value值的变化是由内部引起的 + this.changeFromInner = true; + this.$emit("change", value); + // 尝试调用u-form的验证方法 + uni.$u.formValidate(this, "change"); + }); + }, + // 点击清除控件 + onClear() { + this.innerValue = ""; + this.$nextTick(() => { + this.valueChange(); + this.$emit("clear"); + }); + }, + /** + * 在安卓nvue上,事件无法冒泡 + * 在某些时间,我们希望监听u-from-item的点击事件,此时会导致点击u-form-item内的u-input后 + * 无法触发u-form-item的点击事件,这里通过手动调用u-form-item的方法进行触发 + */ + clickHandler() { + // #ifdef APP-NVUE + if (uni.$u.os() === "android") { + const formItem = uni.$u.$parent.call(this, "u-form-item"); + if (formItem) { + formItem.clickHandler(); + } + } + // #endif + }, + }, +}; +</script> + +<style lang="scss" scoped> +@import "../../libs/css/components.scss"; + +.u-input { + @include flex(row); + align-items: center; + justify-content: space-between; + flex: 1; + + &--radius, + &--square { + border-radius: 4px; + } + + &--no-radius { + border-radius: 0; + } + + &--circle { + border-radius: 100px; + } + + &__content { + flex: 1; + @include flex(row); + align-items: center; + justify-content: space-between; + + &__field-wrapper { + position: relative; + @include flex(row); + margin: 0; + flex: 1; + + &__field { + line-height: 26px; + text-align: left; + color: $u-main-color; + height: 24px; + font-size: 15px; + flex: 1; + } + } + + &__clear { + width: 20px; + height: 20px; + border-radius: 100px; + background-color: #c6c7cb; + @include flex(row); + align-items: center; + justify-content: center; + transform: scale(0.82); + margin-left: 4px; + } + + &__subfix-icon { + margin-left: 4px; + } + + &__prefix-icon { + margin-right: 4px; + } + } +} +</style> diff --git a/uni_modules/uview-ui/components/u-keyboard/props.js b/uni_modules/uview-ui/components/u-keyboard/props.js new file mode 100644 index 0000000..cfdb00a --- /dev/null +++ b/uni_modules/uview-ui/components/u-keyboard/props.js @@ -0,0 +1,84 @@ +export default { + props: { + // 键盘的类型,number-数字键盘,card-身份证键盘,car-车牌号键盘 + mode: { + type: String, + default: uni.$u.props.keyboard.mode + }, + // 是否显示键盘的"."符号 + dotDisabled: { + type: Boolean, + default: uni.$u.props.keyboard.dotDisabled + }, + // 是否显示顶部工具条 + tooltip: { + type: Boolean, + default: uni.$u.props.keyboard.tooltip + }, + // 是否显示工具条中间的提示 + showTips: { + type: Boolean, + default: uni.$u.props.keyboard.showTips + }, + // 工具条中间的提示文字 + tips: { + type: String, + default: uni.$u.props.keyboard.tips + }, + // 是否显示工具条左边的"取消"按钮 + showCancel: { + type: Boolean, + default: uni.$u.props.keyboard.showCancel + }, + // 是否显示工具条右边的"完成"按钮 + showConfirm: { + type: Boolean, + default: uni.$u.props.keyboard.showConfirm + }, + // 是否打乱键盘按键的顺序 + random: { + type: Boolean, + default: uni.$u.props.keyboard.random + }, + // 是否开启底部安全区适配,开启的话,会在iPhoneX机型底部添加一定的内边距 + safeAreaInsetBottom: { + type: Boolean, + default: uni.$u.props.keyboard.safeAreaInsetBottom + }, + // 是否允许通过点击遮罩关闭键盘 + closeOnClickOverlay: { + type: Boolean, + default: uni.$u.props.keyboard.closeOnClickOverlay + }, + // 控制键盘的弹出与收起 + show: { + type: Boolean, + default: uni.$u.props.keyboard.show + }, + // 是否显示遮罩,某些时候数字键盘时,用户希望看到自己的数值,所以可能不想要遮罩 + overlay: { + type: Boolean, + default: uni.$u.props.keyboard.overlay + }, + // z-index值 + zIndex: { + type: [String, Number], + default: uni.$u.props.keyboard.zIndex + }, + // 取消按钮的文字 + cancelText: { + type: String, + default: uni.$u.props.keyboard.cancelText + }, + // 确认按钮的文字 + confirmText: { + type: String, + default: uni.$u.props.keyboard.confirmText + }, + // 输入一个中文后,是否自动切换到英文 + autoChange: { + type: Boolean, + default: uni.$u.props.keyboard.autoChange + } + } +} diff --git a/uni_modules/uview-ui/components/u-keyboard/u-keyboard.vue b/uni_modules/uview-ui/components/u-keyboard/u-keyboard.vue new file mode 100644 index 0000000..14228cb --- /dev/null +++ b/uni_modules/uview-ui/components/u-keyboard/u-keyboard.vue @@ -0,0 +1,164 @@ +<template> + <u-popup + :overlay="overlay" + :closeOnClickOverlay="closeOnClickOverlay" + mode="bottom" + :popup="false" + :show="show" + :safeAreaInsetBottom="safeAreaInsetBottom" + @close="popupClose" + :zIndex="zIndex" + :customStyle="{ + backgroundColor: 'rgb(214, 218, 220)' + }" + > + <view class="u-keyboard"> + <slot /> + <view + class="u-keyboard__tooltip" + v-if="tooltip" + > + <view + hover-class="u-hover-class" + :hover-stay-time="100" + > + <text + class="u-keyboard__tooltip__item u-keyboard__tooltip__cancel" + v-if="showCancel" + @tap="onCancel" + >{{showCancel && cancelText}}</text> + </view> + <view> + <text + v-if="showTips" + class="u-keyboard__tooltip__item u-keyboard__tooltip__tips" + >{{tips ? tips : mode == 'number' ? '数字键盘' : mode == 'card' ? '身份证键盘' : '车牌号键盘'}}</text> + </view> + <view + hover-class="u-hover-class" + :hover-stay-time="100" + > + <text + v-if="showConfirm" + @tap="onConfirm" + class="u-keyboard__tooltip__item u-keyboard__tooltip__submit" + hover-class="u-hover-class" + >{{showConfirm && confirmText}}</text> + </view> + </view> + <template v-if="mode == 'number' || mode == 'card'"> + <u-number-keyboard + :random="random" + @backspace="backspace" + @change="change" + :mode="mode" + :dotDisabled="dotDisabled" + ></u-number-keyboard> + </template> + <template v-else> + <u-car-keyboard + :random="random" + :autoChange="autoChange" + @backspace="backspace" + @change="change" + ></u-car-keyboard> + </template> + </view> + </u-popup> +</template> + +<script> + import props from './props.js'; + + /** + * keyboard 键盘 + * @description 此为uViw自定义的键盘面板,内含了数字键盘,车牌号键,身份证号键盘3中模式,都有可以打乱按键顺序的选项。 + * @tutorial https://www.uviewui.com/components/keyboard.html + * @property {String} mode 键盘类型,见官网基本使用的说明 (默认 'number' ) + * @property {Boolean} dotDisabled 是否显示"."按键,只在mode=number时有效 (默认 false ) + * @property {Boolean} tooltip 是否显示键盘顶部工具条 (默认 true ) + * @property {Boolean} showTips 是否显示工具条中间的提示 (默认 true ) + * @property {String} tips 工具条中间的提示文字,见上方基本使用的说明,如不需要,请传""空字符 + * @property {Boolean} showCancel 是否显示工具条左边的"取消"按钮 (默认 true ) + * @property {Boolean} showConfirm 是否显示工具条右边的"完成"按钮( 默认 true ) + * @property {Boolean} random 是否打乱键盘按键的顺序 (默认 false ) + * @property {Boolean} safeAreaInsetBottom 是否开启底部安全区适配 (默认 true ) + * @property {Boolean} closeOnClickOverlay 是否允许点击遮罩收起键盘 (默认 true ) + * @property {Boolean} show 控制键盘的弹出与收起(默认 false ) + * @property {Boolean} overlay 是否显示遮罩 (默认 true ) + * @property {String | Number} zIndex 弹出键盘的z-index值 (默认 1075 ) + * @property {String} cancelText 取消按钮的文字 (默认 '取消' ) + * @property {String} confirmText 确认按钮的文字 (默认 '确认' ) + * @property {Object} customStyle 自定义样式,对象形式 + * @event {Function} change 按键被点击(不包含退格键被点击) + * @event {Function} cancel 键盘顶部工具条左边的"取消"按钮被点击 + * @event {Function} confirm 键盘顶部工具条右边的"完成"按钮被点击 + * @event {Function} backspace 键盘退格键被点击 + * @example <u-keyboard mode="number" v-model="show"></u-keyboard> + */ + export default { + name: "u-keyboard", + data() { + return { + + } + }, + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + methods: { + change(e) { + this.$emit('change', e); + }, + // 键盘关闭 + popupClose() { + this.$emit('close'); + }, + // 输入完成 + onConfirm() { + this.$emit('confirm'); + }, + // 取消输入 + onCancel() { + this.$emit('cancel'); + }, + // 退格键 + backspace() { + this.$emit('backspace'); + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-keyboard { + + &__tooltip { + @include flex; + justify-content: space-between; + background-color: #FFFFFF; + padding: 14px 12px; + + &__item { + color: #333333; + flex: 1; + text-align: center; + font-size: 15px; + } + + &__submit { + text-align: right; + color: $u-primary; + } + + &__cancel { + text-align: left; + color: #888888; + } + + &__tips { + color: $u-tips-color; + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-line-progress/props.js b/uni_modules/uview-ui/components/u-line-progress/props.js new file mode 100644 index 0000000..a4210bd --- /dev/null +++ b/uni_modules/uview-ui/components/u-line-progress/props.js @@ -0,0 +1,28 @@ +export default { + props: { + // 激活部分的颜色 + activeColor: { + type: String, + default: uni.$u.props.lineProgress.activeColor + }, + inactiveColor: { + type: String, + default: uni.$u.props.lineProgress.color + }, + // 进度百分比,数值 + percentage: { + type: [String, Number], + default: uni.$u.props.lineProgress.inactiveColor + }, + // 是否在进度条内部显示百分比的值 + showText: { + type: Boolean, + default: uni.$u.props.lineProgress.showText + }, + // 进度条的高度,单位px + height: { + type: [String, Number], + default: uni.$u.props.lineProgress.height + } + } +} diff --git a/uni_modules/uview-ui/components/u-line-progress/u-line-progress.vue b/uni_modules/uview-ui/components/u-line-progress/u-line-progress.vue new file mode 100644 index 0000000..4e27931 --- /dev/null +++ b/uni_modules/uview-ui/components/u-line-progress/u-line-progress.vue @@ -0,0 +1,144 @@ +<template> + <view + class="u-line-progress" + :style="[$u.addStyle(customStyle)]" + > + <view + class="u-line-progress__background" + ref="u-line-progress__background" + :style="[{ + backgroundColor: inactiveColor, + height: $u.addUnit(height), + }]" + > + </view> + <view + class="u-line-progress__line" + :style="[progressStyle]" + > + <slot> + <text v-if="showText && percentage >= 10" class="u-line-progress__text">{{innserPercentage + '%'}}</text> + </slot> + </view> + </view> +</template> + +<script> + import props from './props.js'; + // #ifdef APP-NVUE + const dom = uni.requireNativePlugin('dom') + // #endif + /** + * lineProgress 线型进度条 + * @description 展示操作或任务的当前进度,比如上传文件,是一个线形的进度条。 + * @tutorial https://www.uviewui.com/components/lineProgress.html + * @property {String} activeColor 激活部分的颜色 ( 默认 '#19be6b' ) + * @property {String} inactiveColor 背景色 ( 默认 '#ececec' ) + * @property {String | Number} percentage 进度百分比,数值 ( 默认 0 ) + * @property {Boolean} showText 是否在进度条内部显示百分比的值 ( 默认 true ) + * @property {String | Number} height 进度条的高度,单位px ( 默认 12 ) + * + * @example <u-line-progress :percent="70" :show-percent="true"></u-line-progress> + */ + export default { + name: "u-line-progress", + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + lineWidth: 0, + } + }, + watch: { + percentage(n) { + this.resizeProgressWidth() + } + }, + computed: { + progressStyle() { + let style = {} + style.width = this.lineWidth + style.backgroundColor = this.activeColor + style.height = uni.$u.addUnit(this.height) + return style + }, + innserPercentage() { + // 控制范围在0-100之间 + return uni.$u.range(0, 100, this.percentage) + } + }, + mounted() { + this.init() + }, + methods: { + init() { + uni.$u.sleep(20).then(() => { + this.resizeProgressWidth() + }) + }, + getProgressWidth() { + // #ifndef APP-NVUE + return this.$uGetRect('.u-line-progress__background') + // #endif + + // #ifdef APP-NVUE + // 返回一个promise + return new Promise(resolve => { + dom.getComponentRect(this.$refs['u-line-progress__background'], (res) => { + resolve(res.size) + }) + }) + // #endif + }, + resizeProgressWidth() { + this.getProgressWidth().then(size => { + const { + width + } = size + // 通过设置的percentage值,计算其所占总长度的百分比 + this.lineWidth = width * this.innserPercentage / 100 + 'px' + }) + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-line-progress { + align-items: stretch; + position: relative; + @include flex(row); + flex: 1; + overflow: hidden; + border-radius: 100px; + + &__background { + background-color: #ececec; + border-radius: 100px; + flex: 1; + } + + &__line { + position: absolute; + top: 0; + left: 0; + bottom: 0; + align-items: center; + @include flex(row); + color: #ffffff; + border-radius: 100px; + transition: width 0.5s ease; + justify-content: flex-end; + } + + &__text { + font-size: 10px; + align-items: center; + text-align: right; + color: #FFFFFF; + margin-right: 5px; + transform: scale(0.9); + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-line/props.js b/uni_modules/uview-ui/components/u-line/props.js new file mode 100644 index 0000000..2308cc3 --- /dev/null +++ b/uni_modules/uview-ui/components/u-line/props.js @@ -0,0 +1,33 @@ +export default { + props: { + color: { + type: String, + default: uni.$u.props.line.color + }, + // 长度,竖向时表现为高度,横向时表现为长度,可以为百分比,带px单位的值等 + length: { + type: [String, Number], + default: uni.$u.props.line.length + }, + // 线条方向,col-竖向,row-横向 + direction: { + type: String, + default: uni.$u.props.line.direction + }, + // 是否显示细边框 + hairline: { + type: Boolean, + default: uni.$u.props.line.hairline + }, + // 线条与上下左右元素的间距,字符串形式,如"30px"、"20px 30px" + margin: { + type: [String, Number], + default: uni.$u.props.line.margin + }, + // 是否虚线,true-虚线,false-实线 + dashed: { + type: Boolean, + default: uni.$u.props.line.dashed + } + } +} diff --git a/uni_modules/uview-ui/components/u-line/u-line.vue b/uni_modules/uview-ui/components/u-line/u-line.vue new file mode 100644 index 0000000..e0a6d92 --- /dev/null +++ b/uni_modules/uview-ui/components/u-line/u-line.vue @@ -0,0 +1,62 @@ +<template> + <view + class="u-line" + :style="[lineStyle]" + > + + </view> +</template> + +<script> + import props from './props.js'; + /** + * line 线条 + * @description 此组件一般用于显示一根线条,用于分隔内容块,有横向和竖向两种模式,且能设置0.5px线条,使用也很简单 + * @tutorial https://www.uviewui.com/components/line.html + * @property {String} color 线条的颜色 ( 默认 '#d6d7d9' ) + * @property {String | Number} length 长度,竖向时表现为高度,横向时表现为长度,可以为百分比,带px单位的值等 ( 默认 '100%' ) + * @property {String} direction 线条的方向,row-横向,col-竖向 (默认 'row' ) + * @property {Boolean} hairline 是否显示细线条 (默认 true ) + * @property {String | Number} margin 线条与上下左右元素的间距,字符串形式,如"30px" (默认 0 ) + * @property {Boolean} dashed 是否虚线,true-虚线,false-实线 (默认 false ) + * @property {Object} customStyle 定义需要用到的外部样式 + * @example <u-line color="red"></u-line> + */ + export default { + name: 'u-line', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + computed: { + lineStyle() { + const style = {} + style.margin = this.margin + // 如果是水平线条,边框高度为1px,再通过transform缩小一半,就是0.5px了 + if (this.direction === 'row') { + // 此处采用兼容分开写,兼容nvue的写法 + style.borderBottomWidth = '1px' + style.borderBottomStyle = this.dashed ? 'dashed' : 'solid' + style.width = uni.$u.addUnit(this.length) + if (this.hairline) style.transform = 'scaleY(0.5)' + } else { + // 如果是竖向线条,边框宽度为1px,再通过transform缩小一半,就是0.5px了 + style.borderLeftWidth = '1px' + style.borderLeftStyle = this.dashed ? 'dashed' : 'solid' + style.height = uni.$u.addUnit(this.length) + if (this.hairline) style.transform = 'scaleX(0.5)' + } + + style.borderColor = this.color + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-line { + /* #ifndef APP-NVUE */ + vertical-align: middle; + /* #endif */ + } +</style> diff --git a/uni_modules/uview-ui/components/u-link/props.js b/uni_modules/uview-ui/components/u-link/props.js new file mode 100644 index 0000000..d39353f --- /dev/null +++ b/uni_modules/uview-ui/components/u-link/props.js @@ -0,0 +1,39 @@ +export default { + props: { + // 文字颜色 + color: { + type: String, + default: uni.$u.props.link.color + }, + // 字体大小,单位px + fontSize: { + type: [String, Number], + default: uni.$u.props.link.fontSize + }, + // 是否显示下划线 + underLine: { + type: Boolean, + default: uni.$u.props.link.underLine + }, + // 要跳转的链接 + href: { + type: String, + default: uni.$u.props.link.href + }, + // 小程序中复制到粘贴板的提示语 + mpTips: { + type: String, + default: uni.$u.props.link.mpTips + }, + // 下划线颜色 + lineColor: { + type: String, + default: uni.$u.props.link.lineColor + }, + // 超链接的问题,不使用slot形式传入,是因为nvue下无法修改颜色 + text: { + type: String, + default: uni.$u.props.link.text + } + } +} diff --git a/uni_modules/uview-ui/components/u-link/u-link.vue b/uni_modules/uview-ui/components/u-link/u-link.vue new file mode 100644 index 0000000..c6802a5 --- /dev/null +++ b/uni_modules/uview-ui/components/u-link/u-link.vue @@ -0,0 +1,83 @@ +<template> + <text + class="u-link" + @tap.stop="openLink" + :style="[linkStyle, $u.addStyle(customStyle)]" + >{{text}}</text> +</template> + +<script> + import props from './props.js'; + + /** + * link 超链接 + * @description 该组件为超链接组件,在不同平台有不同表现形式:在APP平台会通过plus环境打开内置浏览器,在小程序中把链接复制到粘贴板,同时提示信息,在H5中通过window.open打开链接。 + * @tutorial https://www.uviewui.com/components/link.html + * @property {String} color 文字颜色 (默认 color['u-primary'] ) + * @property {String | Number} fontSize 字体大小,单位px (默认 15 ) + * @property {Boolean} underLine 是否显示下划线 (默认 false ) + * @property {String} href 跳转的链接,要带上http(s) + * @property {String} mpTips 各个小程序平台把链接复制到粘贴板后的提示语(默认“链接已复制,请在浏览器打开”) + * @property {String} lineColor 下划线颜色,默认同color参数颜色 + * @property {String} text 超链接的问题,不使用slot形式传入,是因为nvue下无法修改颜色 + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @example <u-link href="http://www.uviewui.com">蜀道难,难于上青天</u-link> + */ + export default { + name: "u-link", + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + computed: { + linkStyle() { + const style = { + color: this.color, + fontSize: uni.$u.addUnit(this.fontSize), + // line-height设置为比字体大小多2px + lineHeight: uni.$u.addUnit(uni.$u.getPx(this.fontSize) + 2), + textDecoration: this.underLine ? 'underline' : 'none' + } + // if (this.underLine) { + // style.borderBottomColor = this.lineColor || this.color + // style.borderBottomWidth = '1px' + // } + return style + } + }, + methods: { + openLink() { + // #ifdef APP-PLUS + plus.runtime.openURL(this.href) + // #endif + // #ifdef H5 + window.open(this.href) + // #endif + // #ifdef MP + uni.setClipboardData({ + data: this.href, + success: () => { + uni.hideToast(); + this.$nextTick(() => { + uni.$u.toast(this.mpTips); + }) + } + }); + // #endif + this.$emit('click') + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + $u-link-line-height:1 !default; + + .u-link { + /* #ifndef APP-NVUE */ + line-height: $u-link-line-height; + /* #endif */ + @include flex; + flex-wrap: wrap; + flex: 1; + } +</style> diff --git a/uni_modules/uview-ui/components/u-list-item/props.js b/uni_modules/uview-ui/components/u-list-item/props.js new file mode 100644 index 0000000..58ddc49 --- /dev/null +++ b/uni_modules/uview-ui/components/u-list-item/props.js @@ -0,0 +1,9 @@ +export default { + props: { + // 用于滚动到指定item + anchor: { + type: [String, Number], + default: uni.$u.props.listItem.anchor + } + } +} diff --git a/uni_modules/uview-ui/components/u-list-item/u-list-item.vue b/uni_modules/uview-ui/components/u-list-item/u-list-item.vue new file mode 100644 index 0000000..1a25db6 --- /dev/null +++ b/uni_modules/uview-ui/components/u-list-item/u-list-item.vue @@ -0,0 +1,116 @@ +<template> + <!-- #ifdef APP-NVUE --> + <cell> + <!-- #endif --> + <view + class="u-list-item" + :ref="`u-list-item-${anchor}`" + :anchor="`u-list-item-${anchor}`" + :class="[`u-list-item-${anchor}`]" + > + <slot /> + </view> + <!-- #ifdef APP-NVUE --> + </cell> + <!-- #endif --> +</template> + +<script> + import props from './props.js'; + // #ifdef APP-NVUE + const dom = uni.requireNativePlugin('dom') + // #endif + /** + * List 列表 + * @description 该组件为高性能列表组件 + * @tutorial https://www.uviewui.com/components/list.html + * @property {String | Number} anchor 用于滚动到指定item + * @example <u-list-ite v-for="(item, index) in indexList" :key="index" ></u-list-item> + */ + export default { + name: 'u-list-item', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + // 节点信息 + rect: {}, + index: 0, + show: true, + sys: uni.$u.sys() + } + }, + computed: { + + }, + inject: ['uList'], + watch: { + // #ifndef APP-NVUE + 'uList.innerScrollTop'(n) { + const preLoadScreen = this.uList.preLoadScreen + const windowHeight = this.sys.windowHeight + if(n <= windowHeight * preLoadScreen) { + this.parent.updateOffsetFromChild(0) + } else if (this.rect.top <= n - windowHeight * preLoadScreen) { + this.parent.updateOffsetFromChild(this.rect.top) + } + } + // #endif + }, + created() { + this.parent = {} + }, + mounted() { + this.init() + }, + methods: { + init() { + // 初始化数据 + this.updateParentData() + this.index = this.parent.children.indexOf(this) + this.resize() + }, + updateParentData() { + // 此方法在mixin中 + this.getParentData('u-list') + }, + resize() { + this.queryRect(`u-list-item-${this.anchor}`).then(size => { + const lastChild = this.parent.children[this.index - 1] + this.rect = size + const preLoadScreen = this.uList.preLoadScreen + const windowHeight = this.sys.windowHeight + // #ifndef APP-NVUE + if (lastChild) { + this.rect.top = lastChild.rect.top + lastChild.rect.height + } + if (size.top >= this.uList.innerScrollTop + (1 + preLoadScreen) * windowHeight) this.show = + false + // #endif + }) + }, + // 查询元素尺寸 + queryRect(el) { + return new Promise(resolve => { + // #ifndef APP-NVUE + this.$uGetRect(`.${el}`).then(size => { + resolve(size) + }) + // #endif + + // #ifdef APP-NVUE + const ref = this.$refs[el] + dom.getComponentRect(ref, res => { + resolve(res.size) + }) + // #endif + }) + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-list-item {} +</style> diff --git a/uni_modules/uview-ui/components/u-list/props.js b/uni_modules/uview-ui/components/u-list/props.js new file mode 100644 index 0000000..25406f4 --- /dev/null +++ b/uni_modules/uview-ui/components/u-list/props.js @@ -0,0 +1,76 @@ +export default { + props: { + // 控制是否出现滚动条,仅nvue有效 + showScrollbar: { + type: Boolean, + default: uni.$u.props.list.showScrollbar + }, + // 距底部多少时触发scrolltolower事件 + lowerThreshold: { + type: [String, Number], + default: uni.$u.props.list.lowerThreshold + }, + // 距顶部多少时触发scrolltoupper事件,非nvue有效 + upperThreshold: { + type: [String, Number], + default: uni.$u.props.list.upperThreshold + }, + // 设置竖向滚动条位置 + scrollTop: { + type: [String, Number], + default: uni.$u.props.list.scrollTop + }, + // 控制 onscroll 事件触发的频率,仅nvue有效 + offsetAccuracy: { + type: [String, Number], + default: uni.$u.props.list.offsetAccuracy + }, + // 启用 flexbox 布局。开启后,当前节点声明了display: flex就会成为flex container,并作用于其孩子节点,仅微信小程序有效 + enableFlex: { + type: Boolean, + default: uni.$u.props.list.enableFlex + }, + // 是否按分页模式显示List,默认值false + pagingEnabled: { + type: Boolean, + default: uni.$u.props.list.pagingEnabled + }, + // 是否允许List滚动 + scrollable: { + type: Boolean, + default: uni.$u.props.list.scrollable + }, + // 值应为某子元素id(id不能以数字开头) + scrollIntoView: { + type: String, + default: uni.$u.props.list.scrollIntoView + }, + // 在设置滚动条位置时使用动画过渡 + scrollWithAnimation: { + type: Boolean, + default: uni.$u.props.list.scrollWithAnimation + }, + // iOS点击顶部状态栏、安卓双击标题栏时,滚动条返回顶部,只对微信小程序有效 + enableBackToTop: { + type: Boolean, + default: uni.$u.props.list.enableBackToTop + }, + // 列表的高度 + height: { + type: [String, Number], + default: uni.$u.props.list.height + }, + // 列表宽度 + width: { + type: [String, Number], + default: uni.$u.props.list.width + }, + // 列表前后预渲染的屏数,1代表一个屏幕的高度,1.5代表1个半屏幕高度 + preLoadScreen: { + type: [String, Number], + default: uni.$u.props.list.preLoadScreen + } + // vue下,是否开启虚拟列表 + + } +} diff --git a/uni_modules/uview-ui/components/u-list/u-list.vue b/uni_modules/uview-ui/components/u-list/u-list.vue new file mode 100644 index 0000000..4447cab --- /dev/null +++ b/uni_modules/uview-ui/components/u-list/u-list.vue @@ -0,0 +1,157 @@ +<template> + <!-- #ifdef APP-NVUE --> + <list + class="u-list" + :enableBackToTop="enableBackToTop" + :loadmoreoffset="lowerThreshold" + :showScrollbar="showScrollbar" + :style="[listStyle]" + :offset-accuracy="Number(offsetAccuracy)" + @scroll="onScroll" + @loadmore="scrolltolower" + > + <slot /> + </list> + <!-- #endif --> + <!-- #ifndef APP-NVUE --> + <scroll-view + class="u-list" + :scroll-into-view="scrollIntoView" + :style="[listStyle]" + scroll-y + :scroll-top="Number(scrollTop)" + :lower-threshold="Number(lowerThreshold)" + :upper-threshold="Number(upperThreshold)" + :show-scrollbar="showScrollbar" + :enable-back-to-top="enableBackToTop" + :scroll-with-animation="scrollWithAnimation" + @scroll="onScroll" + @scrolltolower="scrolltolower" + @scrolltoupper="scrolltoupper" + > + <view> + <slot /> + </view> + </scroll-view> + <!-- #endif --> +</template> + +<script> + import props from './props.js'; + // #ifdef APP-NVUE + const dom = uni.requireNativePlugin('dom') + // #endif + /** + * List 列表 + * @description 该组件为高性能列表组件 + * @tutorial https://www.uviewui.com/components/list.html + * @property {Boolean} showScrollbar 控制是否出现滚动条,仅nvue有效 (默认 false ) + * @property {String | Number} lowerThreshold 距底部多少时触发scrolltolower事件 (默认 50 ) + * @property {String | Number} upperThreshold 距顶部多少时触发scrolltoupper事件,非nvue有效 (默认 0 ) + * @property {String | Number} scrollTop 设置竖向滚动条位置(默认 0 ) + * @property {String | Number} offsetAccuracy 控制 onscroll 事件触发的频率,仅nvue有效(默认 10 ) + * @property {Boolean} enableFlex 启用 flexbox 布局。开启后,当前节点声明了display: flex就会成为flex container,并作用于其孩子节点,仅微信小程序有效(默认 false ) + * @property {Boolean} pagingEnabled 是否按分页模式显示List,(默认 false ) + * @property {Boolean} scrollable 是否允许List滚动(默认 true ) + * @property {String} scrollIntoView 值应为某子元素id(id不能以数字开头) + * @property {Boolean} scrollWithAnimation 在设置滚动条位置时使用动画过渡 (默认 false ) + * @property {Boolean} enableBackToTop iOS点击顶部状态栏、安卓双击标题栏时,滚动条返回顶部,只对微信小程序有效 (默认 false ) + * @property {String | Number} height 列表的高度 (默认 0 ) + * @property {String | Number} width 列表宽度 (默认 0 ) + * @property {String | Number} preLoadScreen 列表前后预渲染的屏数,1代表一个屏幕的高度,1.5代表1个半屏幕高度 (默认 1 ) + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @example <u-list @scrolltolower="scrolltolower"></u-list> + */ + export default { + name: 'u-list', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + watch: { + scrollIntoView(n) { + this.scrollIntoViewById(n) + } + }, + data() { + return { + // 记录内部滚动的距离 + innerScrollTop: 0, + // vue下,scroll-view在上拉加载时的偏移值 + offset: 0, + sys: uni.$u.sys() + } + }, + computed: { + listStyle() { + const style = {}, + addUnit = uni.$u.addUnit + if (this.width != 0) style.width = addUnit(this.width) + if (this.height != 0) style.height = addUnit(this.height) + // 如果没有定义列表高度,则默认使用屏幕高度 + if (!style.height) style.height = addUnit(this.sys.windowHeight, 'px') + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) + } + }, + provide() { + return { + uList: this + } + }, + created() { + this.refs = [] + this.children = [] + this.anchors = [] + }, + mounted() {}, + methods: { + updateOffsetFromChild(top) { + this.offset = top + }, + onScroll(e) { + let scrollTop = 0 + // #ifdef APP-NVUE + scrollTop = e.contentOffset.y + // #endif + // #ifndef APP-NVUE + scrollTop = e.detail.scrollTop + // #endif + this.innerScrollTop = scrollTop + this.$emit('scroll', Math.abs(scrollTop)) + }, + scrollIntoViewById(id) { + // #ifdef APP-NVUE + // 根据id参数,找到所有u-list-item中匹配的节点,再通过dom模块滚动到对应的位置 + const item = this.refs.find(item => item.$refs[id] ? true : false) + dom.scrollToElement(item.$refs[id], { + // 是否需要滚动动画 + animated: this.scrollWithAnimation + }) + // #endif + }, + // 滚动到底部触发事件 + scrolltolower(e) { + uni.$u.sleep(30).then(() => { + this.$emit('scrolltolower') + }) + }, + // #ifndef APP-NVUE + // 滚动到底部时触发,非nvue有效 + scrolltoupper(e) { + uni.$u.sleep(30).then(() => { + this.$emit('scrolltoupper') + // 这一句很重要,能绝对保证在性功能障碍的webview,滚动条到顶时,取消偏移值,让页面置顶 + this.offset = 0 + }) + } + // #endif + }, + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-list { + @include flex(column); + + } +</style> diff --git a/uni_modules/uview-ui/components/u-loading-icon/props.js b/uni_modules/uview-ui/components/u-loading-icon/props.js new file mode 100644 index 0000000..c35524e --- /dev/null +++ b/uni_modules/uview-ui/components/u-loading-icon/props.js @@ -0,0 +1,59 @@ +export default { + props: { + // 是否显示组件 + show: { + type: Boolean, + default: uni.$u.props.loadingIcon.show + }, + // 颜色 + color: { + type: String, + default: uni.$u.props.loadingIcon.color + }, + // 提示文字颜色 + textColor: { + type: String, + default: uni.$u.props.loadingIcon.textColor + }, + // 文字和图标是否垂直排列 + vertical: { + type: Boolean, + default: uni.$u.props.loadingIcon.vertical + }, + // 模式选择,circle-圆形,spinner-花朵形,semicircle-半圆形 + mode: { + type: String, + default: uni.$u.props.loadingIcon.mode + }, + // 图标大小,单位默认px + size: { + type: [String, Number], + default: uni.$u.props.loadingIcon.size + }, + // 文字大小 + textSize: { + type: [String, Number], + default: uni.$u.props.loadingIcon.textSize + }, + // 文字内容 + text: { + type: [String, Number], + default: uni.$u.props.loadingIcon.text + }, + // 动画模式 + timingFunction: { + type: String, + default: uni.$u.props.loadingIcon.timingFunction + }, + // 动画执行周期时间 + duration: { + type: [String, Number], + default: uni.$u.props.loadingIcon.duration + }, + // mode=circle时的暗边颜色 + inactiveColor: { + type: String, + default: uni.$u.props.loadingIcon.inactiveColor + } + } +} diff --git a/uni_modules/uview-ui/components/u-loading-icon/u-loading-icon.vue b/uni_modules/uview-ui/components/u-loading-icon/u-loading-icon.vue new file mode 100644 index 0000000..2ede5c3 --- /dev/null +++ b/uni_modules/uview-ui/components/u-loading-icon/u-loading-icon.vue @@ -0,0 +1,343 @@ +<template> + <view + class="u-loading-icon" + :style="[$u.addStyle(customStyle)]" + :class="[vertical && 'u-loading-icon--vertical']" + v-if="show" + > + <view + v-if="!webviewHide" + class="u-loading-icon__spinner" + :class="[`u-loading-icon__spinner--${mode}`]" + ref="ani" + :style="{ + color: color, + width: $u.addUnit(size), + height: $u.addUnit(size), + borderTopColor: color, + borderBottomColor: otherBorderColor, + borderLeftColor: otherBorderColor, + borderRightColor: otherBorderColor, + 'animation-duration': `${duration}ms`, + 'animation-timing-function': mode === 'semicircle' || mode === 'circle' ? timingFunction : '' + }" + > + <block v-if="mode === 'spinner'"> + <!-- #ifndef APP-NVUE --> + <view + v-for="(item, index) in array12" + :key="index" + class="u-loading-icon__dot" + > + </view> + <!-- #endif --> + <!-- #ifdef APP-NVUE --> + <!-- 此组件内部图标部分无法设置宽高,即使通过width和height配置了也无效 --> + <loading-indicator + v-if="!webviewHide" + class="u-loading-indicator" + :animating="true" + :style="{ + color: color, + width: $u.addUnit(size), + height: $u.addUnit(size) + }" + /> + <!-- #endif --> + </block> + </view> + <text + v-if="text" + class="u-loading-icon__text" + :style="{ + fontSize: $u.addUnit(textSize), + color: textColor, + }" + >{{text}}</text> + </view> +</template> + +<script> + import props from './props.js'; + // #ifdef APP-NVUE + const animation = weex.requireModule('animation'); + // #endif + /** + * loading 加载动画 + * @description 警此组件为一个小动画,目前用在uView的loadmore加载更多和switch开关等组件的正在加载状态场景。 + * @tutorial https://www.uviewui.com/components/loading.html + * @property {Boolean} show 是否显示组件 (默认 true) + * @property {String} color 动画活动区域的颜色,只对 mode = flower 模式有效(默认color['u-tips-color']) + * @property {String} textColor 提示文本的颜色(默认color['u-tips-color']) + * @property {Boolean} vertical 文字和图标是否垂直排列 (默认 false ) + * @property {String} mode 模式选择,见官网说明(默认 'circle' ) + * @property {String | Number} size 加载图标的大小,单位px (默认 24 ) + * @property {String | Number} textSize 文字大小(默认 15 ) + * @property {String | Number} text 文字内容 + * @property {String} timingFunction 动画模式 (默认 'ease-in-out' ) + * @property {String | Number} duration 动画执行周期时间(默认 1200) + * @property {String} inactiveColor mode=circle时的暗边颜色 + * @property {Object} customStyle 定义需要用到的外部样式 + * @example <u-loading mode="circle"></u-loading> + */ + export default { + name: 'u-loading-icon', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + // Array.form可以通过一个伪数组对象创建指定长度的数组 + // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from + array12: Array.from({ + length: 12 + }), + // 这里需要设置默认值为360,否则在安卓nvue上,会延迟一个duration周期后才执行 + // 在iOS nvue上,则会一开始默认执行两个周期的动画 + aniAngel: 360, // 动画旋转角度 + webviewHide: false, // 监听webview的状态,如果隐藏了页面,则停止动画,以免性能消耗 + loading: false, // 是否运行中,针对nvue使用 + } + }, + computed: { + // 当为circle类型时,给其另外三边设置一个更轻一些的颜色 + // 之所以需要这么做的原因是,比如父组件传了color为红色,那么需要另外的三个边为浅红色 + // 而不能是固定的某一个其他颜色(因为这个固定的颜色可能浅蓝,导致效果没有那么细腻良好) + otherBorderColor() { + const lightColor = uni.$u.colorGradient(this.color, '#ffffff', 100)[80] + if (this.mode === 'circle') { + return this.inactiveColor ? this.inactiveColor : lightColor + } else { + return 'transparent' + } + // return this.mode === 'circle' ? this.inactiveColor ? this.inactiveColor : lightColor : 'transparent' + } + }, + watch: { + show(n) { + // nvue中,show为true,且为非loading状态,就重新执行动画模块 + // #ifdef APP-NVUE + if (n && !this.loading) { + setTimeout(() => { + this.startAnimate() + }, 30) + } + // #endif + } + }, + mounted() { + this.init() + }, + methods: { + init() { + setTimeout(() => { + // #ifdef APP-NVUE + this.show && this.nvueAnimate() + // #endif + // #ifdef APP-PLUS + this.show && this.addEventListenerToWebview() + // #endif + }, 20) + }, + // 监听webview的显示与隐藏 + addEventListenerToWebview() { + // webview的堆栈 + const pages = getCurrentPages() + // 当前页面 + const page = pages[pages.length - 1] + // 当前页面的webview实例 + const currentWebview = page.$getAppWebview() + // 监听webview的显示与隐藏,从而停止或者开始动画(为了性能) + currentWebview.addEventListener('hide', () => { + this.webviewHide = true + }) + currentWebview.addEventListener('show', () => { + this.webviewHide = false + }) + }, + // #ifdef APP-NVUE + nvueAnimate() { + // nvue下,非spinner类型时才需要旋转,因为nvue的spinner类型,使用了weex的 + // loading-indicator组件,自带旋转功能 + this.mode !== 'spinner' && this.startAnimate() + }, + // 执行nvue的animate模块动画 + startAnimate() { + this.loading = true + const ani = this.$refs.ani + if (!ani) return + animation.transition(ani, { + // 进行角度旋转 + styles: { + transform: `rotate(${this.aniAngel}deg)`, + transformOrigin: 'center center' + }, + duration: this.duration, + timingFunction: this.timingFunction, + // delay: 10 + }, () => { + // 每次增加360deg,为了让其重新旋转一周 + this.aniAngel += 360 + // 动画结束后,继续循环执行动画,需要同时判断webviewHide变量 + // nvue安卓,页面隐藏后依然会继续执行startAnimate方法 + this.show && !this.webviewHide ? this.startAnimate() : this.loading = false + }) + } + // #endif + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + $u-loading-icon-color: #c8c9cc !default; + $u-loading-icon-text-margin-left:4px !default; + $u-loading-icon-text-color:$u-content-color !default; + $u-loading-icon-text-font-size:14px !default; + $u-loading-icon-text-line-height:20px !default; + $u-loading-width:30px !default; + $u-loading-height:30px !default; + $u-loading-max-width:100% !default; + $u-loading-max-height:100% !default; + $u-loading-semicircle-border-width: 2px !default; + $u-loading-semicircle-border-color:transparent !default; + $u-loading-semicircle-border-top-right-radius: 100px !default; + $u-loading-semicircle-border-top-left-radius: 100px !default; + $u-loading-semicircle-border-bottom-left-radius: 100px !default; + $u-loading-semicircle-border-bottom-right-radiu: 100px !default; + $u-loading-semicircle-border-style: solid !default; + $u-loading-circle-border-top-right-radius: 100px !default; + $u-loading-circle-border-top-left-radius: 100px !default; + $u-loading-circle-border-bottom-left-radius: 100px !default; + $u-loading-circle-border-bottom-right-radiu: 100px !default; + $u-loading-circle-border-width:2px !default; + $u-loading-circle-border-top-color:#e5e5e5 !default; + $u-loading-circle-border-right-color:$u-loading-circle-border-top-color !default; + $u-loading-circle-border-bottom-color:$u-loading-circle-border-top-color !default; + $u-loading-circle-border-left-color:$u-loading-circle-border-top-color !default; + $u-loading-circle-border-style:solid !default; + $u-loading-icon-host-font-size:0px !default; + $u-loading-icon-host-line-height:1 !default; + $u-loading-icon-vertical-margin:6px 0 0 !default; + $u-loading-icon-dot-top:0 !default; + $u-loading-icon-dot-left:0 !default; + $u-loading-icon-dot-width:100% !default; + $u-loading-icon-dot-height:100% !default; + $u-loading-icon-dot-before-width:2px !default; + $u-loading-icon-dot-before-height:25% !default; + $u-loading-icon-dot-before-margin:0 auto !default; + $u-loading-icon-dot-before-background-color:currentColor !default; + $u-loading-icon-dot-before-border-radius:40% !default; + + .u-loading-icon { + /* #ifndef APP-NVUE */ + // display: inline-flex; + /* #endif */ + flex-direction: row; + align-items: center; + justify-content: center; + color: $u-loading-icon-color; + + &__text { + margin-left: $u-loading-icon-text-margin-left; + color: $u-loading-icon-text-color; + font-size: $u-loading-icon-text-font-size; + line-height: $u-loading-icon-text-line-height; + } + + &__spinner { + width: $u-loading-width; + height: $u-loading-height; + position: relative; + /* #ifndef APP-NVUE */ + box-sizing: border-box; + max-width: $u-loading-max-width; + max-height: $u-loading-max-height; + animation: u-rotate 1s linear infinite; + /* #endif */ + } + + &__spinner--semicircle { + border-width: $u-loading-semicircle-border-width; + border-color: $u-loading-semicircle-border-color; + border-top-right-radius: $u-loading-semicircle-border-top-right-radius; + border-top-left-radius: $u-loading-semicircle-border-top-left-radius; + border-bottom-left-radius: $u-loading-semicircle-border-bottom-left-radius; + border-bottom-right-radius: $u-loading-semicircle-border-bottom-right-radiu; + border-style: $u-loading-semicircle-border-style; + } + + &__spinner--circle { + border-top-right-radius: $u-loading-circle-border-top-right-radius; + border-top-left-radius: $u-loading-circle-border-top-left-radius; + border-bottom-left-radius: $u-loading-circle-border-bottom-left-radius; + border-bottom-right-radius: $u-loading-circle-border-bottom-right-radiu; + border-width: $u-loading-circle-border-width; + border-top-color: $u-loading-circle-border-top-color; + border-right-color: $u-loading-circle-border-right-color; + border-bottom-color: $u-loading-circle-border-bottom-color; + border-left-color: $u-loading-circle-border-left-color; + border-style: $u-loading-circle-border-style; + } + + &--vertical { + flex-direction: column + } + } + + /* #ifndef APP-NVUE */ + :host { + font-size: $u-loading-icon-host-font-size; + line-height: $u-loading-icon-host-line-height; + } + + .u-loading-icon { + &__spinner--spinner { + animation-timing-function: steps(12) + } + + &__text:empty { + display: none + } + + &--vertical &__text { + margin: $u-loading-icon-vertical-margin; + color: $u-content-color; + } + + &__dot { + position: absolute; + top: $u-loading-icon-dot-top; + left: $u-loading-icon-dot-left; + width: $u-loading-icon-dot-width; + height: $u-loading-icon-dot-height; + + &:before { + display: block; + width: $u-loading-icon-dot-before-width; + height: $u-loading-icon-dot-before-height; + margin: $u-loading-icon-dot-before-margin; + background-color: $u-loading-icon-dot-before-background-color; + border-radius: $u-loading-icon-dot-before-border-radius; + content: " " + } + } + } + + @for $i from 1 through 12 { + .u-loading-icon__dot:nth-of-type(#{$i}) { + transform: rotate($i * 30deg); + opacity: 1 - 0.0625 * ($i - 1); + } + } + + @keyframes u-rotate { + 0% { + transform: rotate(0deg) + } + + to { + transform: rotate(1turn) + } + } + + /* #endif */ +</style> diff --git a/uni_modules/uview-ui/components/u-loading-page/props.js b/uni_modules/uview-ui/components/u-loading-page/props.js new file mode 100644 index 0000000..e239b61 --- /dev/null +++ b/uni_modules/uview-ui/components/u-loading-page/props.js @@ -0,0 +1,49 @@ +export default { + props: { + // 提示内容 + loadingText: { + type: [String, Number], + default: uni.$u.props.loadingPage.loadingText + }, + // 文字上方用于替换loading动画的图片 + image: { + type: String, + default: uni.$u.props.loadingPage.image + }, + // 加载动画的模式,circle-圆形,spinner-花朵形,semicircle-半圆形 + loadingMode: { + type: String, + default: uni.$u.props.loadingPage.loadingMode + }, + // 是否加载中 + loading: { + type: Boolean, + default: uni.$u.props.loadingPage.loading + }, + // 背景色 + bgColor: { + type: String, + default: uni.$u.props.loadingPage.bgColor + }, + // 文字颜色 + color: { + type: String, + default: uni.$u.props.loadingPage.color + }, + // 文字大小 + fontSize: { + type: [String, Number], + default: uni.$u.props.loadingPage.fontSize + }, + // 图标大小 + iconSize: { + type: [String, Number], + default: uni.$u.props.loadingPage.fontSize + }, + // 加载中图标的颜色,只能rgb或者十六进制颜色值 + loadingColor: { + type: String, + default: uni.$u.props.loadingPage.loadingColor + } + } +} diff --git a/uni_modules/uview-ui/components/u-loading-page/u-loading-page.vue b/uni_modules/uview-ui/components/u-loading-page/u-loading-page.vue new file mode 100644 index 0000000..03a78ad --- /dev/null +++ b/uni_modules/uview-ui/components/u-loading-page/u-loading-page.vue @@ -0,0 +1,115 @@ +<template> + <u-transition + :show="loading" + :custom-style="{ + position: 'fixed', + top: 0, + left: 0, + right: 0, + bottom: 0, + backgroundColor: bgColor, + display: 'flex', + }" + > + <view class="u-loading-page"> + <view class="u-loading-page__warpper"> + <view class="u-loading-page__warpper__loading-icon"> + <image + v-if="image" + :src="image" + class="u-loading-page__warpper__loading-icon__img" + mode="widthFit" + :style="{ + width: $u.addUnit(iconSize), + height: $u.addUnit(iconSize) + }" + ></image> + <u-loading-icon + v-else + :mode="loadingMode" + :size="$u.addUnit(iconSize)" + :color="loadingColor" + ></u-loading-icon> + </view> + <slot> + <text + class="u-loading-page__warpper__text" + :style="{ + fontSize: $u.addUnit(fontSize), + color: color, + }" + >{{ loadingText }}</text + > + </slot> + </view> + </view> + </u-transition> +</template> + +<script> +import props from "./props.js"; +/** + * loadingPage 加载动画 + * @description 警此组件为一个小动画,目前用在uView的loadmore加载更多和switch开关等组件的正在加载状态场景。 + * @tutorial https://www.uviewui.com/components/loading.html + * @property {String | Number} loadingText 提示内容 (默认 '正在加载' ) + * @property {String} image 文字上方用于替换loading动画的图片 + * @property {String} loadingMode 加载动画的模式,circle-圆形,spinner-花朵形,semicircle-半圆形 (默认 'circle' ) + * @property {Boolean} loading 是否加载中 (默认 false ) + * @property {String} bgColor 背景色 (默认 '#ffffff' ) + * @property {String} color 文字颜色 (默认 '#C8C8C8' ) + * @property {String | Number} fontSize 文字大小 (默认 19 ) + * @property {String | Number} iconSize 图标大小 (默认 28 ) + * @property {String} loadingColor 加载中图标的颜色,只能rgb或者十六进制颜色值 (默认 '#C8C8C8' ) + * @property {Object} customStyle 自定义样式 + * @example <u-loading mode="circle"></u-loading> + */ +export default { + name: "u-loading-page", + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return {}; + }, + methods: {}, +}; +</script> + +<style lang="scss" scoped> +@import "../../libs/css/components.scss"; + +$text-color: rgb(200, 200, 200) !default; +$text-size: 19px !default; +$u-loading-icon-margin-bottom: 10px !default; + +.u-loading-page { + @include flex(column); + flex: 1; + align-items: center; + justify-content: center; + + &__warpper { + margin-top: -150px; + justify-content: center; + align-items: center; + /* #ifndef APP-NVUE */ + color: $text-color; + font-size: $text-size; + /* #endif */ + @include flex(column); + + &__loading-icon { + margin-bottom: $u-loading-icon-margin-bottom; + + &__img { + width: 40px; + height: 40px; + } + } + + &__text { + font-size: $text-size; + color: $text-color; + } + } +} +</style> diff --git a/uni_modules/uview-ui/components/u-loadmore/props.js b/uni_modules/uview-ui/components/u-loadmore/props.js new file mode 100644 index 0000000..1e67d89 --- /dev/null +++ b/uni_modules/uview-ui/components/u-loadmore/props.js @@ -0,0 +1,94 @@ +export default { + props: { + // 组件状态,loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态 + status: { + type: String, + default: uni.$u.props.loadmore.status + }, + // 组件背景色 + bgColor: { + type: String, + default: uni.$u.props.loadmore.bgColor + }, + // 是否显示加载中的图标 + icon: { + type: Boolean, + default: uni.$u.props.loadmore.icon + }, + // 字体大小 + fontSize: { + type: [String, Number], + default: uni.$u.props.loadmore.fontSize + }, + // 图标大小 + iconSize: { + type: [String, Number], + default: uni.$u.props.loadmore.iconSize + }, + // 字体颜色 + color: { + type: String, + default: uni.$u.props.loadmore.color + }, + // 加载中状态的图标,spinner-花朵状图标,circle-圆圈状,semicircle-半圆 + loadingIcon: { + type: String, + default: uni.$u.props.loadmore.loadingIcon + }, + // 加载前的提示语 + loadmoreText: { + type: String, + default: uni.$u.props.loadmore.loadmoreText + }, + // 加载中提示语 + loadingText: { + type: String, + default: uni.$u.props.loadmore.loadingText + }, + // 没有更多的提示语 + nomoreText: { + type: String, + default: uni.$u.props.loadmore.nomoreText + }, + // 在“没有更多”状态下,是否显示粗点 + isDot: { + type: Boolean, + default: uni.$u.props.loadmore.isDot + }, + // 加载中图标的颜色 + iconColor: { + type: String, + default: uni.$u.props.loadmore.iconColor + }, + // 上边距 + marginTop: { + type: [String, Number], + default: uni.$u.props.loadmore.marginTop + }, + // 下边距 + marginBottom: { + type: [String, Number], + default: uni.$u.props.loadmore.marginBottom + }, + // 高度,单位px + height: { + type: [String, Number], + default: uni.$u.props.loadmore.height + }, + // 是否显示左边分割线 + line: { + type: Boolean, + default: uni.$u.props.loadmore.line + }, + // 线条颜色 + lineColor: { + type: String, + default: uni.$u.props.loadmore.lineColor + }, + // 是否虚线,true-虚线,false-实线 + dashed: { + type: Boolean, + default: uni.$u.props.loadmore.dashed + } + } +} diff --git a/uni_modules/uview-ui/components/u-loadmore/u-loadmore.vue b/uni_modules/uview-ui/components/u-loadmore/u-loadmore.vue new file mode 100644 index 0000000..73c79fe --- /dev/null +++ b/uni_modules/uview-ui/components/u-loadmore/u-loadmore.vue @@ -0,0 +1,150 @@ +<template> + <view + class="u-loadmore" + :style="[ + $u.addStyle(customStyle), + { + backgroundColor: bgColor, + marginBottom: $u.addUnit(marginBottom), + marginTop: $u.addUnit(marginTop), + height: $u.addUnit(height), + }, + ]" + > + <u-line + length="140rpx" + :color="lineColor" + :hairline="false" + :dashed="dashed" + v-if="line" + ></u-line> + <!-- 加载中和没有更多的状态才显示两边的横线 --> + <view + :class="status == 'loadmore' || status == 'nomore' ? 'u-more' : ''" + class="u-loadmore__content" + > + <view + class="u-loadmore__content__icon-wrap" + v-if="status === 'loading' && icon" + > + <u-loading-icon + :color="iconColor" + :size="iconSize" + :mode="loadingIcon" + ></u-loading-icon> + </view> + <!-- 如果没有更多的状态下,显示内容为dot(粗点),加载特定样式 --> + <text + class="u-line-1" + :style="[loadTextStyle]" + :class="[(status == 'nomore' && isDot == true) ? 'u-loadmore__content__dot-text' : 'u-loadmore__content__text']" + @tap="loadMore" + >{{ showText }}</text> + </view> + <u-line + length="140rpx" + :color="lineColor" + :hairline="false" + :dashed="dashed" + v-if="line" + ></u-line> + </view> +</template> + +<script> + import props from './props.js'; + + /** + * loadmore 加载更多 + * @description 此组件一般用于标识页面底部加载数据时的状态。 + * @tutorial https://www.uviewui.com/components/loadMore.html + * @property {String} status 组件状态(默认 'loadmore' ) + * @property {String} bgColor 组件背景颜色,在页面是非白色时会用到(默认 'transparent' ) + * @property {Boolean} icon 加载中时是否显示图标(默认 true ) + * @property {String | Number} fontSize 字体大小(默认 14 ) + * @property {String | Number} iconSize 图标大小(默认 17 ) + * @property {String} color 字体颜色(默认 '#606266' ) + * @property {String} loadingIcon 加载图标(默认 'circle' ) + * @property {String} loadmoreText 加载前的提示语(默认 '加载更多' ) + * @property {String} loadingText 加载中提示语(默认 '正在加载...' ) + * @property {String} nomoreText 没有更多的提示语(默认 '没有更多了' ) + * @property {Boolean} isDot 到上一个相邻元素的距离 (默认 false ) + * @property {String} iconColor 加载中图标的颜色 (默认 '#b7b7b7' ) + * @property {String} lineColor 线条颜色(默认 #E6E8EB ) + * @property {String | Number} marginTop 上边距 (默认 10 ) + * @property {String | Number} marginBottom 下边距 (默认 10 ) + * @property {String | Number} height 高度,单位px (默认 'auto' ) + * @property {Boolean} line 是否显示左边分割线 (默认 false ) + * @property {Boolean} dashed // 是否虚线,true-虚线,false-实线 (默认 false ) + * @event {Function} loadmore status为loadmore时,点击组件会发出此事件 + * @example <u-loadmore :status="status" icon-type="iconType" load-text="loadText" /> + */ + export default { + name: "u-loadmore", + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + // 粗点 + dotText: "●" + } + }, + computed: { + // 加载的文字显示的样式 + loadTextStyle() { + return { + color: this.color, + fontSize: uni.$u.addUnit(this.fontSize), + lineHeight: uni.$u.addUnit(this.fontSize), + backgroundColor: this.bgColor, + } + }, + // 显示的提示文字 + showText() { + let text = ''; + if (this.status == 'loadmore') text = this.loadmoreText + else if (this.status == 'loading') text = this.loadingText + else if (this.status == 'nomore' && this.isDot) text = this.dotText; + else text = this.nomoreText; + return text; + } + }, + methods: { + loadMore() { + // 只有在“加载更多”的状态下才发送点击事件,内容不满一屏时无法触发底部上拉事件,所以需要点击来触发 + if (this.status == 'loadmore') this.$emit('loadmore'); + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-loadmore { + @include flex(row); + align-items: center; + justify-content: center; + flex: 1; + + &__content { + margin: 0 15px; + @include flex(row); + align-items: center; + justify-content: center; + + &__icon-wrap { + margin-right: 8px; + } + + &__text { + font-size: 14px; + color: $u-content-color; + } + + &__dot-text { + font-size: 15px; + color: $u-tips-color; + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-modal/props.js b/uni_modules/uview-ui/components/u-modal/props.js new file mode 100644 index 0000000..f76672c --- /dev/null +++ b/uni_modules/uview-ui/components/u-modal/props.js @@ -0,0 +1,84 @@ +export default { + props: { + // 是否展示modal + show: { + type: Boolean, + default: uni.$u.props.modal.show + }, + // 标题 + title: { + type: [String], + default: uni.$u.props.modal.title + }, + // 弹窗内容 + content: { + type: String, + default: uni.$u.props.modal.content + }, + // 确认文案 + confirmText: { + type: String, + default: uni.$u.props.modal.confirmText + }, + // 取消文案 + cancelText: { + type: String, + default: uni.$u.props.modal.cancelText + }, + // 是否显示确认按钮 + showConfirmButton: { + type: Boolean, + default: uni.$u.props.modal.showConfirmButton + }, + // 是否显示取消按钮 + showCancelButton: { + type: Boolean, + default: uni.$u.props.modal.showCancelButton + }, + // 确认按钮颜色 + confirmColor: { + type: String, + default: uni.$u.props.modal.confirmColor + }, + // 取消文字颜色 + cancelColor: { + type: String, + default: uni.$u.props.modal.cancelColor + }, + // 对调确认和取消的位置 + buttonReverse: { + type: Boolean, + default: uni.$u.props.modal.buttonReverse + }, + // 是否开启缩放效果 + zoom: { + type: Boolean, + default: uni.$u.props.modal.zoom + }, + // 是否异步关闭,只对确定按钮有效 + asyncClose: { + type: Boolean, + default: uni.$u.props.modal.asyncClose + }, + // 是否允许点击遮罩关闭modal + closeOnClickOverlay: { + type: Boolean, + default: uni.$u.props.modal.closeOnClickOverlay + }, + // 给一个负的margin-top,往上偏移,避免和键盘重合的情况 + negativeTop: { + type: [String, Number], + default: uni.$u.props.modal.negativeTop + }, + // modal宽度,不支持百分比,可以数值,px,rpx单位 + width: { + type: [String, Number], + default: uni.$u.props.modal.width + }, + // 确认按钮的样式,circle-圆形,square-方形,如设置,将不会显示取消按钮 + confirmButtonShape: { + type: String, + default: uni.$u.props.modal.confirmButtonShape + } + } +} diff --git a/uni_modules/uview-ui/components/u-modal/u-modal.vue b/uni_modules/uview-ui/components/u-modal/u-modal.vue new file mode 100644 index 0000000..4c37ae2 --- /dev/null +++ b/uni_modules/uview-ui/components/u-modal/u-modal.vue @@ -0,0 +1,227 @@ +<template> + <u-popup + mode="center" + :zoom="zoom" + :show="show" + :customStyle="{ + borderRadius: '6px', + overflow: 'hidden', + marginTop: `-${$u.addUnit(negativeTop)}` + }" + :closeOnClickOverlay="closeOnClickOverlay" + :safeAreaInsetBottom="false" + :duration="400" + @click="clickHandler" + > + <view + class="u-modal" + :style="{ + width: $u.addUnit(width), + }" + > + <text + class="u-modal__title" + v-if="title" + >{{ title }}</text> + <view + class="u-modal__content" + :style="{ + paddingTop: `${title ? 12 : 25}px` + }" + > + <slot> + <text class="u-modal__content__text">{{ content }}</text> + </slot> + </view> + <view + class="u-modal__button-group--confirm-button" + v-if="$slots.confirmButton" + > + <slot name="confirmButton"></slot> + </view> + <template v-else> + <u-line></u-line> + <view + class="u-modal__button-group" + :style="{ + flexDirection: buttonReverse ? 'row-reverse' : 'row' + }" + > + <view + class="u-modal__button-group__wrapper u-modal__button-group__wrapper--cancel" + :hover-stay-time="150" + hover-class="u-modal__button-group__wrapper--hover" + :class="[showCancelButton && !showConfirmButton && 'u-modal__button-group__wrapper--only-cancel']" + v-if="showCancelButton" + @tap="cancelHandler" + > + <text + class="u-modal__button-group__wrapper__text" + :style="{ + color: cancelColor + }" + >{{ cancelText }}</text> + </view> + <u-line + direction="column" + v-if="showConfirmButton && showCancelButton" + ></u-line> + <view + class="u-modal__button-group__wrapper u-modal__button-group__wrapper--confirm" + :hover-stay-time="150" + hover-class="u-modal__button-group__wrapper--hover" + :class="[!showCancelButton && showConfirmButton && 'u-modal__button-group__wrapper--only-confirm']" + v-if="showConfirmButton" + @tap="confirmHandler" + > + <u-loading-icon v-if="loading"></u-loading-icon> + <text + v-else + class="u-modal__button-group__wrapper__text" + :style="{ + color: confirmColor + }" + >{{ confirmText }}</text> + </view> + </view> + </template> + </view> + </u-popup> +</template> + +<script> + import props from './props.js'; + /** + * Modal 模态框 + * @description 弹出模态框,常用于消息提示、消息确认、在当前页面内完成特定的交互操作。 + * @tutorial https://www.uviewui.com/components/modul.html + * @property {Boolean} show 是否显示模态框,请赋值给show (默认 false ) + * @property {String} title 标题内容 + * @property {String} content 模态框内容,如传入slot内容,则此参数无效 + * @property {String} confirmText 确认按钮的文字 (默认 '确认' ) + * @property {String} cancelText 取消按钮的文字 (默认 '取消' ) + * @property {Boolean} showConfirmButton 是否显示确认按钮 (默认 true ) + * @property {Boolean} showCancelButton 是否显示取消按钮 (默认 false ) + * @property {String} confirmColor 确认按钮的颜色 (默认 '#2979ff' ) + * @property {String} cancelColor 取消按钮的颜色 (默认 '#606266' ) + * @property {Boolean} buttonReverse 对调确认和取消的位置 (默认 false ) + * @property {Boolean} zoom 是否开启缩放模式 (默认 true ) + * @property {Boolean} asyncClose 是否异步关闭,只对确定按钮有效,见上方说明 (默认 false ) + * @property {Boolean} closeOnClickOverlay 是否允许点击遮罩关闭Modal (默认 false ) + * @property {String | Number} negativeTop 往上偏移的值,给一个负的margin-top,往上偏移,避免和键盘重合的情况,单位任意,数值则默认为px单位 (默认 0 ) + * @property {String | Number} width modal宽度,不支持百分比,可以数值,px,rpx单位 (默认 '650rpx' ) + * @property {String} confirmButtonShape 确认按钮的样式,如设置,将不会显示取消按钮 + * @event {Function} confirm 点击确认按钮时触发 + * @event {Function} cancel 点击取消按钮时触发 + * @event {Function} close 点击遮罩关闭出发,closeOnClickOverlay为true有效 + * @example <u-loadmore :status="status" icon-type="iconType" load-text="loadText" /> + */ + export default { + name: 'u-modal', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + loading: false + } + }, + watch: { + show(n) { + // 为了避免第一次打开modal,又使用了异步关闭的loading + // 第二次打开modal时,loading依然存在的情况 + if (n && this.loading) this.loading = false + } + }, + methods: { + // 点击确定按钮 + confirmHandler() { + // 如果配置了异步关闭,将按钮值为loading状态 + if (this.asyncClose) { + this.loading = true; + } + this.$emit('confirm') + }, + // 点击取消按钮 + cancelHandler() { + this.$emit('cancel') + }, + // 点击遮罩 + // 从原理上来说,modal的遮罩点击,并不是真的点击到了遮罩 + // 因为modal依赖于popup的中部弹窗类型,中部弹窗比较特殊,虽有然遮罩,但是为了让弹窗内容能flex居中 + // 多了一个透明的遮罩,此透明的遮罩会覆盖在灰色的遮罩上,所以实际上是点击不到灰色遮罩的,popup内部在 + // 透明遮罩的子元素做了.stop处理,所以点击内容区,也不会导致误触发 + clickHandler() { + if (this.closeOnClickOverlay) { + this.$emit('close') + } + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + $u-modal-border-radius: 6px; + + .u-modal { + width: 650rpx; + border-radius: $u-modal-border-radius; + overflow: hidden; + + &__title { + font-size: 16px; + font-weight: bold; + color: $u-content-color; + text-align: center; + padding-top: 25px; + } + + &__content { + padding: 12px 25px 25px 25px; + @include flex; + justify-content: center; + + &__text { + font-size: 15px; + color: $u-content-color; + flex: 1; + } + } + + &__button-group { + @include flex; + + &--confirm-button { + flex-direction: column; + padding: 0px 25px 15px 25px; + } + + &__wrapper { + flex: 1; + @include flex; + justify-content: center; + align-items: center; + height: 48px; + + &--confirm, + &--only-cancel { + border-bottom-right-radius: $u-modal-border-radius; + } + + &--cancel, + &--only-confirm { + border-bottom-left-radius: $u-modal-border-radius; + } + + &--hover { + background-color: $u-bg-color; + } + + &__text { + color: $u-content-color; + font-size: 16px; + text-align: center; + } + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-navbar/props.js b/uni_modules/uview-ui/components/u-navbar/props.js new file mode 100644 index 0000000..5398de2 --- /dev/null +++ b/uni_modules/uview-ui/components/u-navbar/props.js @@ -0,0 +1,84 @@ +export default { + props: { + // 是否开启顶部安全区适配 + safeAreaInsetTop: { + type: Boolean, + default: uni.$u.props.navbar.safeAreaInsetTop + }, + // 固定在顶部时,是否生成一个等高元素,以防止塌陷 + placeholder: { + type: Boolean, + default: uni.$u.props.navbar.placeholder + }, + // 是否固定在顶部 + fixed: { + type: Boolean, + default: uni.$u.props.navbar.fixed + }, + // 是否显示下边框 + border: { + type: Boolean, + default: uni.$u.props.navbar.border + }, + // 左边的图标 + leftIcon: { + type: String, + default: uni.$u.props.navbar.leftIcon + }, + // 左边的提示文字 + leftText: { + type: String, + default: uni.$u.props.navbar.leftText + }, + // 左右的提示文字 + rightText: { + type: String, + default: uni.$u.props.navbar.rightText + }, + // 右边的图标 + rightIcon: { + type: String, + default: uni.$u.props.navbar.rightIcon + }, + // 标题 + title: { + type: [String, Number], + default: uni.$u.props.navbar.title + }, + // 背景颜色 + bgColor: { + type: String, + default: uni.$u.props.navbar.bgColor + }, + // 标题的宽度 + titleWidth: { + type: [String, Number], + default: uni.$u.props.navbar.titleWidth + }, + // 导航栏高度 + height: { + type: [String, Number], + default: uni.$u.props.navbar.height + }, + // 左侧返回图标的大小 + leftIconSize: { + type: [String, Number], + default: uni.$u.props.navbar.leftIconSize + }, + // 左侧返回图标的颜色 + leftIconColor: { + type: String, + default: uni.$u.props.navbar.leftIconColor + }, + // 点击左侧区域(返回图标),是否自动返回上一页 + autoBack: { + type: Boolean, + default: uni.$u.props.navbar.autoBack + }, + // 标题的样式,对象或字符串 + titleStyle: { + type: [String, Object], + default: uni.$u.props.navbar.titleStyle + } + } +} diff --git a/uni_modules/uview-ui/components/u-navbar/u-navbar.vue b/uni_modules/uview-ui/components/u-navbar/u-navbar.vue new file mode 100644 index 0000000..2b206b7 --- /dev/null +++ b/uni_modules/uview-ui/components/u-navbar/u-navbar.vue @@ -0,0 +1,186 @@ +<template> + <view class="u-navbar"> + <view + class="u-navbar__placeholder" + v-if="fixed && placeholder" + :style="{ + height: $u.addUnit($u.getPx(height) + $u.sys().statusBarHeight,'px'), + }" + ></view> + <view :class="[fixed && 'u-navbar--fixed']"> + <u-status-bar + v-if="safeAreaInsetTop" + :bgColor="bgColor" + ></u-status-bar> + <view + class="u-navbar__content" + :class="[border && 'u-border-bottom']" + :style="{ + height: $u.addUnit(height), + backgroundColor: bgColor, + }" + > + <view + class="u-navbar__content__left" + hover-class="u-navbar__content__left--hover" + hover-start-time="150" + @tap="leftClick" + > + <slot name="left"> + <u-icon + v-if="leftIcon" + :name="leftIcon" + :size="leftIconSize" + :color="leftIconColor" + ></u-icon> + <text + v-if="leftText" + :style="{ + color: leftIconColor + }" + class="u-navbar__content__left__text" + >{{ leftText }}</text> + </slot> + </view> + <slot name="center"> + <text + class="u-line-1 u-navbar__content__title" + :style="[{ + width: $u.addUnit(titleWidth), + }, $u.addStyle(titleStyle)]" + >{{ title }}</text> + </slot> + <view + class="u-navbar__content__right" + v-if="$slots.right || rightIcon || rightText" + @tap="rightClick" + > + <slot name="right"> + <u-icon + v-if="rightIcon" + :name="rightIcon" + size="20" + ></u-icon> + <text + v-if="rightText" + class="u-navbar__content__right__text" + >{{ rightText }}</text> + </slot> + </view> + </view> + </view> + </view> +</template> + +<script> + import props from './props.js'; + /** + * Navbar 自定义导航栏 + * @description 此组件一般用于在特殊情况下,需要自定义导航栏的时候用到,一般建议使用uni-app带的导航栏。 + * @tutorial https://www.uviewui.com/components/navbar.html + * @property {Boolean} safeAreaInsetTop 是否开启顶部安全区适配 (默认 true ) + * @property {Boolean} placeholder 固定在顶部时,是否生成一个等高元素,以防止塌陷 (默认 false ) + * @property {Boolean} fixed 导航栏是否固定在顶部 (默认 false ) + * @property {Boolean} border 导航栏底部是否显示下边框 (默认 false ) + * @property {String} leftIcon 左边返回图标的名称,只能为uView自带的图标 (默认 'arrow-left' ) + * @property {String} leftText 左边的提示文字 + * @property {String} rightText 右边的提示文字 + * @property {String} rightIcon 右边返回图标的名称,只能为uView自带的图标 + * @property {String} title 导航栏标题,如设置为空字符,将会隐藏标题占位区域 + * @property {String} bgColor 导航栏背景设置 (默认 '#ffffff' ) + * @property {String | Number} titleWidth 导航栏标题的最大宽度,内容超出会以省略号隐藏 (默认 '400rpx' ) + * @property {String | Number} height 导航栏高度(不包括状态栏高度在内,内部自动加上)(默认 '44px' ) + * @property {String | Number} leftIconSize 左侧返回图标的大小(默认 20px ) + * @property {String | Number} leftIconColor 左侧返回图标的颜色(默认 #303133 ) + * @property {Boolean} autoBack 点击左侧区域(返回图标),是否自动返回上一页(默认 false ) + * @property {Object | String} titleStyle 标题的样式,对象或字符串 + * @event {Function} leftClick 点击左侧区域 + * @event {Function} rightClick 点击右侧区域 + * @example <u-navbar title="剑未配妥,出门已是江湖" left-text="返回" right-text="帮助" @click-left="onClickBack" @click-right="onClickRight"></u-navbar> + */ + export default { + name: 'u-navbar', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + + } + }, + methods: { + // 点击左侧区域 + leftClick() { + // 如果配置了autoBack,自动返回上一页 + this.$emit('leftClick') + if(this.autoBack) { + uni.navigateBack() + } + }, + // 点击右侧区域 + rightClick() { + this.$emit('rightClick') + }, + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-navbar { + + &--fixed { + position: fixed; + left: 0; + right: 0; + top: 0; + z-index: 11; + } + + &__content { + @include flex(row); + align-items: center; + height: 44px; + background-color: #9acafc; + position: relative; + justify-content: center; + + &__left, + &__right { + padding: 0 13px; + position: absolute; + top: 0; + bottom: 0; + @include flex(row); + align-items: center; + } + + &__left { + left: 0; + + &--hover { + opacity: 0.7; + } + + &__text { + font-size: 15px; + margin-left: 3px; + } + } + + &__title { + text-align: center; + font-size: 16px; + color: $u-main-color; + } + + &__right { + right: 0; + + &__text { + font-size: 15px; + margin-left: 3px; + } + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-no-network/props.js b/uni_modules/uview-ui/components/u-no-network/props.js new file mode 100644 index 0000000..9f3af62 --- /dev/null +++ b/uni_modules/uview-ui/components/u-no-network/props.js @@ -0,0 +1,19 @@ +export default { + props: { + // 页面文字提示 + tips: { + type: String, + default: uni.$u.props.noNetwork.tips + }, + // 一个z-index值,用于设置没有网络这个组件的层次,因为页面可能会有其他定位的元素层级过高,导致此组件被覆盖 + zIndex: { + type: [String, Number], + default: uni.$u.props.noNetwork.zIndex + }, + // image 没有网络的图片提示 + image: { + type: String, + default: uni.$u.props.noNetwork.image + } + } +} diff --git a/uni_modules/uview-ui/components/u-no-network/u-no-network.vue b/uni_modules/uview-ui/components/u-no-network/u-no-network.vue new file mode 100644 index 0000000..53db905 --- /dev/null +++ b/uni_modules/uview-ui/components/u-no-network/u-no-network.vue @@ -0,0 +1,219 @@ +<template> + <u-overlay + :show="!isConnected" + @touchmove.stop.prevent="noop" + :customStyle="{ + backgroundColor: '#fff', + display: 'flex', + justifyContent: 'center', + }" + > + <view + class="u-no-network" + > + <u-icon + :name="image" + size="150" + imgMode="widthFit" + class="u-no-network__error-icon" + ></u-icon> + <text class="u-no-network__tips">{{tips}}</text> + <!-- 只有APP平台,才能跳转设置页,因为需要调用plus环境 --> + <!-- #ifdef APP-PLUS --> + <view class="u-no-network__app"> + <text class="u-no-network__app__setting">请检查网络,或前往</text> + <text + class="u-no-network__app__to-setting" + @tap="openSettings" + >设置</text> + </view> + <!-- #endif --> + <view class="u-no-network__retry"> + <u-button + size="mini" + text="重试" + type="primary" + plain + @click="retry" + ></u-button> + </view> + </view> + </u-overlay> +</template> + +<script> + import props from './props.js'; + + /** + * noNetwork 无网络提示 + * @description 该组件无需任何配置,引入即可,内部自动处理所有功能和事件。 + * @tutorial https://www.uviewui.com/components/noNetwork.html + * @property {String} tips 没有网络时的提示语 (默认:'哎呀,网络信号丢失' ) + * @property {String | Number} zIndex 组件的z-index值 + * @property {String} image 无网络的图片提示,可用的src地址或base64图片 + * @event {Function} retry 用户点击页面的"重试"按钮时触发 + * @example <u-no-network></u-no-network> + */ + export default { + name: "u-no-network", + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + isConnected: true, // 是否有网络连接 + networkType: "none", // 网络类型 + } + }, + mounted() { + this.isIOS = (uni.getSystemInfoSync().platform === 'ios') + uni.onNetworkStatusChange((res) => { + this.isConnected = res.isConnected + this.networkType = res.networkType + this.emitEvent(this.networkType) + }) + uni.getNetworkType({ + success: (res) => { + this.networkType = res.networkType + this.emitEvent(this.networkType) + if (res.networkType == 'none') { + this.isConnected = false + } else { + this.isConnected = true + } + } + }) + }, + methods: { + retry() { + // 重新检查网络 + uni.getNetworkType({ + success: (res) => { + this.networkType = res.networkType + this.emitEvent(this.networkType) + if (res.networkType == 'none') { + uni.$u.toast('无网络连接') + this.isConnected = false + } else { + uni.$u.toast('网络已连接') + this.isConnected = true + } + } + }) + this.$emit('retry') + }, + // 发出事件给父组件 + emitEvent(networkType) { + this.$emit(networkType === 'none' ? 'disconnected' : 'connected') + }, + async openSettings() { + if (this.networkType == "none") { + this.openSystemSettings() + return + } + }, + openAppSettings() { + this.gotoAppSetting() + }, + openSystemSettings() { + // 以下方法来自5+范畴,如需深究,请自行查阅相关文档 + // https://ask.dcloud.net.cn/docs/ + if (this.isIOS) { + this.gotoiOSSetting() + } else { + this.gotoAndroidSetting() + } + }, + network() { + var result = null + var cellularData = plus.ios.newObject("CTCellularData") + var state = cellularData.plusGetAttribute("restrictedState") + if (state == 0) { + result = null + } else if (state == 2) { + result = 1 + } else if (state == 1) { + result = 2 + } + plus.ios.deleteObject(cellularData) + return result + }, + gotoAppSetting() { + if (this.isIOS) { + var UIApplication = plus.ios.import("UIApplication") + var application2 = UIApplication.sharedApplication() + var NSURL2 = plus.ios.import("NSURL") + var setting2 = NSURL2.URLWithString("app-settings:") + application2.openURL(setting2) + plus.ios.deleteObject(setting2) + plus.ios.deleteObject(NSURL2) + plus.ios.deleteObject(application2) + } else { + var Intent = plus.android.importClass("android.content.Intent") + var Settings = plus.android.importClass("android.provider.Settings") + var Uri = plus.android.importClass("android.net.Uri") + var mainActivity = plus.android.runtimeMainActivity() + var intent = new Intent() + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + var uri = Uri.fromParts("package", mainActivity.getPackageName(), null) + intent.setData(uri) + mainActivity.startActivity(intent) + } + }, + gotoiOSSetting() { + var UIApplication = plus.ios.import("UIApplication") + var application2 = UIApplication.sharedApplication() + var NSURL2 = plus.ios.import("NSURL") + var setting2 = NSURL2.URLWithString("App-prefs:root=General") + application2.openURL(setting2) + plus.ios.deleteObject(setting2) + plus.ios.deleteObject(NSURL2) + plus.ios.deleteObject(application2) + }, + gotoAndroidSetting() { + var Intent = plus.android.importClass("android.content.Intent") + var Settings = plus.android.importClass("android.provider.Settings") + var mainActivity = plus.android.runtimeMainActivity() + var intent = new Intent(Settings.ACTION_SETTINGS) + mainActivity.startActivity(intent) + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-no-network { + @include flex(column); + justify-content: center; + align-items: center; + margin-top: -100px; + + &__tips { + color: $u-tips-color; + font-size: 14px; + margin-top: 15px; + } + + &__app { + @include flex(row); + margin-top: 6px; + + &__setting { + color: $u-light-color; + font-size: 13px; + } + + &__to-setting { + font-size: 13px; + color: $u-primary; + margin-left: 3px; + } + } + + &__retry { + @include flex(row); + justify-content: center; + margin-top: 15px; + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-notice-bar/props.js b/uni_modules/uview-ui/components/u-notice-bar/props.js new file mode 100644 index 0000000..7040c29 --- /dev/null +++ b/uni_modules/uview-ui/components/u-notice-bar/props.js @@ -0,0 +1,70 @@ +export default { + props: { + // 显示的内容,数组 + text: { + type: [Array, String], + default: uni.$u.props.noticeBar.text + }, + // 通告滚动模式,row-横向滚动,column-竖向滚动 + direction: { + type: String, + default: uni.$u.props.noticeBar.direction + }, + // direction = row时,是否使用步进形式滚动 + step: { + type: Boolean, + default: uni.$u.props.noticeBar.step + }, + // 是否显示左侧的音量图标 + icon: { + type: String, + default: uni.$u.props.noticeBar.icon + }, + // 通告模式,link-显示右箭头,closable-显示右侧关闭图标 + mode: { + type: String, + default: uni.$u.props.noticeBar.mode + }, + // 文字颜色,各图标也会使用文字颜色 + color: { + type: String, + default: uni.$u.props.noticeBar.color + }, + // 背景颜色 + bgColor: { + type: String, + default: uni.$u.props.noticeBar.bgColor + }, + // 水平滚动时的滚动速度,即每秒滚动多少px(px),这有利于控制文字无论多少时,都能有一个恒定的速度 + speed: { + type: [String, Number], + default: uni.$u.props.noticeBar.speed + }, + // 字体大小 + fontSize: { + type: [String, Number], + default: uni.$u.props.noticeBar.fontSize + }, + // 滚动一个周期的时间长,单位ms + duration: { + type: [String, Number], + default: uni.$u.props.noticeBar.duration + }, + // 是否禁止用手滑动切换 + // 目前HX2.6.11,只支持App 2.5.5+、H5 2.5.5+、支付宝小程序、字节跳动小程序 + disableTouch: { + type: Boolean, + default: uni.$u.props.noticeBar.disableTouch + }, + // 跳转的页面路径 + url: { + type: String, + default: uni.$u.props.noticeBar.url + }, + // 页面跳转的类型 + linkType: { + type: String, + default: uni.$u.props.noticeBar.linkType + } + } +} diff --git a/uni_modules/uview-ui/components/u-notice-bar/u-notice-bar.vue b/uni_modules/uview-ui/components/u-notice-bar/u-notice-bar.vue new file mode 100644 index 0000000..a06eb39 --- /dev/null +++ b/uni_modules/uview-ui/components/u-notice-bar/u-notice-bar.vue @@ -0,0 +1,101 @@ +<template> + <view + class="u-notice-bar" + v-if="show" + :style="[{ + backgroundColor: bgColor + }, $u.addStyle(customStyle)]" + > + <template v-if="direction === 'column' || (direction === 'row' && step)"> + <u-column-notice + :color="color" + :bgColor="bgColor" + :text="text" + :mode="mode" + :step="step" + :icon="icon" + :disable-touch="disableTouch" + :fontSize="fontSize" + :duration="duration" + @close="close" + @click="click" + ></u-column-notice> + </template> + <template v-else> + <u-row-notice + :color="color" + :bgColor="bgColor" + :text="text" + :mode="mode" + :fontSize="fontSize" + :speed="speed" + :url="url" + :linkType="linkType" + :icon="icon" + @close="close" + @click="click" + ></u-row-notice> + </template> + </view> +</template> +<script> + import props from './props.js'; + + /** + * noticeBar 滚动通知 + * @description 该组件用于滚动通告场景,有多种模式可供选择 + * @tutorial https://www.uviewui.com/components/noticeBar.html + * @property {Array | String} text 显示的内容,数组 + * @property {String} direction 通告滚动模式,row-横向滚动,column-竖向滚动 ( 默认 'row' ) + * @property {Boolean} step direction = row时,是否使用步进形式滚动 ( 默认 false ) + * @property {String} icon 是否显示左侧的音量图标 ( 默认 'volume' ) + * @property {String} mode 通告模式,link-显示右箭头,closable-显示右侧关闭图标 + * @property {String} color 文字颜色,各图标也会使用文字颜色 ( 默认 '#f9ae3d' ) + * @property {String} bgColor 背景颜色 ( 默认 '#fdf6ec' ) + * @property {String | Number} speed 水平滚动时的滚动速度,即每秒滚动多少px(px),这有利于控制文字无论多少时,都能有一个恒定的速度 ( 默认 80 ) + * @property {String | Number} fontSize 字体大小 ( 默认 14 ) + * @property {String | Number} duration 滚动一个周期的时间长,单位ms ( 默认 2000 ) + * @property {Boolean} disableTouch 是否禁止用手滑动切换 目前HX2.6.11,只支持App 2.5.5+、H5 2.5.5+、支付宝小程序、字节跳动小程序(默认34) ( 默认 true ) + * @property {String} url 跳转的页面路径 + * @property {String} linkType 页面跳转的类型 ( 默认 navigateTo ) + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @event {Function} click 点击通告文字触发 + * @event {Function} close 点击右侧关闭图标触发 + * @example <u-notice-bar :more-icon="true" :list="list"></u-notice-bar> + */ + export default { + name: "u-notice-bar", + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + show: true + } + }, + methods: { + // 点击通告栏 + click(index) { + this.$emit('click', index) + if (this.url && this.linkType) { + // 此方法写在mixin中,另外跳转的url和linkType参数也在mixin的props中 + this.openPage() + } + }, + // 点击关闭按钮 + close() { + this.show = false + this.$emit('close') + } + } + }; +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-notice-bar { + overflow: hidden; + padding: 9px 12px; + flex: 1; + } +</style> diff --git a/uni_modules/uview-ui/components/u-notify/props.js b/uni_modules/uview-ui/components/u-notify/props.js new file mode 100644 index 0000000..57a9d71 --- /dev/null +++ b/uni_modules/uview-ui/components/u-notify/props.js @@ -0,0 +1,49 @@ +export default { + props: { + // 到顶部的距离 + top: { + type: [String, Number], + default: uni.$u.props.notify.top + }, + // 是否展示组件 + // show: { + // type: Boolean, + // default: uni.$u.props.notify.show + // }, + // type主题,primary,success,warning,error + type: { + type: String, + default: uni.$u.props.notify.type + }, + // 字体颜色 + color: { + type: String, + default: uni.$u.props.notify.color + }, + // 背景颜色 + bgColor: { + type: String, + default: uni.$u.props.notify.bgColor + }, + // 展示的文字内容 + message: { + type: String, + default: uni.$u.props.notify.message + }, + // 展示时长,为0时不消失,单位ms + duration: { + type: [String, Number], + default: uni.$u.props.notify.duration + }, + // 字体大小 + fontSize: { + type: [String, Number], + default: uni.$u.props.notify.fontSize + }, + // 是否留出顶部安全距离(状态栏高度) + safeAreaInsetTop: { + type: Boolean, + default: uni.$u.props.notify.safeAreaInsetTop + } + } +} diff --git a/uni_modules/uview-ui/components/u-notify/u-notify.vue b/uni_modules/uview-ui/components/u-notify/u-notify.vue new file mode 100644 index 0000000..30adb72 --- /dev/null +++ b/uni_modules/uview-ui/components/u-notify/u-notify.vue @@ -0,0 +1,211 @@ +<template> + <u-transition + mode="slide-down" + :customStyle="containerStyle" + :show="open" + > + <view + class="u-notify" + :class="[`u-notify--${tmpConfig.type}`]" + :style="[backgroundColor, $u.addStyle(customStyle)]" + > + <u-status-bar v-if="tmpConfig.safeAreaInsetTop"></u-status-bar> + <view class="u-notify__warpper"> + <slot name="icon"> + <u-icon + v-if="['success', 'warning', 'error'].includes(tmpConfig.type)" + :name="tmpConfig.icon" + :color="tmpConfig.color" + :size="1.3 * tmpConfig.fontSize" + :customStyle="{marginRight: '4px'}" + ></u-icon> + </slot> + <text + class="u-notify__warpper__text" + :style="{ + fontSize: $u.addUnit(tmpConfig.fontSize), + color: tmpConfig.color + }" + >{{ tmpConfig.message }}</text> + </view> + </view> + </u-transition> +</template> + +<script> + import props from './props.js'; + /** + * notify 顶部提示 + * @description 该组件一般用于页面顶部向下滑出一个提示,尔后自动收起的场景 + * @tutorial + * @property {String | Number} top 到顶部的距离 ( 默认 0 ) + * @property {String} type 主题,primary,success,warning,error ( 默认 'primary' ) + * @property {String} color 字体颜色 ( 默认 '#ffffff' ) + * @property {String} bgColor 背景颜色 + * @property {String} message 展示的文字内容 + * @property {String | Number} duration 展示时长,为0时不消失,单位ms ( 默认 3000 ) + * @property {String | Number} fontSize 字体大小 ( 默认 15 ) + * @property {Boolean} safeAreaInsetTop 是否留出顶部安全距离(状态栏高度) ( 默认 false ) + * @property {Object} customStyle 组件的样式,对象形式 + * @event {Function} open 开启组件时调用的函数 + * @event {Function} close 关闭组件式调用的函数 + * @example <u-notify message="Hi uView"></u-notify> + */ + export default { + name: 'u-notify', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + // 是否展示组件 + open: false, + timer: null, + config: { + // 到顶部的距离 + top: uni.$u.props.notify.top, + // type主题,primary,success,warning,error + type: uni.$u.props.notify.type, + // 字体颜色 + color: uni.$u.props.notify.color, + // 背景颜色 + bgColor: uni.$u.props.notify.bgColor, + // 展示的文字内容 + message: uni.$u.props.notify.message, + // 展示时长,为0时不消失,单位ms + duration: uni.$u.props.notify.duration, + // 字体大小 + fontSize: uni.$u.props.notify.fontSize, + // 是否留出顶部安全距离(状态栏高度) + safeAreaInsetTop: uni.$u.props.notify.safeAreaInsetTop + }, + // 合并后的配置,避免多次调用组件后,可能会复用之前使用的配置参数 + tmpConfig: {} + } + }, + computed: { + containerStyle() { + let top = 0 + if (this.tmpConfig.top === 0) { + // #ifdef H5 + // H5端,导航栏为普通元素,需要将组件移动到导航栏的下边沿 + // H5的导航栏高度为44px + top = 44 + // #endif + } + const style = { + top: uni.$u.addUnit(this.tmpConfig.top === 0 ? top : this.tmpConfig.top), + // 因为组件底层为u-transition组件,必须将其设置为fixed定位 + // 让其出现在导航栏底部 + position: 'fixed', + left: 0, + right: 0, + zIndex: 10076 + } + return style + }, + // 组件背景颜色 + backgroundColor() { + const style = {} + if (this.tmpConfig.bgColor) { + style.backgroundColor = this.tmpConfig.bgColor + } + return style + }, + // 默认主题下的图标 + icon() { + let icon + if (this.tmpConfig.type === 'success') { + icon = 'checkmark-circle' + } else if (this.tmpConfig.type === 'error') { + icon = 'close-circle' + } else if (this.tmpConfig.type === 'warning') { + icon = 'error-circle' + } + return icon + } + }, + created() { + // 通过主题的形式调用toast,批量生成方法函数 + ['primary', 'success', 'error', 'warning'].map(item => { + this[item] = message => this.show({ + type: item, + message + }) + }) + }, + methods: { + show(options) { + // 不将结果合并到this.config变量,避免多次调用u-toast,前后的配置造成混乱 + this.tmpConfig = uni.$u.deepMerge(this.config, options) + // 任何定时器初始化之前,都要执行清除操作,否则可能会造成混乱 + this.clearTimer() + this.open = true + if (this.tmpConfig.duration > 0) { + this.timer = setTimeout(() => { + this.open = false + // 倒计时结束,清除定时器,隐藏toast组件 + this.clearTimer() + // 判断是否存在callback方法,如果存在就执行 + typeof(this.tmpConfig.complete) === 'function' && this.tmpConfig.complete() + }, this.tmpConfig.duration) + } + }, + // 关闭notify + close() { + this.clearTimer() + }, + clearTimer() { + this.open = false + // 清除定时器 + clearTimeout(this.timer) + this.timer = null + } + }, + beforeDestroy() { + this.clearTimer() + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + $u-notify-padding: 8px 10px !default; + $u-notify-text-font-size: 15px !default; + $u-notify-primary-bgColor: $u-primary !default; + $u-notify-success-bgColor: $u-success !default; + $u-notify-error-bgColor: $u-error !default; + $u-notify-warning-bgColor: $u-warning !default; + + + .u-notify { + padding: $u-notify-padding; + + &__warpper { + @include flex; + align-items: center; + text-align: center; + justify-content: center; + + &__text { + font-size: $u-notify-text-font-size; + text-align: center; + } + } + + &--primary { + background-color: $u-notify-primary-bgColor; + } + + &--success { + background-color: $u-notify-success-bgColor; + } + + &--error { + background-color: $u-notify-error-bgColor; + } + + &--warning { + background-color: $u-notify-warning-bgColor; + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-number-box/props.js b/uni_modules/uview-ui/components/u-number-box/props.js new file mode 100644 index 0000000..fb0fa94 --- /dev/null +++ b/uni_modules/uview-ui/components/u-number-box/props.js @@ -0,0 +1,109 @@ +export default { + props: { + // 步进器标识符,在change回调返回 + name: { + type: [String, Number], + default: uni.$u.props.numberBox.name + }, + // 用于双向绑定的值,初始化时设置设为默认min值(最小值) + value: { + type: [String, Number], + default: uni.$u.props.numberBox.value + }, + // 最小值 + min: { + type: [String, Number], + default: uni.$u.props.numberBox.min + }, + // 最大值 + max: { + type: [String, Number], + default: uni.$u.props.numberBox.max + }, + // 加减的步长,可为小数 + step: { + type: [String, Number], + default: uni.$u.props.numberBox.step + }, + // 是否只允许输入整数 + integer: { + type: Boolean, + default: uni.$u.props.numberBox.integer + }, + // 是否禁用,包括输入框,加减按钮 + disabled: { + type: Boolean, + default: uni.$u.props.numberBox.disabled + }, + // 是否禁用输入框 + disabledInput: { + type: Boolean, + default: uni.$u.props.numberBox.disabledInput + }, + // 是否开启异步变更,开启后需要手动控制输入值 + asyncChange: { + type: Boolean, + default: uni.$u.props.numberBox.asyncChange + }, + // 输入框宽度,单位为px + inputWidth: { + type: [String, Number], + default: uni.$u.props.numberBox.inputWidth + }, + // 是否显示减少按钮 + showMinus: { + type: Boolean, + default: uni.$u.props.numberBox.showMinus + }, + // 是否显示增加按钮 + showPlus: { + type: Boolean, + default: uni.$u.props.numberBox.showPlus + }, + // 显示的小数位数 + decimalLength: { + type: [String, Number, null], + default: uni.$u.props.numberBox.decimalLength + }, + // 是否开启长按加减手势 + longPress: { + type: Boolean, + default: uni.$u.props.numberBox.longPress + }, + // 输入框文字和加减按钮图标的颜色 + color: { + type: String, + default: uni.$u.props.numberBox.color + }, + // 按钮大小,宽高等于此值,单位px,输入框高度和此值保持一致 + buttonSize: { + type: [String, Number], + default: uni.$u.props.numberBox.buttonSize + }, + // 输入框和按钮的背景颜色 + bgColor: { + type: String, + default: uni.$u.props.numberBox.bgColor + }, + // 指定光标于键盘的距离,避免键盘遮挡输入框,单位px + cursorSpacing: { + type: [String, Number], + default: uni.$u.props.numberBox.cursorSpacing + }, + // 是否禁用增加按钮 + disablePlus: { + type: Boolean, + default: uni.$u.props.numberBox.disablePlus + }, + // 是否禁用减少按钮 + disableMinus: { + type: Boolean, + default: uni.$u.props.numberBox.disableMinus + }, + // 加减按钮图标的样式 + iconStyle: { + type: [Object, String], + default: uni.$u.props.numberBox.iconStyle + } + } +} diff --git a/uni_modules/uview-ui/components/u-number-box/u-number-box.vue b/uni_modules/uview-ui/components/u-number-box/u-number-box.vue new file mode 100644 index 0000000..69211c5 --- /dev/null +++ b/uni_modules/uview-ui/components/u-number-box/u-number-box.vue @@ -0,0 +1,416 @@ +<template> + <view class="u-number-box"> + <view + class="u-number-box__slot" + @tap.stop="clickHandler('minus')" + @touchstart="onTouchStart('minus')" + @touchend.stop="clearTimeout" + v-if="showMinus && $slots.minus" + > + <slot name="minus" /> + </view> + <view + v-else-if="showMinus" + class="u-number-box__minus" + @tap.stop="clickHandler('minus')" + @touchstart="onTouchStart('minus')" + @touchend.stop="clearTimeout" + hover-class="u-number-box__minus--hover" + hover-stay-time="150" + :class="{ 'u-number-box__minus--disabled': isDisabled('minus') }" + :style="[buttonStyle('minus')]" + > + <u-icon + name="minus" + :color="isDisabled('minus') ? '#c8c9cc' : '#323233'" + size="15" + bold + :customStyle="iconStyle" + ></u-icon> + </view> + + <slot name="input"> + <input + :disabled="disabledInput || disabled" + :cursor-spacing="getCursorSpacing" + :class="{ 'u-number-box__input--disabled': disabled || disabledInput }" + v-model="currentValue" + class="u-number-box__input" + @blur="onBlur" + @focus="onFocus" + @input="onInput" + type="number" + :style="[inputStyle]" + /> + </slot> + <view + class="u-number-box__slot" + @tap.stop="clickHandler('plus')" + @touchstart="onTouchStart('plus')" + @touchend.stop="clearTimeout" + v-if="showPlus && $slots.plus" + > + <slot name="plus" /> + </view> + <view + v-else-if="showPlus" + class="u-number-box__plus" + @tap.stop="clickHandler('plus')" + @touchstart="onTouchStart('plus')" + @touchend.stop="clearTimeout" + hover-class="u-number-box__plus--hover" + hover-stay-time="150" + :class="{ 'u-number-box__minus--disabled': isDisabled('plus') }" + :style="[buttonStyle('plus')]" + > + <u-icon + name="plus" + :color="isDisabled('plus') ? '#c8c9cc' : '#323233'" + size="15" + bold + :customStyle="iconStyle" + ></u-icon> + </view> + </view> +</template> + +<script> + import props from './props.js'; + /** + * numberBox 步进器 + * @description 该组件一般用于商城购物选择物品数量的场景。 + * @tutorial https://uviewui.com/components/numberBox.html + * @property {String | Number} name 步进器标识符,在change回调返回 + * @property {String | Number} value 用于双向绑定的值,初始化时设置设为默认min值(最小值) (默认 0 ) + * @property {String | Number} min 最小值 (默认 1 ) + * @property {String | Number} max 最大值 (默认 Number.MAX_SAFE_INTEGER ) + * @property {String | Number} step 加减的步长,可为小数 (默认 1 ) + * @property {Boolean} integer 是否只允许输入整数 (默认 false ) + * @property {Boolean} disabled 是否禁用,包括输入框,加减按钮 (默认 false ) + * @property {Boolean} disabledInput 是否禁用输入框 (默认 false ) + * @property {Boolean} asyncChange 是否开启异步变更,开启后需要手动控制输入值 (默认 false ) + * @property {String | Number} inputWidth 输入框宽度,单位为px (默认 35 ) + * @property {Boolean} showMinus 是否显示减少按钮 (默认 true ) + * @property {Boolean} showPlus 是否显示增加按钮 (默认 true ) + * @property {String | Number} decimalLength 显示的小数位数 + * @property {Boolean} longPress 是否开启长按加减手势 (默认 true ) + * @property {String} color 输入框文字和加减按钮图标的颜色 (默认 '#323233' ) + * @property {String | Number} buttonSize 按钮大小,宽高等于此值,单位px,输入框高度和此值保持一致 (默认 30 ) + * @property {String} bgColor 输入框和按钮的背景颜色 (默认 '#EBECEE' ) + * @property {String | Number} cursorSpacing 指定光标于键盘的距离,避免键盘遮挡输入框,单位px (默认 100 ) + * @property {Boolean} disablePlus 是否禁用增加按钮 (默认 false ) + * @property {Boolean} disableMinus 是否禁用减少按钮 (默认 false ) + * @property {Object | String} iconStyle 加减按钮图标的样式 + * + * @event {Function} onFocus 输入框活动焦点 + * @event {Function} onBlur 输入框失去焦点 + * @event {Function} onInput 输入框值发生变化 + * @event {Function} onChange + * @example <u-number-box v-model="value" @change="valChange"></u-number-box> + */ + export default { + name: 'u-number-box', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + // 输入框实际操作的值 + currentValue: '', + // 定时器 + longPressTimer: null + } + }, + watch: { + // 多个值之间,只要一个值发生变化,都要重新检查check()函数 + watchChange(n) { + this.check() + }, + // 监听v-mode的变化,重新初始化内部的值 + value(n) { + if (n !== this.currentValue) { + this.currentValue = this.format(this.value) + } + } + }, + computed: { + getCursorSpacing() { + // 判断传入的单位,如果为px单位,需要转成px + return uni.$u.getPx(this.cursorSpacing) + }, + // 按钮的样式 + buttonStyle() { + return (type) => { + const style = { + backgroundColor: this.bgColor, + height: uni.$u.addUnit(this.buttonSize), + color: this.color + } + if (this.isDisabled(type)) { + style.backgroundColor = '#f7f8fa' + } + return style + } + }, + // 输入框的样式 + inputStyle() { + const disabled = this.disabled || this.disabledInput + const style = { + color: this.color, + backgroundColor: this.bgColor, + height: uni.$u.addUnit(this.buttonSize), + width: uni.$u.addUnit(this.inputWidth) + } + return style + }, + // 用于监听多个值发生变化 + watchChange() { + return [this.integer, this.decimalLength, this.min, this.max] + }, + isDisabled() { + return (type) => { + if (type === 'plus') { + // 在点击增加按钮情况下,判断整体的disabled,是否单独禁用增加按钮,以及当前值是否大于最大的允许值 + return ( + this.disabled || + this.disablePlus || + this.currentValue >= this.max + ) + } + // 点击减少按钮同理 + return ( + this.disabled || + this.disableMinus || + this.currentValue <= this.min + ) + } + }, + }, + mounted() { + this.init() + }, + methods: { + init() { + this.currentValue = this.format(this.value) + }, + // 格式化整理数据,限制范围 + format(value) { + value = this.filter(value) + // 如果为空字符串,那么设置为0,同时将值转为Number类型 + value = value === '' ? 0 : +value + // 对比最大最小值,取在min和max之间的值 + value = Math.max(Math.min(this.max, value), this.min) + // 如果设定了最大的小数位数,使用toFixed去进行格式化 + if (this.decimalLength !== null) { + value = value.toFixed(this.decimalLength) + } + return value + }, + // 过滤非法的字符 + filter(value) { + // 只允许0-9之间的数字,"."为小数点,"-"为负数时候使用 + value = String(value).replace(/[^0-9.-]/g, '') + // 如果只允许输入整数,则过滤掉小数点后的部分 + if (this.integer && value.indexOf('.') !== -1) { + value = value.split('.')[0] + } + return value; + }, + check() { + // 格式化了之后,如果前后的值不相等,那么设置为格式化后的值 + const val = this.format(this.currentValue); + if (val !== this.currentValue) { + this.currentValue = val + } + }, + // 判断是否出于禁止操作状态 + // isDisabled(type) { + // if (type === 'plus') { + // // 在点击增加按钮情况下,判断整体的disabled,是否单独禁用增加按钮,以及当前值是否大于最大的允许值 + // return ( + // this.disabled || + // this.disablePlus || + // this.currentValue >= this.max + // ) + // } + // // 点击减少按钮同理 + // return ( + // this.disabled || + // this.disableMinus || + // this.currentValue <= this.min + // ) + // }, + // 输入框活动焦点 + onFocus(event) { + this.$emit('focus', { + ...event.detail, + name: this.name, + }) + }, + // 输入框失去焦点 + onBlur(event) { + // 对输入值进行格式化 + const value = this.format(event.detail.value) + // 发出blur事件 + this.$emit( + 'blur',{ + ...event.detail, + name: this.name, + } + ) + }, + // 输入框值发生变化 + onInput(e) { + const { + value = '' + } = e.detail || {} + // 为空返回 + if (value === '') return + let formatted = this.filter(value) + // 最大允许的小数长度 + if (this.decimalLength !== null && formatted.indexOf('.') !== -1) { + const pair = formatted.split('.'); + formatted = `${pair[0]}.${pair[1].slice(0, this.decimalLength)}` + } + formatted = this.format(formatted) + this.emitChange(formatted); + }, + // 发出change事件 + emitChange(value) { + // 如果开启了异步变更值,则不修改内部的值,需要用户手动在外部通过v-model变更 + if (!this.asyncChange) { + this.$nextTick(() => { + this.$emit('input', value) + this.currentValue = value + this.$forceUpdate() + }) + } + this.$emit('change', { + value, + name: this.name, + }); + }, + onChange() { + const { + type + } = this + if (this.isDisabled(type)) { + return this.$emit('overlimit', type) + } + const diff = type === 'minus' ? -this.step : +this.step + const value = this.format(this.add(+this.currentValue, diff)) + this.emitChange(value) + this.$emit(type) + }, + // 对值扩大后进行四舍五入,再除以扩大因子,避免出现浮点数操作的精度问题 + add(num1, num2) { + const cardinal = Math.pow(10, 10); + return Math.round((num1 + num2) * cardinal) / cardinal + }, + // 点击加减按钮 + clickHandler(type) { + this.type = type + this.onChange() + }, + longPressStep() { + // 每隔一段时间,重新调用longPressStep方法,实现长按加减 + this.clearTimeout() + this.longPressTimer = setTimeout(() => { + this.onChange() + this.longPressStep() + }, 250); + }, + onTouchStart(type) { + if (!this.longPress) return + this.clearTimeout() + this.type = type + // 一定时间后,默认达到长按状态 + this.longPressTimer = setTimeout(() => { + this.onChange() + this.longPressStep() + }, 600) + }, + // 触摸结束,清除定时器,停止长按加减 + onTouchEnd() { + if (!this.longPress) return + this.clearTimeout() + }, + // 清除定时器 + clearTimeout() { + clearTimeout(this.longPressTimer) + this.longPressTimer = null + } + } + } +</script> + +<style lang="scss" scoped> + @import '../../libs/css/components.scss'; + + $u-numberBox-hover-bgColor: #E6E6E6 !default; + $u-numberBox-disabled-color: #c8c9cc !default; + $u-numberBox-disabled-bgColor: #f7f8fa !default; + $u-numberBox-plus-radius: 4px !default; + $u-numberBox-minus-radius: 4px !default; + $u-numberBox-input-text-align: center !default; + $u-numberBox-input-font-size: 15px !default; + $u-numberBox-input-padding: 0 !default; + $u-numberBox-input-margin: 0 2px !default; + $u-numberBox-input-disabled-color: #c8c9cc !default; + $u-numberBox-input-disabled-bgColor: #f2f3f5 !default; + + .u-number-box { + @include flex(row); + align-items: center; + + &__slot { + /* #ifndef APP-NVUE */ + touch-action: none; + /* #endif */ + } + + &__plus, + &__minus { + width: 35px; + @include flex; + justify-content: center; + align-items: center; + /* #ifndef APP-NVUE */ + touch-action: none; + /* #endif */ + + &--hover { + background-color: $u-numberBox-hover-bgColor !important; + } + + &--disabled { + color: $u-numberBox-disabled-color; + background-color: $u-numberBox-disabled-bgColor; + } + } + + &__plus { + border-top-right-radius: $u-numberBox-plus-radius; + border-bottom-right-radius: $u-numberBox-plus-radius; + } + + &__minus { + border-top-left-radius: $u-numberBox-minus-radius; + border-bottom-left-radius: $u-numberBox-minus-radius; + } + + &__input { + position: relative; + text-align: $u-numberBox-input-text-align; + font-size: $u-numberBox-input-font-size; + padding: $u-numberBox-input-padding; + margin: $u-numberBox-input-margin; + @include flex; + align-items: center; + justify-content: center; + + &--disabled { + color: $u-numberBox-input-disabled-color; + background-color: $u-numberBox-input-disabled-bgColor; + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-number-keyboard/props.js b/uni_modules/uview-ui/components/u-number-keyboard/props.js new file mode 100644 index 0000000..5e3bf55 --- /dev/null +++ b/uni_modules/uview-ui/components/u-number-keyboard/props.js @@ -0,0 +1,19 @@ +export default { + props: { + // 键盘的类型,number-数字键盘,card-身份证键盘 + mode: { + type: String, + default: uni.$u.props.numberKeyboard.value + }, + // 是否显示键盘的"."符号 + dotDisabled: { + type: Boolean, + default: uni.$u.props.numberKeyboard.dotDisabled + }, + // 是否打乱键盘按键的顺序 + random: { + type: Boolean, + default: uni.$u.props.numberKeyboard.random + } + } +} diff --git a/uni_modules/uview-ui/components/u-number-keyboard/u-number-keyboard.vue b/uni_modules/uview-ui/components/u-number-keyboard/u-number-keyboard.vue new file mode 100644 index 0000000..4f505c6 --- /dev/null +++ b/uni_modules/uview-ui/components/u-number-keyboard/u-number-keyboard.vue @@ -0,0 +1,196 @@ +<template> + <view + class="u-keyboard" + @touchmove.stop.prevent="noop" + > + <view + class="u-keyboard__button-wrapper" + v-for="(item, index) in numList" + :key="index" + > + <view + class="u-keyboard__button-wrapper__button" + :style="[itemStyle(index)]" + @tap="keyboardClick(item)" + hover-class="u-hover-class" + :hover-stay-time="200" + > + <text class="u-keyboard__button-wrapper__button__text">{{ item }}</text> + </view> + </view> + <view + class="u-keyboard__button-wrapper" + > + <view + class="u-keyboard__button-wrapper__button u-keyboard__button-wrapper__button--gray" + hover-class="u-hover-class" + :hover-stay-time="200" + @touchstart.stop="backspaceClick" + @touchend="clearTimer" + > + <u-icon + name="backspace" + color="#303133" + size="28" + ></u-icon> + </view> + </view> + </view> +</template> + +<script> + import props from './props.js'; + + /** + * keyboard 键盘组件 + * @description + * @tutorial + * @property {String} mode 键盘的类型,number-数字键盘,card-身份证键盘 + * @property {Boolean} dotDisabled 是否显示键盘的"."符号 + * @property {Boolean} random 是否打乱键盘按键的顺序 + * @event {Function} change 点击键盘触发 + * @event {Function} backspace 点击退格键触发 + * @example + */ + export default { + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + backspace: 'backspace', // 退格键内容 + dot: '.', // 点 + timer: null, // 长按多次删除的事件监听 + cardX: 'X' // 身份证的X符号 + }; + }, + computed: { + // 键盘需要显示的内容 + numList() { + let tmp = []; + if (this.dotDisabled && this.mode == 'number') { + if (!this.random) { + return [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + } else { + return uni.$u.randomArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + } + } else if (!this.dotDisabled && this.mode == 'number') { + if (!this.random) { + return [1, 2, 3, 4, 5, 6, 7, 8, 9, this.dot, 0]; + } else { + return uni.$u.randomArray([1, 2, 3, 4, 5, 6, 7, 8, 9, this.dot, 0]); + } + } else if (this.mode == 'card') { + if (!this.random) { + return [1, 2, 3, 4, 5, 6, 7, 8, 9, this.cardX, 0]; + } else { + return uni.$u.randomArray([1, 2, 3, 4, 5, 6, 7, 8, 9, this.cardX, 0]); + } + } + }, + // 按键的样式,在非乱序&&数字键盘&&不显示点按钮时,index为9时,按键占位两个空间 + itemStyle() { + return index => { + let style = {}; + if (this.mode == 'number' && this.dotDisabled && index == 9) style.width = '464rpx'; + return style; + }; + }, + // 是否让按键显示灰色,只在非乱序&&数字键盘&&且允许点按键的时候 + btnBgGray() { + return index => { + if (!this.random && index == 9 && (this.mode != 'number' || (this.mode == 'number' && !this + .dotDisabled))) return true; + else return false; + }; + }, + }, + created() { + + }, + methods: { + // 点击退格键 + backspaceClick() { + this.$emit('backspace'); + clearInterval(this.timer); //再次清空定时器,防止重复注册定时器 + this.timer = null; + this.timer = setInterval(() => { + this.$emit('backspace'); + }, 250); + }, + clearTimer() { + clearInterval(this.timer); + this.timer = null; + }, + // 获取键盘显示的内容 + keyboardClick(val) { + // 允许键盘显示点模式和触发非点按键时,将内容转为数字类型 + if (!this.dotDisabled && val != this.dot && val != this.cardX) val = Number(val); + this.$emit('change', val); + } + } + }; +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + $u-number-keyboard-background-color:rgb(224, 228, 230) !default; + $u-number-keyboard-padding:8px 10rpx 8px 10rpx !default; + $u-number-keyboard-button-width:222rpx !default; + $u-number-keyboard-button-margin:4px 6rpx !default; + $u-number-keyboard-button-border-top-left-radius:4px !default; + $u-number-keyboard-button-border-top-right-radius:4px !default; + $u-number-keyboard-button-border-bottom-left-radius:4px !default; + $u-number-keyboard-button-border-bottom-right-radius:4px !default; + $u-number-keyboard-button-height: 90rpx!default; + $u-number-keyboard-button-background-color:#FFFFFF !default; + $u-number-keyboard-button-box-shadow:0 2px 0px #BBBCBE !default; + $u-number-keyboard-text-font-size:20px !default; + $u-number-keyboard-text-font-weight:500 !default; + $u-number-keyboard-text-color:$u-main-color !default; + $u-number-keyboard-gray-background-color:rgb(200, 202, 210) !default; + $u-number-keyboard-u-hover-class-background-color: #BBBCC6 !default; + + .u-keyboard { + @include flex; + flex-direction: row; + justify-content: space-around; + background-color: $u-number-keyboard-background-color; + flex-wrap: wrap; + padding: $u-number-keyboard-padding; + + &__button-wrapper { + box-shadow: $u-number-keyboard-button-box-shadow; + margin: $u-number-keyboard-button-margin; + border-top-left-radius: $u-number-keyboard-button-border-top-left-radius; + border-top-right-radius: $u-number-keyboard-button-border-top-right-radius; + border-bottom-left-radius: $u-number-keyboard-button-border-bottom-left-radius; + border-bottom-right-radius: $u-number-keyboard-button-border-bottom-right-radius; + + &__button { + width: $u-number-keyboard-button-width; + height: $u-number-keyboard-button-height; + background-color: $u-number-keyboard-button-background-color; + @include flex; + justify-content: center; + align-items: center; + border-top-left-radius: $u-number-keyboard-button-border-top-left-radius; + border-top-right-radius: $u-number-keyboard-button-border-top-right-radius; + border-bottom-left-radius: $u-number-keyboard-button-border-bottom-left-radius; + border-bottom-right-radius: $u-number-keyboard-button-border-bottom-right-radius; + + &__text { + font-size: $u-number-keyboard-text-font-size; + font-weight: $u-number-keyboard-text-font-weight; + color: $u-number-keyboard-text-color; + } + + &--gray { + background-color: $u-number-keyboard-gray-background-color; + } + } + } + } + + .u-hover-class { + background-color: $u-number-keyboard-u-hover-class-background-color; + } +</style> diff --git a/uni_modules/uview-ui/components/u-overlay/props.js b/uni_modules/uview-ui/components/u-overlay/props.js new file mode 100644 index 0000000..e6974df --- /dev/null +++ b/uni_modules/uview-ui/components/u-overlay/props.js @@ -0,0 +1,24 @@ +export default { + props: { + // 是否显示遮罩 + show: { + type: Boolean, + default: uni.$u.props.overlay.show + }, + // 层级z-index + zIndex: { + type: [String, Number], + default: uni.$u.props.overlay.zIndex + }, + // 遮罩的过渡时间,单位为ms + duration: { + type: [String, Number], + default: uni.$u.props.overlay.duration + }, + // 不透明度值,当做rgba的第四个参数 + opacity: { + type: [String, Number], + default: uni.$u.props.overlay.opacity + } + } +} diff --git a/uni_modules/uview-ui/components/u-overlay/u-overlay.vue b/uni_modules/uview-ui/components/u-overlay/u-overlay.vue new file mode 100644 index 0000000..92de4e9 --- /dev/null +++ b/uni_modules/uview-ui/components/u-overlay/u-overlay.vue @@ -0,0 +1,68 @@ +<template> + <u-transition + :show="show" + custom-class="u-overlay" + :duration="duration" + :custom-style="overlayStyle" + @click="clickHandler" + > + <slot /> + </u-transition> +</template> + +<script> + import props from './props.js'; + + /** + * overlay 遮罩 + * @description 创建一个遮罩层,用于强调特定的页面元素,并阻止用户对遮罩下层的内容进行操作,一般用于弹窗场景 + * @tutorial https://www.uviewui.com/components/overlay.html + * @property {Boolean} show 是否显示遮罩(默认 false ) + * @property {String | Number} zIndex zIndex 层级(默认 10070 ) + * @property {String | Number} duration 动画时长,单位毫秒(默认 300 ) + * @property {String | Number} opacity 不透明度值,当做rgba的第四个参数 (默认 0.5 ) + * @property {Object} customStyle 定义需要用到的外部样式 + * @event {Function} click 点击遮罩发送事件 + * @example <u-overlay :show="show" @click="show = false"></u-overlay> + */ + export default { + name: "u-overlay", + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + computed: { + overlayStyle() { + const style = { + position: 'fixed', + top: 0, + left: 0, + right: 0, + zIndex: this.zIndex, + bottom: 0, + 'background-color': `rgba(0, 0, 0, ${this.opacity})` + } + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) + } + }, + methods: { + clickHandler() { + this.$emit('click') + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + $u-overlay-top:0 !default; + $u-overlay-left:0 !default; + $u-overlay-width:100% !default; + $u-overlay-height:100% !default; + $u-overlay-background-color:rgba(0, 0, 0, .7) !default; + .u-overlay { + position: fixed; + top:$u-overlay-top; + left:$u-overlay-left; + width: $u-overlay-width; + height:$u-overlay-height; + background-color:$u-overlay-background-color; + } +</style> diff --git a/uni_modules/uview-ui/components/u-parse/node/node.vue b/uni_modules/uview-ui/components/u-parse/node/node.vue new file mode 100644 index 0000000..73e30fd --- /dev/null +++ b/uni_modules/uview-ui/components/u-parse/node/node.vue @@ -0,0 +1,499 @@ +<template> + <view :id="attrs.id" :class="'_'+name+' '+attrs.class" :style="attrs.style"> + <block v-for="(n, i) in childs" v-bind:key="i"> + <!-- 图片 --> + <!-- 占位图 --> + <image v-if="n.name=='img'&&((opts[1]&&!ctrl[i])||ctrl[i]<0)" class="_img" :style="n.attrs.style" :src="ctrl[i]<0?opts[2]:opts[1]" mode="widthFix" /> + <!-- 显示图片 --> + <!-- #ifdef H5 || APP-PLUS --> + <img v-if="n.name=='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]==-1?'display:none;':'')+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap"/> + <!-- #endif --> + <!-- #ifndef H5 || APP-PLUS --> + <image v-if="n.name=='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]==-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;height:1px;'+n.attrs.style" :src="n.attrs.src" :mode="n.h?'':'widthFix'" :lazy-load="opts[0]" :webp="n.webp" :show-menu-by-longpress="opts[3]&&!n.attrs.ignore" :image-menu-prevent="!opts[3]||n.attrs.ignore" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" /> + <!-- #endif --> + <!-- 文本 --> + <!-- #ifndef MP-BAIDU --> + <text v-else-if="n.type=='text'" decode>{{n.text}}</text> + <!-- #endif --> + <text v-else-if="n.name=='br'">\n</text> + <!-- 链接 --> + <view v-else-if="n.name=='a'" :id="n.attrs.id" :class="(n.attrs.href?'_a ':'')+n.attrs.class" hover-class="_hover" :style="'display:inline;'+n.attrs.style" :data-i="i" @tap.stop="linkTap"> + <node name="span" :childs="n.children" :opts="opts" style="display:inherit" /> + </view> + <!-- 视频 --> + <!-- #ifdef APP-PLUS --> + <view v-else-if="n.html" :id="n.attrs.id" :class="'_video '+n.attrs.class" :style="n.attrs.style" v-html="n.html" /> + <!-- #endif --> + <!-- #ifndef APP-PLUS --> + <video v-else-if="n.name=='video'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :autoplay="n.attrs.autoplay" :controls="n.attrs.controls" :loop="n.attrs.loop" :muted="n.attrs.muted" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" /> + <!-- #endif --> + <!-- #ifdef H5 || APP-PLUS --> + <iframe v-else-if="n.name=='iframe'" :style="n.attrs.style" :allowfullscreen="n.attrs.allowfullscreen" :frameborder="n.attrs.frameborder" :src="n.attrs.src" /> + <embed v-else-if="n.name=='embed'" :style="n.attrs.style" :src="n.attrs.src" /> + <!-- #endif --> + <!-- #ifndef MP-TOUTIAO --> + <!-- 音频 --> + <audio v-else-if="n.name=='audio'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :author="n.attrs.author" :controls="n.attrs.controls" :loop="n.attrs.loop" :name="n.attrs.name" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" /> + <!-- #endif --> + <view v-else-if="(n.name=='table'&&n.c)||n.name=='li'" :id="n.attrs.id" :class="'_'+n.name+' '+n.attrs.class" :style="n.attrs.style"> + <node v-if="n.name=='li'" :childs="n.children" :opts="opts" /> + <view v-else v-for="(tbody, x) in n.children" v-bind:key="x" :class="'_'+tbody.name+' '+tbody.attrs.class" :style="tbody.attrs.style"> + <node v-if="tbody.name=='td'||tbody.name=='th'" :childs="tbody.children" :opts="opts" /> + <block v-else v-for="(tr, y) in tbody.children" v-bind:key="y"> + <view v-if="tr.name=='td'||tr.name=='th'" :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style"> + <node :childs="tr.children" :opts="opts" /> + </view> + <view v-else :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style"> + <view v-for="(td, z) in tr.children" v-bind:key="z" :class="'_'+td.name+' '+td.attrs.class" :style="td.attrs.style"> + <node :childs="td.children" :opts="opts" /> + </view> + </view> + </block> + </view> + </view> + + <!-- 富文本 --> + <!-- #ifdef H5 || MP-WEIXIN || MP-QQ || APP-PLUS || MP-360 --> + <rich-text v-else-if="handler.use(n)" :id="n.attrs.id" :style="n.f" :nodes="[n]" /> + <!-- #endif --> + <!-- #ifndef H5 || MP-WEIXIN || MP-QQ || APP-PLUS || MP-360 --> + <rich-text v-else-if="!n.c" :id="n.attrs.id" :style="n.f+';display:inline'" :preview="false" :nodes="[n]" /> + <!-- #endif --> + <!-- 继续递归 --> + <view v-else-if="n.c==2" :id="n.attrs.id" :class="'_'+n.name+' '+n.attrs.class" :style="n.f+';'+n.attrs.style"> + <node v-for="(n2, j) in n.children" v-bind:key="j" :style="n2.f" :name="n2.name" :attrs="n2.attrs" :childs="n2.children" :opts="opts" /> + </view> + <node v-else :style="n.f" :name="n.name" :attrs="n.attrs" :childs="n.children" :opts="opts" /> + </block> + </view> +</template> +<script module="handler" lang="wxs"> +// 行内标签列表 +var inlineTags = { + abbr: true, + b: true, + big: true, + code: true, + del: true, + em: true, + i: true, + ins: true, + label: true, + q: true, + small: true, + span: true, + strong: true, + sub: true, + sup: true +} +/** + * @description 是否使用 rich-text 显示剩余内容 + */ +module.exports = { + use: function (item) { + // 微信和 QQ 的 rich-text inline 布局无效 + if (inlineTags[item.name] || (item.attrs.style || '').indexOf('display:inline') != -1) + return false + return !item.c + } +} +</script> +<script> + +import node from './node' +export default { + name: 'node', + // #ifdef MP-WEIXIN + options: { + virtualHost: true + }, + // #endif + data() { + return { + ctrl: {} + } + }, + props: { + name: String, + attrs: { + type: Object, + default() { + return {} + } + }, + childs: Array, + opts: Array + }, + components: { + + node + }, + mounted() { + for (this.root = this.$parent; this.root.$options.name != 'mp-html'; this.root = this.root.$parent); + // #ifdef H5 || APP-PLUS + if (this.opts[0]) { + for (var i = this.childs.length; i--;) + if (this.childs[i].name == 'img') + break + if (i != -1) { + this.observer = uni.createIntersectionObserver(this).relativeToViewport({ + top: 500, + bottom: 500 + }) + this.observer.observe('._img', res => { + if (res.intersectionRatio) { + this.$set(this.ctrl, 'load', 1) + this.observer.disconnect() + } + }) + } + } + // #endif + }, + beforeDestroy() { + // #ifdef H5 || APP-PLUS + if (this.observer) + this.observer.disconnect() + // #endif + }, + methods:{ + // #ifdef MP-WEIXIN + toJSON() { }, + // #endif + /** + * @description 播放视频事件 + * @param {Event} e + */ + play(e) { + // #ifndef APP-PLUS + if (this.root.pauseVideo) { + var flag = false, id = e.target.id + for (var i = this.root._videos.length; i--;) { + if (this.root._videos[i].id == id) + flag = true + else + this.root._videos[i].pause() // 自动暂停其他视频 + } + // 将自己加入列表 + if (!flag) { + var ctx = uni.createVideoContext(id + // #ifndef MP-BAIDU + , this + // #endif + ) + ctx.id = id + this.root._videos.push(ctx) + } + } + // #endif + }, + + /** + * @description 图片点击事件 + * @param {Event} e + */ + imgTap(e) { + var node = this.childs[e.currentTarget.dataset.i] + if (node.a) + return this.linkTap(node.a) + if (node.attrs.ignore) + return + // #ifdef H5 || APP-PLUS + node.attrs.src = node.attrs.src || node.attrs['data-src'] + // #endif + this.root.$emit('imgTap', node.attrs) + // 自动预览图片 + if (this.root.previewImg) + uni.previewImage({ + current: parseInt(node.attrs.i), + urls: this.root.imgList + }) + }, + + /** + * @description 图片长按 + */ + imgLongTap(e) { + // #ifdef APP-PLUS + var attrs = this.childs[e.currentTarget.dataset.i].attrs + if (!attrs.ignore) + uni.showActionSheet({ + itemList: ['保存图片'], + success: () => { + uni.downloadFile({ + url: this.root.imgList[attrs.i], + success: res => { + uni.saveImageToPhotosAlbum({ + filePath: res.tempFilePath, + success() { + uni.showToast({ + title: '保存成功' + }) + } + }) + } + }) + } + }) + // #endif + }, + + /** + * @description 图片加载完成事件 + * @param {Event} e + */ + imgLoad(e) { + var i = e.currentTarget.dataset.i + // #ifndef H5 || APP-PLUS + // 设置原宽度 + if (!this.childs[i].w) + this.$set(this.ctrl, i, e.detail.width) + else + // #endif + // 加载完毕,取消加载中占位图 + if ((this.opts[1] && !this.ctrl[i]) || this.ctrl[i] == -1) + this.$set(this.ctrl, i, 1) + }, + + /** + * @description 链接点击事件 + * @param {Event} e + */ + linkTap(e) { + var attrs = e.currentTarget ? this.childs[e.currentTarget.dataset.i].attrs : e, + href = attrs.href + this.root.$emit('linkTap', attrs) + if (href) { + // 跳转锚点 + if (href[0] == '#') + this.root.navigateTo(href.substring(1)).catch(() => { }) + // 复制外部链接 + else if (href.includes('://')) { + if (this.root.copyLink) { + // #ifdef H5 + window.open(href) + // #endif + // #ifdef MP + uni.setClipboardData({ + data: href, + success: () => + uni.showToast({ + title: '链接已复制' + }) + }) + // #endif + // #ifdef APP-PLUS + plus.runtime.openWeb(href) + // #endif + } + } + // 跳转页面 + else + uni.navigateTo({ + url: href, + fail() { + uni.switchTab({ + url: href, + fail() { } + }) + } + }) + } + }, + + /** + * @description 错误事件 + * @param {Event} e + */ + mediaError(e) { + var i = e.currentTarget.dataset.i, + node = this.childs[i] + // 加载其他源 + if (node.name == 'video' || node.name == 'audio') { + var index = (this.ctrl[i] || 0) + 1 + if (index > node.src.length) + index = 0 + if (index < node.src.length) + return this.$set(this.ctrl, i, index) + } + // 显示错误占位图 + else if (node.name == 'img' && this.opts[2]) + this.$set(this.ctrl, i, -1) + if (this.root) + this.root.$emit('error', { + source: node.name, + attrs: node.attrs, + errMsg: e.detail.errMsg + }) + } + } +} +</script> +<style> +/* a 标签默认效果 */ +._a { + padding: 1.5px 0 1.5px 0; + color: #366092; + word-break: break-all; +} + +/* a 标签点击态效果 */ +._hover { + text-decoration: underline; + opacity: 0.7; +} + +/* 图片默认效果 */ +._img { + max-width: 100%; + -webkit-touch-callout: none; +} + +/* 内部样式 */ + +._b, +._strong { + font-weight: bold; +} + +._code { + font-family: monospace; +} + +._del { + text-decoration: line-through; +} + +._em, +._i { + font-style: italic; +} + +._h1 { + font-size: 2em; +} + +._h2 { + font-size: 1.5em; +} + +._h3 { + font-size: 1.17em; +} + +._h5 { + font-size: 0.83em; +} + +._h6 { + font-size: 0.67em; +} + +._h1, +._h2, +._h3, +._h4, +._h5, +._h6 { + display: block; + font-weight: bold; +} + +._image { + height: 1px; +} + +._ins { + text-decoration: underline; +} + +._li { + display: list-item; +} + +._ol { + list-style-type: decimal; +} + +._ol, +._ul { + display: block; + padding-left: 40px; + margin: 1em 0; +} + +._q::before { + content: '"'; +} + +._q::after { + content: '"'; +} + +._sub { + font-size: smaller; + vertical-align: sub; +} + +._sup { + font-size: smaller; + vertical-align: super; +} + +._thead, +._tbody, +._tfoot { + display: table-row-group; +} + +._tr { + display: table-row; +} + +._td, +._th { + display: table-cell; + vertical-align: middle; +} + +._th { + font-weight: bold; + text-align: center; +} + +._ul { + list-style-type: disc; +} + +._ul ._ul { + margin: 0; + list-style-type: circle; +} + +._ul ._ul ._ul { + list-style-type: square; +} + +._abbr, +._b, +._code, +._del, +._em, +._i, +._ins, +._label, +._q, +._span, +._strong, +._sub, +._sup { + display: inline; +} + +/* #ifdef APP-PLUS */ +._video { + width: 300px; + height: 225px; +} +/* #endif */ +</style> diff --git a/uni_modules/uview-ui/components/u-parse/parser.js b/uni_modules/uview-ui/components/u-parse/parser.js new file mode 100644 index 0000000..a78a654 --- /dev/null +++ b/uni_modules/uview-ui/components/u-parse/parser.js @@ -0,0 +1,1075 @@ +'use strict' + +/** + * @fileoverview html 解析器 + */ +// 配置 +const config = { + // 信任的标签(保持标签名不变) + trustTags: makeMap('a,abbr,ad,audio,b,blockquote,br,code,col,colgroup,dd,del,dl,dt,div,em,fieldset,h1,h2,h3,h4,h5,h6,hr,i,img,ins,label,legend,li,ol,p,q,ruby,rt,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,ul,video'), + // 块级标签(转为 div,其他的非信任标签转为 span) + blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,pre,section'), + // 要移除的标签 + ignoreTags: makeMap('area,base,canvas,embed,frame,head,iframe,input,link,map,meta,param,rp,script,source,style,textarea,title,track,wbr'), + // 自闭合的标签 + voidTags: makeMap('area,base,br,col,circle,ellipse,embed,frame,hr,img,input,line,link,meta,param,path,polygon,rect,source,track,use,wbr'), + // html 实体 + entities: { + lt: '<', + gt: '>', + quot: '"', + apos: "'", + ensp: '\u2002', + emsp: '\u2003', + nbsp: '\xA0', + semi: ';', + ndash: '–', + mdash: '—', + middot: '·', + lsquo: '‘', + rsquo: '’', + ldquo: '“', + rdquo: '”', + bull: '•', + hellip: '…' + }, + // 默认的标签样式 + tagStyle: { + // #ifndef APP-PLUS-NVUE + address: 'font-style:italic', + big: 'display:inline;font-size:1.2em', + caption: 'display:table-caption;text-align:center', + center: 'text-align:center', + cite: 'font-style:italic', + dd: 'margin-left:40px', + mark: 'background-color:yellow', + pre: 'font-family:monospace;white-space:pre', + s: 'text-decoration:line-through', + small: 'display:inline;font-size:0.8em', + u: 'text-decoration:underline' // #endif + + } +} +const { windowWidth } = uni.getSystemInfoSync() +const blankChar = makeMap(' ,\r,\n,\t,\f') +let idIndex = 0 // #ifdef H5 || APP-PLUS + +config.ignoreTags.iframe = void 0 +config.trustTags.iframe = true +config.ignoreTags.embed = void 0 +config.trustTags.embed = true // #endif +// #ifdef APP-PLUS-NVUE + +config.ignoreTags.source = void 0 +config.ignoreTags.style = void 0 // #endif + +/** + * @description 创建 map + * @param {String} str 逗号分隔 + */ + +function makeMap(str) { + const map = Object.create(null) + const list = str.split(',') + + for (let i = list.length; i--;) { + map[list[i]] = true + } + + return map +} +/** + * @description 解码 html 实体 + * @param {String} str 要解码的字符串 + * @param {Boolean} amp 要不要解码 & + * @returns {String} 解码后的字符串 + */ + +function decodeEntity(str, amp) { + let i = str.indexOf('&') + + while (i != -1) { + const j = str.indexOf(';', i + 3) + let code = void 0 + if (j == -1) break + + if (str[i + 1] == '#') { + // { 形式的实体 + code = parseInt((str[i + 2] == 'x' ? '0' : '') + str.substring(i + 2, j)) + if (!isNaN(code)) str = str.substr(0, i) + String.fromCharCode(code) + str.substr(j + 1) + } else { + // 形式的实体 + code = str.substring(i + 1, j) + if (config.entities[code] || code == 'amp' && amp) str = str.substr(0, i) + (config.entities[code] || '&') + str.substr(j + 1) + } + + i = str.indexOf('&', i + 1) + } + + return str +} +/** + * @description html 解析器 + * @param {Object} vm 组件实例 + */ + +function parser(vm) { + this.options = vm || {} + this.tagStyle = Object.assign(config.tagStyle, this.options.tagStyle) + this.imgList = vm.imgList || [] + this.plugins = vm.plugins || [] + this.attrs = Object.create(null) + this.stack = [] + this.nodes = [] +} +/** + * @description 执行解析 + * @param {String} content 要解析的文本 + */ + +parser.prototype.parse = function (content) { + // 插件处理 + for (let i = this.plugins.length; i--;) { + if (this.plugins[i].onUpdate) content = this.plugins[i].onUpdate(content, config) || content + } + + new lexer(this).parse(content) // 出栈未闭合的标签 + + while (this.stack.length) { + this.popNode() + } + + return this.nodes +} +/** + * @description 将标签暴露出来(不被 rich-text 包含) + */ + +parser.prototype.expose = function () { + // #ifndef APP-PLUS-NVUE + for (let i = this.stack.length; i--;) { + const item = this.stack[i] + if (item.name == 'a' || item.c) return + item.c = 1 + } // #endif +} +/** + * @description 处理插件 + * @param {Object} node 要处理的标签 + * @returns {Boolean} 是否要移除此标签 + */ + +parser.prototype.hook = function (node) { + for (let i = this.plugins.length; i--;) { + if (this.plugins[i].onParse && this.plugins[i].onParse(node, this) == false) return false + } + + return true +} +/** + * @description 将链接拼接上主域名 + * @param {String} url 需要拼接的链接 + * @returns {String} 拼接后的链接 + */ + +parser.prototype.getUrl = function (url) { + const { domain } = this.options + + if (url[0] == '/') { + // // 开头的补充协议名 + if (url[1] == '/') url = `${domain ? domain.split('://')[0] : 'http'}:${url}` // 否则补充整个域名 + else if (domain) url = domain + url + } else if (domain && !url.includes('data:') && !url.includes('://')) url = `${domain}/${url}` + + return url +} +/** + * @description 解析样式表 + * @param {Object} node 标签 + * @returns {Object} + */ + +parser.prototype.parseStyle = function (node) { + const { attrs } = node + const list = (this.tagStyle[node.name] || '').split(';').concat((attrs.style || '').split(';')) + const styleObj = {} + let tmp = '' + + if (attrs.id) { + // 暴露锚点 + if (this.options.useAnchor) this.expose(); else if (node.name != 'img' && node.name != 'a' && node.name != 'video' && node.name != 'audio') attrs.id = void 0 + } // 转换 width 和 height 属性 + + if (attrs.width) { + styleObj.width = parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px') + attrs.width = void 0 + } + + if (attrs.height) { + styleObj.height = parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px') + attrs.height = void 0 + } + + for (let i = 0, len = list.length; i < len; i++) { + const info = list[i].split(':') + if (info.length < 2) continue + const key = info.shift().trim().toLowerCase() + let value = info.join(':').trim() // 兼容性的 css 不压缩 + + if (value[0] == '-' && value.lastIndexOf('-') > 0 || value.includes('safe')) tmp += ';'.concat(key, ':').concat(value) // 重复的样式进行覆盖 + else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import')) { + // 填充链接 + if (value.includes('url')) { + let j = value.indexOf('(') + 1 + + if (j) { + while (value[j] == '"' || value[j] == "'" || blankChar[value[j]]) { + j++ + } + + value = value.substr(0, j) + this.getUrl(value.substr(j)) + } + } // 转换 rpx(rich-text 内部不支持 rpx) + else if (value.includes('rpx')) { + value = value.replace(/[0-9.]+\s*rpx/g, ($) => `${parseFloat($) * windowWidth / 750}px`) + } + + styleObj[key] = value + } + } + + node.attrs.style = tmp + return styleObj +} +/** + * @description 解析到标签名 + * @param {String} name 标签名 + * @private + */ + +parser.prototype.onTagName = function (name) { + this.tagName = this.xml ? name : name.toLowerCase() + if (this.tagName == 'svg') this.xml = true // svg 标签内大小写敏感 +} +/** + * @description 解析到属性名 + * @param {String} name 属性名 + * @private + */ + +parser.prototype.onAttrName = function (name) { + name = this.xml ? name : name.toLowerCase() + + if (name.substr(0, 5) == 'data-') { + // data-src 自动转为 src + if (name == 'data-src' && !this.attrs.src) this.attrName = 'src' // a 和 img 标签保留 data- 的属性,可以在 imgtap 和 linktap 事件中使用 + else if (this.tagName == 'img' || this.tagName == 'a') this.attrName = name // 剩余的移除以减小大小 + else this.attrName = void 0 + } else { + this.attrName = name + this.attrs[name] = 'T' // boolean 型属性缺省设置 + } +} +/** + * @description 解析到属性值 + * @param {String} val 属性值 + * @private + */ + +parser.prototype.onAttrVal = function (val) { + const name = this.attrName || '' // 部分属性进行实体解码 + + if (name == 'style' || name == 'href') this.attrs[name] = decodeEntity(val, true) // 拼接主域名 + else if (name.includes('src')) this.attrs[name] = this.getUrl(decodeEntity(val, true)); else if (name) this.attrs[name] = val +} +/** + * @description 解析到标签开始 + * @param {Boolean} selfClose 是否有自闭合标识 /> + * @private + */ + +parser.prototype.onOpenTag = function (selfClose) { + // 拼装 node + const node = Object.create(null) + node.name = this.tagName + node.attrs = this.attrs + this.attrs = Object.create(null) + const { attrs } = node + const parent = this.stack[this.stack.length - 1] + const siblings = parent ? parent.children : this.nodes + const close = this.xml ? selfClose : config.voidTags[node.name] // 转换 embed 标签 + + if (node.name == 'embed') { + // #ifndef H5 || APP-PLUS + const src = attrs.src || '' // 按照后缀名和 type 将 embed 转为 video 或 audio + + if (src.includes('.mp4') || src.includes('.3gp') || src.includes('.m3u8') || (attrs.type || '').includes('video')) node.name = 'video'; else if (src.includes('.mp3') || src.includes('.wav') || src.includes('.aac') || src.includes('.m4a') || (attrs.type || '').includes('audio')) node.name = 'audio' + if (attrs.autostart) attrs.autoplay = 'T' + attrs.controls = 'T' // #endif + // #ifdef H5 || APP-PLUS + + this.expose() // #endif + } // #ifndef APP-PLUS-NVUE + // 处理音视频 + + if (node.name == 'video' || node.name == 'audio') { + // 设置 id 以便获取 context + if (node.name == 'video' && !attrs.id) attrs.id = `v${idIndex++}` // 没有设置 controls 也没有设置 autoplay 的自动设置 controls + + if (!attrs.controls && !attrs.autoplay) attrs.controls = 'T' // 用数组存储所有可用的 source + + node.src = [] + + if (attrs.src) { + node.src.push(attrs.src) + attrs.src = void 0 + } + + this.expose() + } // #endif + // 处理自闭合标签 + + if (close) { + if (!this.hook(node) || config.ignoreTags[node.name]) { + // 通过 base 标签设置主域名 + if (node.name == 'base' && !this.options.domain) this.options.domain = attrs.href // #ifndef APP-PLUS-NVUE + // 设置 source 标签(仅父节点为 video 或 audio 时有效) + else if (node.name == 'source' && parent && (parent.name == 'video' || parent.name == 'audio') && attrs.src) parent.src.push(attrs.src) // #endif + + return + } // 解析 style + + const styleObj = this.parseStyle(node) // 处理图片 + + if (node.name == 'img') { + if (attrs.src) { + // 标记 webp + if (attrs.src.includes('webp')) node.webp = 'T' // data url 图片如果没有设置 original-src 默认为不可预览的小图片 + + if (attrs.src.includes('data:') && !attrs['original-src']) attrs.ignore = 'T' + + if (!attrs.ignore || node.webp || attrs.src.includes('cloud://')) { + for (let i = this.stack.length; i--;) { + const item = this.stack[i] + + if (item.name == 'a') { + node.a = item.attrs + break + } // #ifndef H5 || APP-PLUS + + const style = item.attrs.style || '' + + if (style.includes('flex:') && !style.includes('flex:0') && !style.includes('flex: 0') && (!styleObj.width || !styleObj.width.includes('%'))) { + styleObj.width = '100% !important' + styleObj.height = '' + + for (let j = i + 1; j < this.stack.length; j++) { + this.stack[j].attrs.style = (this.stack[j].attrs.style || '').replace('inline-', '') + } + } else if (style.includes('flex') && styleObj.width == '100%') { + for (let _j = i + 1; _j < this.stack.length; _j++) { + const _style = this.stack[_j].attrs.style || '' + + if (!_style.includes(';width') && !_style.includes(' width') && _style.indexOf('width') != 0) { + styleObj.width = '' + break + } + } + } else if (style.includes('inline-block')) { + if (styleObj.width && styleObj.width[styleObj.width.length - 1] == '%') { + item.attrs.style += `;max-width:${styleObj.width}` + styleObj.width = '' + } else item.attrs.style += ';max-width:100%' + } // #endif + + item.c = 1 + } + + attrs.i = this.imgList.length.toString() + + let _src = attrs['original-src'] || attrs.src // #ifndef H5 || MP-ALIPAY || APP-PLUS || MP-360 + + if (this.imgList.includes(_src)) { + // 如果有重复的链接则对域名进行随机大小写变换避免预览时错位 + let _i = _src.indexOf('://') + + if (_i != -1) { + _i += 3 + + let newSrc = _src.substr(0, _i) + + for (; _i < _src.length; _i++) { + if (_src[_i] == '/') break + newSrc += Math.random() > 0.5 ? _src[_i].toUpperCase() : _src[_i] + } + + newSrc += _src.substr(_i) + _src = newSrc + } + } // #endif + + this.imgList.push(_src) // #ifdef H5 || APP-PLUS + + if (this.options.lazyLoad) { + attrs['data-src'] = attrs.src + attrs.src = void 0 + } // #endif + } + } + + if (styleObj.display == 'inline') styleObj.display = '' // #ifndef APP-PLUS-NVUE + + if (attrs.ignore) { + styleObj['max-width'] = styleObj['max-width'] || '100%' + attrs.style += ';-webkit-touch-callout:none' + } // #endif + // 设置的宽度超出屏幕,为避免变形,高度转为自动 + + if (parseInt(styleObj.width) > windowWidth) styleObj.height = void 0 // 记录是否设置了宽高 + + if (styleObj.width) { + if (styleObj.width.includes('auto')) styleObj.width = ''; else { + node.w = 'T' + if (styleObj.height && !styleObj.height.includes('auto')) node.h = 'T' + } + } + } else if (node.name == 'svg') { + siblings.push(node) + this.stack.push(node) + this.popNode() + return + } + + for (const key in styleObj) { + if (styleObj[key]) attrs.style += ';'.concat(key, ':').concat(styleObj[key].replace(' !important', '')) + } + + attrs.style = attrs.style.substr(1) || void 0 + } else { + if (node.name == 'pre' || (attrs.style || '').includes('white-space') && attrs.style.includes('pre')) this.pre = node.pre = true + node.children = [] + this.stack.push(node) + } // 加入节点树 + + siblings.push(node) +} +/** + * @description 解析到标签结束 + * @param {String} name 标签名 + * @private + */ + +parser.prototype.onCloseTag = function (name) { + // 依次出栈到匹配为止 + name = this.xml ? name : name.toLowerCase() + let i + + for (i = this.stack.length; i--;) { + if (this.stack[i].name == name) break + } + + if (i != -1) { + while (this.stack.length > i) { + this.popNode() + } + } else if (name == 'p' || name == 'br') { + const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes + siblings.push({ + name, + attrs: {} + }) + } +} +/** + * @description 处理标签出栈 + * @private + */ + +parser.prototype.popNode = function () { + const node = this.stack.pop() + let { attrs } = node + const { children } = node + const parent = this.stack[this.stack.length - 1] + const siblings = parent ? parent.children : this.nodes + + if (!this.hook(node) || config.ignoreTags[node.name]) { + // 获取标题 + if (node.name == 'title' && children.length && children[0].type == 'text' && this.options.setTitle) { + uni.setNavigationBarTitle({ + title: children[0].text + }) + } + siblings.pop() + return + } + + if (node.pre) { + // 是否合并空白符标识 + node.pre = this.pre = void 0 + + for (let i = this.stack.length; i--;) { + if (this.stack[i].pre) this.pre = true + } + } + + const styleObj = {} // 转换 svg + + if (node.name == 'svg') { + // #ifndef APP-PLUS-NVUE + let src = '' + const { style } = attrs + attrs.style = '' + attrs.xmlns = 'http://www.w3.org/2000/svg'; + + (function traversal(node) { + src += `<${node.name}` + + for (let item in node.attrs) { + const val = node.attrs[item] + + if (val) { + if (item == 'viewbox') item = 'viewBox' + src += ' '.concat(item, '="').concat(val, '"') + } + } + + if (!node.children) src += '/>'; else { + src += '>' + + for (let _i2 = 0; _i2 < node.children.length; _i2++) { + traversal(node.children[_i2]) + } + + src += `</${node.name}>` + } + }(node)) + + node.name = 'img' + node.attrs = { + src: `data:image/svg+xml;utf8,${src.replace(/#/g, '%23')}`, + style, + ignore: 'T' + } + node.children = void 0 // #endif + + this.xml = false + return + } // #ifndef APP-PLUS-NVUE + // 转换 align 属性 + + if (attrs.align) { + if (node.name == 'table') { + if (attrs.align == 'center') styleObj['margin-inline-start'] = styleObj['margin-inline-end'] = 'auto'; else styleObj.float = attrs.align + } else styleObj['text-align'] = attrs.align + + attrs.align = void 0 + } // 转换 font 标签的属性 + + if (node.name == 'font') { + if (attrs.color) { + styleObj.color = attrs.color + attrs.color = void 0 + } + + if (attrs.face) { + styleObj['font-family'] = attrs.face + attrs.face = void 0 + } + + if (attrs.size) { + let size = parseInt(attrs.size) + + if (!isNaN(size)) { + if (size < 1) size = 1; else if (size > 7) size = 7 + styleObj['font-size'] = ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'][size - 1] + } + + attrs.size = void 0 + } + } // #endif + // 一些编辑器的自带 class + + if ((attrs.class || '').includes('align-center')) styleObj['text-align'] = 'center' + Object.assign(styleObj, this.parseStyle(node)) + + if (parseInt(styleObj.width) > windowWidth) { + styleObj['max-width'] = '100%' + styleObj['box-sizing'] = 'border-box' + } // #ifndef APP-PLUS-NVUE + + if (config.blockTags[node.name]) node.name = 'div' // 未知标签转为 span,避免无法显示 + else if (!config.trustTags[node.name] && !this.xml) node.name = 'span' + if (node.name == 'a' || node.name == 'ad' // #ifdef H5 || APP-PLUS + || node.name == 'iframe' // #endif + ) this.expose() // #ifdef APP-PLUS + else if (node.name == 'video') { + let str = '<video style="width:100%;height:100%"' // 空白图占位 + + if (!attrs.poster && !attrs.autoplay) attrs.poster = "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'/>" + + for (const item in attrs) { + if (attrs[item]) str += ` ${item}="${attrs[item]}"` + } + + if (this.options.pauseVideo) str += ' onplay="for(var e=document.getElementsByTagName(\'video\'),t=0;t<e.length;t++)e[t]!=this&&e[t].pause()"' + str += '>' + + for (let _i3 = 0; _i3 < node.src.length; _i3++) { + str += `<source src="${node.src[_i3]}">` + } + + str += '</video>' + node.html = str + } // #endif + // 列表处理 + else if ((node.name == 'ul' || node.name == 'ol') && node.c) { + const types = { + a: 'lower-alpha', + A: 'upper-alpha', + i: 'lower-roman', + I: 'upper-roman' + } + + if (types[attrs.type]) { + attrs.style += `;list-style-type:${types[attrs.type]}` + attrs.type = void 0 + } + + for (let _i4 = children.length; _i4--;) { + if (children[_i4].name == 'li') children[_i4].c = 1 + } + } // 表格处理 + else if (node.name == 'table') { + // cellpadding、cellspacing、border 这几个常用表格属性需要通过转换实现 + let padding = parseFloat(attrs.cellpadding) + let spacing = parseFloat(attrs.cellspacing) + const border = parseFloat(attrs.border) + + if (node.c) { + // padding 和 spacing 默认 2 + if (isNaN(padding)) padding = 2 + if (isNaN(spacing)) spacing = 2 + } + + if (border) attrs.style += `;border:${border}px solid gray` + + if (node.flag && node.c) { + // 有 colspan 或 rowspan 且含有链接的表格通过 grid 布局实现 + styleObj.display = 'grid' + + if (spacing) { + styleObj['grid-gap'] = `${spacing}px` + styleObj.padding = `${spacing}px` + } // 无间隔的情况下避免边框重叠 + else if (border) attrs.style += ';border-left:0;border-top:0' + + const width = [] + // 表格的列宽 + const trList = [] + // tr 列表 + const cells = [] + // 保存新的单元格 + const map = {}; // 被合并单元格占用的格子 + + (function traversal(nodes) { + for (let _i5 = 0; _i5 < nodes.length; _i5++) { + if (nodes[_i5].name == 'tr') trList.push(nodes[_i5]); else traversal(nodes[_i5].children || []) + } + }(children)) + + for (let row = 1; row <= trList.length; row++) { + let col = 1 + + for (let j = 0; j < trList[row - 1].children.length; j++, col++) { + const td = trList[row - 1].children[j] + + if (td.name == 'td' || td.name == 'th') { + // 这个格子被上面的单元格占用,则列号++ + while (map[`${row}.${col}`]) { + col++ + } + + let _style2 = td.attrs.style || '' + const start = _style2.indexOf('width') ? _style2.indexOf(';width') : 0 // 提取出 td 的宽度 + + if (start != -1) { + let end = _style2.indexOf(';', start + 6) + + if (end == -1) end = _style2.length + if (!td.attrs.colspan) width[col] = _style2.substring(start ? start + 7 : 6, end) + _style2 = _style2.substr(0, start) + _style2.substr(end) + } + + _style2 += (border ? ';border:'.concat(border, 'px solid gray') + (spacing ? '' : ';border-right:0;border-bottom:0') : '') + (padding ? ';padding:'.concat(padding, 'px') : '') // 处理列合并 + + if (td.attrs.colspan) { + _style2 += ';grid-column-start:'.concat(col, ';grid-column-end:').concat(col + parseInt(td.attrs.colspan)) + if (!td.attrs.rowspan) _style2 += ';grid-row-start:'.concat(row, ';grid-row-end:').concat(row + 1) + col += parseInt(td.attrs.colspan) - 1 + } // 处理行合并 + + if (td.attrs.rowspan) { + _style2 += ';grid-row-start:'.concat(row, ';grid-row-end:').concat(row + parseInt(td.attrs.rowspan)) + if (!td.attrs.colspan) _style2 += ';grid-column-start:'.concat(col, ';grid-column-end:').concat(col + 1) // 记录下方单元格被占用 + + for (let k = 1; k < td.attrs.rowspan; k++) { + map[`${row + k}.${col}`] = 1 + } + } + + if (_style2) td.attrs.style = _style2 + cells.push(td) + } + } + + if (row == 1) { + let temp = '' + + for (let _i6 = 1; _i6 < col; _i6++) { + temp += `${width[_i6] ? width[_i6] : 'auto'} ` + } + + styleObj['grid-template-columns'] = temp + } + } + + node.children = cells + } else { + // 没有使用合并单元格的表格通过 table 布局实现 + if (node.c) styleObj.display = 'table' + if (!isNaN(spacing)) styleObj['border-spacing'] = `${spacing}px` + + if (border || padding) { + // 遍历 + (function traversal(nodes) { + for (let _i7 = 0; _i7 < nodes.length; _i7++) { + const _td = nodes[_i7] + + if (_td.name == 'th' || _td.name == 'td') { + if (border) _td.attrs.style = 'border:'.concat(border, 'px solid gray;').concat(_td.attrs.style || '') + if (padding) _td.attrs.style = 'padding:'.concat(padding, 'px;').concat(_td.attrs.style || '') + } else if (_td.children) traversal(_td.children) + } + }(children)) + } + } // 给表格添加一个单独的横向滚动层 + + if (this.options.scrollTable && !(attrs.style || '').includes('inline')) { + const table = { ...node } + node.name = 'div' + node.attrs = { + style: 'overflow:auto' + } + node.children = [table] + attrs = table.attrs + } + } else if ((node.name == 'td' || node.name == 'th') && (attrs.colspan || attrs.rowspan)) { + for (let _i8 = this.stack.length; _i8--;) { + if (this.stack[_i8].name == 'table') { + this.stack[_i8].flag = 1 // 指示含有合并单元格 + + break + } + } + } // 转换 ruby + else if (node.name == 'ruby') { + node.name = 'span' + + for (let _i9 = 0; _i9 < children.length - 1; _i9++) { + if (children[_i9].type == 'text' && children[_i9 + 1].name == 'rt') { + children[_i9] = { + name: 'div', + attrs: { + style: 'display:inline-block' + }, + children: [{ + name: 'div', + attrs: { + style: 'font-size:50%;text-align:start' + }, + children: children[_i9 + 1].children + }, children[_i9]] + } + children.splice(_i9 + 1, 1) + } + } + } else if (node.c) { + node.c = 2 + + for (let _i10 = node.children.length; _i10--;) { + if (!node.children[_i10].c || node.children[_i10].name == 'table') node.c = 1 + } + } + if ((styleObj.display || '').includes('flex') && !node.c) { + for (let _i11 = children.length; _i11--;) { + const _item = children[_i11] + + if (_item.f) { + _item.attrs.style = (_item.attrs.style || '') + _item.f + _item.f = void 0 + } + } + } // flex 布局时部分样式需要提取到 rich-text 外层 + + const flex = parent && (parent.attrs.style || '').includes('flex') // #ifdef MP-WEIXIN + // 检查基础库版本 virtualHost 是否可用 + && !(node.c && wx.getNFCAdapter) // #endif + // #ifndef MP-WEIXIN || MP-QQ || MP-BAIDU || MP-TOUTIAO + && !node.c // #endif + + if (flex) node.f = ';max-width:100%' // #endif + + for (const key in styleObj) { + if (styleObj[key]) { + const val = ';'.concat(key, ':').concat(styleObj[key].replace(' !important', '')) // #ifndef APP-PLUS-NVUE + + if (flex && (key.includes('flex') && key != 'flex-direction' || key == 'align-self' || styleObj[key][0] == '-' || key == 'width' && val.includes('%'))) { + node.f += val + if (key == 'width') attrs.style += ';width:100%' + } else // #endif + { attrs.style += val } + } + } + + attrs.style = attrs.style.substr(1) || void 0 +} +/** + * @description 解析到文本 + * @param {String} text 文本内容 + */ + +parser.prototype.onText = function (text) { + if (!this.pre) { + // 合并空白符 + let trim = '' + let flag + + for (let i = 0, len = text.length; i < len; i++) { + if (!blankChar[text[i]]) trim += text[i]; else { + if (trim[trim.length - 1] != ' ') trim += ' ' + if (text[i] == '\n' && !flag) flag = true + } + } // 去除含有换行符的空串 + + if (trim == ' ' && flag) return + text = trim + } + + const node = Object.create(null) + node.type = 'text' + node.text = decodeEntity(text) + + if (this.hook(node)) { + const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes + siblings.push(node) + } +} +/** + * @description html 词法分析器 + * @param {Object} handler 高层处理器 + */ + +function lexer(handler) { + this.handler = handler +} +/** + * @description 执行解析 + * @param {String} content 要解析的文本 + */ + +lexer.prototype.parse = function (content) { + this.content = content || '' + this.i = 0 // 标记解析位置 + + this.start = 0 // 标记一个单词的开始位置 + + this.state = this.text // 当前状态 + + for (let len = this.content.length; this.i != -1 && this.i < len;) { + this.state() + } +} +/** + * @description 检查标签是否闭合 + * @param {String} method 如果闭合要进行的操作 + * @returns {Boolean} 是否闭合 + * @private + */ + +lexer.prototype.checkClose = function (method) { + const selfClose = this.content[this.i] == '/' + + if (this.content[this.i] == '>' || selfClose && this.content[this.i + 1] == '>') { + if (method) this.handler[method](this.content.substring(this.start, this.i)) + this.i += selfClose ? 2 : 1 + this.start = this.i + this.handler.onOpenTag(selfClose) + + if (this.handler.tagName == 'script') { + this.i = this.content.indexOf('</', this.i) + + if (this.i != -1) { + this.i += 2 + this.start = this.i + } + + this.state = this.endTag + } else this.state = this.text + + return true + } + + return false +} +/** + * @description 文本状态 + * @private + */ + +lexer.prototype.text = function () { + this.i = this.content.indexOf('<', this.i) // 查找最近的标签 + + if (this.i == -1) { + // 没有标签了 + if (this.start < this.content.length) this.handler.onText(this.content.substring(this.start, this.content.length)) + return + } + + const c = this.content[this.i + 1] + + if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') { + // 标签开头 + if (this.start != this.i) this.handler.onText(this.content.substring(this.start, this.i)) + this.start = ++this.i + this.state = this.tagName + } else if (c == '/' || c == '!' || c == '?') { + if (this.start != this.i) this.handler.onText(this.content.substring(this.start, this.i)) + const next = this.content[this.i + 2] + + if (c == '/' && (next >= 'a' && next <= 'z' || next >= 'A' && next <= 'Z')) { + // 标签结尾 + this.i += 2 + this.start = this.i + return this.state = this.endTag + } // 处理注释 + + let end = '-->' + if (c != '!' || this.content[this.i + 2] != '-' || this.content[this.i + 3] != '-') end = '>' + this.i = this.content.indexOf(end, this.i) + + if (this.i != -1) { + this.i += end.length + this.start = this.i + } + } else this.i++ +} +/** + * @description 标签名状态 + * @private + */ + +lexer.prototype.tagName = function () { + if (blankChar[this.content[this.i]]) { + // 解析到标签名 + this.handler.onTagName(this.content.substring(this.start, this.i)) + + while (blankChar[this.content[++this.i]]) { + + } + + if (this.i < this.content.length && !this.checkClose()) { + this.start = this.i + this.state = this.attrName + } + } else if (!this.checkClose('onTagName')) this.i++ +} +/** + * @description 属性名状态 + * @private + */ + +lexer.prototype.attrName = function () { + let c = this.content[this.i] + + if (blankChar[c] || c == '=') { + // 解析到属性名 + this.handler.onAttrName(this.content.substring(this.start, this.i)) + let needVal = c == '=' + const len = this.content.length + + while (++this.i < len) { + c = this.content[this.i] + + if (!blankChar[c]) { + if (this.checkClose()) return + + if (needVal) { + // 等号后遇到第一个非空字符 + this.start = this.i + return this.state = this.attrVal + } + + if (this.content[this.i] == '=') needVal = true; else { + this.start = this.i + return this.state = this.attrName + } + } + } + } else if (!this.checkClose('onAttrName')) this.i++ +} +/** + * @description 属性值状态 + * @private + */ + +lexer.prototype.attrVal = function () { + const c = this.content[this.i] + const len = this.content.length // 有冒号的属性 + + if (c == '"' || c == "'") { + this.start = ++this.i + this.i = this.content.indexOf(c, this.i) + if (this.i == -1) return + this.handler.onAttrVal(this.content.substring(this.start, this.i)) + } // 没有冒号的属性 + else { + for (; this.i < len; this.i++) { + if (blankChar[this.content[this.i]]) { + this.handler.onAttrVal(this.content.substring(this.start, this.i)) + break + } else if (this.checkClose('onAttrVal')) return + } + } + + while (blankChar[this.content[++this.i]]) { + + } + + if (this.i < len && !this.checkClose()) { + this.start = this.i + this.state = this.attrName + } +} +/** + * @description 结束标签状态 + * @returns {String} 结束的标签名 + * @private + */ + +lexer.prototype.endTag = function () { + const c = this.content[this.i] + + if (blankChar[c] || c == '>' || c == '/') { + this.handler.onCloseTag(this.content.substring(this.start, this.i)) + + if (c != '>') { + this.i = this.content.indexOf('>', this.i) + if (this.i == -1) return + } + + this.start = ++this.i + this.state = this.text + } else this.i++ +} + +module.exports = parser diff --git a/uni_modules/uview-ui/components/u-parse/props.js b/uni_modules/uview-ui/components/u-parse/props.js new file mode 100644 index 0000000..defd06c --- /dev/null +++ b/uni_modules/uview-ui/components/u-parse/props.js @@ -0,0 +1,45 @@ +export default { + props: { + // #ifdef APP-PLUS-NVUE + bgColor: String, + // #endif + content: String, + copyLink: { + type: Boolean, + default: uni.$u.props.parse.copyLink + }, + domain: String, + errorImg: { + type: String, + default: uni.$u.props.parse.errorImg + }, + lazyLoad: { + type: Boolean, + default: uni.$u.props.parse.lazyLoad + }, + loadingImg: { + type: String, + default: uni.$u.props.parse.loadingImg + }, + pauseVideo: { + type: Boolean, + default: uni.$u.props.parse.pauseVideo + }, + previewImg: { + type: Boolean, + default: uni.$u.props.parse.previewImg + }, + scrollTable: Boolean, + selectable: Boolean, + setTitle: { + type: Boolean, + default: uni.$u.props.parse.setTitle + }, + showImgMenu: { + type: Boolean, + default: uni.$u.props.parse.showImgMenu + }, + tagStyle: Object, + useAnchor: null + } +} diff --git a/uni_modules/uview-ui/components/u-parse/u-parse.vue b/uni_modules/uview-ui/components/u-parse/u-parse.vue new file mode 100644 index 0000000..7bc8b3d --- /dev/null +++ b/uni_modules/uview-ui/components/u-parse/u-parse.vue @@ -0,0 +1,366 @@ +<template> + <view id="_root" :class="(selectable?'_select ':'')+'_root'"> + <slot v-if="!nodes[0]" /> + <!-- #ifndef APP-PLUS-NVUE --> + <node v-else :childs="nodes" :opts="[lazyLoad,loadingImg,errorImg,showImgMenu]" /> + <!-- #endif --> + <!-- #ifdef APP-PLUS-NVUE --> + <web-view ref="web" src="/static/app-plus/mp-html/local.html" :style="'margin-top:-2px;height:' + height + 'px'" @onPostMessage="_onMessage" /> + <!-- #endif --> + </view> +</template> + +<script> + import props from './props.js'; +/** + * mp-html v2.0.4 + * @description 富文本组件 + * @tutorial https://github.com/jin-yufeng/mp-html + * @property {String} bgColor 背景颜色,只适用与APP-PLUS-NVUE + * @property {String} content 用于渲染的富文本字符串(默认 true ) + * @property {Boolean} copyLink 是否允许外部链接被点击时自动复制 + * @property {String} domain 主域名,用于拼接链接 + * @property {String} errorImg 图片出错时的占位图链接 + * @property {Boolean} lazyLoad 是否开启图片懒加载(默认 true ) + * @property {string} loadingImg 图片加载过程中的占位图链接 + * @property {Boolean} pauseVideo 是否在播放一个视频时自动暂停其它视频(默认 true ) + * @property {Boolean} previewImg 是否允许图片被点击时自动预览(默认 true ) + * @property {Boolean} scrollTable 是否给每个表格添加一个滚动层使其能单独横向滚动 + * @property {Boolean} selectable 是否开启长按复制 + * @property {Boolean} setTitle 是否将 title 标签的内容设置到页面标题(默认 true ) + * @property {Boolean} showImgMenu 是否允许图片被长按时显示菜单(默认 true ) + * @property {Object} tagStyle 标签的默认样式 + * @property {Boolean | Number} useAnchor 是否使用锚点链接 + * + * @event {Function} load dom 结构加载完毕时触发 + * @event {Function} ready 所有图片加载完毕时触发 + * @event {Function} imgTap 图片被点击时触发 + * @event {Function} linkTap 链接被点击时触发 + * @event {Function} error 媒体加载出错时触发 + */ +const plugins=[] +const parser = require('./parser') +// #ifndef APP-PLUS-NVUE +import node from './node/node' +// #endif +// #ifdef APP-PLUS-NVUE +const dom = weex.requireModule('dom') +// #endif +export default { + name: 'mp-html', + data() { + return { + nodes: [], + // #ifdef APP-PLUS-NVUE + height: 0 + // #endif + } + }, + mixins:[props], + // #ifndef APP-PLUS-NVUE + components: { + node + }, + // #endif + watch: { + content(content) { + this.setContent(content) + } + }, + created() { + this.plugins = [] + for (let i = plugins.length; i--;) + this.plugins.push(new plugins[i](this)) + }, + mounted() { + if (this.content && !this.nodes.length) + this.setContent(this.content) + }, + beforeDestroy() { + this._hook('onDetached') + clearInterval(this._timer) + }, + methods: { + /** + * @description 将锚点跳转的范围限定在一个 scroll-view 内 + * @param {Object} page scroll-view 所在页面的示例 + * @param {String} selector scroll-view 的选择器 + * @param {String} scrollTop scroll-view scroll-top 属性绑定的变量名 + */ + in(page, selector, scrollTop) { + // #ifndef APP-PLUS-NVUE + if (page && selector && scrollTop) + this._in = { + page, + selector, + scrollTop + } + // #endif + }, + + /** + * @description 锚点跳转 + * @param {String} id 要跳转的锚点 id + * @param {Number} offset 跳转位置的偏移量 + * @returns {Promise} + */ + navigateTo(id, offset) { + return new Promise((resolve, reject) => { + if (!this.useAnchor) + return reject('Anchor is disabled') + offset = offset || parseInt(this.useAnchor) || 0 + // #ifdef APP-PLUS-NVUE + if (!id) { + dom.scrollToElement(this.$refs.web, { + offset + }) + resolve() + } else { + this._navigateTo = { + resolve, + reject, + offset + } + this.$refs.web.evalJs('uni.postMessage({data:{action:"getOffset",offset:(document.getElementById(' + id + ')||{}).offsetTop}})') + } + // #endif + // #ifndef APP-PLUS-NVUE + let deep = ' ' + // #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO + deep = '>>>' + // #endif + const selector = uni.createSelectorQuery() + // #ifndef MP-ALIPAY + .in(this._in ? this._in.page : this) + // #endif + .select((this._in ? this._in.selector : '._root') + (id ? `${deep}#${id}` : '')).boundingClientRect() + if (this._in) + selector.select(this._in.selector).scrollOffset() + .select(this._in.selector).boundingClientRect() // 获取 scroll-view 的位置和滚动距离 + else + selector.selectViewport().scrollOffset() // 获取窗口的滚动距离 + selector.exec(res => { + if (!res[0]) + return reject('Label not found') + const scrollTop = res[1].scrollTop + res[0].top - (res[2] ? res[2].top : 0) + offset + if (this._in) + // scroll-view 跳转 + this._in.page[this._in.scrollTop] = scrollTop + else + // 页面跳转 + uni.pageScrollTo({ + scrollTop, + duration: 300 + }) + resolve() + }) + // #endif + }) + }, + + /** + * @description 获取文本内容 + * @return {String} + */ + getText() { + let text = ''; + (function traversal(nodes) { + for (let i = 0; i < nodes.length; i++) { + const node = nodes[i] + if (node.type == 'text') + text += node.text.replace(/&/g, '&') + else if (node.name == 'br') + text += '\n' + else { + // 块级标签前后加换行 + const isBlock = node.name == 'p' || node.name == 'div' || node.name == 'tr' || node.name == 'li' || (node.name[0] == 'h' && node.name[1] > '0' && node.name[1] < '7') + if (isBlock && text && text[text.length - 1] != '\n') + text += '\n' + // 递归获取子节点的文本 + if (node.children) + traversal(node.children) + if (isBlock && text[text.length - 1] != '\n') + text += '\n' + else if (node.name == 'td' || node.name == 'th') + text += '\t' + } + } + })(this.nodes) + return text + }, + + /** + * @description 获取内容大小和位置 + * @return {Promise} + */ + getRect() { + return new Promise((resolve, reject) => { + uni.createSelectorQuery() + // #ifndef MP-ALIPAY + .in(this) + // #endif + .select('#_root').boundingClientRect().exec(res => res[0] ? resolve(res[0]) : reject('Root label not found')) + }) + }, + + /** + * @description 设置内容 + * @param {String} content html 内容 + * @param {Boolean} append 是否在尾部追加 + */ + setContent(content, append) { + if (!append || !this.imgList) + this.imgList = [] + const nodes = new parser(this).parse(content) + // #ifdef APP-PLUS-NVUE + if (this._ready) + this._set(nodes, append) + // #endif + this.$set(this, 'nodes', append ? (this.nodes || []).concat(nodes) : nodes) + + // #ifndef APP-PLUS-NVUE + this._videos = [] + this.$nextTick(() => { + this._hook('onLoad') + this.$emit('load') + }) + + // 等待图片加载完毕 + let height + clearInterval(this._timer) + this._timer = setInterval(() => { + this.getRect().then(rect => { + // 350ms 总高度无变化就触发 ready 事件 + if (rect.height == height) { + this.$emit('ready', rect) + clearInterval(this._timer) + } + height = rect.height + }).catch(() => { }) + }, 350) + // #endif + }, + + /** + * @description 调用插件钩子函数 + */ + _hook(name) { + for (let i = plugins.length; i--;) + if (this.plugins[i][name]) + this.plugins[i][name]() + }, + + // #ifdef APP-PLUS-NVUE + /** + * @description 设置内容 + */ + _set(nodes, append) { + this.$refs.web.evalJs('setContent(' + JSON.stringify(nodes) + ',' + JSON.stringify([this.bgColor, this.errorImg, this.loadingImg, this.pauseVideo, this.scrollTable, this.selectable]) + ',' + append + ')') + }, + + /** + * @description 接收到 web-view 消息 + */ + _onMessage(e) { + const message = e.detail.data[0] + switch (message.action) { + // web-view 初始化完毕 + case 'onJSBridgeReady': + this._ready = true + if (this.nodes) + this._set(this.nodes) + break + // 内容 dom 加载完毕 + case 'onLoad': + this.height = message.height + this._hook('onLoad') + this.$emit('load') + break + // 所有图片加载完毕 + case 'onReady': + this.getRect().then(res => { + this.$emit('ready', res) + }).catch(() => { }) + break + // 总高度发生变化 + case 'onHeightChange': + this.height = message.height + break + // 图片点击 + case 'onImgTap': + this.$emit('imgTap', message.attrs) + if (this.previewImg) + uni.previewImage({ + current: parseInt(message.attrs.i), + urls: this.imgList + }) + break + // 链接点击 + case 'onLinkTap': + const href = message.attrs.href + this.$emit('linkTap', message.attrs) + if (href) { + // 锚点跳转 + if (href[0] == '#') { + if (this.useAnchor) + dom.scrollToElement(this.$refs.web, { + offset: message.offset + }) + } + // 打开外链 + else if (href.includes('://')) { + if (this.copyLink) + plus.runtime.openWeb(href) + } + else + uni.navigateTo({ + url: href, + fail() { + wx.switchTab({ + url: href + }) + } + }) + } + break + // 获取到锚点的偏移量 + case 'getOffset': + if (typeof message.offset == 'number') { + dom.scrollToElement(this.$refs.web, { + offset: message.offset + this._navigateTo.offset + }) + this._navigateTo.resolve() + } else + this._navigateTo.reject('Label not found') + break + // 点击 + case 'onClick': + this.$emit('tap') + break + // 出错 + case 'onError': + this.$emit('error', { + source: message.source, + attrs: message.attrs + }) + } + } + // #endif + } +} +</script> + +<style> +/* #ifndef APP-PLUS-NVUE */ +/* 根节点样式 */ +._root { + overflow: auto; + -webkit-overflow-scrolling: touch; +} + +/* 长按复制 */ +._select { + user-select: text; +} +/* #endif */ +</style> diff --git a/uni_modules/uview-ui/components/u-picker-column/props.js b/uni_modules/uview-ui/components/u-picker-column/props.js new file mode 100644 index 0000000..7c11331 --- /dev/null +++ b/uni_modules/uview-ui/components/u-picker-column/props.js @@ -0,0 +1,5 @@ +export default { + props: { + + } +} diff --git a/uni_modules/uview-ui/components/u-picker-column/u-picker-column.vue b/uni_modules/uview-ui/components/u-picker-column/u-picker-column.vue new file mode 100644 index 0000000..53553f3 --- /dev/null +++ b/uni_modules/uview-ui/components/u-picker-column/u-picker-column.vue @@ -0,0 +1,27 @@ +<template> + <picker-view-column> + <view class="u-picker-column"> + + </view> + </picker-view-column> +</template> + +<script> + import props from './props.js'; + /** + * PickerColumn + * @description + * @tutorial url + * @property {String} + * @event {Function} + * @example + */ + export default { + name: 'u-picker-column', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; +</style> diff --git a/uni_modules/uview-ui/components/u-picker/props.js b/uni_modules/uview-ui/components/u-picker/props.js new file mode 100644 index 0000000..7b5d091 --- /dev/null +++ b/uni_modules/uview-ui/components/u-picker/props.js @@ -0,0 +1,79 @@ +export default { + props: { + // 是否展示picker弹窗 + show: { + type: Boolean, + default: uni.$u.props.picker.show + }, + // 是否展示顶部的操作栏 + showToolbar: { + type: Boolean, + default: uni.$u.props.picker.showToolbar + }, + // 顶部标题 + title: { + type: String, + default: uni.$u.props.picker.title + }, + // 对象数组,设置每一列的数据 + columns: { + type: Array, + default: uni.$u.props.picker.columns + }, + // 是否显示加载中状态 + loading: { + type: Boolean, + default: uni.$u.props.picker.loading + }, + // 各列中,单个选项的高度 + itemHeight: { + type: [String, Number], + default: uni.$u.props.picker.itemHeight + }, + // 取消按钮的文字 + cancelText: { + type: String, + default: uni.$u.props.picker.cancelText + }, + // 确认按钮的文字 + confirmText: { + type: String, + default: uni.$u.props.picker.confirmText + }, + // 取消按钮的颜色 + cancelColor: { + type: String, + default: uni.$u.props.picker.cancelColor + }, + // 确认按钮的颜色 + confirmColor: { + type: String, + default: uni.$u.props.picker.confirmColor + }, + // 每列中可见选项的数量 + visibleItemCount: { + type: [String, Number], + default: uni.$u.props.picker.visibleItemCount + }, + // 选项对象中,需要展示的属性键名 + keyName: { + type: String, + default: uni.$u.props.picker.keyName + }, + // 是否允许点击遮罩关闭选择器 + closeOnClickOverlay: { + type: Boolean, + default: uni.$u.props.picker.closeOnClickOverlay + }, + // 各列的默认索引 + defaultIndex: { + type: Array, + default: uni.$u.props.picker.defaultIndex + }, + // 是否在手指松开时立即触发 change 事件。若不开启则会在滚动动画结束后触发 change 事件,只在微信2.21.1及以上有效 + immediateChange: { + type: Boolean, + default: uni.$u.props.picker.immediateChange + } + } +} diff --git a/uni_modules/uview-ui/components/u-picker/u-picker.vue b/uni_modules/uview-ui/components/u-picker/u-picker.vue new file mode 100644 index 0000000..8885917 --- /dev/null +++ b/uni_modules/uview-ui/components/u-picker/u-picker.vue @@ -0,0 +1,283 @@ +<template> + <u-popup + :show="show" + @close="closeHandler" + > + <view class="u-picker"> + <u-toolbar + v-if="showToolbar" + :cancelColor="cancelColor" + :confirmColor="confirmColor" + :cancelText="cancelText" + :confirmText="confirmText" + :title="title" + @cancel="cancel" + @confirm="confirm" + ></u-toolbar> + <picker-view + class="u-picker__view" + :indicatorStyle="`height: ${$u.addUnit(itemHeight)}`" + :value="innerIndex" + :immediateChange="immediateChange" + :style="{ + height: `${$u.addUnit(visibleItemCount * itemHeight)}` + }" + @change="changeHandler" + > + <picker-view-column + v-for="(item, index) in innerColumns" + :key="index" + class="u-picker__view__column" + > + <text + v-if="$u.test.array(item)" + class="u-picker__view__column__item u-line-1" + v-for="(item1, index1) in item" + :key="index1" + :style="{ + height: $u.addUnit(itemHeight), + lineHeight: $u.addUnit(itemHeight), + fontWeight: index1 === innerIndex[index] ? 'bold' : 'normal' + }" + >{{ getItemText(item1) }}</text> + </picker-view-column> + </picker-view> + <view + v-if="loading" + class="u-picker--loading" + > + <u-loading-icon mode="circle"></u-loading-icon> + </view> + </view> + </u-popup> +</template> + +<script> +/** + * u-picker + * @description 选择器 + * @property {Boolean} show 是否显示picker弹窗(默认 false ) + * @property {Boolean} showToolbar 是否显示顶部的操作栏(默认 true ) + * @property {String} title 顶部标题 + * @property {Array} columns 对象数组,设置每一列的数据 + * @property {Boolean} loading 是否显示加载中状态(默认 false ) + * @property {String | Number} itemHeight 各列中,单个选项的高度(默认 44 ) + * @property {String} cancelText 取消按钮的文字(默认 '取消' ) + * @property {String} confirmText 确认按钮的文字(默认 '确定' ) + * @property {String} cancelColor 取消按钮的颜色(默认 '#909193' ) + * @property {String} confirmColor 确认按钮的颜色(默认 '#3c9cff' ) + * @property {String | Number} visibleItemCount 每列中可见选项的数量(默认 5 ) + * @property {String} keyName 选项对象中,需要展示的属性键名(默认 'text' ) + * @property {Boolean} closeOnClickOverlay 是否允许点击遮罩关闭选择器(默认 false ) + * @property {Array} defaultIndex 各列的默认索引 + * @property {Boolean} immediateChange 是否在手指松开时立即触发change事件(默认 false ) + * @event {Function} close 关闭选择器时触发 + * @event {Function} cancel 点击取消按钮触发 + * @event {Function} change 当选择值变化时触发 + * @event {Function} confirm 点击确定按钮,返回当前选择的值 + */ +import props from './props.js'; +export default { + name: 'u-picker', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + // 上一次选择的列索引 + lastIndex: [], + // 索引值 ,对应picker-view的value + innerIndex: [], + // 各列的值 + innerColumns: [], + // 上一次的变化列索引 + columnIndex: 0, + } + }, + watch: { + // 监听默认索引的变化,重新设置对应的值 + defaultIndex: { + immediate: true, + handler(n) { + this.setIndexs(n, true) + } + }, + // 监听columns参数的变化 + columns: { + immediate: true, + handler(n) { + this.setColumns(n) + } + }, + }, + methods: { + // 获取item需要显示的文字,判别为对象还是文本 + getItemText(item) { + if (uni.$u.test.object(item)) { + return item[this.keyName] + } else { + return item + } + }, + // 关闭选择器 + closeHandler() { + if (this.closeOnClickOverlay) { + this.$emit('close') + } + }, + // 点击工具栏的取消按钮 + cancel() { + this.$emit('cancel') + }, + // 点击工具栏的确定按钮 + confirm() { + this.$emit('confirm', { + indexs: this.innerIndex, + value: this.innerColumns.map((item, index) => item[this.innerIndex[index]]), + values: this.innerColumns + }) + }, + // 选择器某一列的数据发生变化时触发 + changeHandler(e) { + const { + value + } = e.detail + let index = 0, + columnIndex = 0 + // 通过对比前后两次的列索引,得出当前变化的是哪一列 + for (let i = 0; i < value.length; i++) { + let item = value[i] + if (item !== (this.lastIndex[i] || 0)) { // 把undefined转为合法假值0 + // 设置columnIndex为当前变化列的索引 + columnIndex = i + // index则为变化列中的变化项的索引 + index = item + break // 终止循环,即使少一次循环,也是性能的提升 + } + } + this.columnIndex = columnIndex + const values = this.innerColumns + // 将当前的各项变化索引,设置为"上一次"的索引变化值 + this.setLastIndex(value) + this.setIndexs(value) + + this.$emit('change', { + // #ifndef MP-WEIXIN || MP-LARK + // 微信小程序不能传递this,会因为循环引用而报错 + picker: this, + // #endif + value: this.innerColumns.map((item, index) => item[value[index]]), + index, + indexs: value, + // values为当前变化列的数组内容 + values, + columnIndex + }) + }, + // 设置index索引,此方法可被外部调用设置 + setIndexs(index, setLastIndex) { + this.innerIndex = uni.$u.deepClone(index) + if (setLastIndex) { + this.setLastIndex(index) + } + }, + // 记录上一次的各列索引位置 + setLastIndex(index) { + // 当能进入此方法,意味着当前设置的各列默认索引,即为“上一次”的选中值,需要记录,是因为changeHandler中 + // 需要拿前后的变化值进行对比,得出当前发生改变的是哪一列 + this.lastIndex = uni.$u.deepClone(index) + }, + // 设置对应列选项的所有值 + setColumnValues(columnIndex, values) { + // 替换innerColumns数组中columnIndex索引的值为values,使用的是数组的splice方法 + this.innerColumns.splice(columnIndex, 1, values) + // 拷贝一份原有的innerIndex做临时变量,将大于当前变化列的所有的列的默认索引设置为0 + let tmpIndex = uni.$u.deepClone(this.innerIndex) + for (let i = 0; i < this.innerColumns.length; i++) { + if (i > this.columnIndex) { + tmpIndex[i] = 0 + } + } + // 一次性赋值,不能单个修改,否则无效 + this.setIndexs(tmpIndex) + }, + // 获取对应列的所有选项 + getColumnValues(columnIndex) { + // 进行同步阻塞,因为外部得到change事件之后,可能需要执行setColumnValues更新列的值 + // 索引如果在外部change的回调中调用getColumnValues的话,可能无法得到变更后的列值,这里进行一定延时,保证值的准确性 + (async () => { + await uni.$u.sleep() + })() + return this.innerColumns[columnIndex] + }, + // 设置整体各列的columns的值 + setColumns(columns) { + this.innerColumns = uni.$u.deepClone(columns) + // 如果在设置各列数据时,没有被设置默认的各列索引defaultIndex,那么用0去填充它,数组长度为列的数量 + if (this.innerIndex.length === 0) { + this.innerIndex = new Array(columns.length).fill(0) + } + }, + // 获取各列选中值对应的索引 + getIndexs() { + return this.innerIndex + }, + // 获取各列选中的值 + getValues() { + // 进行同步阻塞,因为外部得到change事件之后,可能需要执行setColumnValues更新列的值 + // 索引如果在外部change的回调中调用getValues的话,可能无法得到变更后的列值,这里进行一定延时,保证值的准确性 + (async () => { + await uni.$u.sleep() + })() + return this.innerColumns.map((item, index) => item[this.innerIndex[index]]) + } + }, +} +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-picker { + position: relative; + + &__view { + + &__column { + @include flex; + flex: 1; + justify-content: center; + + &__item { + @include flex; + justify-content: center; + align-items: center; + font-size: 16px; + text-align: center; + /* #ifndef APP-NVUE */ + display: block; + /* #endif */ + color: $u-main-color; + + &--disabled { + /* #ifndef APP-NVUE */ + cursor: not-allowed; + /* #endif */ + opacity: 0.35; + } + } + } + } + + &--loading { + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + @include flex; + justify-content: center; + align-items: center; + background-color: rgba(255, 255, 255, 0.87); + z-index: 1000; + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-popup/props.js b/uni_modules/uview-ui/components/u-popup/props.js new file mode 100644 index 0000000..d9fe952 --- /dev/null +++ b/uni_modules/uview-ui/components/u-popup/props.js @@ -0,0 +1,79 @@ +export default { + props: { + // 是否展示弹窗 + show: { + type: Boolean, + default: uni.$u.props.popup.show + }, + // 是否显示遮罩 + overlay: { + type: Boolean, + default: uni.$u.props.popup.overlay + }, + // 弹出的方向,可选值为 top bottom right left center + mode: { + type: String, + default: uni.$u.props.popup.mode + }, + // 动画时长,单位ms + duration: { + type: [String, Number], + default: uni.$u.props.popup.duration + }, + // 是否显示关闭图标 + closeable: { + type: Boolean, + default: uni.$u.props.popup.closeable + }, + // 自定义遮罩的样式 + overlayStyle: { + type: [Object, String], + default: uni.$u.props.popup.overlayStyle + }, + // 点击遮罩是否关闭弹窗 + closeOnClickOverlay: { + type: Boolean, + default: uni.$u.props.popup.closeOnClickOverlay + }, + // 层级 + zIndex: { + type: [String, Number], + default: uni.$u.props.popup.zIndex + }, + // 是否为iPhoneX留出底部安全距离 + safeAreaInsetBottom: { + type: Boolean, + default: uni.$u.props.popup.safeAreaInsetBottom + }, + // 是否留出顶部安全距离(状态栏高度) + safeAreaInsetTop: { + type: Boolean, + default: uni.$u.props.popup.safeAreaInsetTop + }, + // 自定义关闭图标位置,top-left为左上角,top-right为右上角,bottom-left为左下角,bottom-right为右下角 + closeIconPos: { + type: String, + default: uni.$u.props.popup.closeIconPos + }, + // 是否显示圆角 + round: { + type: [Boolean, String, Number], + default: uni.$u.props.popup.round + }, + // mode=center,也即中部弹出时,是否使用缩放模式 + zoom: { + type: Boolean, + default: uni.$u.props.popup.zoom + }, + // 弹窗背景色,设置为transparent可去除白色背景 + bgColor: { + type: String, + default: uni.$u.props.popup.bgColor + }, + // 遮罩的透明度,0-1之间 + overlayOpacity: { + type: [Number, String], + default: uni.$u.props.popup.overlayOpacity + } + } +} diff --git a/uni_modules/uview-ui/components/u-popup/u-popup.vue b/uni_modules/uview-ui/components/u-popup/u-popup.vue new file mode 100644 index 0000000..2ca51cc --- /dev/null +++ b/uni_modules/uview-ui/components/u-popup/u-popup.vue @@ -0,0 +1,304 @@ +<template> + <view class="u-popup"> + <u-overlay + :show="show" + @click="overlayClick" + v-if="overlay" + :duration="overlayDuration" + :customStyle="overlayStyle" + :opacity="overlayOpacity" + ></u-overlay> + <u-transition + :show="show" + :customStyle="transitionStyle" + :mode="position" + :duration="duration" + @afterEnter="afterEnter" + @click="clickHandler" + > + <view + class="u-popup__content" + :style="[contentStyle]" + @tap.stop="noop" + > + <u-status-bar v-if="safeAreaInsetTop"></u-status-bar> + <slot></slot> + <view + v-if="closeable" + @tap.stop="close" + class="u-popup__content__close" + :class="['u-popup__content__close--' + closeIconPos]" + hover-class="u-popup__content__close--hover" + hover-stay-time="150" + > + <u-icon + name="close" + color="#909399" + size="18" + bold + ></u-icon> + </view> + <u-safe-bottom v-if="safeAreaInsetBottom"></u-safe-bottom> + </view> + </u-transition> + </view> +</template> + +<script> + import props from './props.js'; + + /** + * popup 弹窗 + * @description 弹出层容器,用于展示弹窗、信息提示等内容,支持上、下、左、右和中部弹出。组件只提供容器,内部内容由用户自定义 + * @tutorial https://www.uviewui.com/components/popup.html + * @property {Boolean} show 是否展示弹窗 (默认 false ) + * @property {Boolean} overlay 是否显示遮罩 (默认 true ) + * @property {String} mode 弹出方向(默认 'bottom' ) + * @property {String | Number} duration 动画时长,单位ms (默认 300 ) + * @property {String | Number} overlayDuration 遮罩层动画时长,单位ms (默认 350 ) + * @property {Boolean} closeable 是否显示关闭图标(默认 false ) + * @property {Object | String} overlayStyle 自定义遮罩的样式 + * @property {String | Number} overlayOpacity 遮罩透明度,0-1之间(默认 0.5) + * @property {Boolean} closeOnClickOverlay 点击遮罩是否关闭弹窗 (默认 true ) + * @property {String | Number} zIndex 层级 (默认 10075 ) + * @property {Boolean} safeAreaInsetBottom 是否为iPhoneX留出底部安全距离 (默认 true ) + * @property {Boolean} safeAreaInsetTop 是否留出顶部安全距离(状态栏高度) (默认 false ) + * @property {String} closeIconPos 自定义关闭图标位置(默认 'top-right' ) + * @property {String | Number} round 圆角值(默认 0) + * @property {Boolean} zoom 当mode=center时 是否开启缩放(默认 true ) + * @property {Object} customStyle 组件的样式,对象形式 + * @event {Function} open 弹出层打开 + * @event {Function} close 弹出层收起 + * @example <u-popup v-model="show"><text>出淤泥而不染,濯清涟而不妖</text></u-popup> + */ + export default { + name: 'u-popup', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + overlayDuration: this.duration + 50 + } + }, + watch: { + show(newValue, oldValue) { + if (newValue === true) { + // #ifdef MP-WEIXIN + const children = this.$children + this.retryComputedComponentRect(children) + // #endif + } + } + }, + computed: { + transitionStyle() { + const style = { + zIndex: this.zIndex, + position: 'fixed', + display: 'flex', + } + style[this.mode] = 0 + if (this.mode === 'left') { + return uni.$u.deepMerge(style, { + bottom: 0, + top: 0, + }) + } else if (this.mode === 'right') { + return uni.$u.deepMerge(style, { + bottom: 0, + top: 0, + }) + } else if (this.mode === 'top') { + return uni.$u.deepMerge(style, { + left: 0, + right: 0 + }) + } else if (this.mode === 'bottom') { + return uni.$u.deepMerge(style, { + left: 0, + right: 0, + }) + } else if (this.mode === 'center') { + return uni.$u.deepMerge(style, { + alignItems: 'center', + 'justify-content': 'center', + top: 0, + left: 0, + right: 0, + bottom: 0 + }) + } + }, + contentStyle() { + const style = {} + // 通过设备信息的safeAreaInsets值来判断是否需要预留顶部状态栏和底部安全局的位置 + // 不使用css方案,是因为nvue不支持css的iPhoneX安全区查询属性 + const { + safeAreaInsets + } = uni.$u.sys() + if (this.mode !== 'center') { + style.flex = 1 + } + // 背景色,一般用于设置为transparent,去除默认的白色背景 + if (this.bgColor) { + style.backgroundColor = this.bgColor + } + if(this.round) { + const value = uni.$u.addUnit(this.round) + if(this.mode === 'top') { + style.borderBottomLeftRadius = value + style.borderBottomRightRadius = value + } else if(this.mode === 'bottom') { + style.borderTopLeftRadius = value + style.borderTopRightRadius = value + } else if(this.mode === 'center') { + style.borderRadius = value + } + } + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) + }, + position() { + if (this.mode === 'center') { + return this.zoom ? 'fade-zoom' : 'fade' + } + if (this.mode === 'left') { + return 'slide-left' + } + if (this.mode === 'right') { + return 'slide-right' + } + if (this.mode === 'bottom') { + return 'slide-up' + } + if (this.mode === 'top') { + return 'slide-down' + } + }, + }, + methods: { + // 点击遮罩 + overlayClick() { + if (this.closeOnClickOverlay) { + this.$emit('close') + } + }, + close(e) { + this.$emit('close') + }, + afterEnter() { + this.$emit('open') + }, + clickHandler() { + // 由于中部弹出时,其u-transition占据了整个页面相当于遮罩,此时需要发出遮罩点击事件,是否无法通过点击遮罩关闭弹窗 + if(this.mode === 'center') { + this.overlayClick() + } + this.$emit('click') + }, + // #ifdef MP-WEIXIN + retryComputedComponentRect(children) { + // 组件内部需要计算节点的组件 + const names = ['u-calendar-month', 'u-album', 'u-collapse-item', 'u-dropdown', 'u-index-item', 'u-index-list', + 'u-line-progress', 'u-list-item', 'u-rate', 'u-read-more', 'u-row', 'u-row-notice', 'u-scroll-list', + 'u-skeleton', 'u-slider', 'u-steps-item', 'u-sticky', 'u-subsection', 'u-swipe-action-item', 'u-tabbar', + 'u-tabs', 'u-tooltip' + ] + // 历遍所有的子组件节点 + for (let i = 0; i < children.length; i++) { + const child = children[i] + // 拿到子组件的子组件 + const grandChild = child.$children + // 判断如果在需要重新初始化的组件数组中名中,并且存在init方法的话,则执行 + if (names.includes(child.$options.name) && typeof child?.init === 'function') { + // 需要进行一定的延时,因为初始化页面需要时间 + uni.$u.sleep(50).then(() => { + child.init() + }) + } + // 如果子组件还有孙组件,进行递归历遍 + if (grandChild.length) { + this.retryComputedComponentRect(grandChild) + } + } + } + // #endif + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + $u-popup-flex:1 !default; + $u-popup-content-background-color: #fff !default; + + .u-popup { + flex: $u-popup-flex; + + &__content { + background-color: $u-popup-content-background-color; + position: relative; + + &--round-top { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + } + + &--round-left { + border-top-left-radius: 0; + border-top-right-radius: 10px; + border-bottom-left-radius: 0; + border-bottom-right-radius: 10px; + } + + &--round-right { + border-top-left-radius: 10px; + border-top-right-radius: 0; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 0; + } + + &--round-bottom { + border-top-left-radius: 10px; + border-top-right-radius: 10px; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } + + &--round-center { + border-top-left-radius: 10px; + border-top-right-radius: 10px; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + } + + &__close { + position: absolute; + + &--hover { + opacity: 0.4; + } + } + + &__close--top-left { + top: 15px; + left: 15px; + } + + &__close--top-right { + top: 15px; + right: 15px; + } + + &__close--bottom-left { + bottom: 15px; + left: 15px; + } + + &__close--bottom-right { + right: 15px; + bottom: 15px; + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-radio-group/props.js b/uni_modules/uview-ui/components/u-radio-group/props.js new file mode 100644 index 0000000..bb86cba --- /dev/null +++ b/uni_modules/uview-ui/components/u-radio-group/props.js @@ -0,0 +1,85 @@ +export default { + props: { + // 绑定的值 + value: { + type: [String, Number, Boolean], + default: uni.$u.props.radioGroup.value + }, + + // 是否禁用全部radio + disabled: { + type: Boolean, + default: uni.$u.props.radioGroup.disabled + }, + // 形状,circle-圆形,square-方形 + shape: { + type: String, + default: uni.$u.props.radioGroup.shape + }, + // 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值 + activeColor: { + type: String, + default: uni.$u.props.radioGroup.activeColor + }, + // 未选中的颜色 + inactiveColor: { + type: String, + default: uni.$u.props.radioGroup.inactiveColor + }, + // 标识符 + name: { + type: String, + default: uni.$u.props.radioGroup.name + }, + // 整个组件的尺寸,默认px + size: { + type: [String, Number], + default: uni.$u.props.radioGroup.size + }, + // 布局方式,row-横向,column-纵向 + placement: { + type: String, + default: uni.$u.props.radioGroup.placement + }, + // label的文本 + label: { + type: [String], + default: uni.$u.props.radioGroup.label + }, + // label的颜色 (默认 '#303133' ) + labelColor: { + type: [String], + default: uni.$u.props.radioGroup.labelColor + }, + // label的字体大小,px单位 + labelSize: { + type: [String, Number], + default: uni.$u.props.radioGroup.labelSize + }, + // 是否禁止点击文本操作checkbox(默认 false ) + labelDisabled: { + type: Boolean, + default: uni.$u.props.radioGroup.labelDisabled + }, + // 图标颜色 + iconColor: { + type: String, + default: uni.$u.props.radioGroup.iconColor + }, + // 图标的大小,单位px + iconSize: { + type: [String, Number], + default: uni.$u.props.radioGroup.iconSize + }, + // 竖向配列时,是否显示下划线 + borderBottom: { + type: Boolean, + default: uni.$u.props.radioGroup.borderBottom + }, + // 图标与文字的对齐方式 + iconPlacement: { + type: String, + default: uni.$u.props.radio.iconPlacement + } + } +} diff --git a/uni_modules/uview-ui/components/u-radio-group/u-radio-group.vue b/uni_modules/uview-ui/components/u-radio-group/u-radio-group.vue new file mode 100644 index 0000000..0d3c9b5 --- /dev/null +++ b/uni_modules/uview-ui/components/u-radio-group/u-radio-group.vue @@ -0,0 +1,108 @@ +<template> + <view + class="u-radio-group" + :class="bemClass" + > + <slot></slot> + </view> +</template> + +<script> + import props from './props.js'; + + /** + * radioRroup 单选框父组件 + * @description 单选框用于有一个选择,用户只能选择其中一个的场景。搭配u-radio使用 + * @tutorial https://www.uviewui.com/components/radio.html + * @property {String | Number | Boolean} value 绑定的值 + * @property {Boolean} disabled 是否禁用所有radio(默认 false ) + * @property {String} shape 外观形状,shape-方形,circle-圆形(默认 circle ) + * @property {String} activeColor 选中时的颜色,应用到所有子Radio组件(默认 '#2979ff' ) + * @property {String} inactiveColor 未选中的颜色 (默认 '#c8c9cc' ) + * @property {String} name 标识符 + * @property {String | Number} size 组件整体的大小,单位px(默认 18 ) + * @property {String} placement 布局方式,row-横向,column-纵向 (默认 'row' ) + * @property {String} label 文本 + * @property {String} labelColor label的颜色 (默认 '#303133' ) + * @property {String | Number} labelSize label的字体大小,px单位 (默认 14 ) + * @property {Boolean} labelDisabled 是否禁止点击文本操作checkbox(默认 false ) + * @property {String} iconColor 图标颜色 (默认 '#ffffff' ) + * @property {String | Number} iconSize 图标的大小,单位px (默认 12 ) + * @property {Boolean} borderBottom placement为row时,是否显示下边框 (默认 false ) + * @property {String} iconPlacement 图标与文字的对齐方式 (默认 'left' ) + * @property {Object} customStyle 组件的样式,对象形式 + * @event {Function} change 任一个radio状态发生变化时触发 + * @example <u-radio-group v-model="value"></u-radio-group> + */ + export default { + name: 'u-radio-group', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + computed: { + // 这里computed的变量,都是子组件u-radio需要用到的,由于头条小程序的兼容性差异,子组件无法实时监听父组件参数的变化 + // 所以需要手动通知子组件,这里返回一个parentData变量,供watch监听,在其中去通知每一个子组件重新从父组件(u-radio-group) + // 拉取父组件新的变化后的参数 + parentData() { + return [this.value, this.disabled, this.inactiveColor, this.activeColor, this.size, this.labelDisabled, this.shape, + this.iconSize, this.borderBottom, this.placement + ] + }, + bemClass() { + // this.bem为一个computed变量,在mixin中 + return this.bem('radio-group', ['placement']) + }, + }, + watch: { + // 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件 + parentData() { + if (this.children.length) { + this.children.map(child => { + // 判断子组件(u-radio)如果有init方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值) + typeof(child.init) === 'function' && child.init() + }) + } + }, + }, + data() { + return { + + } + }, + created() { + this.children = [] + }, + methods: { + // 将其他的radio设置为未选中的状态 + unCheckedOther(childInstance) { + this.children.map(child => { + // 所有子radio中,被操作组件实例的checked的值无需修改 + if (childInstance !== child) { + child.checked = false + } + }) + const { + name + } = childInstance + // 通过emit事件,设置父组件通过v-model双向绑定的值 + this.$emit('input', name) + // 发出事件 + this.$emit('change', name) + }, + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-radio-group { + flex: 1; + + &--row { + @include flex; + } + + &--column { + @include flex(column); + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-radio/props.js b/uni_modules/uview-ui/components/u-radio/props.js new file mode 100644 index 0000000..3ec5f6b --- /dev/null +++ b/uni_modules/uview-ui/components/u-radio/props.js @@ -0,0 +1,64 @@ +export default { + props: { + // radio的名称 + name: { + type: [String, Number, Boolean], + default: uni.$u.props.radio.name + }, + // 形状,square为方形,circle为圆型 + shape: { + type: String, + default: uni.$u.props.radio.shape + }, + // 是否禁用 + disabled: { + type: [String, Boolean], + default: uni.$u.props.radio.disabled + }, + // 是否禁止点击提示语选中单选框 + labelDisabled: { + type: [String, Boolean], + default: uni.$u.props.radio.labelDisabled + }, + // 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值 + activeColor: { + type: String, + default: uni.$u.props.radio.activeColor + }, + // 未选中的颜色 + inactiveColor: { + type: String, + default: uni.$u.props.radio.inactiveColor + }, + // 图标的大小,单位px + iconSize: { + type: [String, Number], + default: uni.$u.props.radio.iconSize + }, + // label的字体大小,px单位 + labelSize: { + type: [String, Number], + default: uni.$u.props.radio.labelSize + }, + // label提示文字,因为nvue下,直接slot进来的文字,由于特殊的结构,无法修改样式 + label: { + type: [String, Number], + default: uni.$u.props.radio.label + }, + // 整体的大小 + size: { + type: [String, Number], + default: uni.$u.props.radio.size + }, + // 图标颜色 + color: { + type: String, + default: uni.$u.props.radio.color + }, + // label的颜色 + labelColor: { + type: String, + default: uni.$u.props.radio.labelColor + } + } +} diff --git a/uni_modules/uview-ui/components/u-radio/u-radio.vue b/uni_modules/uview-ui/components/u-radio/u-radio.vue new file mode 100644 index 0000000..7133542 --- /dev/null +++ b/uni_modules/uview-ui/components/u-radio/u-radio.vue @@ -0,0 +1,337 @@ +<template> + <view + class="u-radio" + @tap.stop="wrapperClickHandler" + :style="[radioStyle]" + :class="[`u-radio-label--${parentData.iconPlacement}`, parentData.borderBottom && parentData.placement === 'column' && 'u-border-bottom']" + > + <view + class="u-radio__icon-wrap" + @tap.stop="iconClickHandler" + :class="iconClasses" + :style="[iconWrapStyle]" + > + <slot name="icon"> + <u-icon + class="u-radio__icon-wrap__icon" + name="checkbox-mark" + :size="elIconSize" + :color="elIconColor" + /> + </slot> + </view> + <text + class="u-radio__text" + @tap.stop="labelClickHandler" + :style="{ + color: elDisabled ? elInactiveColor : elLabelColor, + fontSize: elLabelSize, + lineHeight: elLabelSize + }" + >{{label}}</text> + </view> +</template> + +<script> + import props from './props.js'; + /** + * radio 单选框 + * @description 单选框用于有一个选择,用户只能选择其中一个的场景。搭配u-radio-group使用 + * @tutorial https://www.uviewui.com/components/radio.html + * @property {String | Number} name radio的名称 + * @property {String} shape 形状,square为方形,circle为圆型 + * @property {Boolean} disabled 是否禁用 + * @property {String | Boolean} labelDisabled 是否禁止点击提示语选中单选框 + * @property {String} activeColor 选中时的颜色,如设置parent的active-color将失效 + * @property {String} inactiveColor 未选中的颜色 + * @property {String | Number} iconSize 图标大小,单位px + * @property {String | Number} labelSize label字体大小,单位px + * @property {String | Number} label label提示文字,因为nvue下,直接slot进来的文字,由于特殊的结构,无法修改样式 + * @property {String | Number} size 整体的大小 + * @property {String} iconColor 图标颜色 + * @property {String} labelColor label的颜色 + * @property {Object} customStyle 组件的样式,对象形式 + * + * @event {Function} change 某个radio状态发生变化时触发(选中状态) + * @example <u-radio :labelDisabled="false">门掩黄昏,无计留春住</u-radio> + */ + export default { + name: "u-radio", + + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + checked: false, + // 当你看到这段代码的时候, + // 父组件的默认值,因为头条小程序不支持在computed中使用this.parent.shape的形式 + // 故只能使用如此方法 + parentData: { + iconSize: 12, + labelDisabled: null, + disabled: null, + shape: null, + activeColor: null, + inactiveColor: null, + size: 18, + value: null, + iconColor: null, + placement: 'row', + borderBottom: false, + iconPlacement: 'left' + } + } + }, + computed: { + // 是否禁用,如果父组件u-raios-group禁用的话,将会忽略子组件的配置 + elDisabled() { + return this.disabled !== '' ? this.disabled : this.parentData.disabled !== null ? this.parentData.disabled : false; + }, + // 是否禁用label点击 + elLabelDisabled() { + return this.labelDisabled !== '' ? this.labelDisabled : this.parentData.labelDisabled !== null ? this.parentData.labelDisabled : + false; + }, + // 组件尺寸,对应size的值,默认值为21px + elSize() { + return this.size ? this.size : (this.parentData.size ? this.parentData.size : 21); + }, + // 组件的勾选图标的尺寸,默认12px + elIconSize() { + return this.iconSize ? this.iconSize : (this.parentData.iconSize ? this.parentData.iconSize : 12); + }, + // 组件选中激活时的颜色 + elActiveColor() { + return this.activeColor ? this.activeColor : (this.parentData.activeColor ? this.parentData.activeColor : '#2979ff'); + }, + // 组件选未中激活时的颜色 + elInactiveColor() { + return this.inactiveColor ? this.inactiveColor : (this.parentData.inactiveColor ? this.parentData.inactiveColor : + '#c8c9cc'); + }, + // label的颜色 + elLabelColor() { + return this.labelColor ? this.labelColor : (this.parentData.labelColor ? this.parentData.labelColor : '#606266') + }, + // 组件的形状 + elShape() { + return this.shape ? this.shape : (this.parentData.shape ? this.parentData.shape : 'circle'); + }, + // label大小 + elLabelSize() { + return uni.$u.addUnit(this.labelSize ? this.labelSize : (this.parentData.labelSize ? this.parentData.labelSize : + '15')) + }, + elIconColor() { + const iconColor = this.iconColor ? this.iconColor : (this.parentData.iconColor ? this.parentData.iconColor : + '#ffffff'); + // 图标的颜色 + if (this.elDisabled) { + // disabled状态下,已勾选的radio图标改为elInactiveColor + return this.checked ? this.elInactiveColor : 'transparent' + } else { + return this.checked ? iconColor : 'transparent' + } + }, + iconClasses() { + let classes = [] + // 组件的形状 + classes.push('u-radio__icon-wrap--' + this.elShape) + if (this.elDisabled) { + classes.push('u-radio__icon-wrap--disabled') + } + if (this.checked && this.elDisabled) { + classes.push('u-radio__icon-wrap--disabled--checked') + } + // 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效 + // #ifdef MP-ALIPAY || MP-TOUTIAO + classes = classes.join(' ') + // #endif + return classes + }, + iconWrapStyle() { + // radio的整体样式 + const style = {} + style.backgroundColor = this.checked && !this.elDisabled ? this.elActiveColor : '#ffffff' + style.borderColor = this.checked && !this.elDisabled ? this.elActiveColor : this.elInactiveColor + style.width = uni.$u.addUnit(this.elSize) + style.height = uni.$u.addUnit(this.elSize) + // 如果是图标在右边的话,移除它的右边距 + if (this.parentData.iconPlacement === 'right') { + style.marginRight = 0 + } + return style + }, + radioStyle() { + const style = {} + if(this.parentData.borderBottom && this.parentData.placement === 'row') { + uni.$u.error('检测到您将borderBottom设置为true,需要同时将u-radio-group的placement设置为column才有效') + } + // 当父组件设置了显示下边框并且排列形式为纵向时,给内容和边框之间加上一定间隔 + if(this.parentData.borderBottom && this.parentData.placement === 'column') { + // ios像素密度高,需要多一点的距离 + style.paddingBottom = uni.$u.os() === 'ios' ? '12px' : '8px' + } + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) + } + }, + mounted() { + this.init() + }, + methods: { + init() { + // 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用 + this.updateParentData() + if (!this.parent) { + uni.$u.error('u-radio必须搭配u-radio-group组件使用') + } + // 设置初始化时,是否默认选中的状态 + this.checked = this.name === this.parentData.value + }, + updateParentData() { + this.getParentData('u-radio-group') + }, + // 点击图标 + iconClickHandler(e) { + this.preventEvent(e) + // 如果整体被禁用,不允许被点击 + if (!this.elDisabled) { + this.setRadioCheckedStatus() + } + }, + // 横向两端排列时,点击组件即可触发选中事件 + wrapperClickHandler(e) { + this.parentData.iconPlacement === 'right' && this.iconClickHandler(e) + }, + // 点击label + labelClickHandler(e) { + this.preventEvent(e) + // 如果按钮整体被禁用或者label被禁用,则不允许点击文字修改状态 + if (!this.elLabelDisabled && !this.elDisabled) { + this.setRadioCheckedStatus() + } + }, + emitEvent() { + // u-radio的checked不为true时(意味着未选中),才发出事件,避免多次点击触发事件 + if (!this.checked) { + this.$emit('change', this.name) + // 尝试调用u-form的验证方法,进行一定延迟,否则微信小程序更新可能会不及时 + this.$nextTick(() => { + uni.$u.formValidate(this, 'change') + }) + } + }, + // 改变组件选中状态 + // 这里的改变的依据是,更改本组件的checked值为true,同时通过父组件遍历所有u-radio实例 + // 将本组件外的其他u-radio的checked都设置为false(都被取消选中状态),因而只剩下一个为选中状态 + setRadioCheckedStatus() { + this.emitEvent() + // 将本组件标记为选中状态 + this.checked = true + typeof this.parent.unCheckedOther === 'function' && this.parent.unCheckedOther(this) + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + $u-radio-wrap-margin-right:6px !default; + $u-radio-wrap-font-size:20px !default; + $u-radio-wrap-border-width:1px !default; + $u-radio-wrap-border-color: #c8c9cc !default; + $u-radio-line-height:0 !default; + $u-radio-circle-border-radius:100% !default; + $u-radio-square-border-radius:3px !default; + $u-radio-checked-color:#fff !default; + $u-radio-checked-background-color:red !default; + $u-radio-checked-border-color: #2979ff !default; + $u-radio-disabled-background-color:#ebedf0 !default; + $u-radio-disabled--checked-color:#c8c9cc !default; + $u-radio-label-margin-left: 5px !default; + $u-radio-label-margin-right:12px !default; + $u-radio-label-color:$u-content-color !default; + $u-radio-label-font-size:15px !default; + $u-radio-label-disabled-color:#c8c9cc !default; + + .u-radio { + /* #ifndef APP-NVUE */ + @include flex(row); + /* #endif */ + overflow: hidden; + flex-direction: row; + align-items: center; + + &-label--left { + flex-direction: row + } + + &-label--right { + flex-direction: row-reverse; + justify-content: space-between + } + + &__icon-wrap { + /* #ifndef APP-NVUE */ + box-sizing: border-box; + // nvue下,border-color过渡有问题 + transition-property: border-color, background-color, color; + transition-duration: 0.2s; + /* #endif */ + color: $u-content-color; + @include flex; + align-items: center; + justify-content: center; + color: transparent; + text-align: center; + margin-right: $u-radio-wrap-margin-right; + font-size: $u-radio-wrap-font-size; + border-width: $u-radio-wrap-border-width; + border-color: $u-radio-wrap-border-color; + border-style: solid; + + /* #ifdef MP-TOUTIAO */ + // 头条小程序兼容性问题,需要设置行高为0,否则图标偏下 + &__icon { + line-height: $u-radio-line-height; + } + + /* #endif */ + + &--circle { + border-radius: $u-radio-circle-border-radius; + } + + &--square { + border-radius: $u-radio-square-border-radius; + } + + &--checked { + color: $u-radio-checked-color; + background-color: $u-radio-checked-background-color; + border-color: $u-radio-checked-border-color; + } + + &--disabled { + background-color: $u-radio-disabled-background-color !important; + } + + &--disabled--checked { + color: $u-radio-disabled--checked-color !important; + } + } + + &__label { + /* #ifndef APP-NVUE */ + word-wrap: break-word; + /* #endif */ + margin-left: $u-radio-label-margin-left; + margin-right: $u-radio-label-margin-right; + color: $u-radio-label-color; + font-size: $u-radio-label-font-size; + + &--disabled { + color: $u-radio-label-disabled-color; + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-rate/props.js b/uni_modules/uview-ui/components/u-rate/props.js new file mode 100644 index 0000000..2a56350 --- /dev/null +++ b/uni_modules/uview-ui/components/u-rate/props.js @@ -0,0 +1,69 @@ +export default { + props: { + // 用于v-model双向绑定选中的星星数量 + value: { + type: [String, Number], + default: uni.$u.props.rate.value + }, + // 要显示的星星数量 + count: { + type: [String, Number], + default: uni.$u.props.rate.count + }, + // 是否不可选中 + disabled: { + type: Boolean, + default: uni.$u.props.rate.disabled + }, + // 是否只读 + readonly: { + type: Boolean, + default: uni.$u.props.rate.readonly + }, + // 星星的大小,单位px + size: { + type: [String, Number], + default: uni.$u.props.rate.size + }, + // 未选中时的颜色 + inactiveColor: { + type: String, + default: uni.$u.props.rate.inactiveColor + }, + // 选中的颜色 + activeColor: { + type: String, + default: uni.$u.props.rate.activeColor + }, + // 星星之间的间距,单位px + gutter: { + type: [String, Number], + default: uni.$u.props.rate.gutter + }, + // 最少能选择的星星个数 + minCount: { + type: [String, Number], + default: uni.$u.props.rate.minCount + }, + // 是否允许半星 + allowHalf: { + type: Boolean, + default: uni.$u.props.rate.allowHalf + }, + // 选中时的图标(星星) + activeIcon: { + type: String, + default: uni.$u.props.rate.activeIcon + }, + // 未选中时的图标(星星) + inactiveIcon: { + type: String, + default: uni.$u.props.rate.inactiveIcon + }, + // 是否可以通过滑动手势选择评分 + touchable: { + type: Boolean, + default: uni.$u.props.rate.touchable + } + } +} diff --git a/uni_modules/uview-ui/components/u-rate/u-rate.vue b/uni_modules/uview-ui/components/u-rate/u-rate.vue new file mode 100644 index 0000000..5992038 --- /dev/null +++ b/uni_modules/uview-ui/components/u-rate/u-rate.vue @@ -0,0 +1,304 @@ +<template> + <view + class="u-rate" + :id="elId" + ref="u-rate" + :style="[$u.addStyle(customStyle)]" + > + <view + class="u-rate__content" + @touchmove.stop="touchMove" + @touchend.stop="touchEnd" + > + <view + class="u-rate__content__item" + v-for="(item, index) in Number(count)" + :key="index" + :class="[elClass]" + > + <view + class="u-rate__content__item__icon-wrap" + ref="u-rate__content__item__icon-wrap" + @tap.stop="clickHandler($event, index + 1)" + > + <u-icon + :name=" + Math.floor(activeIndex) > index + ? activeIcon + : inactiveIcon + " + :color=" + disabled + ? '#c8c9cc' + : Math.floor(activeIndex) > index + ? activeColor + : inactiveColor + " + :custom-style="{ + padding: `0 ${$u.addUnit(gutter / 2)}`, + }" + :size="size" + ></u-icon> + </view> + <view + v-if="allowHalf" + @tap.stop="clickHandler($event, index + 1)" + class="u-rate__content__item__icon-wrap u-rate__content__item__icon-wrap--half" + :style="[{ + width: $u.addUnit(rateWidth / 2), + }]" + ref="u-rate__content__item__icon-wrap" + > + <u-icon + :name=" + Math.ceil(activeIndex) > index + ? activeIcon + : inactiveIcon + " + :color=" + disabled + ? '#c8c9cc' + : Math.ceil(activeIndex) > index + ? activeColor + : inactiveColor + " + :custom-style="{ + padding: `0 ${$u.addUnit(gutter / 2)}` + }" + :size="size" + ></u-icon> + </view> + </view> + </view> + </view> +</template> + +<script> + import props from './props.js'; + + // #ifdef APP-NVUE + const dom = weex.requireModule("dom"); + // #endif + /** + * rate 评分 + * @description 该组件一般用于满意度调查,星型评分的场景 + * @tutorial https://www.uviewui.com/components/rate.html + * @property {String | Number} value 用于v-model双向绑定选中的星星数量 (默认 1 ) + * @property {String | Number} count 最多可选的星星数量 (默认 5 ) + * @property {Boolean} disabled 是否禁止用户操作 (默认 false ) + * @property {Boolean} readonly 是否只读 (默认 false ) + * @property {String | Number} size 星星的大小,单位px (默认 18 ) + * @property {String} inactiveColor 未选中星星的颜色 (默认 '#b2b2b2' ) + * @property {String} activeColor 选中的星星颜色 (默认 '#FA3534' ) + * @property {String | Number} gutter 星星之间的距离 (默认 4 ) + * @property {String | Number} minCount 最少选中星星的个数 (默认 1 ) + * @property {Boolean} allowHalf 是否允许半星选择 (默认 false ) + * @property {String} activeIcon 选中时的图标名,只能为uView的内置图标 (默认 'star-fill' ) + * @property {String} inactiveIcon 未选中时的图标名,只能为uView的内置图标 (默认 'star' ) + * @property {Boolean} touchable 是否可以通过滑动手势选择评分 (默认 'true' ) + * @property {Object} customStyle 组件的样式,对象形式 + * @event {Function} change 选中的星星发生变化时触发 + * @example <u-rate :count="count" :value="2"></u-rate> + */ + export default { + name: "u-rate", + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + // 生成一个唯一id,否则一个页面多个评分组件,会造成冲突 + elId: uni.$u.guid(), + elClass: uni.$u.guid(), + rateBoxLeft: 0, // 评分盒子左边到屏幕左边的距离,用于滑动选择时计算距离 + activeIndex: this.value, + rateWidth: 0, // 每个星星的宽度 + // 标识是否正在滑动,由于iOS事件上touch比click先触发,导致快速滑动结束后,接着触发click,导致事件混乱而出错 + moving: false, + }; + }, + watch: { + value(val) { + this.activeIndex = val; + }, + activeIndex: 'emitEvent' + }, + methods: { + init() { + uni.$u.sleep().then(() => { + this.getRateItemRect(); + this.getRateIconWrapRect(); + }) + }, + // 获取评分组件盒子的布局信息 + async getRateItemRect() { + await uni.$u.sleep(); + // uView封装的获取节点的方法,详见文档 + // #ifndef APP-NVUE + this.$uGetRect("#" + this.elId).then((res) => { + this.rateBoxLeft = res.left; + }); + // #endif + // #ifdef APP-NVUE + dom.getComponentRect(this.$refs["u-rate"], (res) => { + this.rateBoxLeft = res.size.left; + }); + // #endif + }, + // 获取单个星星的尺寸 + getRateIconWrapRect() { + // uView封装的获取节点的方法,详见文档 + // #ifndef APP-NVUE + this.$uGetRect("." + this.elClass).then((res) => { + this.rateWidth = res.width; + }); + // #endif + // #ifdef APP-NVUE + dom.getComponentRect( + this.$refs["u-rate__content__item__icon-wrap"][0], + (res) => { + this.rateWidth = res.size.width; + } + ); + // #endif + }, + // 手指滑动 + touchMove(e) { + // 如果禁止通过手动滑动选择,返回 + if (!this.touchable) { + return; + } + this.preventEvent(e); + const x = e.changedTouches[0].pageX; + this.getActiveIndex(x); + }, + // 停止滑动 + touchEnd(e) { + // 如果禁止通过手动滑动选择,返回 + if (!this.touchable) { + return; + } + this.preventEvent(e); + const x = e.changedTouches[0].pageX; + this.getActiveIndex(x); + }, + // 通过点击,直接选中 + clickHandler(e, index) { + // ios上,moving状态取消事件触发 + if (uni.$u.os() === "ios" && this.moving) { + return; + } + this.preventEvent(e); + let x = 0; + // 点击时,在nvue上,无法获得点击的坐标,所以无法实现点击半星选择 + // #ifndef APP-NVUE + x = e.changedTouches[0].pageX; + // #endif + // #ifdef APP-NVUE + // nvue下,无法通过点击获得坐标信息,这里通过元素的位置尺寸值模拟坐标 + x = index * this.rateWidth + this.rateBoxLeft; + // #endif + this.getActiveIndex(x,true); + }, + // 发出事件 + emitEvent() { + // 发出change事件 + this.$emit("change", this.activeIndex); + // 同时修改双向绑定的value的值 + this.$emit("input", this.activeIndex); + }, + // 获取当前激活的评分图标 + getActiveIndex(x,isClick = false) { + if (this.disabled || this.readonly) { + return; + } + // 判断当前操作的点的x坐标值,是否在允许的边界范围内 + const allRateWidth = this.rateWidth * this.count + this.rateBoxLeft; + // 如果小于第一个图标的左边界,设置为最小值,如果大于所有图标的宽度,则设置为最大值 + x = uni.$u.range(this.rateBoxLeft, allRateWidth, x) - this.rateBoxLeft + // 滑动点相对于评分盒子左边的距离 + const distance = x; + // 滑动的距离,相当于多少颗星星 + let index; + // 判断是否允许半星 + if (this.allowHalf) { + index = Math.floor(distance / this.rateWidth); + // 取余,判断小数的区间范围 + const decimal = distance % this.rateWidth; + if (decimal <= this.rateWidth / 2 && decimal > 0) { + index += 0.5; + } else if (decimal > this.rateWidth / 2) { + index++; + } + } else { + index = Math.floor(distance / this.rateWidth); + // 取余,判断小数的区间范围 + const decimal = distance % this.rateWidth; + // 非半星时,只有超过了图标的一半距离,才认为是选择了这颗星 + if (isClick){ + if (decimal > 0) index++; + } else { + if (decimal > this.rateWidth / 2) index++; + } + + } + this.activeIndex = Math.min(index, this.count); + // 对最少颗星星的限制 + if (this.activeIndex < this.minCount) { + this.activeIndex = this.minCount; + } + + // 设置延时为了让click事件在touchmove之前触发 + setTimeout(() => { + this.moving = true; + }, 10); + // 一定时间后,取消标识为移动中状态,是为了让click事件无效 + setTimeout(() => { + this.moving = false; + }, 10); + }, + }, + mounted() { + this.init(); + }, + }; +</script> + +<style lang="scss" scoped> +@import "../../libs/css/components.scss"; +$u-rate-margin: 0 !default; +$u-rate-padding: 0 !default; +$u-rate-item-icon-wrap-half-top: 0 !default; +$u-rate-item-icon-wrap-half-left: 0 !default; + +.u-rate { + @include flex; + align-items: center; + margin: $u-rate-margin; + padding: $u-rate-padding; + /* #ifndef APP-NVUE */ + touch-action: none; + /* #endif */ + + &__content { + @include flex; + + &__item { + position: relative; + + &__icon-wrap { + &--half { + position: absolute; + overflow: hidden; + top: $u-rate-item-icon-wrap-half-top; + left: $u-rate-item-icon-wrap-half-left; + } + } + } + } +} + +.u-icon { + /* #ifndef APP-NVUE */ + box-sizing: border-box; + /* #endif */ +} +</style> diff --git a/uni_modules/uview-ui/components/u-read-more/props.js b/uni_modules/uview-ui/components/u-read-more/props.js new file mode 100644 index 0000000..b444e74 --- /dev/null +++ b/uni_modules/uview-ui/components/u-read-more/props.js @@ -0,0 +1,61 @@ +export default { + props: { + // 默认的显示占位高度 + showHeight: { + type: [String, Number], + default: uni.$u.props.readMore.showHeight + }, + // 展开后是否显示"收起"按钮 + toggle: { + type: Boolean, + default: uni.$u.props.readMore.toggle + }, + // 关闭时的提示文字 + closeText: { + type: String, + default: uni.$u.props.readMore.closeText + }, + // 展开时的提示文字 + openText: { + type: String, + default: uni.$u.props.readMore.openText + }, + // 提示的文字颜色 + color: { + type: String, + default: uni.$u.props.readMore.color + }, + // 提示文字的大小 + fontSize: { + type: [String, Number], + default: uni.$u.props.readMore.fontSize + }, + // 是否显示阴影 + // 此参数不能写在props/readMore.js中进行默认配置,因为使用了条件编译,在外部js中 + // uni无法准确识别当前是否处于nvue还是非nvue下 + shadowStyle: { + type: Object, + default: () => ({ + // #ifndef APP-NVUE + backgroundImage: 'linear-gradient(-180deg, rgba(255, 255, 255, 0) 0%, #fff 80%)', + // #endif + // #ifdef APP-NVUE + // nvue上不支持设置复杂的backgroundImage属性 + backgroundImage: 'linear-gradient(to top, #fff, rgba(255, 255, 255, 0.5))', + // #endif + paddingTop: '100px', + marginTop: '-100px' + }) + }, + // 段落首行缩进的字符个数 + textIndent: { + type: String, + default: uni.$u.props.readMore.textIndent + }, + // open和close事件时,将此参数返回在回调参数中 + name: { + type: [String, Number], + default: uni.$u.props.readMore.name + } + } +} diff --git a/uni_modules/uview-ui/components/u-read-more/u-read-more.vue b/uni_modules/uview-ui/components/u-read-more/u-read-more.vue new file mode 100644 index 0000000..9104e40 --- /dev/null +++ b/uni_modules/uview-ui/components/u-read-more/u-read-more.vue @@ -0,0 +1,157 @@ +<template> + <view class="u-read-more"> + <view + class="u-read-more__content" + :style="{ + height: isLongContent && status === 'close' ? $u.addUnit(showHeight) : $u.addUnit(contentHeight), + textIndent: textIndent + }" + > + <view + class="u-read-more__content__inner" + ref="u-read-more__content__inner" + :class="[elId]" + > + <slot></slot> + </view> + </view> + <view + class="u-read-more__toggle" + :style="[innerShadowStyle]" + v-if="isLongContent" + > + <slot name="toggle"> + <view + class="u-read-more__toggle__text" + @tap="toggleReadMore" + > + <u--text + :text="status === 'close' ? closeText : openText" + :color="color" + :size="fontSize" + :lineHeight="fontSize" + margin="0 5px 0 0" + ></u--text> + <view class="u-read-more__toggle__icon"> + <u-icon + :color="color" + :size="fontSize + 2" + :name="status === 'close' ? 'arrow-down' : 'arrow-up'" + ></u-icon> + </view> + </view> + </slot> + </view> + </view> +</template> + +<script> + // #ifdef APP-NVUE + const dom = uni.requireNativePlugin('dom') + // #endif + import props from './props.js'; + /** + * readMore 阅读更多 + * @description 该组件一般用于内容较长,预先收起一部分,点击展开全部内容的场景。 + * @tutorial https://www.uviewui.com/components/readMore.html + * @property {String | Number} showHeight 内容超出此高度才会显示展开全文按钮,单位px(默认 400 ) + * @property {Boolean} toggle 展开后是否显示收起按钮(默认 false ) + * @property {String} closeText 关闭时的提示文字(默认 '展开阅读全文' ) + * @property {String} openText 展开时的提示文字(默认 '收起' ) + * @property {String} color 提示文字的颜色(默认 '#2979ff' ) + * @property {String | Number} fontSize 提示文字的大小,单位px (默认 14 ) + * @property {Object} shadowStyle 显示阴影的样式 + * @property {String} textIndent 段落首行缩进的字符个数 (默认 '2em' ) + * @property {String | Number} name 用于在 open 和 close 事件中当作回调参数返回 + * @event {Function} open 内容被展开时触发 + * @event {Function} close 内容被收起时触发 + * @example <u-read-more><rich-text :nodes="content"></rich-text></u-read-more> + */ + export default { + name: 'u-read-more', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + isLongContent: false, // 是否需要隐藏一部分内容 + status: 'close', // 当前隐藏与显示的状态,close-收起状态,open-展开状态 + elId: uni.$u.guid(), // 生成唯一class + contentHeight: 100, // 内容高度 + } + }, + computed: { + // 展开后无需阴影,收起时才需要阴影样式 + innerShadowStyle() { + if (this.status === 'open') return {} + else return this.shadowStyle + } + }, + mounted() { + this.init() + }, + methods: { + async init() { + this.getContentHeight().then(height => { + this.contentHeight = height + // 判断高度,如果真实内容高度大于占位高度,则显示收起与展开的控制按钮 + if (height > uni.$u.getPx(this.showHeight)) { + this.isLongContent = true + this.status = 'close' + } + }) + }, + // 获取内容的高度 + async getContentHeight() { + // 延时一定时间再获取节点 + await uni.$u.sleep(30) + return new Promise(resolve => { + // #ifndef APP-NVUE + this.$uGetRect('.' + this.elId).then(res => { + resolve(res.height) + }) + // #endif + + // #ifdef APP-NVUE + const ref = this.$refs['u-read-more__content__inner'] + dom.getComponentRect(ref, (res) => { + resolve(res.size.height) + }) + // #endif + }) + }, + // 展开或者收起 + toggleReadMore() { + this.status = this.status === 'close' ? 'open' : 'close' + // 如果toggle为false,隐藏"收起"部分的内容 + if (this.toggle == false) this.isLongContent = false + // 发出打开或者收齐的事件 + this.$emit(this.status, this.name) + } + } + } +</script> + +<style lang="scss" scoped> +@import "../../libs/css/components.scss"; + +.u-read-more { + + &__content { + overflow: hidden; + color: $u-content-color; + font-size: 15px; + text-align: left; + } + + &__toggle { + @include flex; + justify-content: center; + + &__text { + @include flex; + align-items: center; + justify-content: center; + margin-top: 5px; + } + } +} +</style> diff --git a/uni_modules/uview-ui/components/u-row-notice/props.js b/uni_modules/uview-ui/components/u-row-notice/props.js new file mode 100644 index 0000000..107bd70 --- /dev/null +++ b/uni_modules/uview-ui/components/u-row-notice/props.js @@ -0,0 +1,39 @@ +export default { + props: { + // 显示的内容,字符串 + text: { + type: String, + default: uni.$u.props.rowNotice.text + }, + // 是否显示左侧的音量图标 + icon: { + type: String, + default: uni.$u.props.rowNotice.icon + }, + // 通告模式,link-显示右箭头,closable-显示右侧关闭图标 + mode: { + type: String, + default: uni.$u.props.rowNotice.mode + }, + // 文字颜色,各图标也会使用文字颜色 + color: { + type: String, + default: uni.$u.props.rowNotice.color + }, + // 背景颜色 + bgColor: { + type: String, + default: uni.$u.props.rowNotice.bgColor + }, + // 字体大小,单位px + fontSize: { + type: [String, Number], + default: uni.$u.props.rowNotice.fontSize + }, + // 水平滚动时的滚动速度,即每秒滚动多少px(rpx),这有利于控制文字无论多少时,都能有一个恒定的速度 + speed: { + type: [String, Number], + default: uni.$u.props.rowNotice.speed + } + } +} diff --git a/uni_modules/uview-ui/components/u-row-notice/u-row-notice.vue b/uni_modules/uview-ui/components/u-row-notice/u-row-notice.vue new file mode 100644 index 0000000..20f43c3 --- /dev/null +++ b/uni_modules/uview-ui/components/u-row-notice/u-row-notice.vue @@ -0,0 +1,330 @@ +<template> + <view + class="u-notice" + @tap="clickHandler" + > + <slot name="icon"> + <view + class="u-notice__left-icon" + v-if="icon" + > + <u-icon + :name="icon" + :color="color" + size="19" + ></u-icon> + </view> + </slot> + <view + class="u-notice__content" + ref="u-notice__content" + > + <view + ref="u-notice__content__text" + class="u-notice__content__text" + :style="[animationStyle]" + > + <text + v-for="(item, index) in innerText" + :key="index" + :style="[textStyle]" + >{{item}}</text> + </view> + </view> + <view + class="u-notice__right-icon" + v-if="['link', 'closable'].includes(mode)" + > + <u-icon + v-if="mode === 'link'" + name="arrow-right" + :size="17" + :color="color" + ></u-icon> + <u-icon + v-if="mode === 'closable'" + @click="close" + name="close" + :size="16" + :color="color" + ></u-icon> + </view> + </view> +</template> +<script> + import props from './props.js'; + // #ifdef APP-NVUE + const animation = uni.requireNativePlugin('animation') + const dom = uni.requireNativePlugin('dom') + // #endif + /** + * RowNotice 滚动通知中的水平滚动模式 + * @description 水平滚动 + * @tutorial https://www.uviewui.com/components/noticeBar.html + * @property {String | Number} text 显示的内容,字符串 + * @property {String} icon 是否显示左侧的音量图标 (默认 'volume' ) + * @property {String} mode 通告模式,link-显示右箭头,closable-显示右侧关闭图标 + * @property {String} color 文字颜色,各图标也会使用文字颜色 (默认 '#f9ae3d' ) + * @property {String} bgColor 背景颜色 (默认 ''#fdf6ec' ) + * @property {String | Number} fontSize 字体大小,单位px (默认 14 ) + * @property {String | Number} speed 水平滚动时的滚动速度,即每秒滚动多少px(rpx),这有利于控制文字无论多少时,都能有一个恒定的速度 (默认 80 ) + * + * @event {Function} click 点击通告文字触发 + * @event {Function} close 点击右侧关闭图标触发 + * @example + */ + export default { + name: 'u-row-notice', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + animationDuration: '0', // 动画执行时间 + animationPlayState: 'paused', // 动画的开始和结束执行 + // nvue下,内容发生变化,导致滚动宽度也变化,需要标志为是否需要重新计算宽度 + // 不能在内容变化时直接重新计算,因为nvue的animation模块上一次的滚动不是刚好结束,会有影响 + nvueInit: true, + show: true + }; + }, + watch: { + text: { + immediate: true, + handler(newValue, oldValue) { + // #ifdef APP-NVUE + this.nvueInit = true + // #endif + // #ifndef APP-NVUE + this.vue() + // #endif + + if(!uni.$u.test.string(newValue)) { + uni.$u.error('noticebar组件direction为row时,要求text参数为字符串形式') + } + } + }, + fontSize() { + // #ifdef APP-NVUE + this.nvueInit = true + // #endif + // #ifndef APP-NVUE + this.vue() + // #endif + }, + speed() { + // #ifdef APP-NVUE + this.nvueInit = true + // #endif + // #ifndef APP-NVUE + this.vue() + // #endif + } + }, + computed: { + // 文字内容的样式 + textStyle() { + let style = {} + style.color = this.color + style.fontSize = uni.$u.addUnit(this.fontSize) + return style + }, + animationStyle() { + let style = {} + style.animationDuration = this.animationDuration + style.animationPlayState = this.animationPlayState + return style + }, + // 内部对用户传入的数据进一步分割,放到多个text标签循环,否则如果用户传入的字符串很长(100个字符以上) + // 放在一个text标签中进行滚动,在低端安卓机上,动画可能会出现抖动现象,需要分割到多个text中可解决此问题 + innerText() { + let result = [], + // 每组text标签的字符长度 + len = 20 + const textArr = this.text.split('') + for (let i = 0; i < textArr.length; i += len) { + // 对拆分的后的text进行slice分割,得到的为数组再进行join拼接为字符串 + result.push(textArr.slice(i, i + len).join('')) + } + return result + } + }, + mounted() { + // #ifdef APP-PLUS + // 在APP上(含nvue),监听当前webview是否处于隐藏状态(进入下一页时即为hide状态) + // 如果webivew隐藏了,为了节省性能的损耗,应停止动画的执行,同时也是为了保持进入下一页返回后,滚动位置保持不变 + var pages = getCurrentPages() + var page = pages[pages.length - 1] + var currentWebview = page.$getAppWebview() + currentWebview.addEventListener('hide', () => { + this.webviewHide = true + }) + currentWebview.addEventListener('show', () => { + this.webviewHide = false + }) + // #endif + + this.init() + }, + methods: { + init() { + // #ifdef APP-NVUE + this.nvue() + // #endif + + // #ifndef APP-NVUE + this.vue() + // #endif + + if(!uni.$u.test.string(this.text)) { + uni.$u.error('noticebar组件direction为row时,要求text参数为字符串形式') + } + }, + // vue版处理 + async vue() { + // #ifndef APP-NVUE + let boxWidth = 0, + textWidth = 0 + // 进行一定的延时 + await uni.$u.sleep() + // 查询盒子和文字的宽度 + textWidth = (await this.$uGetRect('.u-notice__content__text')).width + boxWidth = (await this.$uGetRect('.u-notice__content')).width + // 根据t=s/v(时间=路程/速度),这里为何不需要加上#u-notice-box的宽度,因为中设置了.u-notice-content样式中设置了padding-left: 100% + // 恰巧计算出来的结果中已经包含了#u-notice-box的宽度 + this.animationDuration = `${textWidth / uni.$u.getPx(this.speed)}s` + // 这里必须这样开始动画,否则在APP上动画速度不会改变 + this.animationPlayState = 'paused' + setTimeout(() => { + this.animationPlayState = 'running' + }, 10) + // #endif + }, + // nvue版处理 + async nvue() { + // #ifdef APP-NVUE + this.nvueInit = false + let boxWidth = 0, + textWidth = 0 + // 进行一定的延时 + await uni.$u.sleep() + // 查询盒子和文字的宽度 + textWidth = (await this.getNvueRect('u-notice__content__text')).width + boxWidth = (await this.getNvueRect('u-notice__content')).width + // 将文字移动到盒子的右边沿,之所以需要这么做,是因为nvue不支持100%单位,否则可以通过css设置 + animation.transition(this.$refs['u-notice__content__text'], { + styles: { + transform: `translateX(${boxWidth}px)` + }, + }, () => { + // 如果非禁止动画,则开始滚动 + !this.stopAnimation && this.loopAnimation(textWidth, boxWidth) + }); + // #endif + }, + loopAnimation(textWidth, boxWidth) { + // #ifdef APP-NVUE + animation.transition(this.$refs['u-notice__content__text'], { + styles: { + // 目标移动终点为-textWidth,也即当文字的最右边贴到盒子的左边框的位置 + transform: `translateX(-${textWidth}px)` + }, + // 滚动时间的计算为,时间 = 路程(boxWidth + textWidth) / 速度,最后转为毫秒 + duration: (boxWidth + textWidth) / uni.$u.getPx(this.speed) * 1000, + delay: 10 + }, () => { + animation.transition(this.$refs['u-notice__content__text'], { + styles: { + // 重新将文字移动到盒子的右边沿 + transform: `translateX(${this.stopAnimation ? 0 : boxWidth}px)` + }, + }, () => { + // 如果非禁止动画,则继续下一轮滚动 + if (!this.stopAnimation) { + // 判断是否需要初始化计算尺寸 + if (this.nvueInit) { + this.nvue() + } else { + this.loopAnimation(textWidth, boxWidth) + } + } + }); + }) + // #endif + }, + getNvueRect(el) { + // #ifdef APP-NVUE + // 返回一个promise + return new Promise(resolve => { + dom.getComponentRect(this.$refs[el], (res) => { + resolve(res.size) + }) + }) + // #endif + }, + // 点击通告栏 + clickHandler(index) { + this.$emit('click') + }, + // 点击右侧按钮,需要判断点击的是关闭图标还是箭头图标 + close() { + this.$emit('close') + } + }, + // #ifdef APP-NVUE + beforeDestroy() { + this.stopAnimation = true + }, + // #endif + }; +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-notice { + @include flex; + align-items: center; + justify-content: space-between; + + &__left-icon { + align-items: center; + margin-right: 5px; + } + + &__right-icon { + margin-left: 5px; + align-items: center; + } + + &__content { + text-align: right; + flex: 1; + @include flex; + flex-wrap: nowrap; + overflow: hidden; + + &__text { + font-size: 14px; + color: $u-warning; + /* #ifndef APP-NVUE */ + // 这一句很重要,为了能让滚动左右连接起来 + padding-left: 100%; + word-break: keep-all; + white-space: nowrap; + animation: u-loop-animation 10s linear infinite both; + /* #endif */ + @include flex(row); + } + } + + } + + @keyframes u-loop-animation { + 0% { + transform: translate3d(0, 0, 0); + } + + 100% { + transform: translate3d(-100%, 0, 0); + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-row/props.js b/uni_modules/uview-ui/components/u-row/props.js new file mode 100644 index 0000000..4b71b87 --- /dev/null +++ b/uni_modules/uview-ui/components/u-row/props.js @@ -0,0 +1,19 @@ +export default { + props: { + // 给col添加间距,左右边距各占一半 + gutter: { + type: [String, Number], + default: uni.$u.props.row.gutter + }, + // 水平排列方式,可选值为`start`(或`flex-start`)、`end`(或`flex-end`)、`center`、`around`(或`space-around`)、`between`(或`space-between`) + justify: { + type: String, + default: uni.$u.props.row.justify + }, + // 垂直对齐方式,可选值为top、center、bottom + align: { + type: String, + default: uni.$u.props.row.align + } + } +} diff --git a/uni_modules/uview-ui/components/u-row/u-row.vue b/uni_modules/uview-ui/components/u-row/u-row.vue new file mode 100644 index 0000000..e608fc5 --- /dev/null +++ b/uni_modules/uview-ui/components/u-row/u-row.vue @@ -0,0 +1,93 @@ +<template> + <view + class="u-row" + ref="u-row" + :style="[rowStyle]" + @tap="clickHandler" + > + <slot /> + </view> +</template> + +<script> + // #ifdef APP-NVUE + const dom = uni.requireNativePlugin('dom') + // #endif + import props from './props.js'; + /** + * Row 栅格系统中的行 + * @description 通过基础的 12 分栏,迅速简便地创建布局 + * @tutorial https://www.uviewui.com/components/layout.html + * @property {String | Number} gutter 栅格间隔,左右各为此值的一半,单位px (默认 0 ) + * @property {String} justify 水平排列方式(微信小程序暂不支持) 可选值为`start`(或`flex-start`)、`end`(或`flex-end`)、`center`、`around`(或`space-around`)、`between`(或`space-between`) (默认 'start' ) + * @property {String} align 垂直排列方式 (默认 'center' ) + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @event {Function} click row被点击 + * @example <u-row justify="space-between" customStyle="margin-bottom: 10px"></u-row> + */ + export default { + name: "u-row", + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + + } + }, + computed: { + uJustify() { + if (this.justify == 'end' || this.justify == 'start') return 'flex-' + this.justify + else if (this.justify == 'around' || this.justify == 'between') return 'space-' + this.justify + else return this.justify + }, + uAlignItem() { + if (this.align == 'top') return 'flex-start' + if (this.align == 'bottom') return 'flex-end' + else return this.align + }, + rowStyle() { + const style = { + alignItems: this.uAlignItem, + justifyContent: this.uJustify + } + // 通过给u-row左右两边的负外边距,消除u-col在有gutter时,第一个和最后一个元素的左内边距和右内边距造成的影响 + if(this.gutter) { + style.marginLeft = uni.$u.addUnit(-Number(this.gutter)/2) + style.marginRight = uni.$u.addUnit(-Number(this.gutter)/2) + } + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) + } + }, + methods: { + clickHandler(e) { + this.$emit('click') + }, + async getComponentWidth() { + // 延时一定时间,以确保节点渲染完成 + await uni.$u.sleep() + return new Promise(resolve => { + // uView封装的获取节点的方法,详见文档 + // #ifndef APP-NVUE + this.$uGetRect('.u-row').then(res => { + resolve(res.width) + }) + // #endif + // #ifdef APP-NVUE + // nvue的dom模块用于获取节点 + dom.getComponentRect(this.$refs['u-row'], (res) => { + resolve(res.size.width) + }) + // #endif + }) + }, + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-row { + @include flex; + } +</style> diff --git a/uni_modules/uview-ui/components/u-safe-bottom/props.js b/uni_modules/uview-ui/components/u-safe-bottom/props.js new file mode 100644 index 0000000..7c11331 --- /dev/null +++ b/uni_modules/uview-ui/components/u-safe-bottom/props.js @@ -0,0 +1,5 @@ +export default { + props: { + + } +} diff --git a/uni_modules/uview-ui/components/u-safe-bottom/u-safe-bottom.vue b/uni_modules/uview-ui/components/u-safe-bottom/u-safe-bottom.vue new file mode 100644 index 0000000..fb858ea --- /dev/null +++ b/uni_modules/uview-ui/components/u-safe-bottom/u-safe-bottom.vue @@ -0,0 +1,56 @@ +<template> + <view + class="u-safe-bottom" + :style="[style]" + :class="[!isNvue && 'u-safe-area-inset-bottom']" + > + </view> +</template> + +<script> + import props from "./props.js"; + /** + * SafeBottom 底部安全区 + * @description 这个适配,主要是针对IPhone X等一些底部带指示条的机型,指示条的操作区域与页面底部存在重合,容易导致用户误操作,因此我们需要针对这些机型进行底部安全区适配。 + * @tutorial https://www.uviewui.com/components/safeAreaInset.html + * @property {type} prop_name + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @event {Function()} + * @example <u-status-bar></u-status-bar> + */ + export default { + name: "u-safe-bottom", + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + safeAreaBottomHeight: 0, + isNvue: false, + }; + }, + computed: { + style() { + const style = {}; + // #ifdef APP-NVUE + // nvue下,高度使用js计算填充 + style.height = uni.$u.addUnit(uni.$u.sys().safeAreaInsets.bottom, 'px'); + // #endif + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)); + }, + }, + mounted() { + // #ifdef APP-NVUE + // 标识为是否nvue + this.isNvue = true; + // #endif + }, + }; +</script> + +<style lang="scss" scoped> + .u-safe-bottom { + /* #ifndef APP-NVUE */ + width: 100%; + /* #endif */ + } +</style> diff --git a/uni_modules/uview-ui/components/u-scroll-list/nvue.js b/uni_modules/uview-ui/components/u-scroll-list/nvue.js new file mode 100644 index 0000000..94bb056 --- /dev/null +++ b/uni_modules/uview-ui/components/u-scroll-list/nvue.js @@ -0,0 +1,28 @@ +// 引入bindingx,此库类似于微信小程序wxs,目的是让js运行在视图层,减少视图层和逻辑层的通信折损 +const BindingX = uni.requireNativePlugin('bindingx') + +export default { + methods: { + // 此处不写注释,请自行体会 + nvueScrollHandler(e) { + const anchor = this.$refs['u-scroll-list__scroll-view'].ref + const element = this.$refs['u-scroll-list__indicator__line__bar'].ref + const scrollLeft = e.contentOffset.x + const contentSize = e.contentSize.width + const { scrollWidth } = this + const barAllMoveWidth = this.indicatorWidth - this.indicatorBarWidth + // 在安卓和iOS上,需要除的倍数不一样,iOS需要除以2 + const actionNum = uni.$u.os() === 'ios' ? 2 : 1 + const expression = `(x / ${actionNum}) / ${contentSize - scrollWidth} * ${barAllMoveWidth}` + BindingX.bind({ + anchor, + eventType: 'scroll', + props: [{ + element, + property: 'transform.translateX', + expression + }] + }) + } + } +} diff --git a/uni_modules/uview-ui/components/u-scroll-list/other.js b/uni_modules/uview-ui/components/u-scroll-list/other.js new file mode 100644 index 0000000..e69de29 diff --git a/uni_modules/uview-ui/components/u-scroll-list/props.js b/uni_modules/uview-ui/components/u-scroll-list/props.js new file mode 100644 index 0000000..765be54 --- /dev/null +++ b/uni_modules/uview-ui/components/u-scroll-list/props.js @@ -0,0 +1,34 @@ +export default { + props: { + // 指示器的整体宽度 + indicatorWidth: { + type: [String, Number], + default: uni.$u.props.scrollList.indicatorWidth + }, + // 滑块的宽度 + indicatorBarWidth: { + type: [String, Number], + default: uni.$u.props.scrollList.indicatorBarWidth + }, + // 是否显示面板指示器 + indicator: { + type: Boolean, + default: uni.$u.props.scrollList.indicator + }, + // 指示器非激活颜色 + indicatorColor: { + type: String, + default: uni.$u.props.scrollList.indicatorColor + }, + // 指示器的激活颜色 + indicatorActiveColor: { + type: String, + default: uni.$u.props.scrollList.indicatorActiveColor + }, + // 指示器样式,可通过bottom,left,right进行定位 + indicatorStyle: { + type: [String, Object], + default: uni.$u.props.scrollList.indicatorStyle + } + } +} diff --git a/uni_modules/uview-ui/components/u-scroll-list/scrollWxs.wxs b/uni_modules/uview-ui/components/u-scroll-list/scrollWxs.wxs new file mode 100644 index 0000000..ce94f1d --- /dev/null +++ b/uni_modules/uview-ui/components/u-scroll-list/scrollWxs.wxs @@ -0,0 +1,50 @@ +function scroll(event, ownerInstance) { + // detail中含有scroll-view的信息,比如scroll-view的实际宽度,当前时间点scroll-view的移动距离等 + var detail = event.detail + var scrollWidth = detail.scrollWidth + var scrollLeft = detail.scrollLeft + // 获取当前组件的dataset,说白了就是祸国殃民的腾xun搞出来的垃ji + var dataset = event.currentTarget.dataset + // 此为scroll-view外部包裹元素的宽度 + // 某些HX版本(3.1.18),发现view元素中大写的data-scrollWidth,在wxs中,变成了全部小写,所以这里需要特别处理 + var scrollComponentWidth = dataset.scrollWidth || dataset.scrollwidth || 0 + // 指示器和滑块的宽度 + var indicatorWidth = dataset.indicatorWidth || dataset.indicatorwidth || 0 + var barWidth = dataset.barWidth || dataset.barwidth || 0 + // 此处的计算理由为:scroll-view的滚动距离与目标滚动距离(scroll-view的实际宽度减去包裹元素的宽度)之比,等于滑块当前移动距离与总需 + // 滑动距离(指示器的总宽度减去滑块宽度)的比值 + var x = scrollLeft / (scrollWidth - scrollComponentWidth) * (indicatorWidth - barWidth) + setBarStyle(ownerInstance, x) +} + +// 由于webview的无能,无法保证scroll-view在滑动过程中,一直触发scroll事件,会导致 +// 无法监听到某些滚动值,当在首尾临界值无法监听到时,这是致命的,因为错失这些值会导致滑块无法回到起点和终点 +// 所以这里需要对临界值做监听并处理 +function scrolltolower(event, ownerInstance) { + ownerInstance.callMethod('scrollEvent', 'right') + // 获取当前组件的dataset + var dataset = event.currentTarget.dataset + // 指示器和滑块的宽度 + var indicatorWidth = dataset.indicatorWidth || dataset.indicatorwidth || 0 + var barWidth = dataset.barWidth || dataset.barwidth || 0 + // scroll-view滚动到右边终点时,将滑块也设置为到右边的终点,它所需移动的距离为:指示器宽度 - 滑块宽度 + setBarStyle(ownerInstance, indicatorWidth - barWidth) +} + +function scrolltoupper(event, ownerInstance) { + ownerInstance.callMethod('scrollEvent', 'left') + // 滚动到左边时,将滑块设置为0的偏移距离,回到起点 + setBarStyle(ownerInstance, 0) +} + +function setBarStyle(ownerInstance, x) { + ownerInstance.selectComponent('.u-scroll-list__indicator__line__bar') && ownerInstance.selectComponent('.u-scroll-list__indicator__line__bar').setStyle({ + transform: 'translateX(' + x + 'px)' + }) +} + +module.exports = { + scroll: scroll, + scrolltolower: scrolltolower, + scrolltoupper: scrolltoupper +} diff --git a/uni_modules/uview-ui/components/u-scroll-list/u-scroll-list.vue b/uni_modules/uview-ui/components/u-scroll-list/u-scroll-list.vue new file mode 100644 index 0000000..4fe885a --- /dev/null +++ b/uni_modules/uview-ui/components/u-scroll-list/u-scroll-list.vue @@ -0,0 +1,224 @@ +<template> + <view + class="u-scroll-list" + ref="u-scroll-list" + > + <!-- #ifdef APP-NVUE --> + <!-- nvue使用bindingX实现,以得到更好的性能 --> + <scroller + class="u-scroll-list__scroll-view" + ref="u-scroll-list__scroll-view" + scroll-direction="horizontal" + :show-scrollbar="false" + :offset-accuracy="1" + @scroll="nvueScrollHandler" + > + <view class="u-scroll-list__scroll-view__content"> + <slot /> + </view> + </scroller> + <!-- #endif --> + <!-- #ifndef APP-NVUE --> + <!-- #ifdef MP-WEIXIN || APP-VUE || H5 || MP-QQ --> + <!-- 以上平台,支持wxs --> + <scroll-view + class="u-scroll-list__scroll-view" + scroll-x + @scroll="wxs.scroll" + @scrolltoupper="wxs.scrolltoupper" + @scrolltolower="wxs.scrolltolower" + :data-scrollWidth="scrollWidth" + :data-barWidth="$u.getPx(indicatorBarWidth)" + :data-indicatorWidth="$u.getPx(indicatorWidth)" + :show-scrollbar="false" + :upper-threshold="0" + :lower-threshold="0" + > + <!-- #endif --> + <!-- #ifndef APP-NVUE || MP-WEIXIN || H5 || APP-VUE || MP-QQ --> + <!-- 非以上平台,只能使用普通js实现 --> + <scroll-view + class="u-scroll-list__scroll-view" + scroll-x + @scroll="scrollHandler" + @scrolltoupper="scrolltoupperHandler" + @scrolltolower="scrolltolowerHandler" + :show-scrollbar="false" + :upper-threshold="0" + :lower-threshold="0" + > + <!-- #endif --> + <view class="u-scroll-list__scroll-view__content"> + <slot /> + </view> + </scroll-view> + <!-- #endif --> + <view + class="u-scroll-list__indicator" + v-if="indicator" + :style="[$u.addStyle(indicatorStyle)]" + > + <view + class="u-scroll-list__indicator__line" + :style="[lineStyle]" + > + <view + class="u-scroll-list__indicator__line__bar" + :style="[barStyle]" + ref="u-scroll-list__indicator__line__bar" + ></view> + </view> + </view> + </view> +</template> + +<script + src="./scrollWxs.wxs" + module="wxs" + lang="wxs" +></script> + +<script> +/** + * scrollList 横向滚动列表 + * @description 该组件一般用于同时展示多个商品、分类的场景,也可以完成左右滑动的列表。 + * @tutorial https://www.uviewui.com/components/scrollList.html + * @property {String | Number} indicatorWidth 指示器的整体宽度 (默认 50 ) + * @property {String | Number} indicatorBarWidth 滑块的宽度 (默认 20 ) + * @property {Boolean} indicator 是否显示面板指示器 (默认 true ) + * @property {String} indicatorColor 指示器非激活颜色 (默认 '#f2f2f2' ) + * @property {String} indicatorActiveColor 指示器的激活颜色 (默认 '#3c9cff' ) + * @property {String | Object} indicatorStyle 指示器样式,可通过bottom,left,right进行定位 + * @event {Function} left 滑动到左边时触发 + * @event {Function} right 滑动到右边时触发 + * @example + */ +// #ifdef APP-NVUE +const dom = uni.requireNativePlugin('dom') +import nvueMixin from "./nvue.js" +// #endif +import props from './props.js'; +export default { + name: 'u-scroll-list', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + // #ifdef APP-NVUE + mixins: [uni.$u.mpMixin, uni.$u.mixin, nvueMixin, props], + // #endif + data() { + return { + scrollInfo: { + scrollLeft: 0, + scrollWidth: 0 + }, + scrollWidth: 0 + } + }, + computed: { + // 指示器为线型的样式 + barStyle() { + const style = {} + // #ifndef APP-NVUE || MP-WEIXIN || H5 || APP-VUE || MP-QQ + // 此为普通js方案,只有在非nvue和不支持wxs方案的端才使用、 + // 此处的计算理由为:scroll-view的滚动距离与目标滚动距离(scroll-view的实际宽度减去包裹元素的宽度)之比,等于滑块当前移动距离与总需 + // 滑动距离(指示器的总宽度减去滑块宽度)的比值 + const scrollLeft = this.scrollInfo.scrollLeft, + scrollWidth = this.scrollInfo.scrollWidth, + barAllMoveWidth = this.indicatorWidth - this.indicatorBarWidth + const x = scrollLeft / (scrollWidth - this.scrollWidth) * barAllMoveWidth + style.transform = `translateX(${ x }px)` + // #endif + // 设置滑块的宽度和背景色,是每个平台都需要的 + style.width = uni.$u.addUnit(this.indicatorBarWidth) + style.backgroundColor = this.indicatorActiveColor + return style + }, + lineStyle() { + const style = {} + // 指示器整体的样式,需要设置其宽度和背景色 + style.width = uni.$u.addUnit(this.indicatorWidth) + style.backgroundColor = this.indicatorColor + return style + } + }, + mounted() { + this.init() + }, + methods: { + init() { + this.getComponentWidth() + }, + // #ifndef APP-NVUE || MP-WEIXIN || H5 || APP-VUE || MP-QQ + // scroll-view触发滚动事件 + scrollHandler(e) { + this.scrollInfo = e.detail + }, + scrolltoupperHandler() { + this.scrollEvent('left') + this.scrollInfo.scrollLeft = 0 + }, + scrolltolowerHandler() { + this.scrollEvent('right') + // 在普通js方案中,滚动到右边时,通过设置this.scrollInfo,模拟出滚动到右边的情况 + // 因为上方是用过computed计算的,设置后,会自动调整滑块的位置 + this.scrollInfo.scrollLeft = uni.$u.getPx(this.indicatorWidth) - uni.$u.getPx(this.indicatorBarWidth) + }, + // #endif + // + scrollEvent(status) { + this.$emit(status) + }, + // 获取组件的宽度 + async getComponentWidth() { + // 延时一定时间,以获取dom尺寸 + await uni.$u.sleep(30) + // #ifndef APP-NVUE + this.$uGetRect('.u-scroll-list').then(size => { + this.scrollWidth = size.width + }) + // #endif + + // #ifdef APP-NVUE + const ref = this.$refs['u-scroll-list'] + ref && dom.getComponentRect(ref, (res) => { + this.scrollWidth = res.size.width + }) + // #endif + }, + } +} +</script> + +<style lang="scss" scoped> +@import "../../libs/css/components.scss"; + +.u-scroll-list { + padding-bottom: 10px; + + &__scroll-view { + @include flex; + + &__content { + @include flex; + } + } + + &__indicator { + @include flex; + justify-content: center; + margin-top: 15px; + + &__line { + width: 60px; + height: 4px; + border-radius: 100px; + overflow: hidden; + + &__bar { + width: 20px; + height: 4px; + border-radius: 100px; + } + } + } +} +</style> diff --git a/uni_modules/uview-ui/components/u-search/props.js b/uni_modules/uview-ui/components/u-search/props.js new file mode 100644 index 0000000..df1b342 --- /dev/null +++ b/uni_modules/uview-ui/components/u-search/props.js @@ -0,0 +1,118 @@ +export default { + props: { + // 搜索框形状,round-圆形,square-方形 + shape: { + type: String, + default: uni.$u.props.search.shape + }, + // 搜索框背景色,默认值#f2f2f2 + bgColor: { + type: String, + default: uni.$u.props.search.bgColor + }, + // 占位提示文字 + placeholder: { + type: String, + default: uni.$u.props.search.placeholder + }, + // 是否启用清除控件 + clearabled: { + type: Boolean, + default: uni.$u.props.search.clearabled + }, + // 是否自动聚焦 + focus: { + type: Boolean, + default: uni.$u.props.search.focus + }, + // 是否在搜索框右侧显示取消按钮 + showAction: { + type: Boolean, + default: uni.$u.props.search.showAction + }, + // 右边控件的样式 + actionStyle: { + type: Object, + default: uni.$u.props.search.actionStyle + }, + // 取消按钮文字 + actionText: { + type: String, + default: uni.$u.props.search.actionText + }, + // 输入框内容对齐方式,可选值为 left|center|right + inputAlign: { + type: String, + default: uni.$u.props.search.inputAlign + }, + // input输入框的样式,可以定义文字颜色,大小等,对象形式 + inputStyle: { + type: Object, + default: uni.$u.props.search.inputStyle + }, + // 是否启用输入框 + disabled: { + type: Boolean, + default: uni.$u.props.search.disabled + }, + // 边框颜色 + borderColor: { + type: String, + default: uni.$u.props.search.borderColor + }, + // 搜索图标的颜色,默认同输入框字体颜色 + searchIconColor: { + type: String, + default: uni.$u.props.search.searchIconColor + }, + // 输入框字体颜色 + color: { + type: String, + default: uni.$u.props.search.color + }, + // placeholder的颜色 + placeholderColor: { + type: String, + default: uni.$u.props.search.placeholderColor + }, + // 左边输入框的图标,可以为uView图标名称或图片路径 + searchIcon: { + type: String, + default: uni.$u.props.search.searchIcon + }, + searchIconSize: { + type: [Number, String], + default: uni.$u.props.search.searchIconSize + }, + // 组件与其他上下左右元素之间的距离,带单位的字符串形式,如"30px"、"30px 20px"等写法 + margin: { + type: String, + default: uni.$u.props.search.margin + }, + // 开启showAction时,是否在input获取焦点时才显示 + animation: { + type: Boolean, + default: uni.$u.props.search.animation + }, + // 输入框的初始化内容 + value: { + type: String, + default: uni.$u.props.search.value + }, + // 输入框最大能输入的长度,-1为不限制长度(来自uniapp文档) + maxlength: { + type: [String, Number], + default: uni.$u.props.search.maxlength + }, + // 搜索框高度,单位px + height: { + type: [String, Number], + default: uni.$u.props.search.height + }, + // 搜索框左侧文本 + label: { + type: [String, Number, null], + default: uni.$u.props.search.label + } + } +} diff --git a/uni_modules/uview-ui/components/u-search/u-search.vue b/uni_modules/uview-ui/components/u-search/u-search.vue new file mode 100644 index 0000000..f169c7f --- /dev/null +++ b/uni_modules/uview-ui/components/u-search/u-search.vue @@ -0,0 +1,303 @@ +<template> + <view + class="u-search" + @tap="clickHandler" + :style="[{ + margin: margin, + }, $u.addStyle(customStyle)]" + > + <view + class="u-search__content" + :style="{ + backgroundColor: bgColor, + borderRadius: shape == 'round' ? '100px' : '4px', + borderColor: borderColor, + }" + > + <template v-if="$slots.label || label !== null"> + <slot name="label"> + <text class="u-search__content__label">{{ label }}</text> + </slot> + </template> + <view class="u-search__content__icon"> + <u-icon + @tap="clickIcon" + :size="searchIconSize" + :name="searchIcon" + :color="searchIconColor ? searchIconColor : color" + ></u-icon> + </view> + <input + confirm-type="search" + @blur="blur" + :value="value" + @confirm="search" + @input="inputChange" + :disabled="disabled" + @focus="getFocus" + :focus="focus" + :maxlength="maxlength" + placeholder-class="u-search__content__input--placeholder" + :placeholder="placeholder" + :placeholder-style="`color: ${placeholderColor}`" + class="u-search__content__input" + type="text" + :style="[{ + textAlign: inputAlign, + color: color, + backgroundColor: bgColor, + height: $u.addUnit(height) + }, inputStyle]" + /> + <view + class="u-search__content__icon u-search__content__close" + v-if="keyword && clearabled && focused" + @tap="clear" + > + <u-icon + name="close" + size="11" + color="#ffffff" + customStyle="line-height: 12px" + ></u-icon> + </view> + </view> + <text + :style="[actionStyle]" + class="u-search__action" + :class="[(showActionBtn || show) && 'u-search__action--active']" + @tap.stop.prevent="custom" + >{{ actionText }}</text> + </view> +</template> + +<script> + import props from './props.js'; + + /** + * search 搜索框 + * @description 搜索组件,集成了常见搜索框所需功能,用户可以一键引入,开箱即用。 + * @tutorial https://www.uviewui.com/components/search.html + * @property {String} shape 搜索框形状,round-圆形,square-方形(默认 'round' ) + * @property {String} bgColor 搜索框背景颜色(默认 '#f2f2f2' ) + * @property {String} placeholder 占位文字内容(默认 '请输入关键字' ) + * @property {Boolean} clearabled 是否启用清除控件(默认 true ) + * @property {Boolean} focus 是否自动获得焦点(默认 false ) + * @property {Boolean} showAction 是否显示右侧控件(默认 true ) + * @property {Object} actionStyle 右侧控件的样式,对象形式 + * @property {String} actionText 右侧控件文字(默认 '搜索' ) + * @property {String} inputAlign 输入框内容水平对齐方式 (默认 'left' ) + * @property {Object} inputStyle 自定义输入框样式,对象形式 + * @property {Boolean} disabled 是否启用输入框(默认 false ) + * @property {String} borderColor 边框颜色,配置了颜色,才会有边框 (默认 'transparent' ) + * @property {String} searchIconColor 搜索图标的颜色,默认同输入框字体颜色 (默认 '#909399' ) + * @property {Number | String} searchIconSize 搜索图标的字体,默认22 + * @property {String} color 输入框字体颜色(默认 '#606266' ) + * @property {String} placeholderColor placeholder的颜色(默认 '#909399' ) + * @property {String} searchIcon 输入框左边的图标,可以为uView图标名称或图片路径 (默认 'search' ) + * @property {String} margin 组件与其他上下左右元素之间的距离,带单位的字符串形式,如"30px" (默认 '0' ) + * @property {Boolean} animation 是否开启动画,见上方说明(默认 false ) + * @property {String} value 输入框初始值 + * @property {String | Number} maxlength 输入框最大能输入的长度,-1为不限制长度 (默认 '-1' ) + * @property {String | Number} height 输入框高度,单位px(默认 64 ) + * @property {String | Number} label 搜索框左边显示内容 + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @event {Function} change 输入框内容发生变化时触发 + * @event {Function} search 用户确定搜索时触发,用户按回车键,或者手机键盘右下角的"搜索"键时触发 + * @event {Function} custom 用户点击右侧控件时触发 + * @event {Function} clear 用户点击清除按钮时触发 + * @example <u-search placeholder="日照香炉生紫烟" v-model="keyword"></u-search> + */ + export default { + name: "u-search", + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + keyword: '', + showClear: false, // 是否显示右边的清除图标 + show: false, + // 标记input当前状态是否处于聚焦中,如果是,才会显示右侧的清除控件 + focused: this.focus + // 绑定输入框的值 + // inputValue: this.value + }; + }, + watch: { + keyword(nVal) { + // 双向绑定值,让v-model绑定的值双向变化 + this.$emit('input', nVal); + // 触发change事件,事件效果和v-model双向绑定的效果一样,让用户多一个选择 + this.$emit('change', nVal); + }, + value: { + immediate: true, + handler(nVal) { + this.keyword = nVal; + } + } + }, + computed: { + showActionBtn() { + return !this.animation && this.showAction + } + }, + methods: { + // 目前HX2.6.9 v-model双向绑定无效,故监听input事件获取输入框内容的变化 + inputChange(e) { + this.keyword = e.detail.value; + }, + // 清空输入 + // 也可以作为用户通过this.$refs形式调用清空输入框内容 + clear() { + this.keyword = ''; + // 延后发出事件,避免在父组件监听clear事件时,value为更新前的值(不为空) + this.$nextTick(() => { + this.$emit('clear'); + }) + }, + // 确定搜索 + search(e) { + this.$emit('search', e.detail.value); + try { + // 收起键盘 + uni.hideKeyboard(); + } catch (e) {} + }, + // 点击右边自定义按钮的事件 + custom() { + this.$emit('custom', this.keyword); + try { + // 收起键盘 + uni.hideKeyboard(); + } catch (e) {} + }, + // 获取焦点 + getFocus() { + this.focused = true; + // 开启右侧搜索按钮展开的动画效果 + if (this.animation && this.showAction) this.show = true; + this.$emit('focus', this.keyword); + }, + // 失去焦点 + blur() { + // 最开始使用的是监听图标@touchstart事件,自从hx2.8.4后,此方法在微信小程序出错 + // 这里改为监听点击事件,手点击清除图标时,同时也发生了@blur事件,导致图标消失而无法点击,这里做一个延时 + setTimeout(() => { + this.focused = false; + }, 100) + this.show = false; + this.$emit('blur', this.keyword); + }, + // 点击搜索框,只有disabled=true时才发出事件,因为禁止了输入,意味着是想跳转真正的搜索页 + clickHandler() { + if (this.disabled) this.$emit('click'); + }, + // 点击左边图标 + clickIcon() { + this.$emit('clickIcon'); + } + } + } +</script> + +<style lang="scss" scoped> +@import "../../libs/css/components.scss"; +$u-search-content-padding: 0 10px !default; +$u-search-label-color: $u-main-color !default; +$u-search-label-font-size: 14px !default; +$u-search-label-margin: 0 4px !default; +$u-search-close-size: 20px !default; +$u-search-close-radius: 100px !default; +$u-search-close-bgColor: #C6C7CB !default; +$u-search-close-transform: scale(0.82) !default; +$u-search-input-font-size: 14px !default; +$u-search-input-margin: 0 5px !default; +$u-search-input-color: $u-main-color !default; +$u-search-input-placeholder-color: $u-tips-color !default; +$u-search-action-font-size: 14px !default; +$u-search-action-color: $u-main-color !default; +$u-search-action-width: 0 !default; +$u-search-action-active-width: 40px !default; +$u-search-action-margin-left: 5px !default; + +/* #ifdef H5 */ +// iOS15在H5下,hx的某些版本,input type=search时,会多了一个搜索图标,进行移除 +[type="search"]::-webkit-search-decoration { + display: none; +} +/* #endif */ + +.u-search { + @include flex(row); + align-items: center; + flex: 1; + + &__content { + @include flex; + align-items: center; + padding: $u-search-content-padding; + flex: 1; + justify-content: space-between; + border-width: 1px; + border-color: transparent; + border-style: solid; + overflow: hidden; + + &__icon { + @include flex; + align-items: center; + } + + &__label { + color: $u-search-label-color; + font-size: $u-search-label-font-size; + margin: $u-search-label-margin; + } + + &__close { + width: $u-search-close-size; + height: $u-search-close-size; + border-top-left-radius: $u-search-close-radius; + border-top-right-radius: $u-search-close-radius; + border-bottom-left-radius: $u-search-close-radius; + border-bottom-right-radius: $u-search-close-radius; + background-color: $u-search-close-bgColor; + @include flex(row); + align-items: center; + justify-content: center; + transform: $u-search-close-transform; + } + + &__input { + flex: 1; + font-size: $u-search-input-font-size; + line-height: 1; + margin: $u-search-input-margin; + color: $u-search-input-color; + + &--placeholder { + color: $u-search-input-placeholder-color; + } + } + } + + &__action { + font-size: $u-search-action-font-size; + color: $u-search-action-color; + width: $u-search-action-width; + overflow: hidden; + transition-property: width; + transition-duration: 0.3s; + /* #ifndef APP-NVUE */ + white-space: nowrap; + /* #endif */ + text-align: center; + + &--active { + width: $u-search-action-active-width; + margin-left: $u-search-action-margin-left; + } + } +} +</style> diff --git a/uni_modules/uview-ui/components/u-skeleton/props.js b/uni_modules/uview-ui/components/u-skeleton/props.js new file mode 100644 index 0000000..ed3ba5a --- /dev/null +++ b/uni_modules/uview-ui/components/u-skeleton/props.js @@ -0,0 +1,59 @@ +export default { + props: { + // 是否展示骨架组件 + loading: { + type: Boolean, + default: uni.$u.props.skeleton.loading + }, + // 是否开启动画效果 + animate: { + type: Boolean, + default: uni.$u.props.skeleton.animate + }, + // 段落占位图行数 + rows: { + type: [String, Number], + default: uni.$u.props.skeleton.rows + }, + // 段落占位图的宽度 + rowsWidth: { + type: [String, Number, Array], + default: uni.$u.props.skeleton.rowsWidth + }, + // 段落占位图的高度 + rowsHeight: { + type: [String, Number, Array], + default: uni.$u.props.skeleton.rowsHeight + }, + // 是否展示标题占位图 + title: { + type: Boolean, + default: uni.$u.props.skeleton.title + }, + // 段落标题的宽度 + titleWidth: { + type: [String, Number], + default: uni.$u.props.skeleton.titleWidth + }, + // 段落标题的高度 + titleHeight: { + type: [String, Number], + default: uni.$u.props.skeleton.titleHeight + }, + // 是否展示头像占位图 + avatar: { + type: Boolean, + default: uni.$u.props.skeleton.avatar + }, + // 头像占位图大小 + avatarSize: { + type: [String, Number], + default: uni.$u.props.skeleton.avatarSize + }, + // 头像占位图的形状,circle-圆形,square-方形 + avatarShape: { + type: String, + default: uni.$u.props.skeleton.avatarShape + } + } +} diff --git a/uni_modules/uview-ui/components/u-skeleton/u-skeleton.vue b/uni_modules/uview-ui/components/u-skeleton/u-skeleton.vue new file mode 100644 index 0000000..efa649e --- /dev/null +++ b/uni_modules/uview-ui/components/u-skeleton/u-skeleton.vue @@ -0,0 +1,244 @@ +<template> + <view class="u-skeleton"> + <view + class="u-skeleton__wrapper" + ref="u-skeleton__wrapper" + v-if="loading" + style="display: flex; flex-direction: row;" + > + <view + class="u-skeleton__wrapper__avatar" + v-if="avatar" + :class="[`u-skeleton__wrapper__avatar--${avatarShape}`, animate && 'animate']" + :style="{ + height: $u.addUnit(avatarSize), + width: $u.addUnit(avatarSize) + }" + ></view> + <view + class="u-skeleton__wrapper__content" + ref="u-skeleton__wrapper__content" + style="flex: 1;" + > + <view + class="u-skeleton__wrapper__content__title" + v-if="title" + :style="{ + width: uTitleWidth, + height: $u.addUnit(titleHeight), + }" + :class="[animate && 'animate']" + ></view> + <view + class="u-skeleton__wrapper__content__rows" + :class="[animate && 'animate']" + v-for="(item, index) in rowsArray" + :key="index" + :style="{ + width: item.width, + height: item.height, + marginTop: item.marginTop + }" + > + + </view> + </view> + </view> + <slot v-else /> + </view> +</template> + +<script> + import props from './props.js'; + // #ifdef APP-NVUE + // 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度 + const dom = uni.requireNativePlugin('dom') + const animation = uni.requireNativePlugin('animation') + // #endif + /** + * Skeleton 骨架屏 + * @description 骨架屏一般用于页面在请求远程数据尚未完成时,页面用灰色块预显示本来的页面结构,给用户更好的体验。 + * @tutorial https://www.uviewui.com/components/skeleton.html + * @property {Boolean} loading 是否显示骨架占位图,设置为false将会展示子组件内容 (默认 true ) + * @property {Boolean} animate 是否开启动画效果 (默认 true ) + * @property {String | Number} rows 段落占位图行数 (默认 0 ) + * @property {String | Number | Array} rowsWidth 段落占位图的宽度,可以为百分比,数值,带单位字符串等,可通过数组传入指定每个段落行的宽度 (默认 '100%' ) + * @property {String | Number | Array} rowsHeight 段落的高度 (默认 18 ) + * @property {Boolean} title 是否展示标题占位图 (默认 true ) + * @property {String | Number} titleWidth 标题的宽度 (默认 '50%' ) + * @property {String | Number} titleHeight 标题的高度 (默认 18 ) + * @property {Boolean} avatar 是否展示头像占位图 (默认 false ) + * @property {String | Number} avatarSize 头像占位图大小 (默认 32 ) + * @property {String} avatarShape 头像占位图的形状,circle-圆形,square-方形 (默认 'circle' ) + * @example <u-search placeholder="日照香炉生紫烟" v-model="keyword"></u-search> + */ + export default { + name: 'u-skeleton', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + width: 0, + } + }, + watch: { + loading() { + this.getComponentWidth() + } + }, + computed: { + rowsArray() { + if (/%$/.test(this.rowsHeight)) { + uni.$u.error('rowsHeight参数不支持百分比单位') + } + const rows = [] + for (let i = 0; i < this.rows; i++) { + let item = {}, + // 需要预防超出数组边界的情况 + rowWidth = uni.$u.test.array(this.rowsWidth) ? (this.rowsWidth[i] || (i === this.row - 1 ? '70%' : '100%')) : i === + this.rows - 1 ? '70%' : this.rowsWidth, + rowHeight = uni.$u.test.array(this.rowsHeight) ? (this.rowsHeight[i] || '18px') : this.rowsHeight + // 如果有title占位图,第一个段落占位图的外边距需要大一些,如果没有title占位图,第一个段落占位图则无需外边距 + // 之所以需要这么做,是因为weex的无能,以提升性能为借口不支持css的一些伪类 + item.marginTop = !this.title && i === 0 ? 0 : this.title && i === 0 ? '20px' : '12px' + // 如果设置的为百分比的宽度,转换为px值,因为nvue不支持百分比单位 + if (/%$/.test(rowWidth)) { + // 通过parseInt提取出百分比单位中的数值部分,除以100得到百分比的小数值 + item.width = uni.$u.addUnit(this.width * parseInt(rowWidth) / 100) + } else { + item.width = uni.$u.addUnit(rowWidth) + } + item.height = uni.$u.addUnit(rowHeight) + rows.push(item) + } + // console.log(rows); + return rows + }, + uTitleWidth() { + let tWidth = 0 + if (/%$/.test(this.titleWidth)) { + // 通过parseInt提取出百分比单位中的数值部分,除以100得到百分比的小数值 + tWidth = uni.$u.addUnit(this.width * parseInt(this.titleWidth) / 100) + } else { + tWidth = uni.$u.addUnit(this.titleWidth) + } + return uni.$u.addUnit(tWidth) + }, + + }, + mounted() { + this.init() + }, + methods: { + init() { + this.getComponentWidth() + // #ifdef APP-NVUE + this.loading && this.animate && this.setNvueAnimation() + // #endif + }, + async setNvueAnimation() { + // #ifdef APP-NVUE + // 为了让opacity:1的状态保持一定时间,这里做一个延时 + await uni.$u.sleep(500) + const skeleton = this.$refs['u-skeleton__wrapper']; + skeleton && this.loading && this.animate && animation.transition(skeleton, { + styles: { + opacity: 0.5 + }, + duration: 600, + }, () => { + // 这里无需判断是否loading和开启动画状态,因为最终的状态必须达到opacity: 1,否则可能 + // 会停留在opacity: 0.5的状态中 + animation.transition(skeleton, { + styles: { + opacity: 1 + }, + duration: 600, + }, () => { + // 只有在loading中时,才执行动画 + this.loading && this.animate && this.setNvueAnimation() + }) + }) + // #endif + }, + // 获取组件的宽度 + async getComponentWidth() { + // 延时一定时间,以获取dom尺寸 + await uni.$u.sleep(20) + // #ifndef APP-NVUE + this.$uGetRect('.u-skeleton__wrapper__content').then(size => { + this.width = size.width + }) + // #endif + + // #ifdef APP-NVUE + const ref = this.$refs['u-skeleton__wrapper__content'] + ref && dom.getComponentRect(ref, (res) => { + this.width = res.size.width + }) + // #endif + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + @mixin background { + /* #ifdef APP-NVUE */ + background-color: #F1F2F4; + /* #endif */ + /* #ifndef APP-NVUE */ + background: linear-gradient(90deg, #F1F2F4 25%, #e6e6e6 37%, #F1F2F4 50%); + background-size: 400% 100%; + /* #endif */ + } + + .u-skeleton { + flex: 1; + + &__wrapper { + @include flex(row); + + &__avatar { + @include background; + margin-right: 15px; + + &--circle { + border-radius: 100px; + } + + &--square { + border-radius: 4px; + } + } + + &__content { + flex: 1; + + &__rows, + &__title { + @include background; + border-radius: 3px; + } + } + } + } + + /* #ifndef APP-NVUE */ + .animate { + animation: skeleton 1.8s ease infinite + } + + @keyframes skeleton { + 0% { + background-position: 100% 50% + } + + 100% { + background-position: 0 50% + } + } + + /* #endif */ +</style> diff --git a/uni_modules/uview-ui/components/u-slider/mpother.js b/uni_modules/uview-ui/components/u-slider/mpother.js new file mode 100644 index 0000000..040c848 --- /dev/null +++ b/uni_modules/uview-ui/components/u-slider/mpother.js @@ -0,0 +1,113 @@ +/** + * 使用普通的js方案实现slider + */ +export default { + watch: { + value(n) { + // 只有在非滑动状态时,才可以通过value更新滑块值,这里监听,是为了让用户触发 + if (this.status === 'end') { + this.updateSliderPlacement(n, true) + } + } + }, + mounted() { + this.init() + }, + methods: { + init() { + this.getSliderRect() + }, + // 获取slider尺寸 + getSliderRect() { + // 获取滑块条的尺寸信息 + setTimeout(() => { + this.$uGetRect('.u-slider').then((rect) => { + this.sliderRect = rect + this.updateSliderPlacement(this.value, true) + }) + }, 10) + }, + // 是否可以操作 + canNotDo() { + return this.disabled + }, + // 获取当前手势点的X轴位移值 + getTouchX(e) { + return e.touches[0].clientX + }, + formatStep(value) { + // 移动点占总长度的百分比 + return Math.round(Math.max(this.min, Math.min(value, this.max)) / this.step) * this.step + }, + // 发出事件 + emitEvent(event, value) { + this.$emit(event, value || this.value) + }, + // 标记当前手势的状态 + setTouchStatus(status) { + this.status = status + }, + onTouchStart(e) { + if (this.canNotDo()) { + return + } + // 标示当前的状态为开始触摸滑动 + this.emitEvent('start') + this.setTouchStatus('start') + }, + onTouchMove(e) { + if (this.canNotDo()) { + return + } + // 滑块的左边不一定跟屏幕左边接壤,所以需要减去最外层父元素的左边值 + const x = this.getTouchX(e) + const { left, width } = this.sliderRect + const distanceX = x - left + // 获得移动距离对整个滑块的百分比值,此为带有多位小数的值,不能用此更新视图 + // 否则造成通信阻塞,需要每改变一个step值时修改一次视图 + const percent = (distanceX / width) * 100 + this.setTouchStatus('moving') + this.updateSliderPlacement(percent, true, 'moving') + }, + onTouchEnd() { + if (this.canNotDo()) { + return + } + this.emitEvent('end') + this.setTouchStatus('end') + }, + // 设置滑点的位置 + updateSliderPlacement(value, drag, event) { + // 去掉小数部分,同时也是对step步进的处理 + const { width } = this.sliderRect + const percent = this.formatStep(value) + // 设置移动的值 + const barStyle = { + width: `${percent / 100 * width}px` + } + // 移动期间无需过渡动画 + if (drag === true) { + barStyle.transition = 'none' + } else { + // 非移动期间,删掉对过渡为空的声明,让css中的声明起效 + delete barStyle.transition + } + // 修改value值 + this.$emit('input', percent) + // 事件的名称 + if (event) { + this.emitEvent(event, percent) + } + this.barStyle = barStyle + }, + onClick(e) { + if (this.canNotDo()) { + return + } + // 直接点击滑块的情况,计算方式与onTouchMove方法相同 + const { left, width } = this.sliderRect + const value = ((e.detail.x - left) / width) * 100 + this.updateSliderPlacement(value, false, 'click') + } + } +} diff --git a/uni_modules/uview-ui/components/u-slider/mpwxs.js b/uni_modules/uview-ui/components/u-slider/mpwxs.js new file mode 100644 index 0000000..f263911 --- /dev/null +++ b/uni_modules/uview-ui/components/u-slider/mpwxs.js @@ -0,0 +1,42 @@ +export default { + data() { + return { + sliderRect: {}, + info: { + width: null, + left: null, + step: this.step, + disabled: this.disabled, + min: this.min, + max: this.max, + value: this.value + } + } + }, + mounted() { + this.init() + }, + methods: { + init() { + this.getSliderRect() + }, + // 获取slider尺寸 + getSliderRect() { + // 获取滑块条的尺寸信息 + uni.$u.sleep().then(() => { + this.$uGetRect('.u-slider').then((rect) => { + this.info.width = rect.width + this.info.left = rect.left + }) + }) + }, + // 此方法由wxs调用,用于修改v-model绑定的值 + updateValue(value) { + this.$emit('input', value) + }, + // 此方法由wxs调用,发出事件 + emitEvent(e) { + this.$emit(e.event, e.value ? e.value : this.value) + } + } +} diff --git a/uni_modules/uview-ui/components/u-slider/mpwxs.wxs b/uni_modules/uview-ui/components/u-slider/mpwxs.wxs new file mode 100644 index 0000000..847df4a --- /dev/null +++ b/uni_modules/uview-ui/components/u-slider/mpwxs.wxs @@ -0,0 +1,121 @@ +/** + * 使用wxs方案实现slider + * 兼容微信,QQ,H5,Vue版的安卓和iOS + */ +/** + * 开始滑动操作 + * @param {Object} e + * @param {Object} ownerInstance + */ +function onTouchMove(e, ownerInstance) { + // wxs事件对象下有一个instance属性,表示当前触发此事件的组件的实例,通过该实例,可以获取相关的dataset,设置样式等信息 + // https://developers.weixin.qq.com/miniprogram/dev/framework/view/interactive-animation.html + var instance = e.instance; + // getState()为一个对象,挂载在instance上,类似组件的data一样,可以存放一些变量,供以后的触发事件中使用 + var state = instance.getState() + + // 滑块组件的整体尺寸信息 + var mp = state.mp + if(mp.disabled) { + return + } + + var distanceX = getTouchX(e) - mp.left + // 获得移动距离对整个滑块的百分比值,此为带有多位小数的值,step大于1时,不能用此更新视图 + var percent = (distanceX / mp.width) * 100 + + updateSliderPlacement(instance, ownerInstance, percent, 'moving') + + // 阻止页面滚动,可以保证在滑动过程中,不让页面可以上下滚动,造成不好的体验 + e.stopPropagation && e.stopPropagation() + e.preventDefault && e.preventDefault() +} + +function onClick(e, ownerInstance) { + var instance = e.instance + var state = instance.getState() + var mp = state.mp + if(mp.disabled) { + return + } + + // 直接点击滑块的情况,计算方式与onTouchMove方法相同 + var value = ((e.detail.x - mp.left) / mp.width) * 100 + updateSliderPlacement(instance, ownerInstance, value, 'click') +} + +function sizeReady(newValue, oldValue, ownerInstance, instance) { + // 页面初始化时候,也会触发此方法,传递的值为空,这里不执行往后的逻辑 + if(!newValue || newValue.disabled) { + return + } + var state = instance.getState() + state.mp = newValue + updateSliderPlacement(instance, ownerInstance, newValue.value) +} + +// 设置滑点的位置 +function updateSliderPlacement(instance, ownerInstance, value, event) { + var state = instance.getState() + var mp = state.mp + if(mp.disabled) { + return + } + + var percent = 0 + if (mp.step > 1) { + // 如果step步进大于1,需要跳步,所以需要使用Math.round进行取整 + percent = Math.round(Math.max(mp.min, Math.min(value, mp.max)) / mp.step) * mp.step + } else { + // 当step=1时,无需跳步,充分利用wxs性能,滑块实时跟随手势,达到丝滑的效果 + percent = Math.max(mp.min, Math.min(value, mp.max)) + } + // 返回组件的实例 + var gapInstance = ownerInstance.selectComponent('.u-slider__gap') + // 在移动期间,不允许transition动画,否则会造成卡顿 + gapInstance[event === 'click' ? 'addClass' : 'removeClass']('u-slider__gap--ani') + // 调用逻辑层的方法,修改v-model绑定的值 + ownerInstance.callMethod('updateValue', Math.round(percent)) + if(event) { + ownerInstance.callMethod('emitEvent', { + event: event, + value: Math.round(percent) + }) + } + + // 设置移动的值 + gapInstance.requestAnimationFrame(function() { + gapInstance.setStyle({ + width: percent / 100 * mp.width + 'px', + }) + }) +} + +// 开始滑动 +function onTouchStart(e, ownerInstance) { + ownerInstance.callMethod('emitEvent', { + event: 'start', + value: null + }) +} + +// 停止滑动 +function onTouchEnd(e, ownerInstance) { + ownerInstance.callMethod('emitEvent', { + event: 'end', + value: null + }) +} + +// 获取当前手势点的X轴位移值 +function getTouchX(e) { + return e.touches[0].clientX +} + +module.exports = { + onTouchStart: onTouchStart, + onTouchMove: onTouchMove, + onTouchEnd: onTouchEnd, + sizeReady: sizeReady, + onClick: onClick +} diff --git a/uni_modules/uview-ui/components/u-slider/nvue - 副本.js b/uni_modules/uview-ui/components/u-slider/nvue - 副本.js new file mode 100644 index 0000000..df62349 --- /dev/null +++ b/uni_modules/uview-ui/components/u-slider/nvue - 副本.js @@ -0,0 +1,180 @@ +/** + * 使用bindingx方案实现slider + * 只能使用于nvue下 + */ +// 引入bindingx,此库类似于微信小程序wxs,目的是让js运行在视图层,减少视图层和逻辑层的通信折损 +const BindingX = uni.requireNativePlugin('bindingx') +// nvue操作dom的库,用于获取dom的尺寸信息 +const dom = uni.requireNativePlugin('dom') +// nvue中用于操作元素动画的库,类似于uni.animation,只不过uni.animation不能用于nvue +const animation = uni.requireNativePlugin('animation') + +export default { + data() { + return { + // bindingx的回调值,用于取消绑定 + panEvent: null, + // 标记是否移动状态 + moving: false, + // 位移的偏移量 + x: 0, + // 是否正在触摸过程中,用于标记动画类是否添加或移除 + touching: false, + changeFromInside: false + } + }, + watch: { + // 监听vlaue的变化,此变化可能是由于内部修改v-model的值,或者外部 + // 从服务端获取一个值后,赋值给slider的v-model而导致的 + value(n) { + if (!this.changeFromInside) { + this.initX() + } else { + this.changeFromInside = false + } + } + }, + mounted() { + this.init() + }, + methods: { + init() { + this.getSliderRect() + }, + // 获取节点信息 + // 获取slider尺寸 + getSliderRect() { + // 获取滑块条的尺寸信息 + // 通过nvue的dom模块,查询节点信息 + setTimeout(() => { + dom.getComponentRect(this.$refs['slider'], res => { + this.sliderRect = res.size + this.initX() + }) + }, 10) + }, + // 初始化按钮位置 + initButtonStyle({ + barStyle, + buttonWrapperStyle + }) { + this.barStyle = barStyle + this.buttonWrapperStyle = buttonWrapperStyle + }, + emitEvent(event, value) { + this.$emit(event, value ? value : this.value) + }, + formatStep(value) { + // 移动点占总长度的百分比 + return Math.round(Math.max(this.min, Math.min(value, this.max)) / this.step) * this.step + }, + // 滑动开始 + onTouchStart(e) { + // 阻止页面滚动,可以保证在滑动过程中,不让页面可以上下滚动,造成不好的体验 + e.stopPropagation && e.stopPropagation() + e.preventDefault && e.preventDefault() + if (this.moving || this.disabled) { + // 释放上一次的资源 + if (this.panEvent?.token != 0) { + BindingX.unbind({ + token: this.panEvent.token, + // pan为手势事件 + eventType: 'pan' + }) + this.gesToken = 0 + } + return + } + + this.moving = true + this.touching = true + + // 获取元素ref + const button = this.$refs['nvue-button'].ref + const gap = this.$refs['nvue-gap'].ref + + const { + min, + max, + step + } = this + const { + left, + width + } = this.sliderRect + + // 初始值为本次偏移量x,加上次停止滑动时的结束值 + let exporession = `(${this.x} + x)` + // 将偏移的x值,转为总位移的百分比值,为了和min和max进行判断 + exporession = `(${exporession} / ${width}) * 100` + if (step > 1) { + // 如果step步进大于1,需要跳步,所以需要使用Math.round进行取整 + exporession = `round(max(${min}, min(${exporession}, ${max})) / ${step}) * ${step}` + } else { + // 当step=1时,无需跳步,充分利用bindingx性能,滑块实时跟随手势,达到丝滑的效果 + exporession = `max(${min}, min(${exporession}, ${max}))` + } + // 将百分比最后转化为对应的px值 + exporession = `${exporession} / 100 * ${width}` + // 最大值不允许超过轨迹的宽度 + const { + sliderWidth + } = this.sliderRect + exporession = `min(${sliderWidth}, ${exporession})` + // 滑块点总是需要一个左偏移的值,为自身宽度的一半 + const buttonExpression = `${exporession} - ${this.blockHeight / 2}` + // 阿里为了KPI而开源的BindingX + this.panEvent = BindingX.bind({ + anchor: button, + eventType: 'pan', + props: [{ + element: gap, + // 绑定width属性,设置其宽度值 + property: 'width', + expression + }, { + element: button, + // 绑定width属性,设置其宽度值 + property: 'transform.translateX', + expression: buttonExpression + }] + }, (e) => { + if (e.state === 'end' || e.state === 'exit') { + // + this.x = uni.$u.range(0, left + width, e.deltaX + this.x) + // 根据偏移值,得出移动的百分比,进而修改双向绑定的v-model的值 + const value = (this.x / width) * 100 + const percent = this.formatStep(value) + // 修改value值 + this.$emit('input', percent) + // 标记下一次触发value的watch时,这个值的变化,是由内部改变的 + this.changeFromInside = true + this.moving = false + this.touching = false + } + }) + }, + // 从value的变化,倒推得出x的值该为多少 + initX() { + const { + left, + width + } = this.sliderRect + // 得出x的初始偏移值,之所以需要这么做,是因为在bindingX中,触摸滑动时,只能的值本次移动的偏移值 + // 而无法的值准确的前后移动的两个点的坐标值,weex纯粹为阿里巴巴的KPI(部门业绩考核)产物,也就这样了 + this.x = this.value / 100 * width + // 设置移动的值 + const barStyle = { + width: this.x + 'px' + } + // 按钮的初始值 + const buttonWrapperStyle = { + transform: `translateX(${this.x - this.blockHeight / 2}px)` + } + this.initButtonStyle({ + barStyle, + buttonWrapperStyle + }) + } + } +} diff --git a/uni_modules/uview-ui/components/u-slider/nvue.js b/uni_modules/uview-ui/components/u-slider/nvue.js new file mode 100644 index 0000000..344dce8 --- /dev/null +++ b/uni_modules/uview-ui/components/u-slider/nvue.js @@ -0,0 +1,193 @@ +/** + * 使用bindingx方案实现slider + * 只能使用于nvue下 + */ +// 引入bindingx,此库类似于微信小程序wxs,目的是让js运行在视图层,减少视图层和逻辑层的通信折损 +const BindingX = uni.requireNativePlugin('bindingx') +// nvue操作dom的库,用于获取dom的尺寸信息 +const dom = uni.requireNativePlugin('dom') +// nvue中用于操作元素动画的库,类似于uni.animation,只不过uni.animation不能用于nvue +const animation = uni.requireNativePlugin('animation') + +export default { + data() { + return { + // 位移的偏移量 + x: 0, + // 是否正在触摸过程中,用于标记动画类是否添加或移除 + touching: false, + changeFromInside: false + } + }, + watch: { + // 监听vlaue的变化,此变化可能是由于内部修改v-model的值,或者外部 + // 从服务端获取一个值后,赋值给slider的v-model而导致的 + value(n) { + if (!this.changeFromInside) { + this.initX() + } else { + this.changeFromInside = false + } + } + }, + mounted() { + this.init() + }, + methods: { + init() { + // 更新滑块尺寸信息 + this.getSliderRect().then((size) => { + this.sliderRect = size + this.initX() + }) + }, + // 获取节点信息 + // 获取slider尺寸 + getSliderRect() { + // 获取滑块条的尺寸信息 + // 通过nvue的dom模块,查询节点信息 + return new Promise((resolve) => { + this.$nextTick(() => { + dom.getComponentRect(this.$refs.slider, (res) => { + resolve(res.size) + }) + }) + }) + }, + // 初始化按钮位置 + initButtonStyle({ + barStyle, + buttonWrapperStyle + }) { + this.barStyle = barStyle + this.buttonWrapperStyle = buttonWrapperStyle + }, + emitEvent(event, value) { + this.$emit(event, value || this.value) + }, + // 滑动开始 + async onTouchStart(e) { + // if (this.disabled) return + // // 阻止页面滚动,可以保证在滑动过程中,不让页面可以上下滚动,造成不好的体验 + // e.stopPropagation && e.stopPropagation() + // e.preventDefault && e.preventDefault() + // // 更新滑块的尺寸信息 + // this.sliderRect = await this.getSliderRect() + // // 标记滑动过程中触摸点的信息 + // this.touchStart(e) + // this.startValue = this.format(this.value) + // this.dragStatus = 'start' + + // 标记滑动过程中触摸点的信息 + // this.touchStart(e) + }, + // 开始滑动 + onTouchMove(e) { + // if (this.disabled) return; + // if (this.dragStatus === 'start') { + // this.$emit('drag-start') + // } + // // 标记当前滑动过程中的触点信息,此方法在touch mixin中 + // this.touchMove(e) + // this.dragStatus = 'draging' + // const { + // width: sliderWidth + // } = this.sliderRect + // const diff = (this.deltaX / sliderWidth) * this.getRange() + // this.newValue = this.startValue + diff + // this.updateValue(this.newValue, false, true) + // 获取元素ref + // const button = this.$refs['nvue-button'].ref + // const gap = this.$refs['nvue-gap'].ref + + // animation.transition(gap, { + // styles: { + // width: `${this.startX + this.deltaX}px` + // } + // }) + // // console.log(this.startX + this.deltaX); + // animation.transition(button, { + // styles: { + // transform: `translateX(${this.startX + this.deltaX}px)` + // } + // }) + // this.barStyle = { + // width: `${this.startX + this.deltaX}px` + // } + const { + x + } = this.getTouchPoint(e) + this.buttonWrapperStyle = { + transform: `translateX(${x}px)` + } + // this.buttonWrapperStyle = { + // transform: `translateX(${this.format(this.startX + this.deltaX)}px)` + // } + }, + // onTouchEnd() { + // if (this.disabled) return; + // if (this.dragStatus === 'draging') { + // this.updateValue(this.newValue, true) + // this.$emit('drag-end'); + // } + // }, + updateValue(value, end, drag) { + value = this.format(value) + const { + width: sliderWidth + } = this.sliderRect + const width = `${((value - this.min) * sliderWidth) / this.getRange()}` + this.value = value + this.barStyle = { + width: `${width}px` + } + // console.log('width', width); + if (drag) { + this.$emit('drag', { + value + }) + } + if (end) { + this.$emit('change', value) + } + if ((drag || end)) { + this.changeFromInside = true + this.$emit('update', value) + } + }, + // 从value的变化,倒推得出x的值该为多少 + initX() { + const { + left, + width + } = this.sliderRect + // 得出x的初始偏移值,之所以需要这么做,是因为在bindingX中,触摸滑动时,只能的值本次移动的偏移值 + // 而无法的值准确的前后移动的两个点的坐标值,weex纯粹为阿里巴巴的KPI(部门业绩考核)产物,也就这样了 + this.x = this.value / 100 * width + // 设置移动的值 + const barStyle = { + width: `${this.x}px` + } + // 按钮的初始值 + const buttonWrapperStyle = { + transform: `translateX(${this.x - this.blockHeight / 2}px)` + } + this.initButtonStyle({ + barStyle, + buttonWrapperStyle + }) + }, + // 移动点占总长度的百分比,此处需要先除以step,是为了保证step大于1时,比如10,那么在滑动11,12px这样的 + // 距离时,实际上滑块是不会滑动的,到了16,17px,经过四舍五入后,就变成了20px,进行了下一个跳变 + format(value) { + return Math.round(uni.$u.range(this.min, this.max, value) / this.step) * this.step + }, + getRange() { + const { + max, + min + } = this + return max - min + } + } +} diff --git a/uni_modules/uview-ui/components/u-slider/props.js b/uni_modules/uview-ui/components/u-slider/props.js new file mode 100644 index 0000000..433a7b5 --- /dev/null +++ b/uni_modules/uview-ui/components/u-slider/props.js @@ -0,0 +1,54 @@ +export default { + props: { + // 最小可选值 + min: { + type: [Number, String], + default: uni.$u.props.slider.min + }, + // 最大可选值 + max: { + type: [Number, String], + default: uni.$u.props.slider.max + }, + // 步长,取值必须大于 0,并且可被(max - min)整除 + step: { + type: [Number, String], + default: uni.$u.props.slider.step + }, + // 当前取值 + value: { + type: [Number, String], + default: uni.$u.props.slider.value + }, + // 滑块右侧已选择部分的背景色 + activeColor: { + type: String, + default: uni.$u.props.slider.activeColor + }, + // 滑块左侧未选择部分的背景色 + inactiveColor: { + type: String, + default: uni.$u.props.slider.inactiveColor + }, + // 滑块的大小,取值范围为 12 - 28 + blockSize: { + type: [Number, String], + default: uni.$u.props.slider.blockSize + }, + // 滑块的颜色 + blockColor: { + type: String, + default: uni.$u.props.slider.blockColor + }, + // 禁用状态 + disabled: { + type: Boolean, + default: uni.$u.props.slider.disabled + }, + // 是否显示当前的选择值 + showValue: { + type: Boolean, + default: uni.$u.props.slider.showValue + } + } +} diff --git a/uni_modules/uview-ui/components/u-slider/u-slider.vue b/uni_modules/uview-ui/components/u-slider/u-slider.vue new file mode 100644 index 0000000..80ebbed --- /dev/null +++ b/uni_modules/uview-ui/components/u-slider/u-slider.vue @@ -0,0 +1,55 @@ +<template> + <view + class="u-slider" + :style="[$u.addStyle(customStyle)]" + > + <slider + :min="min" + :max="max" + :step="step" + :value="value" + :activeColor="activeColor" + :inactiveColor="inactiveColor" + :blockSize="$u.getPx(blockSize)" + :blockColor="blockColor" + :showValue="showValue" + :disabled="disabled" + @changing="changingHandler" + @change="changeHandler" + ></slider> + </view> +</template> + +<script> + import props from './props.js' + export default { + name: 'u--slider', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + methods: { + // 拖动过程中触发 + changingHandler(e) { + const { + value + } = e.detail + // 更新v-model的值 + this.$emit('input', value) + // 触发事件 + this.$emit('changing', value) + }, + // 滑动结束时触发 + changeHandler(e) { + const { + value + } = e.detail + // 更新v-model的值 + this.$emit('input', value) + // 触发事件 + this.$emit('change', value) + } + }, + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; +</style> diff --git a/uni_modules/uview-ui/components/u-status-bar/props.js b/uni_modules/uview-ui/components/u-status-bar/props.js new file mode 100644 index 0000000..64b9e63 --- /dev/null +++ b/uni_modules/uview-ui/components/u-status-bar/props.js @@ -0,0 +1,8 @@ +export default { + props: { + bgColor: { + type: String, + default: uni.$u.props.statusBar.bgColor + } + } +} diff --git a/uni_modules/uview-ui/components/u-status-bar/u-status-bar.vue b/uni_modules/uview-ui/components/u-status-bar/u-status-bar.vue new file mode 100644 index 0000000..ed91373 --- /dev/null +++ b/uni_modules/uview-ui/components/u-status-bar/u-status-bar.vue @@ -0,0 +1,46 @@ +<template> + <view + :style="[style]" + class="u-status-bar" + > + <slot /> + </view> +</template> + +<script> + import props from './props.js'; + /** + * StatbusBar 状态栏占位 + * @description 本组件主要用于状态填充,比如在自定导航栏的时候,它会自动适配一个恰当的状态栏高度。 + * @tutorial https://uviewui.com/components/statusBar.html + * @property {String} bgColor 背景色 (默认 'transparent' ) + * @property {String | Object} customStyle 自定义样式 + * @example <u-status-bar></u-status-bar> + */ + export default { + name: 'u-status-bar', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + } + }, + computed: { + style() { + const style = {} + // 状态栏高度,由于某些安卓和微信开发工具无法识别css的顶部状态栏变量,所以使用js获取的方式 + style.height = uni.$u.addUnit(uni.$u.sys().statusBarHeight, 'px') + style.backgroundColor = this.bgColor + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) + } + }, + } +</script> + +<style lang="scss" scoped> + .u-status-bar { + // nvue会默认100%,如果nvue下,显式写100%的话,会导致宽度不为100%而异常 + /* #ifndef APP-NVUE */ + width: 100%; + /* #endif */ + } +</style> diff --git a/uni_modules/uview-ui/components/u-steps-item/props.js b/uni_modules/uview-ui/components/u-steps-item/props.js new file mode 100644 index 0000000..825727a --- /dev/null +++ b/uni_modules/uview-ui/components/u-steps-item/props.js @@ -0,0 +1,24 @@ +export default { + props: { + // 标题 + title: { + type: [String, Number], + default: uni.$u.props.stepsItem.title + }, + // 描述文本 + desc: { + type: [String, Number], + default: uni.$u.props.stepsItem.desc + }, + // 图标大小 + iconSize: { + type: [String, Number], + default: uni.$u.props.stepsItem.iconSize + }, + // 当前步骤是否处于失败状态 + error: { + type: Boolean, + default: uni.$u.props.stepsItem.error + } + } +} diff --git a/uni_modules/uview-ui/components/u-steps-item/u-steps-item.vue b/uni_modules/uview-ui/components/u-steps-item/u-steps-item.vue new file mode 100644 index 0000000..342fa63 --- /dev/null +++ b/uni_modules/uview-ui/components/u-steps-item/u-steps-item.vue @@ -0,0 +1,316 @@ +<template> + <view class="u-steps-item" ref="u-steps-item" :class="[`u-steps-item--${parentData.direction}`]"> + <view class="u-steps-item__line" v-if="index + 1 < childLength" + :class="[`u-steps-item__line--${parentData.direction}`]" :style="[lineStyle]"></view> + <view class="u-steps-item__wrapper" + :class="[`u-steps-item__wrapper--${parentData.direction}`, parentData.dot && `u-steps-item__wrapper--${parentData.direction}--dot`]"> + <slot name="icon"> + <view class="u-steps-item__wrapper__dot" v-if="parentData.dot" :style="{ + backgroundColor: statusColor + }"> + + </view> + <view class="u-steps-item__wrapper__icon" v-else-if="parentData.activeIcon || parentData.inactiveIcon"> + <u-icon :name="index <= parentData.current ? parentData.activeIcon : parentData.inactiveIcon" + :size="iconSize" + :color="index <= parentData.current ? parentData.activeColor : parentData.inactiveColor"> + </u-icon> + </view> + <view v-else :style="{ + backgroundColor: statusClass === 'process' ? parentData.activeColor : 'transparent', + borderColor: statusColor + }" class="u-steps-item__wrapper__circle"> + <text v-if="statusClass === 'process' || statusClass === 'wait'" + class="u-steps-item__wrapper__circle__text" :style="{ + color: index == parentData.current ? '#ffffff' : parentData.inactiveColor + }">{{ index + 1}}</text> + <u-icon v-else :color="statusClass === 'error' ? 'error' : parentData.activeColor" size="12" + :name="statusClass === 'error' ? 'close' : 'checkmark'"></u-icon> + </view> + </slot> + </view> + <view class="u-steps-item__content" :class="[`u-steps-item__content--${parentData.direction}`]" + :style="[contentStyle]"> + <u--text :text="title" :type="parentData.current == index ? 'main' : 'content'" lineHeight="20px" + :size="parentData.current == index ? 14 : 13"></u--text> + <slot name="desc"> + <u--text :text="desc" type="tips" size="12"></u--text> + </slot> + </view> + <!-- <view + class="u-steps-item__line" + v-if="showLine && parentData.direction === 'column'" + :class="[`u-steps-item__line--${parentData.direction}`]" + :style="[lineStyle]" + ></view> --> + </view> +</template> + +<script> + import props from './props.js'; + // #ifdef APP-NVUE + const dom = uni.requireNativePlugin('dom') + // #endif + /** + * StepsItem 步骤条的子组件 + * @description 本组件需要和u-steps配合使用 + * @tutorial https://uviewui.com/components/steps.html + * @property {String} title 标题文字 + * @property {String} current 描述文本 + * @property {String | Number} iconSize 图标大小 (默认 17 ) + * @property {Boolean} error 当前步骤是否处于失败状态 (默认 false ) + * @example <u-steps current="0"><u-steps-item title="已出库" desc="10:35" ></u-steps-item></u-steps> + */ + export default { + name: 'u-steps-item', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + index: 0, + childLength: 0, + showLine: false, + size: { + height: 0, + width: 0 + }, + parentData: { + direction: 'row', + current: 0, + activeColor: '', + inactiveColor: '', + activeIcon: '', + inactiveIcon: '', + dot: false + } + } + }, + watch: { + 'parentData'(newValue, oldValue) { + } + }, + created() { + this.init() + }, + computed: { + lineStyle() { + const style = {} + if (this.parentData.direction === 'row') { + style.width = this.size.width + 'px' + style.left = this.size.width / 2 + 'px' + } else { + style.height = this.size.height + 'px' + // style.top = this.size.height / 2 + 'px' + } + style.backgroundColor = this.parent.children?.[this.index + 1]?.error ? uni.$u.color.error : this.index < + this + .parentData + .current ? this.parentData.activeColor : this.parentData.inactiveColor + return style + }, + statusClass() { + const { + index, + error + } = this + const { + current + } = this.parentData + if (current == index) { + return error === true ? 'error' : 'process' + } else if (error) { + return 'error' + } else if (current > index) { + return 'finish' + } else { + return 'wait' + } + }, + statusColor() { + let color = '' + switch (this.statusClass) { + case 'finish': + color = this.parentData.activeColor + break + case 'error': + color = uni.$u.color.error + break + case 'process': + color = this.parentData.dot ? this.parentData.activeColor : 'transparent' + break + default: + color = this.parentData.inactiveColor + break + } + return color + }, + contentStyle() { + const style = {} + if (this.parentData.direction === 'column') { + style.marginLeft = this.parentData.dot ? '2px' : '6px' + style.marginTop = this.parentData.dot ? '0px' : '6px' + } else { + style.marginTop = this.parentData.dot ? '2px' : '6px' + style.marginLeft = this.parentData.dot ? '2px' : '6px' + } + + return style + } + }, + mounted() { + this.parent && this.parent.updateFromChild() + uni.$u.sleep().then(() => { + this.getStepsItemRect() + }) + }, + methods: { + init() { + // 初始化数据 + this.updateParentData() + if (!this.parent) { + return uni.$u.error('u-steps-item必须要搭配u-steps组件使用') + } + this.index = this.parent.children.indexOf(this) + this.childLength = this.parent.children.length + }, + updateParentData() { + // 此方法在mixin中 + this.getParentData('u-steps') + }, + // 父组件数据发生变化 + updateFromParent() { + this.init() + }, + // 获取组件的尺寸,用于设置横线的位置 + getStepsItemRect() { + // #ifndef APP-NVUE + this.$uGetRect('.u-steps-item').then(size => { + this.size = size + }) + // #endif + + // #ifdef APP-NVUE + dom.getComponentRect(this.$refs['u-steps-item'], res => { + const { + size + } = res + this.size = size + }) + // #endif + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-steps-item { + flex: 1; + @include flex; + + &--row { + flex-direction: column; + align-items: center; + position: relative; + } + + &--column { + position: relative; + flex-direction: row; + justify-content: flex-start; + padding-bottom: 5px; + } + + &__wrapper { + @include flex; + justify-content: center; + align-items: center; + position: relative; + background-color: #fff; + + &--column { + width: 20px; + height: 32px; + + &--dot { + height: 20px; + width: 20px; + } + } + + &--row { + width: 32px; + height: 20px; + + &--dot { + width: 20px; + height: 20px; + } + } + + &__circle { + width: 20px; + height: 20px; + /* #ifndef APP-NVUE */ + box-sizing: border-box; + flex-shrink: 0; + /* #endif */ + border-radius: 100px; + border-width: 1px; + border-color: $u-tips-color; + border-style: solid; + @include flex(row); + align-items: center; + justify-content: center; + transition: background-color 0.3s; + + &__text { + color: $u-tips-color; + font-size: 11px; + @include flex(row); + align-items: center; + justify-content: center; + text-align: center; + line-height: 11px; + } + } + + &__dot { + width: 10px; + height: 10px; + border-radius: 100px; + background-color: $u-content-color; + } + } + + &__content { + @include flex; + flex: 1; + + &--row { + flex-direction: column; + align-items: center; + } + + &--column { + flex-direction: column; + margin-left: 6px; + } + } + + &__line { + position: absolute; + background: $u-tips-color; + + &--row { + top: 10px; + height: 1px; + } + + &--column { + width: 1px; + left: 10px; + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-steps/props.js b/uni_modules/uview-ui/components/u-steps/props.js new file mode 100644 index 0000000..6d4c768 --- /dev/null +++ b/uni_modules/uview-ui/components/u-steps/props.js @@ -0,0 +1,39 @@ +export default { + props: { + // 排列方向 + direction: { + type: String, + default: uni.$u.props.steps.direction + }, + // 设置第几个步骤 + current: { + type: [String, Number], + default: uni.$u.props.steps.current + }, + // 激活状态颜色 + activeColor: { + type: String, + default: uni.$u.props.steps.activeColor + }, + // 未激活状态颜色 + inactiveColor: { + type: String, + default: uni.$u.props.steps.inactiveColor + }, + // 激活状态的图标 + activeIcon: { + type: String, + default: uni.$u.props.steps.activeIcon + }, + // 未激活状态图标 + inactiveIcon: { + type: String, + default: uni.$u.props.steps.inactiveIcon + }, + // 是否显示点类型 + dot: { + type: Boolean, + default: uni.$u.props.steps.dot + } + } +} diff --git a/uni_modules/uview-ui/components/u-steps/u-steps.vue b/uni_modules/uview-ui/components/u-steps/u-steps.vue new file mode 100644 index 0000000..3ab7764 --- /dev/null +++ b/uni_modules/uview-ui/components/u-steps/u-steps.vue @@ -0,0 +1,80 @@ +<template> + <view + class="u-steps" + :class="[`u-steps--${direction}`]" + > + <slot></slot> + </view> +</template> + +<script> + import props from './props.js'; + /** + * Steps 步骤条 + * @description 该组件一般用于完成一个任务要分几个步骤,标识目前处于第几步的场景。 + * @tutorial https://uviewui.com/components/steps.html + * @property {String} direction row-横向,column-竖向 (默认 'row' ) + * @property {String | Number} current 设置当前处于第几步 (默认 0 ) + * @property {String} activeColor 激活状态颜色 (默认 '#3c9cff' ) + * @property {String} inactiveColor 未激活状态颜色 (默认 '#969799' ) + * @property {String} activeIcon 激活状态的图标 + * @property {String} inactiveIcon 未激活状态图标 + * @property {Boolean} dot 是否显示点类型 (默认 false ) + * @example <u-steps current="0"><u-steps-item title="已出库" desc="10:35" ></u-steps-item></u-steps> + */ + export default { + name: 'u-steps', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + } + }, + watch: { + children() { + this.updateChildData() + }, + parentData() { + this.updateChildData() + } + }, + computed: { + // 监听参数的变化,通过watch中,手动去更新子组件的数据,否则子组件不会自动变化 + parentData() { + return [this.current, this.direction, this.activeColor, this.inactiveColor, this.activeIcon, this.inactiveIcon, this.dot] + } + }, + methods: { + // 更新子组件的数据 + updateChildData() { + this.children.map(child => { + // 先判断子组件是否存在对应的方法 + uni.$u.test.func((child || {}).updateFromParent()) && child.updateFromParent() + }) + }, + // 接受子组件的通知,去修改其他子组件的数据 + updateFromChild() { + this.updateChildData() + } + }, + created() { + this.children = [] + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-steps { + @include flex; + + &--column { + flex-direction: column + } + + &--row { + flex-direction: row; + flex: 1; + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-sticky/props.js b/uni_modules/uview-ui/components/u-sticky/props.js new file mode 100644 index 0000000..c2ca8da --- /dev/null +++ b/uni_modules/uview-ui/components/u-sticky/props.js @@ -0,0 +1,40 @@ +export default { + props: { + // 吸顶容器到顶部某个距离的时候,进行吸顶,在H5平台,NavigationBar为44px + offsetTop: { + type: [String, Number], + default: uni.$u.props.sticky.offsetTop + }, + // 自定义导航栏的高度 + customNavHeight: { + type: [String, Number], + // #ifdef H5 + // H5端的导航栏属于“自定义”导航栏的范畴,因为它是非原生的,与普通元素一致 + default: 44, + // #endif + // #ifndef H5 + default: uni.$u.props.sticky.customNavHeight + // #endif + }, + // 是否开启吸顶功能 + disabled: { + type: Boolean, + default: uni.$u.props.sticky.disabled + }, + // 吸顶区域的背景颜色 + bgColor: { + type: String, + default: uni.$u.props.sticky.bgColor + }, + // z-index值 + zIndex: { + type: [String, Number], + default: uni.$u.props.sticky.zIndex + }, + // 列表中的索引值 + index: { + type: [String, Number], + default: uni.$u.props.sticky.index + } + } +} diff --git a/uni_modules/uview-ui/components/u-sticky/u-sticky.vue b/uni_modules/uview-ui/components/u-sticky/u-sticky.vue new file mode 100644 index 0000000..ff74688 --- /dev/null +++ b/uni_modules/uview-ui/components/u-sticky/u-sticky.vue @@ -0,0 +1,212 @@ +<template> + <view + class="u-sticky" + :id="elId" + :style="[style]" + > + <view + :style="[stickyContent]" + class="u-sticky__content" + > + <slot /> + </view> + </view> +</template> + +<script> + import props from './props.js';; + /** + * sticky 吸顶 + * @description 该组件与CSS中position: sticky属性实现的效果一致,当组件达到预设的到顶部距离时, 就会固定在指定位置,组件位置大于预设的顶部距离时,会重新按照正常的布局排列。 + * @tutorial https://www.uviewui.com/components/sticky.html + * @property {String | Number} offsetTop 吸顶时与顶部的距离,单位px(默认 0 ) + * @property {String | Number} customNavHeight 自定义导航栏的高度 (h5 默认44 其他默认 0 ) + * @property {Boolean} disabled 是否开启吸顶功能 (默认 false ) + * @property {String} bgColor 组件背景颜色(默认 '#ffffff' ) + * @property {String | Number} zIndex 吸顶时的z-index值 + * @property {String | Number} index 自定义标识,用于区分是哪一个组件 + * @property {Object} customStyle 组件的样式,对象形式 + * @event {Function} fixed 组件吸顶时触发 + * @event {Function} unfixed 组件取消吸顶时触发 + * @example <u-sticky offsetTop="200"><view>塞下秋来风景异,衡阳雁去无留意</view></u-sticky> + */ + export default { + name: 'u-sticky', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + cssSticky: false, // 是否使用css的sticky实现 + stickyTop: 0, // 吸顶的top值,因为可能受自定义导航栏影响,最终的吸顶值非offsetTop值 + elId: uni.$u.guid(), + left: 0, // js模式时,吸顶的内容因为处于postition: fixed模式,为了和原来保持一致的样式,需要记录并重新设置它的left,height,width属性 + width: 'auto', + height: 'auto', + fixed: false, // js模式时,是否处于吸顶模式 + } + }, + computed: { + style() { + const style = {} + if(!this.disabled) { + if (this.cssSticky) { + style.position = 'sticky' + style.zIndex = this.uZindex + style.top = uni.$u.addUnit(this.stickyTop) + } else { + style.height = this.fixed ? this.height + 'px' : 'auto' + } + } else { + // 无需吸顶时,设置会默认的relative(nvue)和非nvue的static静态模式即可 + // #ifdef APP-NVUE + style.position = 'relative' + // #endif + // #ifndef APP-NVUE + style.position = 'static' + // #endif + } + style.backgroundColor = this.bgColor + return uni.$u.deepMerge(uni.$u.addStyle(this.customStyle), style) + }, + // 吸顶内容的样式 + stickyContent() { + const style = {} + if (!this.cssSticky) { + style.position = this.fixed ? 'fixed' : 'static' + style.top = this.stickyTop + 'px' + style.left = this.left + 'px' + style.width = this.width == 'auto' ? 'auto' : this.width + 'px' + style.zIndex = this.uZindex + } + return style + }, + uZindex() { + return this.zIndex ? this.zIndex : uni.$u.zIndex.sticky + } + }, + mounted() { + this.init() + }, + methods: { + init() { + this.getStickyTop() + // 判断使用的模式 + this.checkSupportCssSticky() + // 如果不支持css sticky,则使用js方案,此方案性能比不上css方案 + if (!this.cssSticky) { + !this.disabled && this.initObserveContent() + } + }, + initObserveContent() { + // 获取吸顶内容的高度,用于在js吸顶模式时,给父元素一个填充高度,防止"塌陷" + this.$uGetRect('#' + this.elId).then((res) => { + this.height = res.height + this.left = res.left + this.width = res.width + this.$nextTick(() => { + this.observeContent() + }) + }) + }, + observeContent() { + // 先断掉之前的观察 + this.disconnectObserver('contentObserver') + const contentObserver = uni.createIntersectionObserver({ + // 检测的区间范围 + thresholds: [0.95, 0.98, 1] + }) + // 到屏幕顶部的高度时触发 + contentObserver.relativeToViewport({ + top: -this.stickyTop + }) + // 绑定观察的元素 + contentObserver.observe(`#${this.elId}`, res => { + this.setFixed(res.boundingClientRect.top) + }) + this.contentObserver = contentObserver + }, + setFixed(top) { + // 判断是否出于吸顶条件范围 + const fixed = top <= this.stickyTop + this.fixed = fixed + }, + disconnectObserver(observerName) { + // 断掉观察,释放资源 + const observer = this[observerName] + observer && observer.disconnect() + }, + getStickyTop() { + this.stickyTop = uni.$u.getPx(this.offsetTop) + uni.$u.getPx(this.customNavHeight) + }, + async checkSupportCssSticky() { + // #ifdef H5 + // H5,一般都是现代浏览器,是支持css sticky的,这里使用创建元素嗅探的形式判断 + if (this.checkCssStickyForH5()) { + this.cssSticky = true + } + // #endif + + // 如果安卓版本高于8.0,依然认为是支持css sticky的(因为安卓7在某些机型,可能不支持sticky) + if (uni.$u.os() === 'android' && Number(uni.$u.sys().system) > 8) { + this.cssSticky = true + } + + // APP-Vue和微信平台,通过computedStyle判断是否支持css sticky + // #ifdef APP-VUE || MP-WEIXIN + this.cssSticky = await this.checkComputedStyle() + // #endif + + // ios上,从ios6开始,都是支持css sticky的 + if (uni.$u.os() === 'ios') { + this.cssSticky = true + } + + // nvue,是支持css sticky的 + // #ifdef APP-NVUE + this.cssSticky = true + // #endif + }, + // 在APP和微信小程序上,通过uni.createSelectorQuery可以判断是否支持css sticky + checkComputedStyle() { + // 方法内进行判断,避免在其他平台生成无用代码 + // #ifdef APP-VUE || MP-WEIXIN + return new Promise(resolve => { + uni.createSelectorQuery().in(this).select('.u-sticky').fields({ + computedStyle: ["position"] + }).exec(e => { + resolve('sticky' === e[0].position) + }) + }) + // #endif + }, + // H5通过创建元素的形式嗅探是否支持css sticky + // 判断浏览器是否支持sticky属性 + checkCssStickyForH5() { + // 方法内进行判断,避免在其他平台生成无用代码 + // #ifdef H5 + const vendorList = ['', '-webkit-', '-ms-', '-moz-', '-o-'], + vendorListLength = vendorList.length, + stickyElement = document.createElement('div') + for (let i = 0; i < vendorListLength; i++) { + stickyElement.style.position = vendorList[i] + 'sticky' + if (stickyElement.style.position !== '') { + return true + } + } + return false; + // #endif + } + }, + beforeDestroy() { + this.disconnectObserver('contentObserver') + } + } +</script> + +<style lang="scss" scoped> + .u-sticky { + /* #ifdef APP-VUE || MP-WEIXIN */ + // 此处默认写sticky属性,是为了给微信和APP通过uni.createSelectorQuery查询是否支持css sticky使用 + position: sticky; + /* #endif */ + } +</style> diff --git a/uni_modules/uview-ui/components/u-subsection/props.js b/uni_modules/uview-ui/components/u-subsection/props.js new file mode 100644 index 0000000..5675eaa --- /dev/null +++ b/uni_modules/uview-ui/components/u-subsection/props.js @@ -0,0 +1,49 @@ +export default { + props: { + // tab的数据 + list: { + type: Array, + default: uni.$u.props.subsection.list + }, + // 当前活动的tab的index + current: { + type: [String, Number], + default: uni.$u.props.subsection.current + }, + // 激活的颜色 + activeColor: { + type: String, + default: uni.$u.props.subsection.activeColor + }, + // 未激活的颜色 + inactiveColor: { + type: String, + default: uni.$u.props.subsection.inactiveColor + }, + // 模式选择,mode=button为按钮形式,mode=subsection时为分段模式 + mode: { + type: String, + default: uni.$u.props.subsection.mode + }, + // 字体大小 + fontSize: { + type: [String, Number], + default: uni.$u.props.subsection.fontSize + }, + // 激活tab的字体是否加粗 + bold: { + type: Boolean, + default: uni.$u.props.subsection.bold + }, + // mode = button时,组件背景颜色 + bgColor: { + type: String, + default: uni.$u.props.subsection.bgColor + }, + // 从list元素对象中读取的键名 + keyName: { + type: String, + default: uni.$u.props.subsection.keyName + } + } +} diff --git a/uni_modules/uview-ui/components/u-subsection/u-subsection.vue b/uni_modules/uview-ui/components/u-subsection/u-subsection.vue new file mode 100644 index 0000000..cc4d540 --- /dev/null +++ b/uni_modules/uview-ui/components/u-subsection/u-subsection.vue @@ -0,0 +1,299 @@ +<template> + <view + class="u-subsection" + ref="u-subsection" + :class="[`u-subsection--${mode}`]" + :style="[$u.addStyle(customStyle), wrapperStyle]" + > + <view + class="u-subsection__bar" + ref="u-subsection__bar" + :style="[barStyle]" + :class="[ + mode === 'button' && 'u-subsection--button__bar', + current === 0 && + mode === 'subsection' && + 'u-subsection__bar--first', + current > 0 && + current < list.length - 1 && + mode === 'subsection' && + 'u-subsection__bar--center', + current === list.length - 1 && + mode === 'subsection' && + 'u-subsection__bar--last', + ]" + ></view> + <view + class="u-subsection__item" + :class="[ + `u-subsection__item--${index}`, + index < list.length - 1 && + 'u-subsection__item--no-border-right', + index === 0 && 'u-subsection__item--first', + index === list.length - 1 && 'u-subsection__item--last', + ]" + :ref="`u-subsection__item--${index}`" + :style="[itemStyle(index)]" + @tap="clickHandler(index)" + v-for="(item, index) in list" + :key="index" + > + <text + class="u-subsection__item__text" + :style="[textStyle(index)]" + >{{ getText(item) }}</text + > + </view> + </view> +</template> + +<script> +// #ifdef APP-NVUE +const dom = uni.requireNativePlugin("dom"); +const animation = uni.requireNativePlugin("animation"); +// #endif +import props from "./props.js"; +/** + * Subsection 分段器 + * @description 该分段器一般用于用户从几个选项中选择某一个的场景 + * @tutorial https://www.uviewui.com/components/subsection.html + * @property {Array} list tab的数据 + * @property {String | Number} current 当前活动的tab的index(默认 0 ) + * @property {String} activeColor 激活时的颜色(默认 '#3c9cff' ) + * @property {String} inactiveColor 未激活时的颜色(默认 '#303133' ) + * @property {String} mode 模式选择,mode=button为按钮形式,mode=subsection时为分段模式(默认 'button' ) + * @property {String | Number} fontSize 字体大小,单位px(默认 12 ) + * @property {Boolean} bold 激活选项的字体是否加粗(默认 true ) + * @property {String} bgColor 组件背景颜色,mode为button时有效(默认 '#eeeeef' ) + * @property {Object} customStyle 定义需要用到的外部样式 + * @property {String} keyName 从`list`元素对象中读取的键名(默认 'name' ) + * + * @event {Function} change 分段器选项发生改变时触发 回调 index:选项的index索引值,从0开始 + * @example <u-subsection :list="list" :current="curNow" @change="sectionChange"></u-subsection> + */ +export default { + name: "u-subsection", + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + // 组件尺寸 + itemRect: { + width: 0, + height: 0, + }, + }; + }, + watch: { + list(newValue, oldValue) { + this.init(); + }, + current: { + immediate: true, + handler(n) { + // #ifdef APP-NVUE + // 在安卓nvue上,如果通过translateX进行位移,到最后一个时,会导致右侧无法绘制圆角 + // 故用animation模块进行位移 + const ref = this.$refs?.["u-subsection__bar"]?.ref; + // 不存在ref的时候(理解为第一次初始化时,需要渲染dom,进行一定延时再获取ref),这里的100ms是经过测试得出的结果(某些安卓需要延时久一点),勿随意修改 + uni.$u.sleep(ref ? 0 : 100).then(() => { + animation.transition(this.$refs["u-subsection__bar"].ref, { + styles: { + transform: `translateX(${ + n * this.itemRect.width + }px)`, + transformOrigin: "center center", + }, + duration: 300, + }); + }); + // #endif + }, + }, + }, + computed: { + wrapperStyle() { + const style = {}; + // button模式时,设置背景色 + if (this.mode === "button") { + style.backgroundColor = this.bgColor; + } + return style; + }, + // 滑块的样式 + barStyle() { + const style = {}; + style.width = `${this.itemRect.width}px`; + style.height = `${this.itemRect.height}px`; + // 通过translateX移动滑块,其移动的距离为索引*item的宽度 + // #ifndef APP-NVUE + style.transform = `translateX(${ + this.current * this.itemRect.width + }px)`; + // #endif + if (this.mode === "subsection") { + // 在subsection模式下,需要动态设置滑块的圆角,因为移动滑块使用的是translateX,无法通过父元素设置overflow: hidden隐藏滑块的直角 + style.backgroundColor = this.activeColor; + } + return style; + }, + // 分段器item的样式 + itemStyle(index) { + return (index) => { + const style = {}; + if (this.mode === "subsection") { + // 设置border的样式 + style.borderColor = this.activeColor; + style.borderWidth = "1px"; + style.borderStyle = "solid"; + } + return style; + }; + }, + // 分段器文字颜色 + textStyle(index) { + return (index) => { + const style = {}; + style.fontWeight = + this.bold && this.current === index ? "bold" : "normal"; + style.fontSize = uni.$u.addUnit(this.fontSize); + // subsection模式下,激活时默认为白色的文字 + if (this.mode === "subsection") { + style.color = + this.current === index ? "#fff" : this.inactiveColor; + } else { + // button模式下,激活时文字颜色默认为activeColor + style.color = + this.current === index + ? this.activeColor + : this.inactiveColor; + } + return style; + }; + }, + }, + mounted() { + this.init(); + }, + methods: { + init() { + uni.$u.sleep().then(() => this.getRect()); + }, + // 判断展示文本 + getText(item) { + return typeof item === 'object' ? item[this.keyName] : item + }, + // 获取组件的尺寸 + getRect() { + // #ifndef APP-NVUE + this.$uGetRect(".u-subsection__item--0").then((size) => { + this.itemRect = size; + }); + // #endif + + // #ifdef APP-NVUE + const ref = this.$refs["u-subsection__item--0"][0]; + ref && + dom.getComponentRect(ref, (res) => { + this.itemRect = res.size; + }); + // #endif + }, + clickHandler(index) { + this.$emit("change", index); + }, + }, +}; +</script> + +<style lang="scss" scoped> +@import "../../libs/css/components.scss"; + +.u-subsection { + @include flex; + position: relative; + overflow: hidden; + /* #ifndef APP-NVUE */ + width: 100%; + box-sizing: border-box; + /* #endif */ + + &--button { + height: 32px; + background-color: rgb(238, 238, 239); + padding: 3px; + border-radius: 3px; + align-items: stretch; + + &__bar { + background-color: #ffffff; + border-radius: 3px !important; + } + } + + &--subsection { + height: 30px; + } + + &__bar { + position: absolute; + /* #ifndef APP-NVUE */ + transition-property: transform, color; + transition-duration: 0.3s; + transition-timing-function: ease-in-out; + /* #endif */ + + &--first { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + border-top-right-radius: 0px; + border-bottom-right-radius: 0px; + } + + &--center { + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; + border-top-right-radius: 0px; + border-bottom-right-radius: 0px; + } + + &--last { + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + } + } + + &__item { + @include flex; + flex: 1; + justify-content: center; + align-items: center; + // vue环境下,需要设置相对定位,因为滑块为绝对定位,item需要在滑块的上面 + position: relative; + + &--no-border-right { + border-right-width: 0 !important; + } + + &--first { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + } + + &--last { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + } + + &__text { + font-size: 12px; + line-height: 12px; + @include flex; + align-items: center; + transition-property: color; + transition-duration: 0.3s; + } + } +} +</style> diff --git a/uni_modules/uview-ui/components/u-swipe-action-item/index - backup.wxs b/uni_modules/uview-ui/components/u-swipe-action-item/index - backup.wxs new file mode 100644 index 0000000..04cab92 --- /dev/null +++ b/uni_modules/uview-ui/components/u-swipe-action-item/index - backup.wxs @@ -0,0 +1,256 @@ +/** + * 此为wxs模块,只支持APP-VUE,微信和QQ小程序以及H5平台 + * wxs内部不支持es6语法,变量只能使用var定义,无法使用解构,箭头函数等特性 + */ + +// 开始触摸 +function touchstart(event, ownerInstance) { + // 触发事件的组件的ComponentDescriptor实例 + var instance = event.instance + // wxs内的局部变量快照,此快照是属于整个组件的,在touchstart和touchmove事件中都能获取到相同的结果 + var state = instance.getState() + if (state.disable) return + var touches = event.touches + // 如果进行的是多指触控,不允许进行操作 + if (touches && touches.length > 1) return + // 标识当前为滑动中状态 + state.moving = true + // 记录触摸开始点的坐标值 + state.startX = touches[0].pageX + state.startY = touches[0].pageY +} + +// 触摸滑动 +function touchmove(event, ownerInstance) { + // 触发事件的组件的ComponentDescriptor实例 + var instance = event.instance + // wxs内的局部变量快照 + var state = instance.getState() + if (state.disabled || !state.moving) return + + var touches = event.touches + var pageX = touches[0].pageX + var pageY = touches[0].pageY + var moveX = pageX - state.startX + var moveY = pageY - state.startY + var buttonsWidth = state.buttonsWidth + + // 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动 + if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > state.threshold) { + event.preventDefault() + event.stopPropagation() + } + // 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格 + if (Math.abs(moveX) < Math.abs(moveY)) return + + // 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断 + // 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是 + // 在超出后,设置为0 + if (state.status === 'open') { + // 在开启状态下,向左滑动,需忽略 + if (moveX < 0) moveX = 0 + // 想要收起菜单,最大能移动的距离为按钮的总宽度 + if (moveX > buttonsWidth) moveX = buttonsWidth + // 如果是已经打开了的状态,向左滑动时,移动收起菜单 + moveSwipeAction(-buttonsWidth + moveX, instance, ownerInstance) + } else { + // 关闭状态下,右滑动需忽略 + if (moveX > 0) moveX = 0 + // 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数 + if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth + // 只要是在滑过程中,就不断移动菜单的内容部分,从而使隐藏的菜单显示出来 + moveSwipeAction(moveX, instance, ownerInstance) + } +} + +// 触摸结束 +function touchend(event, ownerInstance) { + // 触发事件的组件的ComponentDescriptor实例 + var instance = event.instance + // wxs内的局部变量快照 + var state = instance.getState() + if (!state.moving || state.disabled) return + var touches = event.changedTouches ? event.changedTouches[0] : {} + var pageX = touches.pageX + var pageY = touches.pageY + var moveX = pageX - state.startX + if (state.status === 'open') { + // 在展开的状态下,继续左滑,无需操作 + if (moveX < 0) return + // 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑 + if (moveX === 0) { + return closeSwipeAction(instance, ownerInstance) + } + // 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态 + if (Math.abs(moveX) < state.threshold) { + openSwipeAction(instance, ownerInstance) + } else { + // 如果滑动距离大于阈值,则执行收起逻辑 + closeSwipeAction(instance, ownerInstance) + } + } else { + // 在关闭的状态下,右滑,无需操作 + if (moveX > 0) return + // 理由同上 + if (Math.abs(moveX) < state.threshold) { + closeSwipeAction(instance, ownerInstance) + } else { + openSwipeAction(instance, ownerInstance) + } + } +} + +// 获取过渡时间 +function getDuration(value) { + if (value.toString().indexOf('s') >= 0) return value + return value > 30 ? value + 'ms' : value + 's' +} + +// 滑动结束时判断滑动的方向 +function getMoveDirection(instance, ownerInstance) { + var state = instance.getState() +} + +// 移动滑动选择器内容区域,同时显示出其隐藏的菜单 +function moveSwipeAction(moveX, instance, ownerInstance) { + var state = instance.getState() + // 获取所有按钮的实例,需要通过它去设置按钮的位移 + var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button') + var len = buttons.length + var previewButtonsMoveX = 0 + + // 设置菜单内容部分的偏移 + instance.requestAnimationFrame(function() { + instance.setStyle({ + // 设置translateX的值 + 'transition': 'none', + transform: 'translateX(' + moveX + 'px)', + '-webkit-transform': 'translateX(' + moveX + 'px)' + }) + // 折叠按钮动画 + for (var i = len - 1; i >= 0; i--) { + // 通过比例,得出元素自身该移动的距离 + var translateX = state.buttons[i].width / state.buttonsWidth * moveX + // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和 + var realTranslateX = translateX + previewButtonsMoveX + buttons[i].setStyle({ + // 在移动期间,不能使用过渡效果,否则会造成卡顿,本质原因是每次移动一点,就要花一定时间去过渡这个过程 + 'transition': 'none', + 'transform': 'translateX(' + realTranslateX + 'px)', + '-webkit-transform': 'translateX(' + realTranslateX + 'px)' + }) + // 记录本按钮之前的所有按钮的移动距离之和 + previewButtonsMoveX += translateX + } + }) +} + +// 一次性展开滑动菜单 +function openSwipeAction(instance, ownerInstance) { + var state = instance.getState() + // 获取所有按钮的实例,需要通过它去设置按钮的位移 + var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button') + var len = buttons.length + // 处理duration单位问题 + const duration = getDuration(state.duration) + // 展开过程中,是向左移动,所以X的偏移应该为负值 + var buttonsWidth = -state.buttonsWidth + var previewButtonsMoveX = 0 + instance.requestAnimationFrame(function() { + // 设置菜单主体内容 + instance.setStyle({ + 'transition': 'transform ' + duration, + 'transform': 'translateX(' + buttonsWidth + 'px)', + '-webkit-transform': 'translateX(' + buttonsWidth + 'px)', + }) + // 设置各个隐藏的按钮为展开的状态 + for (var i = len - 1; i >= 0; i--) { + // 通过比例,得出元素自身该移动的距离 + var translateX = state.buttons[i].width / state.buttonsWidth * buttonsWidth + // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和 + var realTranslateX = translateX + previewButtonsMoveX + buttons[i].setStyle({ + // 在移动期间,需要加上动画效果 + 'transition': 'transform ' + duration, + 'transform': 'translateX(' + realTranslateX + 'px)', + '-webkit-transform': 'translateX(' + realTranslateX + 'px)' + }) + // 记录本按钮之前的所有按钮的移动距离之和 + previewButtonsMoveX += translateX + } + }) + setStatus('open', instance) +} + +// 标记菜单的当前状态,open-已经打开,close-已经关闭 +function setStatus(status, instance) { + var state = instance.getState() + state.status = status +} + +// 一次性收起滑动菜单 +function closeSwipeAction(instance, ownerInstance) { + var state = instance.getState() + // 获取所有按钮的实例,需要通过它去设置按钮的位移 + var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button') + var len = buttons.length + // 处理duration单位问题 + const duration = getDuration(state.duration) + instance.requestAnimationFrame(function() { + // 设置菜单主体内容 + instance.setStyle({ + 'transition': 'transform ' + duration, + 'transform': 'translateX(0px)', + '-webkit-transform': 'translateX(0px)' + }) + // 设置各个隐藏的按钮为收起的状态 + for (var i = len - 1; i >= 0; i--) { + buttons[i].setStyle({ + 'transition': 'transform ' + duration, + 'transform': 'translateX(0px)', + '-webkit-transform': 'translateX(0px)' + }) + } + }) + setStatus('close', instance) +} + +// show的状态发生变化 +function showChange(newValue, oldValue, ownerInstance, instance) { + var state = instance.getState() + if (state.disabled) return + // 打开或关闭单元格 + if (newValue) { + openSwipeAction(instance, ownerInstance) + } else { + closeSwipeAction(instance, ownerInstance) + } +} + +// 菜单尺寸发生变化 +function sizeChange(newValue, oldValue, ownerInstance, instance) { + // wxs内的局部变量快照 + var state = instance.getState() + state.disabled = newValue.disabled + state.duration = newValue.duration + state.show = newValue.show + state.threshold = newValue.threshold + state.buttons = newValue.buttons + + var len = state.buttons.length + if (len) { + var buttonsWidth = 0 + var buttons = newValue.buttons + for (var i = 0; i < len; i++) { + buttonsWidth += buttons[i].width + } + } + state.buttonsWidth = buttonsWidth +} + +module.exports = { + touchstart: touchstart, + touchmove: touchmove, + touchend: touchend, + sizeChange: sizeChange +} diff --git a/uni_modules/uview-ui/components/u-swipe-action-item/index.wxs b/uni_modules/uview-ui/components/u-swipe-action-item/index.wxs new file mode 100644 index 0000000..728275f --- /dev/null +++ b/uni_modules/uview-ui/components/u-swipe-action-item/index.wxs @@ -0,0 +1,225 @@ +/** + * 此为wxs模块,只支持APP-VUE,微信和QQ小程序以及H5平台 + * wxs内部不支持es6语法,变量只能使用var定义,无法使用解构,箭头函数等特性 + */ + +// 开始触摸 +function touchstart(event, ownerInstance) { + // 触发事件的组件的ComponentDescriptor实例 + var instance = event.instance + // wxs内的局部变量快照,此快照是属于整个组件的,在touchstart和touchmove事件中都能获取到相同的结果 + var state = instance.getState() + if (state.disabled) return + var touches = event.touches + // 如果进行的是多指触控,不允许进行操作 + if (touches && touches.length > 1) return + // 标识当前为滑动中状态 + state.moving = true + // 记录触摸开始点的坐标值 + state.startX = touches[0].pageX + state.startY = touches[0].pageY + + ownerInstance.callMethod('closeOther') +} + +// 触摸滑动 +function touchmove(event, ownerInstance) { + // 触发事件的组件的ComponentDescriptor实例 + var instance = event.instance + // wxs内的局部变量快照 + var state = instance.getState() + if (state.disabled || !state.moving) return + var touches = event.touches + var pageX = touches[0].pageX + var pageY = touches[0].pageY + var moveX = pageX - state.startX + var moveY = pageY - state.startY + var buttonsWidth = state.buttonsWidth + + // 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动 + if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > state.threshold) { + event.preventDefault && event.preventDefault() + event.stopPropagation && event.stopPropagation() + } + // 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格 + if (Math.abs(moveX) < Math.abs(moveY)) return + + // 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断 + // 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是 + // 在超出后,设置为0 + if (state.status === 'open') { + // 在开启状态下,向左滑动,需忽略 + if (moveX < 0) moveX = 0 + // 想要收起菜单,最大能移动的距离为按钮的总宽度 + if (moveX > buttonsWidth) moveX = buttonsWidth + // 如果是已经打开了的状态,向左滑动时,移动收起菜单 + moveSwipeAction(-buttonsWidth + moveX, instance, ownerInstance) + } else { + // 关闭状态下,右滑动需忽略 + if (moveX > 0) moveX = 0 + // 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数 + if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth + // 只要是在滑过程中,就不断移动单元格内容部分,从而使隐藏的菜单显示出来 + moveSwipeAction(moveX, instance, ownerInstance) + } +} + +// 触摸结束 +function touchend(event, ownerInstance) { + // 触发事件的组件的ComponentDescriptor实例 + var instance = event.instance + // wxs内的局部变量快照 + var state = instance.getState() + if (!state.moving || state.disabled) return + var touches = event.changedTouches ? event.changedTouches[0] : {} + var pageX = touches.pageX + var pageY = touches.pageY + var moveX = pageX - state.startX + if (state.status === 'open') { + // 在展开的状态下,继续左滑,无需操作 + if (moveX < 0) return + // 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑 + if (moveX === 0) { + return closeSwipeAction(instance, ownerInstance) + } + // 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态 + if (Math.abs(moveX) < state.threshold) { + openSwipeAction(instance, ownerInstance) + } else { + // 如果滑动距离大于阈值,则执行收起逻辑 + closeSwipeAction(instance, ownerInstance) + } + } else { + // 在关闭的状态下,右滑,无需操作 + if (moveX > 0) return + // 理由同上 + if (Math.abs(moveX) < state.threshold) { + closeSwipeAction(instance, ownerInstance) + } else { + openSwipeAction(instance, ownerInstance) + } + } +} + +// 获取过渡时间 +function getDuration(value) { + if (value.toString().indexOf('s') >= 0) return value + return value > 30 ? value + 'ms' : value + 's' +} + +// 滑动结束时判断滑动的方向 +function getMoveDirection(instance, ownerInstance) { + var state = instance.getState() +} + +// 移动滑动选择器内容区域,同时显示出其隐藏的菜单 +function moveSwipeAction(moveX, instance, ownerInstance) { + var state = instance.getState() + // 获取所有按钮的实例,需要通过它去设置按钮的位移 + var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button') + + // 设置菜单内容部分的偏移 + instance.requestAnimationFrame(function() { + instance.setStyle({ + // 设置translateX的值 + 'transition': 'none', + transform: 'translateX(' + moveX + 'px)', + '-webkit-transform': 'translateX(' + moveX + 'px)' + }) + }) +} + +// 一次性展开滑动菜单 +function openSwipeAction(instance, ownerInstance) { + var state = instance.getState() + // 获取所有按钮的实例,需要通过它去设置按钮的位移 + var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button') + // 处理duration单位问题 + var duration = getDuration(state.duration) + // 展开过程中,是向左移动,所以X的偏移应该为负值 + var buttonsWidth = -state.buttonsWidth + instance.requestAnimationFrame(function() { + // 设置菜单主体内容 + instance.setStyle({ + 'transition': 'transform ' + duration, + 'transform': 'translateX(' + buttonsWidth + 'px)', + '-webkit-transform': 'translateX(' + buttonsWidth + 'px)', + }) + }) + setStatus('open', instance, ownerInstance) +} + +// 标记菜单的当前状态,open-已经打开,close-已经关闭 +function setStatus(status, instance, ownerInstance) { + var state = instance.getState() + state.status = status + ownerInstance.callMethod('setState', status) +} + +// 一次性收起滑动菜单 +function closeSwipeAction(instance, ownerInstance) { + var state = instance.getState() + // 获取所有按钮的实例,需要通过它去设置按钮的位移 + var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button') + var len = buttons.length + // 处理duration单位问题 + var duration = getDuration(state.duration) + instance.requestAnimationFrame(function() { + // 设置菜单主体内容 + instance.setStyle({ + 'transition': 'transform ' + duration, + 'transform': 'translateX(0px)', + '-webkit-transform': 'translateX(0px)' + }) + // 设置各个隐藏的按钮为收起的状态 + for (var i = len - 1; i >= 0; i--) { + buttons[i].setStyle({ + 'transition': 'transform ' + duration, + 'transform': 'translateX(0px)', + '-webkit-transform': 'translateX(0px)' + }) + } + }) + setStatus('close', instance, ownerInstance) +} + +// status的状态发生变化 +function statusChange(newValue, oldValue, ownerInstance, instance) { + var state = instance.getState() + if (state.disabled) return + // 打开或关闭单元格 + if (newValue === 'close' && state.status === 'open') { + closeSwipeAction(instance, ownerInstance) + } else if(newValue === 'open' && state.status === 'close') { + openSwipeAction(instance, ownerInstance) + } +} + +// 菜单尺寸发生变化 +function sizeChange(newValue, oldValue, ownerInstance, instance) { + // wxs内的局部变量快照 + var state = instance.getState() + state.disabled = newValue.disabled + state.duration = newValue.duration + state.show = newValue.show + state.threshold = newValue.threshold + state.buttons = newValue.buttons + + if (state.buttons) { + var len = state.buttons.length + var buttonsWidth = 0 + var buttons = newValue.buttons + for (var i = 0; i < len; i++) { + buttonsWidth += buttons[i].width + } + } + state.buttonsWidth = buttonsWidth +} + +module.exports = { + touchstart: touchstart, + touchmove: touchmove, + touchend: touchend, + sizeChange: sizeChange, + statusChange: statusChange +} diff --git a/uni_modules/uview-ui/components/u-swipe-action-item/nvue - backup.js b/uni_modules/uview-ui/components/u-swipe-action-item/nvue - backup.js new file mode 100644 index 0000000..6b9f116 --- /dev/null +++ b/uni_modules/uview-ui/components/u-swipe-action-item/nvue - backup.js @@ -0,0 +1,270 @@ +// nvue操作dom的库,用于获取dom的尺寸信息 +const dom = uni.requireNativePlugin('dom') +// nvue中用于操作元素动画的库,类似于uni.animation,只不过uni.animation不能用于nvue +const animation = uni.requireNativePlugin('animation') + +export default { + data() { + return { + // 是否滑动中 + moving: false, + // 状态,open-打开状态,close-关闭状态 + status: 'close', + // 开始触摸点的X和Y轴坐标 + startX: 0, + startY: 0, + // 所有隐藏按钮的尺寸信息数组 + buttons: [], + // 所有按钮的总宽度 + buttonsWidth: 0, + // 记录上一次移动的位置值 + moveX: 0, + // 记录上一次滑动的位置,用于前后两次做对比,如果移动的距离小于某一阈值,则认为前后之间没有移动,为了解决可能存在的通信阻塞问题 + lastX: 0 + } + }, + computed: { + // 获取过渡时间 + getDuratin() { + let duration = String(this.duration) + // 如果ms为单位,返回ms的数值部分 + if (duration.indexOf('ms') >= 0) return parseInt(duration) + // 如果s为单位,为了得到ms的数值,需要乘以1000 + if (duration.indexOf('s') >= 0) return parseInt(duration) * 1000 + // 如果值传了数值,且小于30,认为是s单位 + duration = Number(duration) + return duration < 30 ? duration * 1000 : duration + } + }, + watch: { + show: { + immediate: true, + handler(n) { + // if(n === true) { + // uni.$u.sleep(50).then(() => { + // this.openSwipeAction() + // }) + // } else { + // this.closeSwipeAction() + // } + } + } + }, + mounted() { + uni.$u.sleep(20).then(() => { + this.queryRect() + }) + }, + methods: { + close() { + this.closeSwipeAction() + }, + // 触摸单元格 + touchstart(event) { + if (this.disabled) return + this.closeOther() + const { touches } = event + // 记录触摸开始点的坐标值 + this.startX = touches[0].pageX + this.startY = touches[0].pageY + }, + // // 触摸滑动 + touchmove(event) { + if (this.disabled) return + const { touches } = event + const { pageX } = touches[0] + const { pageY } = touches[0] + let moveX = pageX - this.startX + const moveY = pageY - this.startY + const { buttonsWidth } = this + const len = this.buttons.length + + // 判断前后两次的移动距离,如果小于一定值,则不进行移动处理 + if (Math.abs(pageX - this.lastX) < 0.3) return + this.lastX = pageX + + // 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动 + if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > this.threshold) { + event.stopPropagation() + } + // 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格 + if (Math.abs(moveX) < Math.abs(moveY)) return + + // 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断 + // 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是 + // 在超出后,设置为0 + if (this.status === 'open') { + // 在开启状态下,向左滑动,需忽略 + if (moveX < 0) moveX = 0 + // 想要收起菜单,最大能移动的距离为按钮的总宽度 + if (moveX > buttonsWidth) moveX = buttonsWidth + // 如果是已经打开了的状态,向左滑动时,移动收起菜单 + this.moveSwipeAction(-buttonsWidth + moveX) + } else { + // 关闭状态下,右滑动需忽略 + if (moveX > 0) moveX = 0 + // 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数 + if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth + // 只要是在滑过程中,就不断移动菜单的内容部分,从而使隐藏的菜单显示出来 + this.moveSwipeAction(moveX) + } + }, + // 单元格结束触摸 + touchend(event) { + if (this.disabled) return + const touches = event.changedTouches ? event.changedTouches[0] : {} + const { pageX } = touches + const { pageY } = touches + const { buttonsWidth } = this + this.moveX = pageX - this.startX + if (this.status === 'open') { + // 在展开的状态下,继续左滑,无需操作 + if (this.moveX < 0) this.moveX = 0 + if (this.moveX > buttonsWidth) this.moveX = buttonsWidth + // 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑 + if (this.moveX === 0) { + return this.closeSwipeAction() + } + // 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态 + if (Math.abs(this.moveX) < this.threshold) { + this.openSwipeAction() + } else { + // 如果滑动距离大于阈值,则执行收起逻辑 + this.closeSwipeAction() + } + } else { + // 在关闭的状态下,右滑,无需操作 + if (this.moveX >= 0) this.moveX = 0 + if (this.moveX <= -buttonsWidth) this.moveX = -buttonsWidth + // 理由同上 + if (Math.abs(this.moveX) < this.threshold) { + this.closeSwipeAction() + } else { + this.openSwipeAction() + } + } + }, + // 移动滑动选择器内容区域,同时显示出其隐藏的菜单 + moveSwipeAction(moveX) { + if (this.moving) return + this.moving = true + + let previewButtonsMoveX = 0 + const len = this.buttons.length + animation.transition(this.$refs['u-swipe-action-item__content'].ref, { + styles: { + transform: `translateX(${moveX}px)` + }, + timingFunction: 'linear' + }, () => { + this.moving = false + }) + // 按钮的组的长度 + for (let i = len - 1; i >= 0; i--) { + const buttonRef = this.$refs[`u-swipe-action-item__right__button-${i}`][0].ref + // 通过比例,得出元素自身该移动的距离 + const translateX = this.buttons[i].width / this.buttonsWidth * moveX + // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和 + const realTranslateX = translateX + previewButtonsMoveX + animation.transition(buttonRef, { + styles: { + transform: `translateX(${realTranslateX}px)` + }, + duration: 0, + delay: 0, + timingFunction: 'linear' + }, () => {}) + // 记录本按钮之前的所有按钮的移动距离之和 + previewButtonsMoveX += translateX + } + }, + // 关闭菜单 + closeSwipeAction() { + if (this.status === 'close') return + this.moving = true + const { buttonsWidth } = this + animation.transition(this.$refs['u-swipe-action-item__content'].ref, { + styles: { + transform: 'translateX(0px)' + }, + duration: this.getDuratin, + timingFunction: 'ease-in-out' + }, () => { + this.status = 'close' + this.moving = false + this.closeHandler() + }) + // 按钮的组的长度 + const len = this.buttons.length + for (let i = len - 1; i >= 0; i--) { + const buttonRef = this.$refs[`u-swipe-action-item__right__button-${i}`][0].ref + // 如果不满足边界条件,返回 + if (this.buttons.length === 0 || !this.buttons[i] || !this.buttons[i].width) return + + animation.transition(buttonRef, { + styles: { + transform: 'translateX(0px)' + }, + duration: this.getDuratin, + timingFunction: 'ease-in-out' + }, () => {}) + } + }, + // 打开菜单 + openSwipeAction() { + if (this.status === 'open') return + this.moving = true + const buttonsWidth = -this.buttonsWidth + let previewButtonsMoveX = 0 + animation.transition(this.$refs['u-swipe-action-item__content'].ref, { + styles: { + transform: `translateX(${buttonsWidth}px)` + }, + duration: this.getDuratin, + timingFunction: 'ease-in-out' + }, () => { + this.status = 'open' + this.moving = false + this.openHandler() + }) + // 按钮的组的长度 + const len = this.buttons.length + for (let i = len - 1; i >= 0; i--) { + const buttonRef = this.$refs[`u-swipe-action-item__right__button-${i}`][0].ref + // 如果不满足边界条件,返回 + if (this.buttons.length === 0 || !this.buttons[i] || !this.buttons[i].width) return + // 通过比例,得出元素自身该移动的距离 + const translateX = this.buttons[i].width / this.buttonsWidth * buttonsWidth + // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和 + const realTranslateX = translateX + previewButtonsMoveX + animation.transition(buttonRef, { + styles: { + transform: `translateX(${realTranslateX}px)` + }, + duration: this.getDuratin, + timingFunction: 'ease-in-out' + }, () => {}) + previewButtonsMoveX += translateX + } + }, + // 查询按钮节点信息 + queryRect() { + // 历遍所有按钮数组,通过getRectByDom返回一个promise + const promiseAll = this.rightOptions.map((item, index) => this.getRectByDom(this.$refs[`u-swipe-action-item__right__button-${index}`][0])) + // 通过promise.all方法,让所有按钮的查询结果返回一个数组的形式 + Promise.all(promiseAll).then((sizes) => { + this.buttons = sizes + // 计算所有按钮总宽度 + this.buttonsWidth = sizes.reduce((sum, cur) => sum + cur.width, 0) + }) + }, + // 通过nvue的dom模块,查询节点信息 + getRectByDom(ref) { + return new Promise((resolve) => { + dom.getComponentRect(ref, (res) => { + resolve(res.size) + }) + }) + } + } +} diff --git a/uni_modules/uview-ui/components/u-swipe-action-item/nvue.js b/uni_modules/uview-ui/components/u-swipe-action-item/nvue.js new file mode 100644 index 0000000..118e4cf --- /dev/null +++ b/uni_modules/uview-ui/components/u-swipe-action-item/nvue.js @@ -0,0 +1,174 @@ +// nvue操作dom的库,用于获取dom的尺寸信息 +const dom = uni.requireNativePlugin('dom'); +const bindingX = uni.requireNativePlugin('bindingx'); +const animation = uni.requireNativePlugin('animation'); + +export default { + data() { + return { + // 所有按钮的总宽度 + buttonsWidth: 0, + // 是否正在移动中 + moving: false + } + }, + computed: { + // 获取过渡时间 + getDuratin() { + let duration = String(this.duration) + // 如果ms为单位,返回ms的数值部分 + if (duration.indexOf('ms') >= 0) return parseInt(duration) + // 如果s为单位,为了得到ms的数值,需要乘以1000 + if (duration.indexOf('s') >= 0) return parseInt(duration) * 1000 + // 如果值传了数值,且小于30,认为是s单位 + duration = Number(duration) + return duration < 30 ? duration * 1000 : duration + } + }, + watch: { + show(n) { + if(n) { + this.moveCellByAnimation('open') + } else { + this.moveCellByAnimation('close') + } + } + }, + mounted() { + this.initialize() + }, + methods: { + initialize() { + this.queryRect() + }, + // 关闭单元格,用于打开一个,自动关闭其他单元格的场景 + closeHandler() { + if(this.status === 'open') { + // 如果在打开状态下,进行点击的话,直接关闭单元格 + return this.moveCellByAnimation('close') && this.unbindBindingX() + } + }, + // 点击单元格 + clickHandler() { + // 如果在移动中被点击,进行忽略 + if(this.moving) return + // 尝试关闭其他打开的单元格 + this.parent && this.parent.closeOther(this) + if(this.status === 'open') { + // 如果在打开状态下,进行点击的话,直接关闭单元格 + return this.moveCellByAnimation('close') && this.unbindBindingX() + } + }, + // 滑动单元格 + onTouchstart(e) { + // 如果当前正在移动中,或者disabled状态,则返回 + if(this.moving || this.disabled) { + return this.unbindBindingX() + } + if(this.status === 'open') { + // 如果在打开状态下,进行点击的话,直接关闭单元格 + return this.moveCellByAnimation('close') && this.unbindBindingX() + } + // 特殊情况下,e可能不为一个对象 + e?.stopPropagation && e.stopPropagation() + e?.preventDefault && e.preventDefault() + this.moving = true + // 获取元素ref + const content = this.getContentRef() + let expression = `min(max(${-this.buttonsWidth}, x), 0)` + // 尝试关闭其他打开的单元格 + this.parent && this.parent.closeOther(this) + + // 阿里为了KPI而开源的BindingX + this.panEvent = bindingX.bind({ + anchor: content, + eventType: 'pan', + props: [{ + element: content, + // 绑定width属性,设置其宽度值 + property: 'transform.translateX', + expression + }] + }, (res) => { + this.moving = false + if (res.state === 'end' || res.state === 'exit') { + const deltaX = res.deltaX + if(deltaX <= -this.buttonsWidth || deltaX >= 0) { + // 如果触摸滑动的过程中,大于单元格的总宽度,或者大于0,意味着已经动过滑动达到了打开或者关闭的状态 + // 这里直接进行状态的标记 + this.$nextTick(() => { + this.status = deltaX <= -this.buttonsWidth ? 'open' : 'close' + }) + } else if(Math.abs(deltaX) > uni.$u.getPx(this.threshold)) { + // 在移动大于阈值、并且小于总按钮宽度时,进行自动打开或者关闭 + // 移动距离大于0时,意味着需要关闭状态 + if(Math.abs(deltaX) < this.buttonsWidth) { + this.moveCellByAnimation(deltaX > 0 ? 'close' : 'open') + } + } else { + // 在小于阈值时,进行关闭操作(如果在打开状态下,将不会执行bindingX) + this.moveCellByAnimation('close') + } + } + }) + }, + // 释放bindingX + unbindBindingX() { + // 释放上一次的资源 + if (this?.panEvent?.token != 0) { + bindingX.unbind({ + token: this.panEvent?.token, + // pan为手势事件 + eventType: 'pan' + }) + } + }, + // 查询按钮节点信息 + queryRect() { + // 历遍所有按钮数组,通过getRectByDom返回一个promise + const promiseAll = this.options.map((item, index) => { + return this.getRectByDom(this.$refs[`u-swipe-action-item__right__button-${index}`][0]) + }) + // 通过promise.all方法,让所有按钮的查询结果返回一个数组的形式 + Promise.all(promiseAll).then(sizes => { + this.buttons = sizes + // 计算所有按钮总宽度 + this.buttonsWidth = sizes.reduce((sum, cur) => sum + cur.width, 0) + }) + }, + // 通过nvue的dom模块,查询节点信息 + getRectByDom(ref) { + return new Promise(resolve => { + dom.getComponentRect(ref, res => { + resolve(res.size) + }) + }) + }, + // 移动单元格到左边或者右边尽头 + moveCellByAnimation(status = 'open') { + if(this.moving) return + // 标识当前状态 + this.moveing = true + const content = this.getContentRef() + const x = status === 'open' ? -this.buttonsWidth : 0 + animation.transition(content, { + styles: { + transform: `translateX(${x}px)`, + }, + duration: uni.$u.getDuration(this.duration, false), + timingFunction: 'ease-in-out' + }, () => { + this.moving = false + this.status = status + this.unbindBindingX() + }) + }, + // 获取元素ref + getContentRef() { + return this.$refs['u-swipe-action-item__content'].ref + }, + beforeDestroy() { + this.unbindBindingX() + } + } +} diff --git a/uni_modules/uview-ui/components/u-swipe-action-item/props.js b/uni_modules/uview-ui/components/u-swipe-action-item/props.js new file mode 100644 index 0000000..ed82a42 --- /dev/null +++ b/uni_modules/uview-ui/components/u-swipe-action-item/props.js @@ -0,0 +1,41 @@ +export default { + props: { + // 控制打开或者关闭 + show: { + type: Boolean, + default: uni.$u.props.swipeActionItem.show + }, + // 标识符,如果是v-for,可用index索引值 + name: { + type: [String, Number], + default: uni.$u.props.swipeActionItem.name + }, + // 是否禁用 + disabled: { + type: Boolean, + default: uni.$u.props.swipeActionItem.disabled + }, + // 是否自动关闭其他swipe按钮组 + autoClose: { + type: Boolean, + default: uni.$u.props.swipeActionItem.autoClose + }, + // 滑动距离阈值,只有大于此值,才被认为是要打开菜单 + threshold: { + type: Number, + default: uni.$u.props.swipeActionItem.threshold + }, + // 右侧按钮内容 + options: { + type: Array, + default() { + return uni.$u.props.swipeActionItem.rightOptions + } + }, + // 动画过渡时间,单位ms + duration: { + type: [String, Number], + default: uni.$u.props.swipeActionItem.duration + } + } +} diff --git a/uni_modules/uview-ui/components/u-swipe-action-item/u-swipe-action-item.vue b/uni_modules/uview-ui/components/u-swipe-action-item/u-swipe-action-item.vue new file mode 100644 index 0000000..1fab304 --- /dev/null +++ b/uni_modules/uview-ui/components/u-swipe-action-item/u-swipe-action-item.vue @@ -0,0 +1,190 @@ +<template> + <view class="u-swipe-action-item" ref="u-swipe-action-item"> + <view class="u-swipe-action-item__right"> + <slot name="button"> + <view v-for="(item,index) in options" :key="index" class="u-swipe-action-item__right__button" + :ref="`u-swipe-action-item__right__button-${index}`" :style="[{ + alignItems: item.style && item.style.borderRadius ? 'center' : 'stretch' + }]" @tap="buttonClickHandler(item, index)"> + <view class="u-swipe-action-item__right__button__wrapper" :style="[{ + backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD', + borderRadius: item.style && item.style.borderRadius ? item.style.borderRadius : '0', + padding: item.style && item.style.borderRadius ? '0' : '0 15px', + }, item.style]"> + <u-icon v-if="item.icon" :name="item.icon" + :color="item.style && item.style.color ? item.style.color : '#ffffff'" + :size="item.iconSize ? $u.addUnit(item.iconSize) : item.style && item.style.fontSize ? $u.getPx(item.style.fontSize) * 1.2 : 17" + :customStyle="{ + marginRight: item.text ? '2px' : 0 + }"></u-icon> + <text v-if="item.text" class="u-swipe-action-item__right__button__wrapper__text u-line-1" + :style="[{ + color: item.style && item.style.color ? item.style.color : '#ffffff', + fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px', + lineHeight: item.style && item.style.fontSize ? item.style.fontSize : '16px', + }]">{{ item.text }}</text> + </view> + </view> + </slot> + </view> + <!-- #ifdef APP-VUE || MP-WEIXIN || H5 || MP-QQ --> + <view class="u-swipe-action-item__content" @touchstart="wxs.touchstart" @touchmove="wxs.touchmove" + @touchend="wxs.touchend" :status="status" :change:status="wxs.statusChange" :size="size" + :change:size="wxs.sizeChange"> + <!-- #endif --> + <!-- #ifdef APP-NVUE --> + <view class="u-swipe-action-item__content" ref="u-swipe-action-item__content" @panstart="onTouchstart" + @tap="clickHandler"> + <!-- #endif --> + <slot /> + </view> + </view> +</template> +<!-- #ifdef APP-VUE || MP-WEIXIN || H5 || MP-QQ --> +<script src="./index.wxs" module="wxs" lang="wxs"></script> +<!-- #endif --> +<script> + import touch from '../../libs/mixin/touch.js' + import props from './props.js'; + // #ifdef APP-NVUE + import nvue from './nvue.js'; + // #endif + // #ifdef APP-VUE || MP-WEIXIN || H5 || MP-QQ + import wxs from './wxs.js'; + // #endif + /** + * SwipeActionItem 滑动单元格子组件 + * @description 该组件一般用于左滑唤出操作菜单的场景,用的最多的是左滑删除操作 + * @tutorial https://www.uviewui.com/components/swipeAction.html + * @property {Boolean} show 控制打开或者关闭(默认 false ) + * @property {String | Number} index 标识符,如果是v-for,可用index索引 + * @property {Boolean} disabled 是否禁用(默认 false ) + * @property {Boolean} autoClose 是否自动关闭其他swipe按钮组(默认 true ) + * @property {Number} threshold 滑动距离阈值,只有大于此值,才被认为是要打开菜单(默认 30 ) + * @property {Array} options 右侧按钮内容 + * @property {String | Number} duration 动画过渡时间,单位ms(默认 350 ) + * @event {Function(index)} open 组件打开时触发 + * @event {Function(index)} close 组件关闭时触发 + * @example <u-swipe-action><u-swipe-action-item :options="options1" ></u-swipe-action-item></u-swipe-action> + */ + export default { + name: 'u-swipe-action-item', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props, touch], + // #ifdef APP-NVUE + mixins: [uni.$u.mpMixin, uni.$u.mixin, props, nvue, touch], + // #endif + // #ifdef APP-VUE || MP-WEIXIN || H5 || MP-QQ + mixins: [uni.$u.mpMixin, uni.$u.mixin, props, touch, wxs], + // #endif + data() { + return { + // 按钮的尺寸信息 + size: {}, + // 父组件u-swipe-action的参数 + parentData: { + autoClose: true, + }, + // 当前状态,open-打开,close-关闭 + status: 'close', + } + }, + watch: { + // 由于wxs无法直接读取外部的值,需要在外部值变化时,重新执行赋值逻辑 + wxsInit(newValue, oldValue) { + this.queryRect() + } + }, + computed: { + wxsInit() { + return [this.disabled, this.autoClose, this.threshold, this.options, this.duration] + } + }, + mounted() { + this.init() + }, + methods: { + init() { + // 初始化父组件数据 + this.updateParentData() + // #ifndef APP-NVUE + uni.$u.sleep().then(() => { + this.queryRect() + }) + // #endif + }, + updateParentData() { + // 此方法在mixin中 + this.getParentData('u-swipe-action') + }, + // #ifndef APP-NVUE + // 查询节点 + queryRect() { + this.$uGetRect('.u-swipe-action-item__right__button', true).then(buttons => { + this.size = { + buttons, + show: this.show, + disabled: this.disabled, + threshold: this.threshold, + duration: this.duration + } + }) + }, + // #endif + // 按钮被点击 + buttonClickHandler(item, index) { + this.$emit('click', { + index, + name: this.name + }) + } + }, + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-swipe-action-item { + position: relative; + overflow: hidden; + /* #ifndef APP-NVUE || MP-WEIXIN */ + touch-action: pan-y; + /* #endif */ + + &__content { + background-color: #FFFFFF; + z-index: 10; + } + + &__right { + position: absolute; + top: 0; + bottom: 0; + right: 0; + @include flex; + + &__button { + @include flex; + justify-content: center; + overflow: hidden; + align-items: center; + + &__wrapper { + @include flex; + align-items: center; + justify-content: center; + padding: 0 15px; + + &__text { + @include flex; + align-items: center; + color: #FFFFFF; + font-size: 15px; + text-align: center; + justify-content: center; + } + } + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-swipe-action-item/wxs.js b/uni_modules/uview-ui/components/u-swipe-action-item/wxs.js new file mode 100644 index 0000000..ee49c10 --- /dev/null +++ b/uni_modules/uview-ui/components/u-swipe-action-item/wxs.js @@ -0,0 +1,15 @@ +export default { + methods: { + // 关闭时执行 + closeHandler() { + this.status = 'close' + }, + setState(status) { + this.status = status + }, + closeOther() { + // 尝试关闭其他打开的单元格 + this.parent && this.parent.closeOther(this) + } + } +} diff --git a/uni_modules/uview-ui/components/u-swipe-action/props.js b/uni_modules/uview-ui/components/u-swipe-action/props.js new file mode 100644 index 0000000..3a84536 --- /dev/null +++ b/uni_modules/uview-ui/components/u-swipe-action/props.js @@ -0,0 +1,9 @@ +export default { + props: { + // 是否自动关闭其他swipe按钮组 + autoClose: { + type: Boolean, + default: uni.$u.props.swipeAction.autoClose + } + } +} diff --git a/uni_modules/uview-ui/components/u-swipe-action/u-swipe-action.vue b/uni_modules/uview-ui/components/u-swipe-action/u-swipe-action.vue new file mode 100644 index 0000000..ad8f019 --- /dev/null +++ b/uni_modules/uview-ui/components/u-swipe-action/u-swipe-action.vue @@ -0,0 +1,67 @@ +<template> + <view class="u-swipe-action"> + <slot></slot> + </view> +</template> + +<script> + import props from './props.js'; + /** + * SwipeAction 滑动单元格 + * @description 该组件一般用于左滑唤出操作菜单的场景,用的最多的是左滑删除操作 + * @tutorial https://www.uviewui.com/components/swipeAction.html + * @property {Boolean} autoClose 是否自动关闭其他swipe按钮组 + * @event {Function(index)} click 点击组件时触发 + * @example <u-swipe-action><u-swipe-action-item :rightOptions="options1" ></u-swipe-action-item></u-swipe-action> + */ + export default { + name: 'u-swipe-action', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return {} + }, + provide() { + return { + swipeAction: this + } + }, + computed: { + // 这里computed的变量,都是子组件u-swipe-action-item需要用到的,由于头条小程序的兼容性差异,子组件无法实时监听父组件参数的变化 + // 所以需要手动通知子组件,这里返回一个parentData变量,供watch监听,在其中去通知每一个子组件重新从父组件(u-swipe-action-item) + // 拉取父组件新的变化后的参数 + parentData() { + return [this.autoClose] + } + }, + watch: { + // 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件 + parentData() { + if (this.children.length) { + this.children.map(child => { + // 判断子组件(u-swipe-action-item)如果有updateParentData方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值) + typeof(child.updateParentData) === 'function' && child.updateParentData() + }) + } + }, + }, + created() { + this.children = [] + }, + methods: { + closeOther(child) { + if (this.autoClose) { + // 历遍所有的单元格,找出非当前操作中的单元格,进行关闭 + this.children.map((item, index) => { + if (child !== item) { + item.closeHandler() + } + }) + } + } + } + } +</script> + +<style lang="scss" scoped> + +</style> diff --git a/uni_modules/uview-ui/components/u-swiper-indicator/props.js b/uni_modules/uview-ui/components/u-swiper-indicator/props.js new file mode 100644 index 0000000..302aca7 --- /dev/null +++ b/uni_modules/uview-ui/components/u-swiper-indicator/props.js @@ -0,0 +1,29 @@ +export default { + props: { + // 轮播的长度 + length: { + type: [String, Number], + default: uni.$u.props.swiperIndicator.length + }, + // 当前处于活动状态的轮播的索引 + current: { + type: [String, Number], + default: uni.$u.props.swiperIndicator.current + }, + // 指示器非激活颜色 + indicatorActiveColor: { + type: String, + default: uni.$u.props.swiperIndicator.indicatorActiveColor + }, + // 指示器的激活颜色 + indicatorInactiveColor: { + type: String, + default: uni.$u.props.swiperIndicator.indicatorInactiveColor + }, + // 指示器模式,line-线型,dot-点型 + indicatorMode: { + type: String, + default: uni.$u.props.swiperIndicator.indicatorMode + } + } +} diff --git a/uni_modules/uview-ui/components/u-swiper-indicator/u-swiper-indicator.vue b/uni_modules/uview-ui/components/u-swiper-indicator/u-swiper-indicator.vue new file mode 100644 index 0000000..8923e13 --- /dev/null +++ b/uni_modules/uview-ui/components/u-swiper-indicator/u-swiper-indicator.vue @@ -0,0 +1,110 @@ +<template> + <view class="u-swiper-indicator"> + <view + class="u-swiper-indicator__wrapper" + v-if="indicatorMode === 'line'" + :class="[`u-swiper-indicator__wrapper--${indicatorMode}`]" + :style="{ + width: $u.addUnit(lineWidth * length), + backgroundColor: indicatorInactiveColor + }" + > + <view + class="u-swiper-indicator__wrapper--line__bar" + :style="[lineStyle]" + ></view> + </view> + <view + class="u-swiper-indicator__wrapper" + v-if="indicatorMode === 'dot'" + > + <view + class="u-swiper-indicator__wrapper__dot" + v-for="(item, index) in length" + :key="index" + :class="[index === current && 'u-swiper-indicator__wrapper__dot--active']" + :style="[dotStyle(index)]" + > + + </view> + </view> + </view> +</template> + +<script> + import props from './props.js'; + /** + * SwiperIndicator 轮播图指示器 + * @description 该组件一般用于导航轮播,广告展示等场景,可开箱即用, + * @tutorial https://www.uviewui.com/components/swiper.html + * @property {String | Number} length 轮播的长度(默认 0 ) + * @property {String | Number} current 当前处于活动状态的轮播的索引(默认 0 ) + * @property {String} indicatorActiveColor 指示器非激活颜色 + * @property {String} indicatorInactiveColor 指示器的激活颜色 + * @property {String} indicatorMode 指示器模式(默认 'line' ) + * @example <u-swiper :list="list4" indicator keyName="url" :autoplay="false"></u-swiper> + */ + export default { + name: 'u-swiper-indicator', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + lineWidth: 22 + } + }, + computed: { + // 指示器为线型的样式 + lineStyle() { + let style = {} + style.width = uni.$u.addUnit(this.lineWidth) + style.transform = `translateX(${ uni.$u.addUnit(this.current * this.lineWidth) })` + style.backgroundColor = this.indicatorActiveColor + return style + }, + // 指示器为点型的样式 + dotStyle() { + return index => { + let style = {} + style.backgroundColor = index === this.current ? this.indicatorActiveColor : this.indicatorInactiveColor + return style + } + } + }, + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-swiper-indicator { + + &__wrapper { + @include flex; + + &--line { + border-radius: 100px; + height: 4px; + + &__bar { + width: 22px; + height: 4px; + border-radius: 100px; + background-color: #FFFFFF; + transition: transform 0.3s; + } + } + + &__dot { + width: 5px; + height: 5px; + border-radius: 100px; + margin: 0 4px; + + &--active { + width: 12px; + } + } + + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-swiper/props.js b/uni_modules/uview-ui/components/u-swiper/props.js new file mode 100644 index 0000000..bac6d31 --- /dev/null +++ b/uni_modules/uview-ui/components/u-swiper/props.js @@ -0,0 +1,125 @@ +export default { + props: { + // 列表数组,元素可为字符串,如为对象可通过keyName指定目标属性名 + list: { + type: Array, + default: uni.$u.props.swiper.list + }, + // 是否显示面板指示器 + indicator: { + type: Boolean, + default: uni.$u.props.swiper.indicator + }, + // 指示器非激活颜色 + indicatorActiveColor: { + type: String, + default: uni.$u.props.swiper.indicatorActiveColor + }, + // 指示器的激活颜色 + indicatorInactiveColor: { + type: String, + default: uni.$u.props.swiper.indicatorInactiveColor + }, + // 指示器样式,可通过bottom,left,right进行定位 + indicatorStyle: { + type: [String, Object], + default: uni.$u.props.swiper.indicatorStyle + }, + // 指示器模式,line-线型,dot-点型 + indicatorMode: { + type: String, + default: uni.$u.props.swiper.indicatorMode + }, + // 是否自动切换 + autoplay: { + type: Boolean, + default: uni.$u.props.swiper.autoplay + }, + // 当前所在滑块的 index + current: { + type: [String, Number], + default: uni.$u.props.swiper.current + }, + // 当前所在滑块的 item-id ,不能与 current 被同时指定 + currentItemId: { + type: String, + default: uni.$u.props.swiper.currentItemId + }, + // 滑块自动切换时间间隔 + interval: { + type: [String, Number], + default: uni.$u.props.swiper.interval + }, + // 滑块切换过程所需时间 + duration: { + type: [String, Number], + default: uni.$u.props.swiper.duration + }, + // 播放到末尾后是否重新回到开头 + circular: { + type: Boolean, + default: uni.$u.props.swiper.circular + }, + // 前边距,可用于露出前一项的一小部分,nvue和支付宝不支持 + previousMargin: { + type: [String, Number], + default: uni.$u.props.swiper.previousMargin + }, + // 后边距,可用于露出后一项的一小部分,nvue和支付宝不支持 + nextMargin: { + type: [String, Number], + default: uni.$u.props.swiper.nextMargin + }, + // 当开启时,会根据滑动速度,连续滑动多屏,支付宝不支持 + acceleration: { + type: Boolean, + default: uni.$u.props.swiper.acceleration + }, + // 同时显示的滑块数量,nvue、支付宝小程序不支持 + displayMultipleItems: { + type: Number, + default: uni.$u.props.swiper.displayMultipleItems + }, + // 指定swiper切换缓动动画类型,有效值:default、linear、easeInCubic、easeOutCubic、easeInOutCubic + // 只对微信小程序有效 + easingFunction: { + type: String, + default: uni.$u.props.swiper.easingFunction + }, + // list数组中指定对象的目标属性名 + keyName: { + type: String, + default: uni.$u.props.swiper.keyName + }, + // 图片的裁剪模式 + imgMode: { + type: String, + default: uni.$u.props.swiper.imgMode + }, + // 组件高度 + height: { + type: [String, Number], + default: uni.$u.props.swiper.height + }, + // 背景颜色 + bgColor: { + type: String, + default: uni.$u.props.swiper.bgColor + }, + // 组件圆角,数值或带单位的字符串 + radius: { + type: [String, Number], + default: uni.$u.props.swiper.radius + }, + // 是否加载中 + loading: { + type: Boolean, + default: uni.$u.props.swiper.loading + }, + // 是否显示标题,要求数组对象中有title属性 + showTitle: { + type: Boolean, + default: uni.$u.props.swiper.showTitle + } + } +} diff --git a/uni_modules/uview-ui/components/u-swiper/u-swiper.vue b/uni_modules/uview-ui/components/u-swiper/u-swiper.vue new file mode 100644 index 0000000..0cfb229 --- /dev/null +++ b/uni_modules/uview-ui/components/u-swiper/u-swiper.vue @@ -0,0 +1,255 @@ +<template> + <view + class="u-swiper" + :style="{ + backgroundColor: bgColor, + height: $u.addUnit(height), + borderRadius: $u.addUnit(radius) + }" + > + <view + class="u-swiper__loading" + v-if="loading" + > + <u-loading-icon mode="circle"></u-loading-icon> + </view> + <swiper + v-else + class="u-swiper__wrapper" + :style="{ + height: $u.addUnit(height), + }" + @change="change" + :circular="circular" + :interval="interval" + :duration="duration" + :autoplay="autoplay" + :current="current" + :currentItemId="currentItemId" + :previousMargin="$u.addUnit(previousMargin)" + :nextMargin="$u.addUnit(nextMargin)" + :acceleration="acceleration" + :displayMultipleItems="displayMultipleItems" + :easingFunction="easingFunction" + > + <swiper-item + class="u-swiper__wrapper__item" + v-for="(item, index) in list" + :key="index" + > + <view + class="u-swiper__wrapper__item__wrapper" + :style="[itemStyle(index)]" + > + <!-- 在nvue中,image图片的宽度默认为屏幕宽度,需要通过flex:1撑开,另外必须设置高度才能显示图片 --> + <image + class="u-swiper__wrapper__item__wrapper__image" + v-if="getItemType(item) === 'image'" + :src="getSource(item)" + :mode="imgMode" + @tap="clickHandler(index)" + :style="{ + height: $u.addUnit(height), + borderRadius: $u.addUnit(radius) + }" + ></image> + <video + class="u-swiper__wrapper__item__wrapper__video" + v-if="getItemType(item) === 'video'" + :id="`video-${index}`" + :enable-progress-gesture="false" + :src="getSource(item)" + :poster="getPoster(item)" + :title="showTitle && $u.test.object(item) && item.title ? item.title : ''" + :style="{ + height: $u.addUnit(height) + }" + controls + @tap="clickHandler(index)" + ></video> + <text + v-if="showTitle && $u.test.object(item) && item.title && $u.test.image(getSource(item))" + class="u-swiper__wrapper__item__wrapper__title u-line-1" + >{{ item.title }}</text> + </view> + </swiper-item> + </swiper> + <view class="u-swiper__indicator" :style="[$u.addStyle(indicatorStyle)]"> + <slot name="indicator"> + <u-swiper-indicator + v-if="!loading && indicator && !showTitle" + :indicatorActiveColor="indicatorActiveColor" + :indicatorInactiveColor="indicatorInactiveColor" + :length="list.length" + :current="currentIndex" + :indicatorMode="indicatorMode" + ></u-swiper-indicator> + </slot> + </view> + </view> +</template> + +<script> + import props from './props.js'; + /** + * Swiper 轮播图 + * @description 该组件一般用于导航轮播,广告展示等场景,可开箱即用, + * @tutorial https://www.uviewui.com/components/swiper.html + * @property {Array} list 轮播图数据 + * @property {Boolean} indicator 是否显示面板指示器(默认 false ) + * @property {String} indicatorActiveColor 指示器非激活颜色(默认 '#FFFFFF' ) + * @property {String} indicatorInactiveColor 指示器的激活颜色(默认 'rgba(255, 255, 255, 0.35)' ) + * @property {String | Object} indicatorStyle 指示器样式,可通过bottom,left,right进行定位 + * @property {String} indicatorMode 指示器模式(默认 'line' ) + * @property {Boolean} autoplay 是否自动切换(默认 true ) + * @property {String | Number} current 当前所在滑块的 index(默认 0 ) + * @property {String} currentItemId 当前所在滑块的 item-id ,不能与 current 被同时指定 + * @property {String | Number} interval 滑块自动切换时间间隔(ms)(默认 3000 ) + * @property {String | Number} duration 滑块切换过程所需时间(ms)(默认 300 ) + * @property {Boolean} circular 播放到末尾后是否重新回到开头(默认 false ) + * @property {String | Number} previousMargin 前边距,可用于露出前一项的一小部分,nvue和支付宝不支持(默认 0 ) + * @property {String | Number} nextMargin 后边距,可用于露出后一项的一小部分,nvue和支付宝不支持(默认 0 ) + * @property {Boolean} acceleration 当开启时,会根据滑动速度,连续滑动多屏,支付宝不支持(默认 false ) + * @property {Number} displayMultipleItems 同时显示的滑块数量,nvue、支付宝小程序不支持(默认 1 ) + * @property {String} easingFunction 指定swiper切换缓动动画类型, 只对微信小程序有效(默认 'default' ) + * @property {String} keyName list数组中指定对象的目标属性名(默认 'url' ) + * @property {String} imgMode 图片的裁剪模式(默认 'aspectFill' ) + * @property {String | Number} height 组件高度(默认 130 ) + * @property {String} bgColor 背景颜色(默认 '#f3f4f6' ) + * @property {String | Number} radius 组件圆角,数值或带单位的字符串(默认 4 ) + * @property {Boolean} loading 是否加载中(默认 false ) + * @property {Boolean} showTitle 是否显示标题,要求数组对象中有title属性(默认 false ) + * @event {Function(index)} click 点击轮播图时触发 index:点击了第几张图片,从0开始 + * @event {Function(index)} change 轮播图切换时触发(自动或者手动切换) index:切换到了第几张图片,从0开始 + * @example <u-swiper :list="list4" keyName="url" :autoplay="false"></u-swiper> + */ + export default { + name: 'u-swiper', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + currentIndex: 0 + } + }, + watch: { + current(val, preVal) { + if(val === preVal) return; + this.currentIndex = val; // 和上游数据关联上 + } + }, + computed: { + itemStyle() { + return index => { + const style = {} + // #ifndef APP-NVUE || MP-TOUTIAO + // 左右流出空间的写法不支持nvue和头条 + // 只有配置了此二值,才加上对应的圆角,以及缩放 + if (this.nextMargin && this.previousMargin) { + style.borderRadius = uni.$u.addUnit(this.radius) + if (index !== this.currentIndex) style.transform = 'scale(0.92)' + } + // #endif + return style + } + } + }, + methods: { + getItemType(item) { + if (typeof item === 'string') return uni.$u.test.video(this.getSource(item)) ? 'video' : 'image' + if (typeof item === 'object' && this.keyName) { + if (!item.type) return uni.$u.test.video(this.getSource(item)) ? 'video' : 'image' + if (item.type === 'image') return 'image' + if (item.type === 'video') return 'video' + return 'image' + } + }, + // 获取目标路径,可能数组中为字符串,对象的形式,额外可指定对象的目标属性名keyName + getSource(item) { + if (typeof item === 'string') return item + if (typeof item === 'object' && this.keyName) return item[this.keyName] + else uni.$u.error('请按格式传递列表参数') + return '' + }, + // 轮播切换事件 + change(e) { + // 当前的激活索引 + const { + current + } = e.detail + this.pauseVideo(this.currentIndex) + this.currentIndex = current + this.$emit('change', e.detail) + }, + // 切换轮播时,暂停视频播放 + pauseVideo(index) { + const lastItem = this.getSource(this.list[index]) + if (uni.$u.test.video(lastItem)) { + // 当视频隐藏时,暂停播放 + const video = uni.createVideoContext(`video-${index}`, this) + video.pause() + } + }, + // 当一个轮播item为视频时,获取它的视频海报 + getPoster(item) { + return typeof item === 'object' && item.poster ? item.poster : '' + }, + // 点击某个item + clickHandler(index) { + this.$emit('click', index) + } + }, + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-swiper { + @include flex; + justify-content: center; + align-items: center; + position: relative; + overflow: hidden; + + &__wrapper { + flex: 1; + + &__item { + flex: 1; + + &__wrapper { + @include flex; + position: relative; + overflow: hidden; + transition: transform 0.3s; + flex: 1; + + &__image { + flex: 1; + } + + &__video { + flex: 1; + } + + &__title { + position: absolute; + background-color: rgba(0, 0, 0, 0.3); + bottom: 0; + left: 0; + right: 0; + font-size: 28rpx; + padding: 12rpx 24rpx; + color: #FFFFFF; + flex: 1; + } + } + } + } + + &__indicator { + position: absolute; + bottom: 10px; + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-switch/props.js b/uni_modules/uview-ui/components/u-switch/props.js new file mode 100644 index 0000000..4eef963 --- /dev/null +++ b/uni_modules/uview-ui/components/u-switch/props.js @@ -0,0 +1,54 @@ +export default { + props: { + // 是否为加载中状态 + loading: { + type: Boolean, + default: uni.$u.props.switch.loading + }, + // 是否为禁用装填 + disabled: { + type: Boolean, + default: uni.$u.props.switch.disabled + }, + // 开关尺寸,单位px + size: { + type: [String, Number], + default: uni.$u.props.switch.size + }, + // 打开时的背景颜色 + activeColor: { + type: String, + default: uni.$u.props.switch.activeColor + }, + // 关闭时的背景颜色 + inactiveColor: { + type: String, + default: uni.$u.props.switch.inactiveColor + }, + // 通过v-model双向绑定的值 + value: { + type: [Boolean, String, Number], + default: uni.$u.props.switch.value + }, + // switch打开时的值 + activeValue: { + type: [String, Number, Boolean], + default: uni.$u.props.switch.activeValue + }, + // switch关闭时的值 + inactiveValue: { + type: [String, Number, Boolean], + default: uni.$u.props.switch.inactiveValue + }, + // 是否开启异步变更,开启后需要手动控制输入值 + asyncChange: { + type: Boolean, + default: uni.$u.props.switch.asyncChange + }, + // 圆点与外边框的距离 + space: { + type: [String, Number], + default: uni.$u.props.switch.space + } + } +} diff --git a/uni_modules/uview-ui/components/u-switch/u-switch.vue b/uni_modules/uview-ui/components/u-switch/u-switch.vue new file mode 100644 index 0000000..6f8577b --- /dev/null +++ b/uni_modules/uview-ui/components/u-switch/u-switch.vue @@ -0,0 +1,177 @@ +<template> + <view + class="u-switch" + :class="[disabled && 'u-switch--disabled']" + :style="[switchStyle, $u.addStyle(customStyle)]" + @tap="clickHandler" + > + <view + class="u-switch__bg" + :style="[bgStyle]" + > + </view> + <view + class="u-switch__node" + :class="[value && 'u-switch__node--on']" + :style="[nodeStyle]" + ref="u-switch__node" + > + <u-loading-icon + :show="loading" + mode="circle" + timingFunction='linear' + :color="value ? activeColor : '#AAABAD'" + :size="size * 0.6" + /> + </view> + </view> +</template> + +<script> + import props from './props.js'; + /** + * switch 开关选择器 + * @description 选择开关一般用于只有两个选择,且只能选其一的场景。 + * @tutorial https://www.uviewui.com/components/switch.html + * @property {Boolean} loading 是否处于加载中(默认 false ) + * @property {Boolean} disabled 是否禁用(默认 false ) + * @property {String | Number} size 开关尺寸,单位px (默认 25 ) + * @property {String} activeColor 打开时的背景色 (默认 '#2979ff' ) + * @property {String} inactiveColor 关闭时的背景色 (默认 '#ffffff' ) + * @property {Boolean | String | Number} value 通过v-model双向绑定的值 (默认 false ) + * @property {Boolean | String | Number} activeValue 打开选择器时通过change事件发出的值 (默认 true ) + * @property {Boolean | String | Number} inactiveValue 关闭选择器时通过change事件发出的值 (默认 false ) + * @property {Boolean} asyncChange 是否开启异步变更,开启后需要手动控制输入值 (默认 false ) + * @property {String | Number} space 圆点与外边框的距离 (默认 0 ) + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @event {Function} change 在switch打开或关闭时触发 + * @example <u-switch v-model="checked" active-color="red" inactive-color="#eee"></u-switch> + */ + export default { + name: "u-switch", + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + watch: { + value: { + immediate: true, + handler(n) { + if(n !== this.inactiveValue && n !== this.activeValue) { + uni.$u.error('v-model绑定的值必须为inactiveValue、activeValue二者之一') + } + } + } + }, + data() { + return { + bgColor: '#ffffff' + } + }, + computed: { + isActive(){ + return this.value === this.activeValue; + }, + switchStyle() { + let style = {} + // 这里需要加2,是为了腾出边框的距离,否则圆点node会和外边框紧贴在一起 + style.width = uni.$u.addUnit(this.size * 2 + 2) + style.height = uni.$u.addUnit(Number(this.size) + 2) + // style.borderColor = this.value ? 'rgba(0, 0, 0, 0)' : 'rgba(0, 0, 0, 0.12)' + // 如果自定义了“非激活”演示,name边框颜色设置为透明(跟非激活颜色一致) + // 这里不能简单的设置为非激活的颜色,否则打开状态时,会有边框,所以需要透明 + if(this.customInactiveColor) { + style.borderColor = 'rgba(0, 0, 0, 0)' + } + style.backgroundColor = this.isActive ? this.activeColor : this.inactiveColor + return style; + }, + nodeStyle() { + let style = {} + // 如果自定义非激活颜色,将node圆点的尺寸减少两个像素,让其与外边框距离更大一点 + style.width = uni.$u.addUnit(this.size - this.space) + style.height = uni.$u.addUnit(this.size - this.space) + const translateX = this.isActive ? uni.$u.addUnit(this.space) : uni.$u.addUnit(this.size); + style.transform = `translateX(-${translateX})` + return style + }, + bgStyle() { + let style = {} + // 这里配置一个多余的元素在HTML中,是为了让switch切换时,有更良好的背景色扩充体验(见实际效果) + style.width = uni.$u.addUnit(Number(this.size) * 2 - this.size / 2) + style.height = uni.$u.addUnit(this.size) + style.backgroundColor = this.inactiveColor + // 打开时,让此元素收缩,否则反之 + style.transform = `scale(${this.isActive ? 0 : 1})` + return style + }, + customInactiveColor() { + // 之所以需要判断是否自定义了“非激活”颜色,是为了让node圆点离外边框更宽一点的距离 + return this.inactiveColor !== '#fff' && this.inactiveColor !== '#ffffff' + } + }, + methods: { + clickHandler() { + if (!this.disabled && !this.loading) { + const oldValue = this.isActive ? this.inactiveValue : this.activeValue + if(!this.asyncChange) { + this.$emit('input', oldValue) + } + // 放到下一个生命周期,因为双向绑定的value修改父组件状态需要时间,且是异步的 + this.$nextTick(() => { + this.$emit('change', oldValue) + }) + } + } + } + }; +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-switch { + @include flex(row); + /* #ifndef APP-NVUE */ + box-sizing: border-box; + /* #endif */ + position: relative; + background-color: #fff; + border-width: 1px; + border-radius: 100px; + transition: background-color 0.4s; + border-color: rgba(0, 0, 0, 0.12); + border-style: solid; + justify-content: flex-end; + align-items: center; + // 由于weex为阿里逗着玩的KPI项目,导致bug奇多,这必须要写这一行, + // 否则在iOS上,点击页面任意地方,都会触发switch的点击事件 + overflow: hidden; + + &__node { + @include flex(row); + align-items: center; + justify-content: center; + border-radius: 100px; + background-color: #fff; + border-radius: 100px; + box-shadow: 1px 1px 1px 0 rgba(0, 0, 0, 0.25); + transition-property: transform; + transition-duration: 0.4s; + transition-timing-function: cubic-bezier(0.3, 1.05, 0.4, 1.05); + } + + &__bg { + position: absolute; + border-radius: 100px; + background-color: #FFFFFF; + transition-property: transform; + transition-duration: 0.4s; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + transition-timing-function: ease; + } + + &--disabled { + opacity: 0.6; + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-tabbar-item/props.js b/uni_modules/uview-ui/components/u-tabbar-item/props.js new file mode 100644 index 0000000..a2e6a24 --- /dev/null +++ b/uni_modules/uview-ui/components/u-tabbar-item/props.js @@ -0,0 +1,35 @@ +export default { + props: { + // item标签的名称,作为与u-tabbar的value参数匹配的标识符 + name: { + type: [String, Number, null], + default: uni.$u.props.tabbarItem.name + }, + // uView内置图标或者绝对路径的图片 + icon: { + icon: String, + default: uni.$u.props.tabbarItem.icon + }, + // 右上角的角标提示信息 + badge: { + type: [String, Number, null], + default: uni.$u.props.tabbarItem.badge + }, + // 是否显示圆点,将会覆盖badge参数 + dot: { + type: Boolean, + default: uni.$u.props.tabbarItem.dot + }, + // 描述文本 + text: { + type: String, + default: uni.$u.props.tabbarItem.text + }, + // 控制徽标的位置,对象或者字符串形式,可以设置top和right属性 + badgeStyle: { + type: [Object, String], + default: uni.$u.props.tabbarItem.badgeStyle + } + + } +} diff --git a/uni_modules/uview-ui/components/u-tabbar-item/u-tabbar-item.vue b/uni_modules/uview-ui/components/u-tabbar-item/u-tabbar-item.vue new file mode 100644 index 0000000..8ee00cf --- /dev/null +++ b/uni_modules/uview-ui/components/u-tabbar-item/u-tabbar-item.vue @@ -0,0 +1,142 @@ +<template> + <view + class="u-tabbar-item" + :style="[$u.addStyle(customStyle)]" + @tap="clickHandler" + > + <view class="u-tabbar-item__icon"> + <u-icon + v-if="icon" + :name="icon" + :color="isActive? parentData.activeColor : parentData.inactiveColor" + :size="20" + ></u-icon> + <template v-else> + <slot + v-if="isActive" + name="active-icon" + /> + <slot + v-else + name="inactive-icon" + /> + </template> + <u-badge + absolute + :offset="[0, dot ? '34rpx' : badge > 9 ? '14rpx' : '20rpx']" + :customStyle="badgeStyle" + :isDot="dot" + :value="badge || (dot ? 1 : null)" + :show="dot || badge > 0" + ></u-badge> + </view> + + <slot name="text"> + <text + class="u-tabbar-item__text" + :style="{ + color: isActive? parentData.activeColor : parentData.inactiveColor + }" + >{{ text }}</text> + </slot> + </view> +</template> + +<script> + import props from './props.js'; + /** + * TabbarItem 底部导航栏子组件 + * @description 此组件提供了自定义tabbar的能力。 + * @tutorial https://www.uviewui.com/components/tabbar.html + * @property {String | Number} name item标签的名称,作为与u-tabbar的value参数匹配的标识符 + * @property {String} icon uView内置图标或者绝对路径的图片 + * @property {String | Number} badge 右上角的角标提示信息 + * @property {Boolean} dot 是否显示圆点,将会覆盖badge参数(默认 false ) + * @property {String} text 描述文本 + * @property {Object | String} badgeStyle 控制徽标的位置,对象或者字符串形式,可以设置top和right属性(默认 'top: 6px;right:2px;' ) + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @example <u-tabbar :value="value2" :placeholder="false" @change="name => value2 = name" :fixed="false" :safeAreaInsetBottom="false"><u-tabbar-item text="首页" icon="home" dot ></u-tabbar-item></u-tabbar> + */ + export default { + name: 'u-tabbar-item', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + isActive: false, // 是否处于激活状态 + parentData: { + value: null, + activeColor: '', + inactiveColor: '' + } + } + }, + created() { + this.init() + }, + methods: { + init() { + // 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用 + this.updateParentData() + if (!this.parent) { + uni.$u.error('u-tabbar-item必须搭配u-tabbar组件使用') + } + // 本子组件在u-tabbar的children数组中的索引 + const index = this.parent.children.indexOf(this) + // 判断本组件的name(如果没有定义name,就用index索引)是否等于父组件的value参数 + this.isActive = (this.name || index) === this.parentData.value + }, + updateParentData() { + // 此方法在mixin中 + this.getParentData('u-tabbar') + }, + // 此方法将会被父组件u-tabbar调用 + updateFromParent() { + // 重新初始化 + this.init() + }, + clickHandler() { + this.$nextTick(() => { + const index = this.parent.children.indexOf(this) + const name = this.name || index + // 点击的item为非激活的item才发出change事件 + if (name !== this.parent.value) { + this.parent.$emit('change', name) + } + this.$emit('click', name) + }) + } + }, + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-tabbar-item { + @include flex(column); + align-items: center; + justify-content: center; + flex: 1; + + &__icon { + @include flex; + position: relative; + width: 150rpx; + justify-content: center; + } + + &__text { + margin-top: 2px; + font-size: 12px; + color: $u-content-color; + } + } + + /* #ifdef MP */ + // 由于小程序都使用shadow DOM形式实现,需要给影子宿主设置flex: 1才能让其撑开 + :host { + flex: 1 + } + /* #endif */ +</style> diff --git a/uni_modules/uview-ui/components/u-tabbar/props.js b/uni_modules/uview-ui/components/u-tabbar/props.js new file mode 100644 index 0000000..7f8171c --- /dev/null +++ b/uni_modules/uview-ui/components/u-tabbar/props.js @@ -0,0 +1,44 @@ +export default { + props: { + // 当前匹配项的name + value: { + type: [String, Number, null], + default: uni.$u.props.tabbar.value + }, + // 是否为iPhoneX留出底部安全距离 + safeAreaInsetBottom: { + type: Boolean, + default: uni.$u.props.tabbar.safeAreaInsetBottom + }, + // 是否显示上方边框 + border: { + type: Boolean, + default: uni.$u.props.tabbar.border + }, + // 元素层级z-index + zIndex: { + type: [String, Number], + default: uni.$u.props.tabbar.zIndex + }, + // 选中标签的颜色 + activeColor: { + type: String, + default: uni.$u.props.tabbar.activeColor + }, + // 未选中标签的颜色 + inactiveColor: { + type: String, + default: uni.$u.props.tabbar.inactiveColor + }, + // 是否固定在底部 + fixed: { + type: Boolean, + default: uni.$u.props.tabbar.fixed + }, + // fixed定位固定在底部时,是否生成一个等高元素防止塌陷 + placeholder: { + type: Boolean, + default: uni.$u.props.tabbar.placeholder + } + } +} diff --git a/uni_modules/uview-ui/components/u-tabbar/u-tabbar.vue b/uni_modules/uview-ui/components/u-tabbar/u-tabbar.vue new file mode 100644 index 0000000..953f33a --- /dev/null +++ b/uni_modules/uview-ui/components/u-tabbar/u-tabbar.vue @@ -0,0 +1,141 @@ +<template> + <view class="u-tabbar"> + <view + class="u-tabbar__content" + ref="u-tabbar__content" + @touchmove.stop.prevent="noop" + :class="[border && 'u-border-top', fixed && 'u-tabbar--fixed']" + :style="[tabbarStyle]" + > + <view class="u-tabbar__content__item-wrapper"> + <slot /> + </view> + <u-safe-bottom v-if="safeAreaInsetBottom"></u-safe-bottom> + </view> + <view + class="u-tabbar__placeholder" + v-if="placeholder" + :style="{ + height: placeholderHeight + 'px', + }" + ></view> + </view> +</template> + +<script> + import props from './props.js'; + // #ifdef APP-NVUE + const dom = uni.requireNativePlugin('dom') + // #endif + /** + * Tabbar 底部导航栏 + * @description 此组件提供了自定义tabbar的能力。 + * @tutorial https://www.uviewui.com/components/tabbar.html + * @property {String | Number} value 当前匹配项的name + * @property {Boolean} safeAreaInsetBottom 是否为iPhoneX留出底部安全距离(默认 true ) + * @property {Boolean} border 是否显示上方边框(默认 true ) + * @property {String | Number} zIndex 元素层级z-index(默认 1 ) + * @property {String} activeColor 选中标签的颜色(默认 '#1989fa' ) + * @property {String} inactiveColor 未选中标签的颜色(默认 '#7d7e80' ) + * @property {Boolean} fixed 是否固定在底部(默认 true ) + * @property {Boolean} placeholder fixed定位固定在底部时,是否生成一个等高元素防止塌陷(默认 true ) + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @example <u-tabbar :value="value2" :placeholder="false" @change="name => value2 = name" :fixed="false" :safeAreaInsetBottom="false"><u-tabbar-item text="首页" icon="home" dot ></u-tabbar-item></u-tabbar> + */ + export default { + name: 'u-tabbar', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + placeholderHeight: 0 + } + }, + computed: { + tabbarStyle() { + const style = { + zIndex: this.zIndex + } + // 合并来自父组件的customStyle样式 + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) + }, + // 监听多个参数的变化,通过在computed执行对应的操作 + updateChild() { + return [this.value, this.activeColor, this.inactiveColor] + }, + updatePlaceholder() { + return [this.fixed, this.placeholder] + } + }, + watch: { + updateChild() { + // 如果updateChildren中的元素发生了变化,则执行子元素初始化操作 + this.updateChildren() + }, + updatePlaceholder() { + // 如果fixed,placeholder等参数发生变化,重新计算占位元素的高度 + this.setPlaceholderHeight() + } + }, + created() { + this.children = [] + }, + mounted() { + this.setPlaceholderHeight() + }, + methods: { + updateChildren() { + // 如果存在子元素,则执行子元素的updateFromParent进行更新数据 + this.children.length && this.children.map(child => child.updateFromParent()) + }, + // 设置用于防止塌陷元素的高度 + async setPlaceholderHeight() { + if (!this.fixed || !this.placeholder) return + // 延时一定时间 + await uni.$u.sleep(20) + // #ifndef APP-NVUE + this.$uGetRect('.u-tabbar__content').then(({height = 50}) => { + // 修复IOS safearea bottom 未填充高度 + this.placeholderHeight = height + }) + // #endif + + // #ifdef APP-NVUE + dom.getComponentRect(this.$refs['u-tabbar__content'], (res) => { + const { + size + } = res + this.placeholderHeight = size.height + }) + // #endif + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-tabbar { + @include flex(column); + flex: 1; + justify-content: center; + + &__content { + @include flex(column); + background-color: #fff; + + &__item-wrapper { + height: 50px; + @include flex(row); + } + } + + &--fixed { + position: fixed; + bottom: 0; + left: 0; + right: 0; + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-table/props.js b/uni_modules/uview-ui/components/u-table/props.js new file mode 100644 index 0000000..7c11331 --- /dev/null +++ b/uni_modules/uview-ui/components/u-table/props.js @@ -0,0 +1,5 @@ +export default { + props: { + + } +} diff --git a/uni_modules/uview-ui/components/u-table/u-table.vue b/uni_modules/uview-ui/components/u-table/u-table.vue new file mode 100644 index 0000000..b64ce69 --- /dev/null +++ b/uni_modules/uview-ui/components/u-table/u-table.vue @@ -0,0 +1,29 @@ +<template> + <view class="u-table"> + + </view> +</template> + +<script> + import props from './props.js'; + /** + * Table 表格 + * @description 表格组件一般用于展示大量结构化数据的场景 本组件标签类似HTML的table表格,由table、tr、th、td四个组件组成 + * @tutorial https://www.uviewui.com/components/table.html + * @example <u-table><u-tr><u-th>学校</u-th </u-tr> <u-tr><u-td>浙江大学</u-td> </u-tr> <u-tr><u-td>清华大学</u-td> </u-tr></u-table> + */ + export default { + name: 'u-table', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + +</style> diff --git a/uni_modules/uview-ui/components/u-tabs-item/props.js b/uni_modules/uview-ui/components/u-tabs-item/props.js new file mode 100644 index 0000000..7c11331 --- /dev/null +++ b/uni_modules/uview-ui/components/u-tabs-item/props.js @@ -0,0 +1,5 @@ +export default { + props: { + + } +} diff --git a/uni_modules/uview-ui/components/u-tabs-item/u-tabs-item.vue b/uni_modules/uview-ui/components/u-tabs-item/u-tabs-item.vue new file mode 100644 index 0000000..effb796 --- /dev/null +++ b/uni_modules/uview-ui/components/u-tabs-item/u-tabs-item.vue @@ -0,0 +1,29 @@ +<template> + <swiper-item> + <slot /> + </swiper-item> +</template> + +<script> + import props from './props.js'; + /** + * TabsItem tabs标签组件的自组件 + * @description tabs标签组件,在标签多的时候,可以配置为左右滑动,标签少的时候,可以禁止滑动。 该组件的一个特点是配置为滚动模式时,激活的tab会自动移动到组件的中间位置。 + * @tutorial https://www.uviewui.com/components/tabs.html + * @property {type} prop_name + * @event {Function()} + * @example + */ + export default { + name: 'u-tabs-item', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + + } + } + } +</script> + +<style> +</style> diff --git a/uni_modules/uview-ui/components/u-tabs/props.js b/uni_modules/uview-ui/components/u-tabs/props.js new file mode 100644 index 0000000..2cfa41f --- /dev/null +++ b/uni_modules/uview-ui/components/u-tabs/props.js @@ -0,0 +1,64 @@ +export default { + props: { + // 滑块的移动过渡时间,单位ms + duration: { + type: Number, + default: uni.$u.props.tabs.duration + }, + // tabs标签数组 + list: { + type: Array, + default: uni.$u.props.tabs.list + }, + // 滑块颜色 + lineColor: { + type: String, + default: uni.$u.props.tabs.lineColor + }, + // 菜单选择中时的样式 + activeStyle: { + type: [String, Object], + default: uni.$u.props.tabs.activeStyle + }, + // 菜单非选中时的样式 + inactiveStyle: { + type: [String, Object], + default: uni.$u.props.tabs.inactiveStyle + }, + // 滑块长度 + lineWidth: { + type: [String, Number], + default: uni.$u.props.tabs.lineWidth + }, + // 滑块高度 + lineHeight: { + type: [String, Number], + default: uni.$u.props.tabs.lineHeight + }, + // 滑块背景显示大小,当滑块背景设置为图片时使用 + lineBgSize: { + type: String, + default: uni.$u.props.tabs.lineBgSize + }, + // 菜单item的样式 + itemStyle: { + type: [String, Object], + default: uni.$u.props.tabs.itemStyle + }, + // 菜单是否可滚动 + scrollable: { + type: Boolean, + default: uni.$u.props.tabs.scrollable + }, + // 当前选中标签的索引 + current: { + type: [Number, String], + default: uni.$u.props.tabs.current + }, + // 默认读取的键名 + keyName: { + type: String, + default: uni.$u.props.tabs.keyName + } + } +} diff --git a/uni_modules/uview-ui/components/u-tabs/u-tabs.vue b/uni_modules/uview-ui/components/u-tabs/u-tabs.vue new file mode 100644 index 0000000..9c54cc1 --- /dev/null +++ b/uni_modules/uview-ui/components/u-tabs/u-tabs.vue @@ -0,0 +1,354 @@ +<template> + <view class="u-tabs"> + <view class="u-tabs__wrapper"> + <slot name="left" /> + <view class="u-tabs__wrapper__scroll-view-wrapper"> + <scroll-view + :scroll-x="scrollable" + :scroll-left="scrollLeft" + scroll-with-animation + class="u-tabs__wrapper__scroll-view" + :show-scrollbar="false" + ref="u-tabs__wrapper__scroll-view" + > + <view + class="u-tabs__wrapper__nav" + ref="u-tabs__wrapper__nav" + > + <view + class="u-tabs__wrapper__nav__item" + v-for="(item, index) in list" + :key="index" + @tap="clickHandler(item, index)" + :ref="`u-tabs__wrapper__nav__item-${index}`" + :style="[$u.addStyle(itemStyle), {flex: scrollable ? '' : 1}]" + :class="[`u-tabs__wrapper__nav__item-${index}`, item.disabled && 'u-tabs__wrapper__nav__item--disabled']" + > + <text + :class="[item.disabled && 'u-tabs__wrapper__nav__item__text--disabled']" + class="u-tabs__wrapper__nav__item__text" + :style="[textStyle(index)]" + >{{ item[keyName] }}</text> + <u-badge + :show="!!(item.badge && (item.badge.show || item.badge.isDot || item.badge.value))" + :isDot="item.badge && item.badge.isDot || propsBadge.isDot" + :value="item.badge && item.badge.value || propsBadge.value" + :max="item.badge && item.badge.max || propsBadge.max" + :type="item.badge && item.badge.type || propsBadge.type" + :showZero="item.badge && item.badge.showZero || propsBadge.showZero" + :bgColor="item.badge && item.badge.bgColor || propsBadge.bgColor" + :color="item.badge && item.badge.color || propsBadge.color" + :shape="item.badge && item.badge.shape || propsBadge.shape" + :numberType="item.badge && item.badge.numberType || propsBadge.numberType" + :inverted="item.badge && item.badge.inverted || propsBadge.inverted" + customStyle="margin-left: 4px;" + ></u-badge> + </view> + <!-- #ifdef APP-NVUE --> + <view + class="u-tabs__wrapper__nav__line" + ref="u-tabs__wrapper__nav__line" + :style="[{ + width: $u.addUnit(lineWidth), + height: $u.addUnit(lineHeight), + background: lineColor, + backgroundSize: lineBgSize, + }]" + > + <!-- #endif --> + <!-- #ifndef APP-NVUE --> + <view + class="u-tabs__wrapper__nav__line" + ref="u-tabs__wrapper__nav__line" + :style="[{ + width: $u.addUnit(lineWidth), + transform: `translate(${lineOffsetLeft}px)`, + transitionDuration: `${firstTime ? 0 : duration}ms`, + height: $u.addUnit(lineHeight), + background: lineColor, + backgroundSize: lineBgSize, + }]" + > + <!-- #endif --> + </view> + </view> + </scroll-view> + </view> + <slot name="right" /> + </view> + </view> +</template> + +<script> + // #ifdef APP-NVUE + const animation = uni.requireNativePlugin('animation') + const dom = uni.requireNativePlugin('dom') + // #endif + import props from './props.js'; + /** + * Tabs 标签 + * @description tabs标签组件,在标签多的时候,可以配置为左右滑动,标签少的时候,可以禁止滑动。 该组件的一个特点是配置为滚动模式时,激活的tab会自动移动到组件的中间位置。 + * @tutorial https://www.uviewui.com/components/tabs.html + * @property {String | Number} duration 滑块移动一次所需的时间,单位秒(默认 200 ) + * @property {String | Number} swierWidth swiper的宽度(默认 '750rpx' ) + * @property {String} keyName 从`list`元素对象中读取的键名(默认 'name' ) + * @event {Function(index)} change 标签改变时触发 index: 点击了第几个tab,索引从0开始 + * @event {Function(index)} click 点击标签时触发 index: 点击了第几个tab,索引从0开始 + * @example <u-tabs :list="list" :is-scroll="false" :current="current" @change="change"></u-tabs> + */ + export default { + name: 'u-tabs', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + firstTime: true, + scrollLeft: 0, + scrollViewWidth: 0, + lineOffsetLeft: 0, + tabsRect: { + left: 0 + }, + innerCurrent: 0, + moving: false, + } + }, + watch: { + current: { + immediate: true, + handler (newValue, oldValue) { + // 内外部值不相等时,才尝试移动滑块 + if (newValue !== this.innerCurrent) { + this.innerCurrent = newValue + this.$nextTick(() => { + this.resize() + }) + } + } + }, + // list变化时,重新渲染list各项信息 + list() { + this.$nextTick(() => { + this.resize() + }) + } + }, + computed: { + textStyle() { + return index => { + const style = {} + // 取当期是否激活的样式 + const customeStyle = index === this.innerCurrent ? uni.$u.addStyle(this.activeStyle) : uni.$u + .addStyle( + this.inactiveStyle) + // 如果当前菜单被禁用,则加上对应颜色,需要在此做处理,是因为nvue下,无法在style样式中通过!import覆盖标签的内联样式 + if (this.list[index].disabled) { + style.color = '#c8c9cc' + } + return uni.$u.deepMerge(customeStyle, style) + } + }, + propsBadge() { + return uni.$u.props.badge + } + }, + async mounted() { + this.init() + }, + methods: { + setLineLeft() { + const tabItem = this.list[this.innerCurrent]; + if (!tabItem) { + return; + } + // 获取滑块该移动的位置 + let lineOffsetLeft = this.list + .slice(0, this.innerCurrent) + .reduce((total, curr) => total + curr.rect.width, 0); + // 获取下划线的数值px表示法 + const lineWidth = uni.$u.getPx(this.lineWidth); + this.lineOffsetLeft = lineOffsetLeft + (tabItem.rect.width - lineWidth) / 2 + // #ifdef APP-NVUE + // 第一次移动滑块,无需过渡时间 + this.animation(this.lineOffsetLeft, this.firstTime ? 0 : parseInt(this.duration)) + // #endif + + // 如果是第一次执行此方法,让滑块在初始化时,瞬间滑动到第一个tab item的中间 + // 这里需要一个定时器,因为在非nvue下,是直接通过style绑定过渡时间,需要等其过渡完成后,再设置为false(非第一次移动滑块) + if (this.firstTime) { + setTimeout(() => { + this.firstTime = false + }, 10); + } + }, + // nvue下设置滑块的位置 + animation(x, duration = 0) { + // #ifdef APP-NVUE + const ref = this.$refs['u-tabs__wrapper__nav__line'] + animation.transition(ref, { + styles: { + transform: `translateX(${x}px)` + }, + duration + }) + // #endif + }, + // 点击某一个标签 + clickHandler(item, index) { + // 因为标签可能为disabled状态,所以click是一定会发出的,但是change事件是需要可用的状态才发出 + this.$emit('click', { + ...item, + index + }) + // 如果disabled状态,返回 + if (item.disabled) return + this.innerCurrent = index + this.resize() + this.$emit('change', { + ...item, + index + }) + }, + init() { + uni.$u.sleep().then(() => { + this.resize() + }) + }, + setScrollLeft() { + // 当前活动tab的布局信息,有tab菜单的width和left(为元素左边界到父元素左边界的距离)等信息 + const tabRect = this.list[this.innerCurrent] + // 累加得到当前item到左边的距离 + const offsetLeft = this.list + .slice(0, this.innerCurrent) + .reduce((total, curr) => { + return total + curr.rect.width + }, 0) + // 此处为屏幕宽度 + const windowWidth = uni.$u.sys().windowWidth + // 将活动的tabs-item移动到屏幕正中间,实际上是对scroll-view的移动 + let scrollLeft = offsetLeft - (this.tabsRect.width - tabRect.rect.width) / 2 - (windowWidth - this.tabsRect + .right) / 2 + this.tabsRect.left / 2 + // 这里做一个限制,限制scrollLeft的最大值为整个scroll-view宽度减去tabs组件的宽度 + scrollLeft = Math.min(scrollLeft, this.scrollViewWidth - this.tabsRect.width) + this.scrollLeft = Math.max(0, scrollLeft) + }, + // 获取所有标签的尺寸 + resize() { + // 如果不存在list,则不处理 + if(this.list.length === 0) { + return + } + Promise.all([this.getTabsRect(), this.getAllItemRect()]).then(([tabsRect, itemRect = []]) => { + this.tabsRect = tabsRect + this.scrollViewWidth = 0 + itemRect.map((item, index) => { + // 计算scroll-view的宽度,这里 + this.scrollViewWidth += item.width + // 另外计算每一个item的中心点X轴坐标 + this.list[index].rect = item + }) + // 获取了tabs的尺寸之后,设置滑块的位置 + this.setLineLeft() + this.setScrollLeft() + }) + }, + // 获取导航菜单的尺寸 + getTabsRect() { + return new Promise(resolve => { + this.queryRect('u-tabs__wrapper__scroll-view').then(size => resolve(size)) + }) + }, + // 获取所有标签的尺寸 + getAllItemRect() { + return new Promise(resolve => { + const promiseAllArr = this.list.map((item, index) => this.queryRect( + `u-tabs__wrapper__nav__item-${index}`, true)) + Promise.all(promiseAllArr).then(sizes => resolve(sizes)) + }) + }, + // 获取各个标签的尺寸 + queryRect(el, item) { + // #ifndef APP-NVUE + // $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://www.uviewui.com/js/getRect.html + // 组件内部一般用this.$uGetRect,对外的为uni.$u.getRect,二者功能一致,名称不同 + return new Promise(resolve => { + this.$uGetRect(`.${el}`).then(size => { + resolve(size) + }) + }) + // #endif + + // #ifdef APP-NVUE + // nvue下,使用dom模块查询元素高度 + // 返回一个promise,让调用此方法的主体能使用then回调 + return new Promise(resolve => { + dom.getComponentRect(item ? this.$refs[el][0] : this.$refs[el], res => { + resolve(res.size) + }) + }) + // #endif + }, + }, + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-tabs { + + &__wrapper { + @include flex; + align-items: center; + + &__scroll-view-wrapper { + flex: 1; + /* #ifndef APP-NVUE */ + overflow: auto hidden; + /* #endif */ + } + + &__scroll-view { + @include flex; + flex: 1; + } + + &__nav { + @include flex; + position: relative; + + &__item { + padding: 0 11px; + @include flex; + align-items: center; + justify-content: center; + + &--disabled { + /* #ifndef APP-NVUE */ + cursor: not-allowed; + /* #endif */ + } + + &__text { + font-size: 15px; + color: $u-content-color; + + &--disabled { + color: $u-disabled-color !important; + } + } + } + + &__line { + height: 3px; + background: $u-primary; + width: 30px; + position: absolute; + bottom: 2px; + border-radius: 100px; + transition-property: transform; + transition-duration: 300ms; + } + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-tag/props.js b/uni_modules/uview-ui/components/u-tag/props.js new file mode 100644 index 0000000..6bffaa2 --- /dev/null +++ b/uni_modules/uview-ui/components/u-tag/props.js @@ -0,0 +1,84 @@ +export default { + props: { + // 标签类型info、primary、success、warning、error + type: { + type: String, + default: uni.$u.props.tag.type + }, + // 不可用 + disabled: { + type: [Boolean, String], + default: uni.$u.props.tag.disabled + }, + // 标签的大小,large,medium,mini + size: { + type: String, + default: uni.$u.props.tag.size + }, + // tag的形状,circle(两边半圆形), square(方形,带圆角) + shape: { + type: String, + default: uni.$u.props.tag.shape + }, + // 标签文字 + text: { + type: [String, Number], + default: uni.$u.props.tag.text + }, + // 背景颜色,默认为空字符串,即不处理 + bgColor: { + type: String, + default: uni.$u.props.tag.bgColor + }, + // 标签字体颜色,默认为空字符串,即不处理 + color: { + type: String, + default: uni.$u.props.tag.color + }, + // 标签的边框颜色 + borderColor: { + type: String, + default: uni.$u.props.tag.borderColor + }, + // 关闭按钮图标的颜色 + closeColor: { + type: String, + default: uni.$u.props.tag.closeColor + }, + // 点击时返回的索引值,用于区分例遍的数组哪个元素被点击了 + name: { + type: [String, Number], + default: uni.$u.props.tag.name + }, + // // 模式选择,dark|light|plain + // mode: { + // type: String, + // default: 'light' + // }, + // 镂空时是否填充背景色 + plainFill: { + type: Boolean, + default: uni.$u.props.tag.plainFill + }, + // 是否镂空 + plain: { + type: Boolean, + default: uni.$u.props.tag.plain + }, + // 是否可关闭 + closable: { + type: Boolean, + default: uni.$u.props.tag.closable + }, + // 是否显示 + show: { + type: Boolean, + default: uni.$u.props.tag.show + }, + // 内置图标,或绝对路径的图片 + icon: { + type: String, + default: uni.$u.props.tag.icon + } + } +} diff --git a/uni_modules/uview-ui/components/u-tag/u-tag.vue b/uni_modules/uview-ui/components/u-tag/u-tag.vue new file mode 100644 index 0000000..95f33c4 --- /dev/null +++ b/uni_modules/uview-ui/components/u-tag/u-tag.vue @@ -0,0 +1,358 @@ +<template> + <u-transition + mode="fade" + :show="show" + > + <view class="u-tag-wrapper"> + <view + class="u-tag" + :class="[`u-tag--${shape}`, !plain && `u-tag--${type}`, plain && `u-tag--${type}--plain`, `u-tag--${size}`, plain && plainFill && `u-tag--${type}--plain--fill`]" + @tap.stop="clickHandler" + :style="[{ + marginRight: closable ? '10px' : 0, + marginTop: closable ? '10px' : 0, + }, style]" + > + <slot name="icon"> + <view + class="u-tag__icon" + v-if="icon" + > + <image + v-if="$u.test.image(icon)" + :src="icon" + :style="[imgStyle]" + ></image> + <u-icon + v-else + :color="elIconColor" + :name="icon" + :size="iconSize" + ></u-icon> + </view> + </slot> + <text + class="u-tag__text" + :style="[textColor]" + :class="[`u-tag__text--${type}`, plain && `u-tag__text--${type}--plain`, `u-tag__text--${size}`]" + >{{ text }}</text> + </view> + <view + class="u-tag__close" + :class="[`u-tag__close--${size}`]" + v-if="closable" + @tap.stop="closeHandler" + :style="{backgroundColor: closeColor}" + > + <u-icon + name="close" + :size="closeSize" + color="#ffffff" + ></u-icon> + </view> + </view> + </u-transition> +</template> + +<script> + import props from './props.js'; + /** + * Tag 标签 + * @description tag组件一般用于标记和选择,我们提供了更加丰富的表现形式,能够较全面的涵盖您的使用场景 + * @tutorial https://www.uviewui.com/components/tag.html + * @property {String} type 标签类型info、primary、success、warning、error (默认 'primary' ) + * @property {Boolean | String} disabled 不可用(默认 false ) + * @property {String} size 标签的大小,large,medium,mini (默认 'medium' ) + * @property {String} shape tag的形状,circle(两边半圆形), square(方形,带圆角)(默认 'square' ) + * @property {String | Number} text 标签的文字内容 + * @property {String} bgColor 背景颜色,默认为空字符串,即不处理 + * @property {String} color 标签字体颜色,默认为空字符串,即不处理 + * @property {String} borderColor 镂空形式标签的边框颜色 + * @property {String} closeColor 关闭按钮图标的颜色(默认 #C6C7CB) + * @property {String | Number} name 点击时返回的索引值,用于区分例遍的数组哪个元素被点击了 + * @property {Boolean} plainFill 镂空时是否填充背景色(默认 false ) + * @property {Boolean} plain 是否镂空(默认 false ) + * @property {Boolean} closable 是否可关闭,设置为true,文字右边会出现一个关闭图标(默认 false ) + * @property {Boolean} show 标签显示与否(默认 true ) + * @property {String} icon 内置图标,或绝对路径的图片 + * @event {Function(index)} click 点击标签时触发 index: 传递的index参数值 + * @event {Function(index)} close closable为true时,点击标签关闭按钮触发 index: 传递的index参数值 + * @example <u-tag text="标签" type="error" plain plainFill></u-tag> + */ + export default { + name: 'u-tag', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + + } + }, + computed: { + style() { + const style = {} + if (this.bgColor) { + style.backgroundColor = this.bgColor + } + if (this.color) { + style.color = this.color + } + if(this.borderColor) { + style.borderColor = this.borderColor + } + return style + }, + // nvue下,文本颜色无法继承父元素 + textColor() { + const style = {} + if (this.color) { + style.color = this.color + } + return style + }, + imgStyle() { + const width = this.size === 'large' ? '17px' : this.size === 'medium' ? '15px' : '13px' + return { + width, + height: width + } + }, + // 文本的样式 + closeSize() { + const size = this.size === 'large' ? 15 : this.size === 'medium' ? 13 : 12 + return size + }, + // 图标大小 + iconSize() { + const size = this.size === 'large' ? 21 : this.size === 'medium' ? 19 : 16 + return size + }, + // 图标颜色 + elIconColor() { + return this.iconColor ? this.iconColor : this.plain ? this.type : '#ffffff' + } + }, + methods: { + // 点击关闭按钮 + closeHandler() { + this.$emit('close', this.name) + }, + // 点击标签 + clickHandler() { + this.$emit('click', this.name) + } + } + } +</script> + +<style + lang="scss" + scoped +> + @import "../../libs/css/components.scss"; + + .u-tag-wrapper { + position: relative; + } + + .u-tag { + @include flex; + align-items: center; + border-style: solid; + + &--circle { + border-radius: 100px; + } + + &--square { + border-radius: 3px; + } + + &__icon { + margin-right: 4px; + } + + &__text { + &--mini { + font-size: 12px; + line-height: 12px; + } + + &--medium { + font-size: 13px; + line-height: 13px; + } + + &--large { + font-size: 15px; + line-height: 15px; + } + } + + &--mini { + height: 22px; + line-height: 22px; + padding: 0 5px; + } + + &--medium { + height: 26px; + line-height: 22px; + padding: 0 10px; + } + + &--large { + height: 32px; + line-height: 32px; + padding: 0 15px; + } + + &--primary { + background-color: $u-primary; + border-width: 1px; + border-color: $u-primary; + } + + &--primary--plain { + border-width: 1px; + border-color: $u-primary; + } + + &--primary--plain--fill { + background-color: #ecf5ff; + } + + &__text--primary { + color: #FFFFFF; + } + + &__text--primary--plain { + color: $u-primary; + } + + &--error { + background-color: $u-error; + border-width: 1px; + border-color: $u-error; + } + + &--error--plain { + border-width: 1px; + border-color: $u-error; + } + + &--error--plain--fill { + background-color: #fef0f0; + } + + &__text--error { + color: #FFFFFF; + } + + &__text--error--plain { + color: $u-error; + } + + &--warning { + background-color: $u-warning; + border-width: 1px; + border-color: $u-warning; + } + + &--warning--plain { + border-width: 1px; + border-color: $u-warning; + } + + &--warning--plain--fill { + background-color: #fdf6ec; + } + + &__text--warning { + color: #FFFFFF; + } + + &__text--warning--plain { + color: $u-warning; + } + + &--success { + background-color: $u-success; + border-width: 1px; + border-color: $u-success; + } + + &--success--plain { + border-width: 1px; + border-color: $u-success; + } + + &--success--plain--fill { + background-color: #f5fff0; + } + + &__text--success { + color: #FFFFFF; + } + + &__text--success--plain { + color: $u-success; + } + + &--info { + background-color: $u-info; + border-width: 1px; + border-color: $u-info; + } + + &--info--plain { + border-width: 1px; + border-color: $u-info; + } + + &--info--plain--fill { + background-color: #f4f4f5; + } + + &__text--info { + color: #FFFFFF; + } + + &__text--info--plain { + color: $u-info; + } + + &__close { + position: absolute; + z-index: 999; + top: 10px; + right: 10px; + border-radius: 100px; + background-color: #C6C7CB; + @include flex(row); + align-items: center; + justify-content: center; + /* #ifndef APP-NVUE */ + transform: scale(0.6) translate(80%, -80%); + /* #endif */ + /* #ifdef APP-NVUE */ + transform: scale(0.6) translate(50%, -50%); + /* #endif */ + + &--mini { + width: 18px; + height: 18px; + } + + &--medium { + width: 22px; + height: 22px; + } + + &--large { + width: 25px; + height: 25px; + } + } + + } +</style> diff --git a/uni_modules/uview-ui/components/u-td/props.js b/uni_modules/uview-ui/components/u-td/props.js new file mode 100644 index 0000000..7c11331 --- /dev/null +++ b/uni_modules/uview-ui/components/u-td/props.js @@ -0,0 +1,5 @@ +export default { + props: { + + } +} diff --git a/uni_modules/uview-ui/components/u-td/u-td.vue b/uni_modules/uview-ui/components/u-td/u-td.vue new file mode 100644 index 0000000..600dce5 --- /dev/null +++ b/uni_modules/uview-ui/components/u-td/u-td.vue @@ -0,0 +1,31 @@ +<template> + <view class="u-td"> + + </view> +</template> + +<script> + import props from './props.js'; + /** + * Td 表格中的单元格 + * @description + * @tutorial url + * @property {String | Number} + * @event {Function} + * @example + */ + export default { + name: 'u-td', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + +</style> diff --git a/uni_modules/uview-ui/components/u-text/props.js b/uni_modules/uview-ui/components/u-text/props.js new file mode 100644 index 0000000..85a0836 --- /dev/null +++ b/uni_modules/uview-ui/components/u-text/props.js @@ -0,0 +1,110 @@ +export default { + props: { + // 主题颜色 + type: { + type: String, + default: uni.$u.props.text.type + }, + // 是否显示 + show: { + type: Boolean, + default: uni.$u.props.text.show + }, + // 显示的值 + text: { + type: [String, Number], + default: uni.$u.props.text.text + }, + // 前置图标 + prefixIcon: { + type: String, + default: uni.$u.props.text.prefixIcon + }, + // 后置图标 + suffixIcon: { + type: String, + default: uni.$u.props.text.suffixIcon + }, + // 文本处理的匹配模式 + // text-普通文本,price-价格,phone-手机号,name-姓名,date-日期,link-超链接 + mode: { + type: String, + default: uni.$u.props.text.mode + }, + // mode=link下,配置的链接 + href: { + type: String, + default: uni.$u.props.text.href + }, + // 格式化规则 + format: { + type: [String, Function], + default: uni.$u.props.text.format + }, + // mode=phone时,点击文本是否拨打电话 + call: { + type: Boolean, + default: uni.$u.props.text.call + }, + // 小程序的打开方式 + openType: { + type: String, + default: uni.$u.props.text.openType + }, + // 是否粗体,默认normal + bold: { + type: Boolean, + default: uni.$u.props.text.bold + }, + // 是否块状 + block: { + type: Boolean, + default: uni.$u.props.text.block + }, + // 文本显示的行数,如果设置,超出此行数,将会显示省略号 + lines: { + type: [String, Number], + default: uni.$u.props.text.lines + }, + // 文本颜色 + color: { + type: String, + default: uni.$u.props.text.color + }, + // 字体大小 + size: { + type: [String, Number], + default: uni.$u.props.text.size + }, + // 图标的样式 + iconStyle: { + type: [Object, String], + default: uni.$u.props.text.iconStyle + }, + // 文字装饰,下划线,中划线等,可选值 none|underline|line-through + decoration: { + tepe: String, + default: uni.$u.props.text.decoration + }, + // 外边距,对象、字符串,数值形式均可 + margin: { + type: [Object, String, Number], + default: uni.$u.props.text.margin + }, + // 文本行高 + lineHeight: { + type: [String, Number], + default: uni.$u.props.text.lineHeight + }, + // 文本对齐方式,可选值left|center|right + align: { + type: String, + default: uni.$u.props.text.align + }, + // 文字换行,可选值break-word|normal|anywhere + wordWrap: { + type: String, + default: uni.$u.props.text.wordWrap + } + } +} diff --git a/uni_modules/uview-ui/components/u-text/u-text.vue b/uni_modules/uview-ui/components/u-text/u-text.vue new file mode 100644 index 0000000..99d0809 --- /dev/null +++ b/uni_modules/uview-ui/components/u-text/u-text.vue @@ -0,0 +1,223 @@ +<template> + <view + class="u-text" + :class="[]" + v-if="show" + :style="{ + margin: margin, + justifyContent: align === 'left' ? 'flex-start' : align === 'center' ? 'center' : 'flex-end' + }" + @tap="clickHandler" + > + <text + :class="['u-text__price', type && `u-text__value--${type}`]" + v-if="mode === 'price'" + :style="[valueStyle]" + >¥</text + > + <view class="u-text__prefix-icon" v-if="prefixIcon"> + <u-icon + :name="prefixIcon" + :customStyle="$u.addStyle(iconStyle)" + ></u-icon> + </view> + <u-link + v-if="mode === 'link'" + :text="value" + :href="href" + underLine + ></u-link> + <template v-else-if="openType && isMp"> + <button + class="u-reset-button u-text__value" + :style="[valueStyle]" + :data-index="index" + :openType="openType" + @getuserinfo="onGetUserInfo" + @contact="onContact" + @getphonenumber="onGetPhoneNumber" + @error="onError" + @launchapp="onLaunchApp" + @opensetting="onOpenSetting" + :lang="lang" + :session-from="sessionFrom" + :send-message-title="sendMessageTitle" + :send-message-path="sendMessagePath" + :send-message-img="sendMessageImg" + :show-message-card="showMessageCard" + :app-parameter="appParameter" + > + {{ value }} + </button> + </template> + <text + v-else + class="u-text__value" + :style="[valueStyle]" + :class="[ + type && `u-text__value--${type}`, + lines && `u-line-${lines}` + ]" + >{{ value }}</text + > + <view class="u-text__suffix-icon" v-if="suffixIcon"> + <u-icon + :name="suffixIcon" + :customStyle="$u.addStyle(iconStyle)" + ></u-icon> + </view> + </view> +</template> + +<script> +import value from './value.js' +import button from '../../libs/mixin/button.js' +import openType from '../../libs/mixin/openType.js' +import props from './props.js' +/** + * Text 文本 + * @description 此组件集成了文本类在项目中的常用功能,包括状态,拨打电话,格式化日期,*替换,超链接...等功能。 您大可不必在使用特殊文本时自己定义,text组件几乎涵盖您能使用的大部分场景。 + * @tutorial https://www.uviewui.com/components/loading.html + * @property {String} type 主题颜色 + * @property {Boolean} show 是否显示(默认 true ) + * @property {String | Number} text 显示的值 + * @property {String} prefixIcon 前置图标 + * @property {String} suffixIcon 后置图标 + * @property {String} mode 文本处理的匹配模式 text-普通文本,price-价格,phone-手机号,name-姓名,date-日期,link-超链接 + * @property {String} href mode=link下,配置的链接 + * @property {String | Function} format 格式化规则 + * @property {Boolean} call mode=phone时,点击文本是否拨打电话(默认 false ) + * @property {String} openType 小程序的打开方式 + * @property {Boolean} bold 是否粗体,默认normal(默认 false ) + * @property {Boolean} block 是否块状(默认 false ) + * @property {String | Number} lines 文本显示的行数,如果设置,超出此行数,将会显示省略号 + * @property {String} color 文本颜色(默认 '#303133' ) + * @property {String | Number} size 字体大小(默认 15 ) + * @property {Object | String} iconStyle 图标的样式 (默认 {fontSize: '15px'} ) + * @property {String} decoration 文字装饰,下划线,中划线等,可选值 none|underline|line-through(默认 'none' ) + * @property {Object | String | Number} margin 外边距,对象、字符串,数值形式均可(默认 0 ) + * @property {String | Number} lineHeight 文本行高 + * @property {String} align 文本对齐方式,可选值left|center|right(默认 'left' ) + * @property {String} wordWrap 文字换行,可选值break-word|normal|anywhere(默认 'normal' ) + * @event {Function} click 点击触发事件 + * @example <u--text text="我用十年青春,赴你最后之约"></u--text> + */ +export default { + name: 'u--text', + // #ifdef MP + mixins: [uni.$u.mpMixin, uni.$u.mixin, value, button, openType, props], + // #endif + // #ifndef MP + mixins: [uni.$u.mpMixin, uni.$u.mixin, value, props], + // #endif + computed: { + valueStyle() { + const style = { + textDecoration: this.decoration, + fontWeight: this.bold ? 'bold' : 'normal', + wordWrap: this.wordWrap, + fontSize: uni.$u.addUnit(this.size) + } + !this.type && (style.color = this.color) + this.isNvue && this.lines && (style.lines = this.lines) + this.lineHeight && + (style.lineHeight = uni.$u.addUnit(this.lineHeight)) + !this.isNvue && this.block && (style.display = 'block') + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) + }, + isNvue() { + let nvue = false + // #ifdef APP-NVUE + nvue = true + // #endif + return nvue + }, + isMp() { + let mp = false + // #ifdef MP + mp = true + // #endif + return mp + } + }, + data() { + return {} + }, + methods: { + clickHandler() { + // 如果为手机号模式,拨打电话 + if (this.call && this.mode === 'phone') { + uni.makePhoneCall({ + phoneNumber: this.text + }) + } + this.$emit('click') + } + } +} +</script> + +<style lang="scss" scoped> +@import '../../libs/css/components.scss'; + +.u-text { + @include flex(row); + align-items: center; + flex-wrap: nowrap; + flex: 1; + /* #ifndef APP-NVUE */ + width: 100%; + /* #endif */ + + &__price { + font-size: 14px; + color: $u-content-color; + } + + &__value { + font-size: 14px; + @include flex; + color: $u-content-color; + flex-wrap: wrap; + // flex: 1; + text-overflow: ellipsis; + align-items: center; + + &--primary { + color: $u-primary; + } + + &--warning { + color: $u-warning; + } + + &--success { + color: $u-success; + } + + &--info { + color: $u-info; + } + + &--error { + color: $u-error; + } + + &--main { + color: $u-main-color; + } + + &--content { + color: $u-content-color; + } + + &--tips { + color: $u-tips-color; + } + + &--light { + color: $u-light-color; + } + } +} +</style> diff --git a/uni_modules/uview-ui/components/u-text/value.js b/uni_modules/uview-ui/components/u-text/value.js new file mode 100644 index 0000000..9859bbb --- /dev/null +++ b/uni_modules/uview-ui/components/u-text/value.js @@ -0,0 +1,85 @@ +export default { + computed: { + // 经处理后需要显示的值 + value() { + const { + text, + mode, + format, + href + } = this + // 价格类型 + if (mode === 'price') { + // 如果text不为金额进行提示 + if (!/^\d+(\.\d+)?$/.test(text)) { + uni.$u.error('金额模式下,text参数需要为金额格式'); + } + // 进行格式化,判断用户传入的format参数为正则,或者函数,如果没有传入format,则使用默认的金额格式化处理 + if (uni.$u.test.func(format)) { + // 如果用户传入的是函数,使用函数格式化 + return format(text) + } + // 如果format非正则,非函数,则使用默认的金额格式化方法进行操作 + return uni.$u.priceFormat(text, 2) + } if (mode === 'date') { + // 判断是否合法的日期或者时间戳 + !uni.$u.test.date(text) && uni.$u.error('日期模式下,text参数需要为日期或时间戳格式') + // 进行格式化,判断用户传入的format参数为正则,或者函数,如果没有传入format,则使用默认的格式化处理 + if (uni.$u.test.func(format)) { + // 如果用户传入的是函数,使用函数格式化 + return format(text) + } if (format) { + // 如果format非正则,非函数,则使用默认的时间格式化方法进行操作 + return uni.$u.timeFormat(text, format) + } + // 如果没有设置format,则设置为默认的时间格式化形式 + return uni.$u.timeFormat(text, 'yyyy-mm-dd') + } if (mode === 'phone') { + // 判断是否合法的手机号 + // !uni.$u.test.mobile(text) && uni.$u.error('手机号模式下,text参数需要为手机号码格式') + if (uni.$u.test.func(format)) { + // 如果用户传入的是函数,使用函数格式化 + return format(text) + } if (format === 'encrypt') { + // 如果format为encrypt,则将手机号进行星号加密处理 + return `${text.substr(0, 3)}****${text.substr(7)}` + } + return text + } if (mode === 'name') { + // 判断是否合法的字符粗 + !(typeof (text) === 'string') && uni.$u.error('姓名模式下,text参数需要为字符串格式') + if (uni.$u.test.func(format)) { + // 如果用户传入的是函数,使用函数格式化 + return format(text) + } if (format === 'encrypt') { + // 如果format为encrypt,则将姓名进行星号加密处理 + return this.formatName(text) + } + return text + } if (mode === 'link') { + // 判断是否合法的字符粗 + !uni.$u.test.url(href) && uni.$u.error('超链接模式下,href参数需要为URL格式') + return text + } + return text + } + }, + methods: { + // 默认的姓名脱敏规则 + formatName(name) { + let value = '' + if (name.length === 2) { + value = name.substr(0, 1) + '*' + } else if (name.length > 2) { + let char = '' + for (let i = 0, len = name.length - 2; i < len; i++) { + char += '*' + } + value = name.substr(0, 1) + char + name.substr(-1, 1) + } else { + value = name + } + return value + } + } +} diff --git a/uni_modules/uview-ui/components/u-textarea/props.js b/uni_modules/uview-ui/components/u-textarea/props.js new file mode 100644 index 0000000..fd5a498 --- /dev/null +++ b/uni_modules/uview-ui/components/u-textarea/props.js @@ -0,0 +1,114 @@ +export default { + props: { + // 输入框的内容 + value: { + type: [String, Number], + default: uni.$u.props.textarea.value + }, + // 输入框为空时占位符 + placeholder: { + type: [String, Number], + default: uni.$u.props.textarea.placeholder + }, + // 指定placeholder的样式类,注意页面或组件的style中写了scoped时,需要在类名前写/deep/ + placeholderClass: { + type: String, + default: uni.$u.props.input.placeholderClass + }, + // 指定placeholder的样式 + placeholderStyle: { + type: [String, Object], + default: uni.$u.props.input.placeholderStyle + }, + // 输入框高度 + height: { + type: [String, Number], + default: uni.$u.props.textarea.height + }, + // 设置键盘右下角按钮的文字,仅微信小程序,App-vue和H5有效 + confirmType: { + type: String, + default: uni.$u.props.textarea.confirmType + }, + // 是否禁用 + disabled: { + type: Boolean, + default: uni.$u.props.textarea.disabled + }, + // 是否显示统计字数 + count: { + type: Boolean, + default: uni.$u.props.textarea.count + }, + // 是否自动获取焦点,nvue不支持,H5取决于浏览器的实现 + focus: { + type: Boolean, + default: uni.$u.props.textarea.focus + }, + // 是否自动增加高度 + autoHeight: { + type: Boolean, + default: uni.$u.props.textarea.autoHeight + }, + // 如果textarea是在一个position:fixed的区域,需要显示指定属性fixed为true + fixed: { + type: Boolean, + default: uni.$u.props.textarea.fixed + }, + // 指定光标与键盘的距离 + cursorSpacing: { + type: Number, + default: uni.$u.props.textarea.cursorSpacing + }, + // 指定focus时的光标位置 + cursor: { + type: [String, Number], + default: uni.$u.props.textarea.cursor + }, + // 是否显示键盘上方带有”完成“按钮那一栏, + showConfirmBar: { + type: Boolean, + default: uni.$u.props.textarea.showConfirmBar + }, + // 光标起始位置,自动聚焦时有效,需与selection-end搭配使用 + selectionStart: { + type: Number, + default: uni.$u.props.textarea.selectionStart + }, + // 光标结束位置,自动聚焦时有效,需与selection-start搭配使用 + selectionEnd: { + type: Number, + default: uni.$u.props.textarea.selectionEnd + }, + // 键盘弹起时,是否自动上推页面 + adjustPosition: { + type: Boolean, + default: uni.$u.props.textarea.adjustPosition + }, + // 是否去掉 iOS 下的默认内边距,只微信小程序有效 + disableDefaultPadding: { + type: Boolean, + default: uni.$u.props.textarea.disableDefaultPadding + }, + // focus时,点击页面的时候不收起键盘,只微信小程序有效 + holdKeyboard: { + type: Boolean, + default: uni.$u.props.textarea.holdKeyboard + }, + // 最大输入长度,设置为 -1 的时候不限制最大长度 + maxlength: { + type: [String, Number], + default: uni.$u.props.textarea.maxlength + }, + // 边框类型,surround-四周边框,bottom-底部边框 + border: { + type: String, + default: uni.$u.props.textarea.border + }, + // 用于处理或者过滤输入框内容的方法 + formatter: { + type: [Function, null], + default: uni.$u.props.textarea.formatter + } + } +} diff --git a/uni_modules/uview-ui/components/u-textarea/u-textarea.vue b/uni_modules/uview-ui/components/u-textarea/u-textarea.vue new file mode 100644 index 0000000..0dac03e --- /dev/null +++ b/uni_modules/uview-ui/components/u-textarea/u-textarea.vue @@ -0,0 +1,237 @@ +<template> + <view class="u-textarea" :class="textareaClass" :style="[textareaStyle]"> + <textarea + class="u-textarea__field" + :value="innerValue" + :style="{ height: $u.addUnit(height) }" + :placeholder="placeholder" + :placeholder-style="$u.addStyle(placeholderStyle, 'string')" + :placeholder-class="placeholderClass" + :disabled="disabled" + :focus="focus" + :autoHeight="autoHeight" + :fixed="fixed" + :cursorSpacing="cursorSpacing" + :cursor="cursor" + :showConfirmBar="showConfirmBar" + :selectionStart="selectionStart" + :selectionEnd="selectionEnd" + :adjustPosition="adjustPosition" + :disableDefaultPadding="disableDefaultPadding" + :holdKeyboard="holdKeyboard" + :maxlength="maxlength" + :confirmType="confirmType" + @focus="onFocus" + @blur="onBlur" + @linechange="onLinechange" + @input="onInput" + @confirm="onConfirm" + @keyboardheightchange="onKeyboardheightchange" + ></textarea> + <text + class="u-textarea__count" + :style="{ + 'background-color': disabled ? 'transparent' : '#fff', + }" + v-if="count" + >{{ innerValue.length }}/{{ maxlength }}</text + > + </view> +</template> + +<script> +import props from "./props.js"; +/** + * Textarea 文本域 + * @description 文本域此组件满足了可能出现的表单信息补充,编辑等实际逻辑的功能,内置了字数校验等 + * @tutorial https://www.uviewui.com/components/textarea.html + * + * @property {String | Number} value 输入框的内容 + * @property {String | Number} placeholder 输入框为空时占位符 + * @property {String} placeholderClass 指定placeholder的样式类,注意页面或组件的style中写了scoped时,需要在类名前写/deep/ ( 默认 'input-placeholder' ) + * @property {String | Object} placeholderStyle 指定placeholder的样式,字符串/对象形式,如"color: red;" + * @property {String | Number} height 输入框高度(默认 70 ) + * @property {String} confirmType 设置键盘右下角按钮的文字,仅微信小程序,App-vue和H5有效(默认 'done' ) + * @property {Boolean} disabled 是否禁用(默认 false ) + * @property {Boolean} count 是否显示统计字数(默认 false ) + * @property {Boolean} focus 是否自动获取焦点,nvue不支持,H5取决于浏览器的实现(默认 false ) + * @property {Boolean | Function} autoHeight 是否自动增加高度(默认 false ) + * @property {Boolean} fixed 如果textarea是在一个position:fixed的区域,需要显示指定属性fixed为true(默认 false ) + * @property {Number} cursorSpacing 指定光标与键盘的距离(默认 0 ) + * @property {String | Number} cursor 指定focus时的光标位置 + * @property {Function} formatter 内容式化函数 + * @property {Boolean} showConfirmBar 是否显示键盘上方带有”完成“按钮那一栏,(默认 true ) + * @property {Number} selectionStart 光标起始位置,自动聚焦时有效,需与selection-end搭配使用,(默认 -1 ) + * @property {Number | Number} selectionEnd 光标结束位置,自动聚焦时有效,需与selection-start搭配使用(默认 -1 ) + * @property {Boolean} adjustPosition 键盘弹起时,是否自动上推页面(默认 true ) + * @property {Boolean | Number} disableDefaultPadding 是否去掉 iOS 下的默认内边距,只微信小程序有效(默认 false ) + * @property {Boolean} holdKeyboard focus时,点击页面的时候不收起键盘,只微信小程序有效(默认 false ) + * @property {String | Number} maxlength 最大输入长度,设置为 -1 的时候不限制最大长度(默认 140 ) + * @property {String} border 边框类型,surround-四周边框,none-无边框,bottom-底部边框(默认 'surround' ) + * + * @event {Function(e)} focus 输入框聚焦时触发,event.detail = { value, height },height 为键盘高度 + * @event {Function(e)} blur 输入框失去焦点时触发,event.detail = {value, cursor} + * @event {Function(e)} linechange 输入框行数变化时调用,event.detail = {height: 0, heightRpx: 0, lineCount: 0} + * @event {Function(e)} input 当键盘输入时,触发 input 事件 + * @event {Function(e)} confirm 点击完成时, 触发 confirm 事件 + * @event {Function(e)} keyboardheightchange 键盘高度发生变化的时候触发此事件 + * @example <u--textarea v-model="value1" placeholder="请输入内容" ></u--textarea> + */ +export default { + name: "u-textarea", + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + // 输入框的值 + innerValue: "", + // 是否处于获得焦点状态 + focused: false, + // value是否第一次变化,在watch中,由于加入immediate属性,会在第一次触发,此时不应该认为value发生了变化 + firstChange: true, + // value绑定值的变化是由内部还是外部引起的 + changeFromInner: false, + // 过滤处理方法 + innerFormatter: value => value + } + }, + watch: { + value: { + immediate: true, + handler(newVal, oldVal) { + this.innerValue = newVal; + /* #ifdef H5 */ + // 在H5中,外部value变化后,修改input中的值,不会触发@input事件,此时手动调用值变化方法 + if ( + this.firstChange === false && + this.changeFromInner === false + ) { + this.valueChange(); + } + /* #endif */ + this.firstChange = false; + // 重置changeFromInner的值为false,标识下一次引起默认为外部引起的 + this.changeFromInner = false; + }, + }, + }, + computed: { + // 组件的类名 + textareaClass() { + let classes = [], + { border, disabled, shape } = this; + border === "surround" && + (classes = classes.concat(["u-border", "u-textarea--radius"])); + border === "bottom" && + (classes = classes.concat([ + "u-border-bottom", + "u-textarea--no-radius", + ])); + disabled && classes.push("u-textarea--disabled"); + return classes.join(" "); + }, + // 组件的样式 + textareaStyle() { + const style = {}; + // #ifdef APP-NVUE + // 由于textarea在安卓nvue上的差异性,需要额外再调整其内边距 + if (uni.$u.os() === "android") { + style.paddingTop = "6px"; + style.paddingLeft = "9px"; + style.paddingBottom = "3px"; + style.paddingRight = "6px"; + } + // #endif + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)); + }, + }, + methods: { + // 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用 + setFormatter(e) { + this.innerFormatter = e + }, + onFocus(e) { + this.$emit("focus", e); + }, + onBlur(e) { + this.$emit("blur", e); + // 尝试调用u-form的验证方法 + uni.$u.formValidate(this, "blur"); + }, + onLinechange(e) { + this.$emit("linechange", e); + }, + onInput(e) { + let { value = "" } = e.detail || {}; + // 格式化过滤方法 + const formatter = this.formatter || this.innerFormatter + const formatValue = formatter(value) + // 为了避免props的单向数据流特性,需要先将innerValue值设置为当前值,再在$nextTick中重新赋予设置后的值才有效 + this.innerValue = value + this.$nextTick(() => { + this.innerValue = formatValue; + this.valueChange(); + }) + }, + // 内容发生变化,进行处理 + valueChange() { + const value = this.innerValue; + this.$nextTick(() => { + this.$emit("input", value); + // 标识value值的变化是由内部引起的 + this.changeFromInner = true; + this.$emit("change", value); + // 尝试调用u-form的验证方法 + uni.$u.formValidate(this, "change"); + }); + }, + onConfirm(e) { + this.$emit("confirm", e); + }, + onKeyboardheightchange(e) { + this.$emit("keyboardheightchange", e); + }, + }, +}; +</script> + +<style lang="scss" scoped> +@import "../../libs/css/components.scss"; + +.u-textarea { + border-radius: 4px; + background-color: #fff; + position: relative; + @include flex; + flex: 1; + padding: 9px; + + &--radius { + border-radius: 4px; + } + + &--no-radius { + border-radius: 0; + } + + &--disabled { + background-color: #f5f7fa; + } + + &__field { + flex: 1; + font-size: 15px; + color: $u-content-color; + width: 100%; + } + + &__count { + position: absolute; + right: 5px; + bottom: 2px; + font-size: 12px; + color: $u-tips-color; + background-color: #ffffff; + padding: 1px 4px; + } +} +</style> diff --git a/uni_modules/uview-ui/components/u-toast/u-toast.vue b/uni_modules/uview-ui/components/u-toast/u-toast.vue new file mode 100644 index 0000000..f194830 --- /dev/null +++ b/uni_modules/uview-ui/components/u-toast/u-toast.vue @@ -0,0 +1,291 @@ +<template> + <view class="u-toast"> + <u-overlay + :show="isShow" + :custom-style="overlayStyle" + > + <view + class="u-toast__content" + :style="[contentStyle]" + :class="['u-type-' + tmpConfig.type, (tmpConfig.type === 'loading' || tmpConfig.loading) ? 'u-toast__content--loading' : '']" + > + <u-loading-icon + v-if="tmpConfig.type === 'loading'" + mode="circle" + color="rgb(255, 255, 255)" + inactiveColor="rgb(120, 120, 120)" + size="25" + ></u-loading-icon> + <u-icon + v-else-if="tmpConfig.type !== 'defalut' && iconName" + :name="iconName" + size="17" + :color="tmpConfig.type" + :customStyle="iconStyle" + ></u-icon> + <u-gap + v-if="tmpConfig.type === 'loading' || tmpConfig.loading" + height="12" + bgColor="transparent" + ></u-gap> + <text + class="u-toast__content__text" + :class="['u-toast__content__text--' + tmpConfig.type]" + style="max-width: 400rpx;" + >{{ tmpConfig.message }}</text> + </view> + </u-overlay> + </view> +</template> + +<script> + /** + * toast 消息提示 + * @description 此组件表现形式类似uni的uni.showToastAPI,但也有不同的地方。 + * @tutorial https://www.uviewui.com/components/toast.html + * @property {String | Number} zIndex toast展示时的zIndex值 (默认 10090 ) + * @property {Boolean} loading 是否加载中 (默认 false ) + * @property {String | Number} message 显示的文字内容 + * @property {String} icon 图标,或者绝对路径的图片 + * @property {String} type 主题类型 (默认 default) + * @property {Boolean} show 是否显示该组件 (默认 false) + * @property {Boolean} overlay 是否显示透明遮罩,防止点击穿透 (默认 false ) + * @property {String} position 位置 (默认 'center' ) + * @property {Object} params 跳转的参数 + * @property {String | Number} duration 展示时间,单位ms (默认 2000 ) + * @property {Boolean} isTab 是否返回的为tab页面 (默认 false ) + * @property {String} url toast消失后是否跳转页面,有则跳转,优先级高于back参数 + * @property {Function} complete 执行完后的回调函数 + * @property {Boolean} back 结束toast是否自动返回上一页 (默认 false ) + * @property {Object} customStyle 组件的样式,对象形式 + * @event {Function} show 显示toast,如需一进入页面就显示toast,请在onReady生命周期调用 + * @example <u-toast ref="uToast" /> + */ + export default { + name: 'u-toast', + mixins: [uni.$u.mpMixin, uni.$u.mixin], + data() { + return { + isShow: false, + timer: null, // 定时器 + config: { + message: '', // 显示文本 + type: '', // 主题类型,primary,success,error,warning,black + duration: 2000, // 显示的时间,毫秒 + icon: true, // 显示的图标 + position: 'center', // toast出现的位置 + complete: null, // 执行完后的回调函数 + overlay: false, // 是否防止触摸穿透 + loading: false, // 是否加载中状态 + }, + tmpConfig: {}, // 将用户配置和内置配置合并后的临时配置变量 + } + }, + computed: { + iconName() { + // 只有不为none,并且type为error|warning|succes|info时候,才显示图标 + if(!this.tmpConfig.icon || this.tmpConfig.icon == 'none') { + return ''; + } + if (['error', 'warning', 'success', 'primary'].includes(this.tmpConfig.type)) { + return uni.$u.type2icon(this.tmpConfig.type) + } else { + return '' + } + }, + overlayStyle() { + const style = { + justifyContent: 'center', + alignItems: 'center', + display: 'flex' + } + // 将遮罩设置为100%透明度,避免出现灰色背景 + style.backgroundColor = 'rgba(0, 0, 0, 0)' + return style + }, + iconStyle() { + const style = {} + // 图标需要一个右边距,以跟右边的文字有隔开的距离 + style.marginRight = '4px' + // #ifdef APP-NVUE + // iOSAPP下,图标有1px的向下偏移,这里进行修正 + if (uni.$u.os() === 'ios') { + style.marginTop = '-1px' + } + // #endif + return style + }, + loadingIconColor() { + let color = 'rgb(255, 255, 255)' + if (['error', 'warning', 'success', 'primary'].includes(this.tmpConfig.type)) { + // loading-icon组件内部会对color参数进行一个透明度处理,该方法要求传入的颜色值 + // 必须为rgb格式的,所以这里做一个处理 + color = uni.$u.hexToRgb(uni.$u.color[this.tmpConfig.type]) + } + return color + }, + // 内容盒子的样式 + contentStyle() { + const windowHeight = uni.$u.sys().windowHeight, style = {} + let value = 0 + // 根据top和bottom,对Y轴进行窗体高度的百分比偏移 + if(this.tmpConfig.position === 'top') { + value = - windowHeight * 0.25 + } else if(this.tmpConfig.position === 'bottom') { + value = windowHeight * 0.25 + } + style.transform = `translateY(${value}px)` + return style + } + }, + created() { + // 通过主题的形式调用toast,批量生成方法函数 + ['primary', 'success', 'error', 'warning', 'default', 'loading'].map(item => { + this[item] = message => this.show({ + type: item, + message + }) + }) + }, + methods: { + // 显示toast组件,由父组件通过this.$refs.xxx.show(options)形式调用 + show(options) { + // 不将结果合并到this.config变量,避免多次调用u-toast,前后的配置造成混乱 + this.tmpConfig = uni.$u.deepMerge(this.config, options) + // 清除定时器 + this.clearTimer() + this.isShow = true + this.timer = setTimeout(() => { + // 倒计时结束,清除定时器,隐藏toast组件 + this.clearTimer() + // 判断是否存在callback方法,如果存在就执行 + typeof(this.tmpConfig.complete) === 'function' && this.tmpConfig.complete() + }, this.tmpConfig.duration) + }, + // 隐藏toast组件,由父组件通过this.$refs.xxx.hide()形式调用 + hide() { + this.clearTimer() + }, + clearTimer() { + this.isShow = false + // 清除定时器 + clearTimeout(this.timer) + this.timer = null + } + }, + beforeDestroy() { + this.clearTimer() + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + $u-toast-color:#fff !default; + $u-toast-border-radius:4px !default; + $u-toast-border-background-color:#585858 !default; + $u-toast-border-font-size:14px !default; + $u-toast-border-padding:12px 20px !default; + $u-toast-loading-border-padding: 20px 20px !default; + $u-toast-content-text-color:#fff !default; + $u-toast-content-text-font-size:15px !default; + $u-toast-u-icon:10rpx !default; + $u-toast-u-type-primary-color:$u-primary !default; + $u-toast-u-type-primary-background-color:#ecf5ff !default; + $u-toast-u-type-primary-border-color:rgb(215, 234, 254) !default; + $u-toast-u-type-primary-border-width:1px !default; + $u-toast-u-type-success-color: $u-success !default; + $u-toast-u-type-success-background-color: #dbf1e1 !default; + $u-toast-u-type-success-border-color: #BEF5C8 !default; + $u-toast-u-type-success-border-width: 1px !default; + $u-toast-u-type-error-color:$u-error !default; + $u-toast-u-type-error-background-color:#fef0f0 !default; + $u-toast-u-type-error-border-color:#fde2e2 !default; + $u-toast-u-type-error-border-width: 1px !default; + $u-toast-u-type-warning-color:$u-warning !default; + $u-toast-u-type-warning-background-color:#fdf6ec !default; + $u-toast-u-type-warning-border-color:#faecd8 !default; + $u-toast-u-type-warning-border-width: 1px !default; + $u-toast-u-type-default-color:#fff !default; + $u-toast-u-type-default-background-color:#585858 !default; + + .u-toast { + &__content { + @include flex; + padding: $u-toast-border-padding; + border-radius: $u-toast-border-radius; + background-color: $u-toast-border-background-color; + color: $u-toast-color; + align-items: center; + /* #ifndef APP-NVUE */ + max-width: 600rpx; + /* #endif */ + position: relative; + + &--loading { + flex-direction: column; + padding: $u-toast-loading-border-padding; + } + + &__text { + color: $u-toast-content-text-color; + font-size: $u-toast-content-text-font-size; + line-height: $u-toast-content-text-font-size; + + &--default { + color: $u-toast-content-text-color; + } + + &--error { + color: $u-error; + } + + &--primary { + color: $u-primary; + } + + &--success { + color: $u-success; + } + + &--warning { + color: $u-warning; + } + } + } + } + + .u-type-primary { + color: $u-toast-u-type-primary-color; + background-color: $u-toast-u-type-primary-background-color; + border-color: $u-toast-u-type-primary-border-color; + border-width: $u-toast-u-type-primary-border-width; + } + + .u-type-success { + color: $u-toast-u-type-success-color; + background-color: $u-toast-u-type-success-background-color; + border-color: $u-toast-u-type-success-border-color; + border-width: 1px; + } + + .u-type-error { + color: $u-toast-u-type-error-color; + background-color: $u-toast-u-type-error-background-color; + border-color: $u-toast-u-type-error-border-color; + border-width: $u-toast-u-type-error-border-width; + } + + .u-type-warning { + color: $u-toast-u-type-warning-color; + background-color: $u-toast-u-type-warning-background-color; + border-color: $u-toast-u-type-warning-border-color; + border-width: 1px; + } + + .u-type-default { + color: $u-toast-u-type-default-color; + background-color: $u-toast-u-type-default-background-color; + } +</style> diff --git a/uni_modules/uview-ui/components/u-toolbar/props.js b/uni_modules/uview-ui/components/u-toolbar/props.js new file mode 100644 index 0000000..1b72966 --- /dev/null +++ b/uni_modules/uview-ui/components/u-toolbar/props.js @@ -0,0 +1,34 @@ +export default { + props: { + // 是否展示工具条 + show: { + type: Boolean, + default: uni.$u.props.toolbar.show + }, + // 取消按钮的文字 + cancelText: { + type: String, + default: uni.$u.props.toolbar.cancelText + }, + // 确认按钮的文字 + confirmText: { + type: String, + default: uni.$u.props.toolbar.confirmText + }, + // 取消按钮的颜色 + cancelColor: { + type: String, + default: uni.$u.props.toolbar.cancelColor + }, + // 确认按钮的颜色 + confirmColor: { + type: String, + default: uni.$u.props.toolbar.confirmColor + }, + // 标题文字 + title: { + type: String, + default: uni.$u.props.toolbar.title + } + } +} diff --git a/uni_modules/uview-ui/components/u-toolbar/u-toolbar.vue b/uni_modules/uview-ui/components/u-toolbar/u-toolbar.vue new file mode 100644 index 0000000..290b771 --- /dev/null +++ b/uni_modules/uview-ui/components/u-toolbar/u-toolbar.vue @@ -0,0 +1,102 @@ +<template> + <view + class="u-toolbar" + @touchmove.stop.prevent="noop" + v-if="show" + > + <view + class="u-toolbar__cancel__wrapper" + hover-class="u-hover-class" + > + <text + class="u-toolbar__wrapper__cancel" + @tap="cancel" + :style="{ + color: cancelColor + }" + >{{ cancelText }}</text> + </view> + <text + class="u-toolbar__title u-line-1" + v-if="title" + >{{ title }}</text> + <view + class="u-toolbar__confirm__wrapper" + hover-class="u-hover-class" + > + <text + class="u-toolbar__wrapper__confirm" + @tap="confirm" + :style="{ + color: confirmColor + }" + >{{ confirmText }}</text> + </view> + </view> +</template> + +<script> + import props from './props.js'; + /** + * Toolbar 工具条 + * @description + * @tutorial https://www.uviewui.com/components/toolbar.html + * @property {Boolean} show 是否展示工具条(默认 true ) + * @property {String} cancelText 取消按钮的文字(默认 '取消' ) + * @property {String} confirmText 确认按钮的文字(默认 '确认' ) + * @property {String} cancelColor 取消按钮的颜色(默认 '#909193' ) + * @property {String} confirmColor 确认按钮的颜色(默认 '#3c9cff' ) + * @property {String} title 标题文字 + * @event {Function} + * @example + */ + export default { + name: 'u-toolbar', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + methods: { + // 点击取消按钮 + cancel() { + this.$emit('cancel') + }, + // 点击确定按钮 + confirm() { + this.$emit('confirm') + } + }, + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-toolbar { + height: 42px; + @include flex; + justify-content: space-between; + align-items: center; + + &__wrapper { + &__cancel { + color: $u-tips-color; + font-size: 15px; + padding: 0 15px; + } + } + + &__title { + color: $u-main-color; + padding: 0 60rpx; + font-size: 16px; + flex: 1; + text-align: center; + } + + &__wrapper { + &__confirm { + color: $u-primary; + font-size: 15px; + padding: 0 15px; + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-tooltip/clipboard.min.js b/uni_modules/uview-ui/components/u-tooltip/clipboard.min.js new file mode 100644 index 0000000..b7bff12 --- /dev/null +++ b/uni_modules/uview-ui/components/u-tooltip/clipboard.min.js @@ -0,0 +1,58 @@ +/*! + * clipboard.js v1.6.1 + * https://zenorocha.github.io/clipboard.js + * + * Licensed MIT © Zeno Rocha + */ +!(function (e) { if (typeof exports === 'object' && typeof module !== 'undefined')module.exports = e(); else if (typeof define === 'function' && define.amd)define([], e); else { let t; t = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this, t.Clipboard = e() } }(() => { + let e; let t; let n; return (function e(t, n, o) { function i(a, c) { if (!n[a]) { if (!t[a]) { const l = typeof require === 'function' && require; if (!c && l) return l(a, !0); if (r) return r(a, !0); const u = new Error(`Cannot find module '${a}'`); throw u.code = 'MODULE_NOT_FOUND', u } const s = n[a] = { exports: {} }; t[a][0].call(s.exports, (e) => { const n = t[a][1][e]; return i(n || e) }, s, s.exports, e, t, n, o) } return n[a].exports } for (var r = typeof require === 'function' && require, a = 0; a < o.length; a++)i(o[a]); return i }({ + 1: [function (e, t, n) { function o(e, t) { for (;e && e.nodeType !== i;) { if (e.matches(t)) return e; e = e.parentNode } } var i = 9; if (typeof Element !== 'undefined' && !Element.prototype.matches) { const r = Element.prototype; r.matches = r.matchesSelector || r.mozMatchesSelector || r.msMatchesSelector || r.oMatchesSelector || r.webkitMatchesSelector }t.exports = o }, {}], + 2: [function (e, t, n) { function o(e, t, n, o, r) { const a = i.apply(this, arguments); return e.addEventListener(n, a, r), { destroy() { e.removeEventListener(n, a, r) } } } function i(e, t, n, o) { return function (n) { n.delegateTarget = r(n.target, t), n.delegateTarget && o.call(e, n) } } var r = e('./closest'); t.exports = o }, { './closest': 1 }], + 3: [function (e, t, n) { n.node = function (e) { return void 0 !== e && e instanceof HTMLElement && e.nodeType === 1 }, n.nodeList = function (e) { const t = Object.prototype.toString.call(e); return void 0 !== e && (t === '[object NodeList]' || t === '[object HTMLCollection]') && 'length' in e && (e.length === 0 || n.node(e[0])) }, n.string = function (e) { return typeof e === 'string' || e instanceof String }, n.fn = function (e) { const t = Object.prototype.toString.call(e); return t === '[object Function]' } }, {}], + 4: [function (e, t, n) { function o(e, t, n) { if (!e && !t && !n) throw new Error('Missing required arguments'); if (!c.string(t)) throw new TypeError('Second argument must be a String'); if (!c.fn(n)) throw new TypeError('Third argument must be a Function'); if (c.node(e)) return i(e, t, n); if (c.nodeList(e)) return r(e, t, n); if (c.string(e)) return a(e, t, n); throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList') } function i(e, t, n) { return e.addEventListener(t, n), { destroy() { e.removeEventListener(t, n) } } } function r(e, t, n) { return Array.prototype.forEach.call(e, (e) => { e.addEventListener(t, n) }), { destroy() { Array.prototype.forEach.call(e, (e) => { e.removeEventListener(t, n) }) } } } function a(e, t, n) { return l(document.body, e, t, n) } var c = e('./is'); var l = e('delegate'); t.exports = o }, { './is': 3, delegate: 2 }], + 5: [function (e, t, n) { function o(e) { let t; if (e.nodeName === 'SELECT')e.focus(), t = e.value; else if (e.nodeName === 'INPUT' || e.nodeName === 'TEXTAREA') { const n = e.hasAttribute('readonly'); n || e.setAttribute('readonly', ''), e.select(), e.setSelectionRange(0, e.value.length), n || e.removeAttribute('readonly'), t = e.value } else { e.hasAttribute('contenteditable') && e.focus(); const o = window.getSelection(); const i = document.createRange(); i.selectNodeContents(e), o.removeAllRanges(), o.addRange(i), t = o.toString() } return t }t.exports = o }, {}], + 6: [function (e, t, n) { + function o() {}o.prototype = { + on(e, t, n) { const o = this.e || (this.e = {}); return (o[e] || (o[e] = [])).push({ fn: t, ctx: n }), this }, once(e, t, n) { function o() { i.off(e, o), t.apply(n, arguments) } var i = this; return o._ = t, this.on(e, o, n) }, emit(e) { const t = [].slice.call(arguments, 1); const n = ((this.e || (this.e = {}))[e] || []).slice(); let o = 0; const i = n.length; for (o; o < i; o++)n[o].fn.apply(n[o].ctx, t); return this }, off(e, t) { const n = this.e || (this.e = {}); const o = n[e]; const i = []; if (o && t) for (let r = 0, a = o.length; r < a; r++)o[r].fn !== t && o[r].fn._ !== t && i.push(o[r]); return i.length ? n[e] = i : delete n[e], this } + }, t.exports = o + }, {}], + 7: [function (t, n, o) { + !(function (i, r) { if (typeof e === 'function' && e.amd)e(['module', 'select'], r); else if (typeof o !== 'undefined')r(n, t('select')); else { const a = { exports: {} }; r(a, i.select), i.clipboardAction = a.exports } }(this, (e, t) => { + 'use strict' + + function n(e) { return e && e.__esModule ? e : { default: e } } function o(e, t) { if (!(e instanceof t)) throw new TypeError('Cannot call a class as a function') } const i = n(t); const r = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ? function (e) { return typeof e } : function (e) { return e && typeof Symbol === 'function' && e.constructor === Symbol && e !== Symbol.prototype ? 'symbol' : typeof e }; const a = (function () { function e(e, t) { for (let n = 0; n < t.length; n++) { const o = t[n]; o.enumerable = o.enumerable || !1, o.configurable = !0, 'value' in o && (o.writable = !0), Object.defineProperty(e, o.key, o) } } return function (t, n, o) { return n && e(t.prototype, n), o && e(t, o), t } }()); const c = (function () { + function e(t) { o(this, e), this.resolveOptions(t), this.initSelection() } return a(e, [{ key: 'resolveOptions', value: function e() { const t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; this.action = t.action, this.emitter = t.emitter, this.target = t.target, this.text = t.text, this.trigger = t.trigger, this.selectedText = '' } }, { key: 'initSelection', value: function e() { this.text ? this.selectFake() : this.target && this.selectTarget() } }, { key: 'selectFake', value: function e() { const t = this; const n = document.documentElement.getAttribute('dir') == 'rtl'; this.removeFake(), this.fakeHandlerCallback = function () { return t.removeFake() }, this.fakeHandler = document.body.addEventListener('click', this.fakeHandlerCallback) || !0, this.fakeElem = document.createElement('textarea'), this.fakeElem.style.fontSize = '12pt', this.fakeElem.style.border = '0', this.fakeElem.style.padding = '0', this.fakeElem.style.margin = '0', this.fakeElem.style.position = 'absolute', this.fakeElem.style[n ? 'right' : 'left'] = '-9999px'; const o = window.pageYOffset || document.documentElement.scrollTop; this.fakeElem.style.top = `${o}px`, this.fakeElem.setAttribute('readonly', ''), this.fakeElem.value = this.text, document.body.appendChild(this.fakeElem), this.selectedText = (0, i.default)(this.fakeElem), this.copyText() } }, { key: 'removeFake', value: function e() { this.fakeHandler && (document.body.removeEventListener('click', this.fakeHandlerCallback), this.fakeHandler = null, this.fakeHandlerCallback = null), this.fakeElem && (document.body.removeChild(this.fakeElem), this.fakeElem = null) } }, { key: 'selectTarget', value: function e() { this.selectedText = (0, i.default)(this.target), this.copyText() } }, { key: 'copyText', value: function e() { let t = void 0; try { t = document.execCommand(this.action) } catch (e) { t = !1 } this.handleResult(t) } }, { + key: 'handleResult', + value: function e(t) { + this.emitter.emit(t ? 'success' : 'error', { + action: this.action, text: this.selectedText, trigger: this.trigger, clearSelection: this.clearSelection.bind(this) + }) + } + }, { key: 'clearSelection', value: function e() { this.target && this.target.blur(), window.getSelection().removeAllRanges() } }, { key: 'destroy', value: function e() { this.removeFake() } }, { key: 'action', set: function e() { const t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : 'copy'; if (this._action = t, this._action !== 'copy' && this._action !== 'cut') throw new Error('Invalid "action" value, use either "copy" or "cut"') }, get: function e() { return this._action } }, { key: 'target', set: function e(t) { if (void 0 !== t) { if (!t || (typeof t === 'undefined' ? 'undefined' : r(t)) !== 'object' || t.nodeType !== 1) throw new Error('Invalid "target" value, use a valid Element'); if (this.action === 'copy' && t.hasAttribute('disabled')) throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute'); if (this.action === 'cut' && (t.hasAttribute('readonly') || t.hasAttribute('disabled'))) throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes'); this._target = t } }, get: function e() { return this._target } }]), e + }()); e.exports = c + })) + }, { select: 5 }], + 8: [function (t, n, o) { + !(function (i, r) { if (typeof e === 'function' && e.amd)e(['module', './clipboard-action', 'tiny-emitter', 'good-listener'], r); else if (typeof o !== 'undefined')r(n, t('./clipboard-action'), t('tiny-emitter'), t('good-listener')); else { const a = { exports: {} }; r(a, i.clipboardAction, i.tinyEmitter, i.goodListener), i.clipboard = a.exports } }(this, (e, t, n, o) => { + 'use strict' + + function i(e) { return e && e.__esModule ? e : { default: e } } function r(e, t) { if (!(e instanceof t)) throw new TypeError('Cannot call a class as a function') } function a(e, t) { if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return !t || typeof t !== 'object' && typeof t !== 'function' ? e : t } function c(e, t) { + if (typeof t !== 'function' && t !== null) throw new TypeError(`Super expression must either be null or a function, not ${typeof t}`); e.prototype = Object.create(t && t.prototype, { + constructor: { + value: e, enumerable: !1, writable: !0, configurable: !0 + } + }), t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : e.__proto__ = t) + } function l(e, t) { const n = `data-clipboard-${e}`; if (t.hasAttribute(n)) return t.getAttribute(n) } const u = i(t); const s = i(n); const f = i(o); const d = (function () { function e(e, t) { for (let n = 0; n < t.length; n++) { const o = t[n]; o.enumerable = o.enumerable || !1, o.configurable = !0, 'value' in o && (o.writable = !0), Object.defineProperty(e, o.key, o) } } return function (t, n, o) { return n && e(t.prototype, n), o && e(t, o), t } }()); const h = (function (e) { + function t(e, n) { r(this, t); const o = a(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this)); return o.resolveOptions(n), o.listenClick(e), o } return c(t, e), d(t, [{ key: 'resolveOptions', value: function e() { const t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; this.action = typeof t.action === 'function' ? t.action : this.defaultAction, this.target = typeof t.target === 'function' ? t.target : this.defaultTarget, this.text = typeof t.text === 'function' ? t.text : this.defaultText } }, { key: 'listenClick', value: function e(t) { const n = this; this.listener = (0, f.default)(t, 'click', (e) => n.onClick(e)) } }, { + key: 'onClick', + value: function e(t) { + const n = t.delegateTarget || t.currentTarget; this.clipboardAction && (this.clipboardAction = null), this.clipboardAction = new u.default({ + action: this.action(n), target: this.target(n), text: this.text(n), trigger: n, emitter: this + }) + } + }, { key: 'defaultAction', value: function e(t) { return l('action', t) } }, { key: 'defaultTarget', value: function e(t) { const n = l('target', t); if (n) return document.querySelector(n) } }, { key: 'defaultText', value: function e(t) { return l('text', t) } }, { key: 'destroy', value: function e() { this.listener.destroy(), this.clipboardAction && (this.clipboardAction.destroy(), this.clipboardAction = null) } }], [{ key: 'isSupported', value: function e() { const t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : ['copy', 'cut']; const n = typeof t === 'string' ? [t] : t; let o = !!document.queryCommandSupported; return n.forEach((e) => { o = o && !!document.queryCommandSupported(e) }), o } }]), t + }(s.default)); e.exports = h + })) + }, { './clipboard-action': 7, 'good-listener': 4, 'tiny-emitter': 6 }] + }, {}, [8]))(8) +})) diff --git a/uni_modules/uview-ui/components/u-tooltip/props.js b/uni_modules/uview-ui/components/u-tooltip/props.js new file mode 100644 index 0000000..16aecbc --- /dev/null +++ b/uni_modules/uview-ui/components/u-tooltip/props.js @@ -0,0 +1,59 @@ +export default { + props: { + // 需要显示的提示文字 + text: { + type: [String, Number], + default: uni.$u.props.tooltip.text + }, + // 点击复制按钮时,复制的文本,为空则使用text值 + copyText: { + type: [String, Number], + default: uni.$u.props.tooltip.copyText + }, + // 文本大小 + size: { + type: [String, Number], + default: uni.$u.props.tooltip.size + }, + // 字体颜色 + color: { + type: String, + default: uni.$u.props.tooltip.color + }, + // 弹出提示框时,文本的背景色 + bgColor: { + type: String, + default: uni.$u.props.tooltip.bgColor + }, + // 弹出提示的方向,top-上方,bottom-下方 + direction: { + type: String, + default: uni.$u.props.tooltip.direction + }, + // 弹出提示的z-index,nvue无效 + zIndex: { + type: [String, Number], + default: uni.$u.props.tooltip.zIndex + }, + // 是否显示复制按钮 + showCopy: { + type: Boolean, + default: uni.$u.props.tooltip.showCopy + }, + // 扩展的按钮组 + buttons: { + type: Array, + default: uni.$u.props.tooltip.buttons + }, + // 是否显示透明遮罩以防止触摸穿透 + overlay: { + type: Boolean, + default: uni.$u.props.tooltip.overlay + }, + // 是否显示复制成功或者失败的toast + showToast: { + type: Boolean, + default: uni.$u.props.tooltip.showToast + } + } +} diff --git a/uni_modules/uview-ui/components/u-tooltip/u-tooltip.vue b/uni_modules/uview-ui/components/u-tooltip/u-tooltip.vue new file mode 100644 index 0000000..4bd8fc9 --- /dev/null +++ b/uni_modules/uview-ui/components/u-tooltip/u-tooltip.vue @@ -0,0 +1,365 @@ +<template> + <view + class="u-tooltip" + :style="[$u.addStyle(customStyle)]" + > + <u-overlay + :show="showTooltip && tooltipTop !== -10000 && overlay" + customStyle="backgroundColor: rgba(0, 0, 0, 0)" + @click="overlayClickHandler" + ></u-overlay> + <view class="u-tooltip__wrapper"> + <text + class="u-tooltip__wrapper__text" + :id="textId" + :ref="textId" + :userSelect="false" + :selectable="false" + @longpress.stop="longpressHandler" + :style="{ + color: color, + backgroundColor: bgColor && showTooltip && tooltipTop !== -10000 ? bgColor : 'transparent' + }" + >{{ text }}</text> + <u-transition + mode="fade" + :show="showTooltip" + duration="300" + :customStyle="{ + position: 'absolute', + top: $u.addUnit(tooltipTop), + zIndex: zIndex, + ...tooltipStyle + }" + > + <view + class="u-tooltip__wrapper__popup" + :id="tooltipId" + :ref="tooltipId" + > + <view + class="u-tooltip__wrapper__popup__indicator" + hover-class="u-tooltip__wrapper__popup__indicator--hover" + v-if="showCopy || buttons.length" + :style="[indicatorStyle, { + width: $u.addUnit(indicatorWidth), + height: $u.addUnit(indicatorWidth), + }]" + > + <!-- 由于nvue不支持三角形绘制,这里就做一个四方形,再旋转45deg,得到露出的一个三角 --> + </view> + <view class="u-tooltip__wrapper__popup__list"> + <view + v-if="showCopy" + class="u-tooltip__wrapper__popup__list__btn" + hover-class="u-tooltip__wrapper__popup__list__btn--hover" + @tap="setClipboardData" + > + <text + class="u-tooltip__wrapper__popup__list__btn__text" + >复制</text> + </view> + <u-line + direction="column" + color="#8d8e90" + v-if="showCopy && buttons.length > 0" + length="18" + ></u-line> + <block v-for="(item , index) in buttons" :key="index"> + <view + class="u-tooltip__wrapper__popup__list__btn" + hover-class="u-tooltip__wrapper__popup__list__btn--hover" + > + <text + class="u-tooltip__wrapper__popup__list__btn__text" + @tap="btnClickHandler(index)" + >{{ item }}</text> + </view> + <u-line + direction="column" + color="#8d8e90" + v-if="index < buttons.length - 1" + length="18" + ></u-line> + </block> + </view> + </view> + </u-transition> + </view> + </view> +</template> + +<script> + import props from './props.js'; + // #ifdef APP-NVUE + const dom = uni.requireNativePlugin('dom') + // #endif + // #ifdef H5 + import ClipboardJS from "./clipboard.min.js" + // #endif + /** + * Tooltip + * @description + * @tutorial https://www.uviewui.com/components/tooltip.html + * @property {String | Number} text 需要显示的提示文字 + * @property {String | Number} copyText 点击复制按钮时,复制的文本,为空则使用text值 + * @property {String | Number} size 文本大小(默认 14 ) + * @property {String} color 字体颜色(默认 '#606266' ) + * @property {String} bgColor 弹出提示框时,文本的背景色(默认 'transparent' ) + * @property {String} direction 弹出提示的方向,top-上方,bottom-下方(默认 'top' ) + * @property {String | Number} zIndex 弹出提示的z-index,nvue无效(默认 10071 ) + * @property {Boolean} showCopy 是否显示复制按钮(默认 true ) + * @property {Array} buttons 扩展的按钮组 + * @property {Boolean} overlay 是否显示透明遮罩以防止触摸穿透(默认 true ) + * @property {Object} customStyle 定义需要用到的外部样式 + * + * @event {Function} + * @example + */ + export default { + name: 'u-tooltip', + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], + data() { + return { + // 是否展示气泡 + showTooltip: true, + // 生成唯一id,防止一个页面多个组件,造成干扰 + textId: uni.$u.guid(), + tooltipId: uni.$u.guid(), + // 初始时甚至为很大的值,让其移到屏幕外面,为了计算元素的尺寸 + tooltipTop: -10000, + // 气泡的位置信息 + tooltipInfo: { + width: 0, + left: 0 + }, + // 文本的位置信息 + textInfo: { + width: 0, + left: 0 + }, + // 三角形指示器的样式 + indicatorStyle: {}, + // 气泡在可能超出屏幕边沿范围时,重新定位后,距离屏幕边沿的距离 + screenGap: 12, + // 三角形指示器的宽高,由于对元素进行了角度旋转,精确计算指示器位置时,需要用到其尺寸信息 + indicatorWidth: 14, + } + }, + watch: { + propsChange() { + this.getElRect() + } + }, + computed: { + // 特别处理H5的复制,因为H5浏览器是自带系统复制功能的,在H5环境 + // 当一些依赖参数变化时,需要重新计算气泡和指示器的位置信息 + propsChange() { + return [this.text, this.buttons] + }, + // 计算气泡和指示器的位置信息 + tooltipStyle() { + const style = { + transform: `translateY(${this.direction === 'top' ? '-100%' : '100%'})`, + }, + sys = uni.$u.sys(), + getPx = uni.$u.getPx, + addUnit = uni.$u.addUnit + if (this.tooltipInfo.width / 2 > this.textInfo.left + this.textInfo.width / 2 - this.screenGap) { + this.indicatorStyle = {} + style.left = `-${addUnit(this.textInfo.left - this.screenGap)}` + this.indicatorStyle.left = addUnit(this.textInfo.width / 2 - getPx(style.left) - this.indicatorWidth / + 2) + } else if (this.tooltipInfo.width / 2 > sys.windowWidth - this.textInfo.right + this.textInfo.width / 2 - + this.screenGap) { + this.indicatorStyle = {} + style.right = `-${addUnit(sys.windowWidth - this.textInfo.right - this.screenGap)}` + this.indicatorStyle.right = addUnit(this.textInfo.width / 2 - getPx(style.right) - this + .indicatorWidth / 2) + } else { + const left = Math.abs(this.textInfo.width / 2 - this.tooltipInfo.width / 2) + style.left = this.textInfo.width > this.tooltipInfo.width ? addUnit(left) : -addUnit(left) + this.indicatorStyle = {} + } + if (this.direction === 'top') { + style.marginTop = '-10px' + this.indicatorStyle.bottom = '-4px' + } else { + style.marginBottom = '-10px' + this.indicatorStyle.top = '-4px' + } + return style + } + }, + mounted() { + this.init() + }, + methods: { + init() { + this.getElRect() + }, + // 长按触发事件 + async longpressHandler() { + this.tooltipTop = 0 + this.showTooltip = true + }, + // 点击透明遮罩 + overlayClickHandler() { + this.showTooltip = false + }, + // 点击弹出按钮 + btnClickHandler(index) { + this.showTooltip = false + // 如果需要展示复制按钮,此处index需要加1,因为复制按钮在第一个位置 + this.$emit('click', this.showCopy ? index + 1 : index) + }, + // 查询内容高度 + queryRect(ref) { + // #ifndef APP-NVUE + // $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://www.uviewui.com/js/getRect.html + // 组件内部一般用this.$uGetRect,对外的为uni.$u.getRect,二者功能一致,名称不同 + return new Promise(resolve => { + this.$uGetRect(`#${ref}`).then(size => { + resolve(size) + }) + }) + // #endif + + // #ifdef APP-NVUE + // nvue下,使用dom模块查询元素高度 + // 返回一个promise,让调用此方法的主体能使用then回调 + return new Promise(resolve => { + dom.getComponentRect(this.$refs[ref], res => { + resolve(res.size) + }) + }) + // #endif + }, + // 元素尺寸 + getElRect() { + // 调用之前,先将指示器调整到屏幕外,方便获取尺寸 + this.showTooltip = true + this.tooltipTop = -10000 + uni.$u.sleep(500).then(() => { + this.queryRect(this.tooltipId).then(size => { + this.tooltipInfo = size + // 获取气泡尺寸之后,将其隐藏,为了让下次切换气泡显示与隐藏时,有淡入淡出的效果 + this.showTooltip = false + }) + this.queryRect(this.textId).then(size => { + this.textInfo = size + }) + }) + }, + // 复制文本到粘贴板 + setClipboardData() { + // 关闭组件 + this.showTooltip = false + this.$emit('click', 0) + // #ifndef H5 + uni.setClipboardData({ + // 优先使用copyText字段,如果没有,则默认使用text字段当做复制的内容 + data: this.copyText || this.text, + success: () => { + this.showToast && uni.$u.toast('复制成功') + }, + fail: () => { + this.showToast && uni.$u.toast('复制失败') + }, + complete: () => { + this.showTooltip = false + } + }) + // #endif + + // #ifdef H5 + let event = window.event || e || {} + let clipboard = new ClipboardJS('', { + text: () => this.copyText || this.text + }) + clipboard.on('success', (e) => { + this.showToast && uni.$u.toast('复制成功') + clipboard.off('success') + clipboard.off('error') + // 在单页应用中,需要销毁DOM的监听 + clipboard.destroy() + }) + clipboard.on('error', (e) => { + this.showToast && uni.$u.toast('复制失败') + clipboard.off('success') + clipboard.off('error') + // 在单页应用中,需要销毁DOM的监听 + clipboard.destroy() + }) + clipboard.onClick(event) + // #endif + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + + .u-tooltip { + position: relative; + @include flex; + + &__wrapper { + @include flex; + justify-content: center; + /* #ifndef APP-NVUE */ + white-space: nowrap; + /* #endif */ + + &__text { + font-size: 14px; + } + + &__popup { + @include flex; + justify-content: center; + + &__list { + background-color: #060607; + position: relative; + flex: 1; + border-radius: 5px; + padding: 0px 0; + @include flex(row); + align-items: center; + overflow: hidden; + + &__btn { + padding: 11px 13px; + + &--hover { + background-color: #58595B; + } + + &__text { + line-height: 12px; + font-size: 13px; + color: #FFFFFF; + } + } + } + + &__indicator { + position: absolute; + background-color: #060607; + width: 14px; + height: 14px; + bottom: -4px; + transform: rotate(45deg); + border-radius: 2px; + z-index: -1; + + &--hover { + background-color: #58595B; + } + } + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-tr/props.js b/uni_modules/uview-ui/components/u-tr/props.js new file mode 100644 index 0000000..7c11331 --- /dev/null +++ b/uni_modules/uview-ui/components/u-tr/props.js @@ -0,0 +1,5 @@ +export default { + props: { + + } +} diff --git a/uni_modules/uview-ui/components/u-tr/u-tr.vue b/uni_modules/uview-ui/components/u-tr/u-tr.vue new file mode 100644 index 0000000..dbbca08 --- /dev/null +++ b/uni_modules/uview-ui/components/u-tr/u-tr.vue @@ -0,0 +1,31 @@ +<template> + <view class="u-tr"> + + </view> +</template> + +<script> + import props from './props.js'; + /** + * Tr + * @description + * @tutorial url + * @property {String} + * @event {Function} + * @example + */ + export default { + name: 'u-tr', + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], + data() { + return { + + } + } + } +</script> + +<style lang="scss" scoped> + @import "../../libs/css/components.scss"; + +</style> diff --git a/uni_modules/uview-ui/components/u-transition/nvue.ani-map.js b/uni_modules/uview-ui/components/u-transition/nvue.ani-map.js new file mode 100644 index 0000000..b86b962 --- /dev/null +++ b/uni_modules/uview-ui/components/u-transition/nvue.ani-map.js @@ -0,0 +1,68 @@ +export default { + fade: { + enter: { opacity: 0 }, + 'enter-to': { opacity: 1 }, + leave: { opacity: 1 }, + 'leave-to': { opacity: 0 } + }, + 'fade-up': { + enter: { opacity: 0, transform: 'translateY(100%)' }, + 'enter-to': { opacity: 1, transform: 'translateY(0)' }, + leave: { opacity: 1, transform: 'translateY(0)' }, + 'leave-to': { opacity: 0, transform: 'translateY(100%)' } + }, + 'fade-down': { + enter: { opacity: 0, transform: 'translateY(-100%)' }, + 'enter-to': { opacity: 1, transform: 'translateY(0)' }, + leave: { opacity: 1, transform: 'translateY(0)' }, + 'leave-to': { opacity: 0, transform: 'translateY(-100%)' } + }, + 'fade-left': { + enter: { opacity: 0, transform: 'translateX(-100%)' }, + 'enter-to': { opacity: 1, transform: 'translateY(0)' }, + leave: { opacity: 1, transform: 'translateY(0)' }, + 'leave-to': { opacity: 0, transform: 'translateX(-100%)' } + }, + 'fade-right': { + enter: { opacity: 0, transform: 'translateX(100%)' }, + 'enter-to': { opacity: 1, transform: 'translateY(0)' }, + leave: { opacity: 1, transform: 'translateY(0)' }, + 'leave-to': { opacity: 0, transform: 'translateX(100%)' } + }, + 'slide-up': { + enter: { transform: 'translateY(100%)' }, + 'enter-to': { transform: 'translateY(0)' }, + leave: { transform: 'translateY(0)' }, + 'leave-to': { transform: 'translateY(100%)' } + }, + 'slide-down': { + enter: { transform: 'translateY(-100%)' }, + 'enter-to': { transform: 'translateY(0)' }, + leave: { transform: 'translateY(0)' }, + 'leave-to': { transform: 'translateY(-100%)' } + }, + 'slide-left': { + enter: { transform: 'translateX(-100%)' }, + 'enter-to': { transform: 'translateY(0)' }, + leave: { transform: 'translateY(0)' }, + 'leave-to': { transform: 'translateX(-100%)' } + }, + 'slide-right': { + enter: { transform: 'translateX(100%)' }, + 'enter-to': { transform: 'translateY(0)' }, + leave: { transform: 'translateY(0)' }, + 'leave-to': { transform: 'translateX(100%)' } + }, + zoom: { + enter: { transform: 'scale(0.95)' }, + 'enter-to': { transform: 'scale(1)' }, + leave: { transform: 'scale(1)' }, + 'leave-to': { transform: 'scale(0.95)' } + }, + 'fade-zoom': { + enter: { opacity: 0, transform: 'scale(0.95)' }, + 'enter-to': { opacity: 1, transform: 'scale(1)' }, + leave: { opacity: 1, transform: 'scale(1)' }, + 'leave-to': { opacity: 0, transform: 'scale(0.95)' } + } +} diff --git a/uni_modules/uview-ui/components/u-transition/props.js b/uni_modules/uview-ui/components/u-transition/props.js new file mode 100644 index 0000000..f7b1c22 --- /dev/null +++ b/uni_modules/uview-ui/components/u-transition/props.js @@ -0,0 +1,24 @@ +export default { + props: { + // 是否展示组件 + show: { + type: Boolean, + default: uni.$u.props.transition.show + }, + // 使用的动画模式 + mode: { + type: String, + default: uni.$u.props.transition.mode + }, + // 动画的执行时间,单位ms + duration: { + type: [String, Number], + default: uni.$u.props.transition.duration + }, + // 使用的动画过渡函数 + timingFunction: { + type: String, + default: uni.$u.props.transition.timingFunction + } + } +} diff --git a/uni_modules/uview-ui/components/u-transition/transition.js b/uni_modules/uview-ui/components/u-transition/transition.js new file mode 100644 index 0000000..92e5681 --- /dev/null +++ b/uni_modules/uview-ui/components/u-transition/transition.js @@ -0,0 +1,157 @@ +// 定义一个一定时间后自动成功的promise,让调用nextTick方法处,进入下一个then方法 +const nextTick = () => new Promise(resolve => setTimeout(resolve, 1000 / 50)) +// nvue动画模块实现细节抽离在外部文件 +import animationMap from './nvue.ani-map.js' + +// #ifndef APP-NVUE +// 定义类名,通过给元素动态切换类名,赋予元素一定的css动画样式 +const getClassNames = (name) => ({ + enter: `u-${name}-enter u-${name}-enter-active`, + 'enter-to': `u-${name}-enter-to u-${name}-enter-active`, + leave: `u-${name}-leave u-${name}-leave-active`, + 'leave-to': `u-${name}-leave-to u-${name}-leave-active` +}) +// #endif + +// #ifdef APP-NVUE +// 引入nvue(weex)的animation动画模块,文档见: +// https://weex.apache.org/zh/docs/modules/animation.html#transition +const animation = uni.requireNativePlugin('animation') +const getStyle = (name) => animationMap[name] +// #endif + +export default { + methods: { + // 组件被点击发出事件 + clickHandler() { + this.$emit('click') + }, + // #ifndef APP-NVUE + // vue版本的组件进场处理 + vueEnter() { + // 动画进入时的类名 + const classNames = getClassNames(this.mode) + // 定义状态和发出动画进入前事件 + this.status = 'enter' + this.$emit('beforeEnter') + this.inited = true + this.display = true + this.classes = classNames.enter + this.$nextTick(async () => { + // #ifdef H5 + await uni.$u.sleep(20) + // #endif + // 标识动画尚未结束 + this.$emit('enter') + this.transitionEnded = false + // 组件动画进入后触发的事件 + this.$emit('afterEnter') + // 赋予组件enter-to类名 + this.classes = classNames['enter-to'] + }) + }, + // 动画离场处理 + vueLeave() { + // 如果不是展示状态,无需执行逻辑 + if (!this.display) return + const classNames = getClassNames(this.mode) + // 标记离开状态和发出事件 + this.status = 'leave' + this.$emit('beforeLeave') + // 获得类名 + this.classes = classNames.leave + + this.$nextTick(() => { + // 动画正在离场的状态 + this.transitionEnded = false + this.$emit('leave') + // 组件执行动画,到了执行的执行时间后,执行一些额外处理 + setTimeout(this.onTransitionEnd, this.duration) + this.classes = classNames['leave-to'] + }) + }, + // #endif + // #ifdef APP-NVUE + // nvue版本动画进场 + nvueEnter() { + // 获得样式的名称 + const currentStyle = getStyle(this.mode) + // 组件动画状态和发出事件 + this.status = 'enter' + this.$emit('beforeEnter') + // 展示生成组件元素 + this.inited = true + this.display = true + // 在nvue安卓上,由于渲染速度慢,在弹窗,键盘,日历等组件中,渲染其中的内容需要时间 + // 导致出现弹窗卡顿,这里让其一开始为透明状态,等一定时间渲染完成后,再让其隐藏起来,再让其按正常逻辑出现 + this.viewStyle = { + opacity: 0 + } + // 等待弹窗内容渲染完成 + this.$nextTick(() => { + // 合并样式 + this.viewStyle = currentStyle.enter + Promise.resolve() + .then(nextTick) + .then(() => { + // 组件开始进入前的事件 + this.$emit('enter') + // nvue的transition动画模块需要通过ref调用组件,注意此处的ref不同于vue的this.$refs['u-transition']用法 + animation.transition(this.$refs['u-transition'].ref, { + styles: currentStyle['enter-to'], + duration: this.duration, + timingFunction: this.timingFunction, + needLayout: false, + delay: 0 + }, () => { + // 动画执行完毕,发出事件 + this.$emit('afterEnter') + }) + }) + .catch(() => {}) + }) + }, + nvueLeave() { + if (!this.display) { + return + } + const currentStyle = getStyle(this.mode) + // 定义状态和事件 + this.status = 'leave' + this.$emit('beforeLeave') + // 合并样式 + this.viewStyle = currentStyle.leave + // 放到promise中处理执行过程 + Promise.resolve() + .then(nextTick) // 等待几十ms + .then(() => { + this.transitionEnded = false + // 动画正在离场的状态 + this.$emit('leave') + animation.transition(this.$refs['u-transition'].ref, { + styles: currentStyle['leave-to'], + duration: this.duration, + timingFunction: this.timingFunction, + needLayout: false, + delay: 0 + }, () => { + this.onTransitionEnd() + }) + }) + .catch(() => {}) + }, + // #endif + // 完成过渡后触发 + onTransitionEnd() { + // 如果已经是结束的状态,无需再处理 + if (this.transitionEnded) return + this.transitionEnded = true + // 发出组件动画执行后的事件 + this.$emit(this.status === 'leave' ? 'afterLeave' : 'afterEnter') + if (!this.show && this.display) { + this.display = false + this.inited = false + } + } + } +} diff --git a/uni_modules/uview-ui/components/u-transition/u-transition.vue b/uni_modules/uview-ui/components/u-transition/u-transition.vue new file mode 100644 index 0000000..22831dc --- /dev/null +++ b/uni_modules/uview-ui/components/u-transition/u-transition.vue @@ -0,0 +1,92 @@ +<template> + <view + v-if="inited" + class="u-transition" + ref="u-transition" + @tap="clickHandler" + :class="classes" + :style="[mergeStyle]" + @touchmove="noop" + > + <slot /> + </view> +</template> + +<script> +import props from './props.js'; +// 组件的methods方法,由于内容较长,写在外部文件中通过mixin引入 +import transition from "./transition.js"; +/** + * transition 动画组件 + * @description + * @tutorial + * @property {String} show 是否展示组件 (默认 false ) + * @property {String} mode 使用的动画模式 (默认 'fade' ) + * @property {String | Number} duration 动画的执行时间,单位ms (默认 '300' ) + * @property {String} timingFunction 使用的动画过渡函数 (默认 'ease-out' ) + * @property {Object} customStyle 自定义样式 + * @event {Function} before-enter 进入前触发 + * @event {Function} enter 进入中触发 + * @event {Function} after-enter 进入后触发 + * @event {Function} before-leave 离开前触发 + * @event {Function} leave 离开中触发 + * @event {Function} after-leave 离开后触发 + * @example + */ +export default { + name: 'u-transition', + data() { + return { + inited: false, // 是否显示/隐藏组件 + viewStyle: {}, // 组件内部的样式 + status: '', // 记录组件动画的状态 + transitionEnded: false, // 组件是否结束的标记 + display: false, // 组件是否展示 + classes: '', // 应用的类名 + } + }, + computed: { + mergeStyle() { + const { viewStyle, customStyle } = this + return { + // #ifndef APP-NVUE + transitionDuration: `${this.duration}ms`, + // display: `${this.display ? '' : 'none'}`, + transitionTimingFunction: this.timingFunction, + // #endif + // 避免自定义样式影响到动画属性,所以写在viewStyle前面 + ...uni.$u.addStyle(customStyle), + ...viewStyle + } + } + }, + // 将mixin挂在到组件中,uni.$u.mixin实际上为一个vue格式对象 + mixins: [uni.$u.mpMixin, uni.$u.mixin, transition, props], + watch: { + show: { + handler(newVal) { + // vue和nvue分别执行不同的方法 + // #ifdef APP-NVUE + newVal ? this.nvueEnter() : this.nvueLeave() + // #endif + // #ifndef APP-NVUE + newVal ? this.vueEnter() : this.vueLeave() + // #endif + }, + // 表示同时监听初始化时的props的show的意思 + immediate: true + } + } +} +</script> + +<style lang="scss" scoped> +@import '../../libs/css/components.scss'; + +/* #ifndef APP-NVUE */ +// vue版本动画相关的样式抽离在外部文件 +@import './vue.ani-style.scss'; +/* #endif */ + +.u-transition {} +</style> diff --git a/uni_modules/uview-ui/components/u-transition/vue.ani-style.scss b/uni_modules/uview-ui/components/u-transition/vue.ani-style.scss new file mode 100644 index 0000000..a31d88b --- /dev/null +++ b/uni_modules/uview-ui/components/u-transition/vue.ani-style.scss @@ -0,0 +1,113 @@ +/** + * vue版本动画内置的动画模式有如下: + * fade:淡入 + * zoom:缩放 + * fade-zoom:缩放淡入 + * fade-up:上滑淡入 + * fade-down:下滑淡入 + * fade-left:左滑淡入 + * fade-right:右滑淡入 + * slide-up:上滑进入 + * slide-down:下滑进入 + * slide-left:左滑进入 + * slide-right:右滑进入 + */ + +$u-zoom-scale: scale(0.95); + +.u-fade-enter-active, +.u-fade-leave-active { + transition-property: opacity; +} + +.u-fade-enter, +.u-fade-leave-to { + opacity: 0 +} + +.u-fade-zoom-enter, +.u-fade-zoom-leave-to { + transform: $u-zoom-scale; + opacity: 0; +} + +.u-fade-zoom-enter-active, +.u-fade-zoom-leave-active { + transition-property: transform, opacity; +} + +.u-fade-down-enter-active, +.u-fade-down-leave-active, +.u-fade-left-enter-active, +.u-fade-left-leave-active, +.u-fade-right-enter-active, +.u-fade-right-leave-active, +.u-fade-up-enter-active, +.u-fade-up-leave-active { + transition-property: opacity, transform; +} + +.u-fade-up-enter, +.u-fade-up-leave-to { + transform: translate3d(0, 100%, 0); + opacity: 0 +} + +.u-fade-down-enter, +.u-fade-down-leave-to { + transform: translate3d(0, -100%, 0); + opacity: 0 +} + +.u-fade-left-enter, +.u-fade-left-leave-to { + transform: translate3d(-100%, 0, 0); + opacity: 0 +} + +.u-fade-right-enter, +.u-fade-right-leave-to { + transform: translate3d(100%, 0, 0); + opacity: 0 +} + +.u-slide-down-enter-active, +.u-slide-down-leave-active, +.u-slide-left-enter-active, +.u-slide-left-leave-active, +.u-slide-right-enter-active, +.u-slide-right-leave-active, +.u-slide-up-enter-active, +.u-slide-up-leave-active { + transition-property: transform; +} + +.u-slide-up-enter, +.u-slide-up-leave-to { + transform: translate3d(0, 100%, 0) +} + +.u-slide-down-enter, +.u-slide-down-leave-to { + transform: translate3d(0, -100%, 0) +} + +.u-slide-left-enter, +.u-slide-left-leave-to { + transform: translate3d(-100%, 0, 0) +} + +.u-slide-right-enter, +.u-slide-right-leave-to { + transform: translate3d(100%, 0, 0) +} + +.u-zoom-enter-active, +.u-zoom-leave-active { + transition-property: transform +} + +.u-zoom-enter, +.u-zoom-leave-to { + transform: $u-zoom-scale +} diff --git a/uni_modules/uview-ui/components/u-upload/mixin.js b/uni_modules/uview-ui/components/u-upload/mixin.js new file mode 100644 index 0000000..410c775 --- /dev/null +++ b/uni_modules/uview-ui/components/u-upload/mixin.js @@ -0,0 +1,21 @@ +export default { + watch: { + // 监听accept的变化,判断是否符合个平台要求 + // 只有微信小程序才支持选择媒体,文件类型,所以这里做一个判断提示 + accept: { + immediate: true, + handler(val) { + // #ifndef MP-WEIXIN + if (val === 'all' || val === 'media') { + uni.$u.error('只有微信小程序才支持把accept配置为all、media之一') + } + // #endif + // #ifndef H5 || MP-WEIXIN + if (val === 'file') { + uni.$u.error('只有微信小程序和H5(HX2.9.9)才支持把accept配置为file') + } + // #endif + } + } + } +} diff --git a/uni_modules/uview-ui/components/u-upload/props.js b/uni_modules/uview-ui/components/u-upload/props.js new file mode 100644 index 0000000..b106ae7 --- /dev/null +++ b/uni_modules/uview-ui/components/u-upload/props.js @@ -0,0 +1,124 @@ +export default { + props: { + // 接受的文件类型, 可选值为all media image file video + accept: { + type: String, + default: uni.$u.props.upload.accept + }, + // 图片或视频拾取模式,当accept为image类型时设置capture可选额外camera可以直接调起摄像头 + capture: { + type: [String, Array], + default: uni.$u.props.upload.capture + }, + // 当accept为video时生效,是否压缩视频,默认为true + compressed: { + type: Boolean, + default: uni.$u.props.upload.compressed + }, + // 当accept为video时生效,可选值为back或front + camera: { + type: String, + default: uni.$u.props.upload.camera + }, + // 当accept为video时生效,拍摄视频最长拍摄时间,单位秒 + maxDuration: { + type: Number, + default: uni.$u.props.upload.maxDuration + }, + // 上传区域的图标,只能内置图标 + uploadIcon: { + type: String, + default: uni.$u.props.upload.uploadIcon + }, + // 上传区域的图标的颜色,默认 + uploadIconColor: { + type: String, + default: uni.$u.props.upload.uploadIconColor + }, + // 是否开启文件读取前事件 + useBeforeRead: { + type: Boolean, + default: uni.$u.props.upload.useBeforeRead + }, + // 读取后的处理函数 + afterRead: { + type: Function, + default: null + }, + // 读取前的处理函数 + beforeRead: { + type: Function, + default: null + }, + // 是否显示组件自带的图片预览功能 + previewFullImage: { + type: Boolean, + default: uni.$u.props.upload.previewFullImage + }, + // 最大上传数量 + maxCount: { + type: [String, Number], + default: uni.$u.props.upload.maxCount + }, + // 是否启用 + disabled: { + type: Boolean, + default: uni.$u.props.upload.disabled + }, + // 预览上传的图片时的裁剪模式,和image组件mode属性一致 + imageMode: { + type: String, + default: uni.$u.props.upload.imageMode + }, + // 标识符,可以在回调函数的第二项参数中获取 + name: { + type: String, + default: uni.$u.props.upload.name + }, + // 所选的图片的尺寸, 可选值为original compressed + sizeType: { + type: Array, + default: uni.$u.props.upload.sizeType + }, + // 是否开启图片多选,部分安卓机型不支持 + multiple: { + type: Boolean, + default: uni.$u.props.upload.multiple + }, + // 是否展示删除按钮 + deletable: { + type: Boolean, + default: uni.$u.props.upload.deletable + }, + // 文件大小限制,单位为byte + maxSize: { + type: [String, Number], + default: uni.$u.props.upload.maxSize + }, + // 显示已上传的文件列表 + fileList: { + type: Array, + default: uni.$u.props.upload.fileList + }, + // 上传区域的提示文字 + uploadText: { + type: String, + default: uni.$u.props.upload.uploadText + }, + // 内部预览图片区域和选择图片按钮的区域宽度 + width: { + type: [String, Number], + default: uni.$u.props.upload.width + }, + // 内部预览图片区域和选择图片按钮的区域高度 + height: { + type: [String, Number], + default: uni.$u.props.upload.height + }, + // 是否在上传完成后展示预览图 + previewImage: { + type: Boolean, + default: uni.$u.props.upload.previewImage + } + } +} diff --git a/uni_modules/uview-ui/components/u-upload/u-upload.vue b/uni_modules/uview-ui/components/u-upload/u-upload.vue new file mode 100644 index 0000000..1dac8a7 --- /dev/null +++ b/uni_modules/uview-ui/components/u-upload/u-upload.vue @@ -0,0 +1,558 @@ +<template> + <view class="u-upload" :style="[$u.addStyle(customStyle)]"> + <view class="u-upload__wrap" > + <template v-if="previewImage"> + <view + class="u-upload__wrap__preview" + v-for="(item, index) in lists" + :key="index" + > + <image + v-if="item.isImage || (item.type && item.type === 'image')" + :src="item.thumb || item.url" + :mode="imageMode" + class="u-upload__wrap__preview__image" + @tap="onPreviewImage(item)" + :style="[{ + width: $u.addUnit(width), + height: $u.addUnit(height) + }]" + /> + <view + v-else + class="u-upload__wrap__preview__other" + > + <u-icon + color="#80CBF9" + size="26" + :name="item.isVideo || (item.type && item.type === 'video') ? 'movie' : 'folder'" + ></u-icon> + <text class="u-upload__wrap__preview__other__text">{{item.isVideo || (item.type && item.type === 'video') ? '视频' : '文件'}}</text> + </view> + <view + class="u-upload__status" + v-if="item.status === 'uploading' || item.status === 'failed'" + > + <view class="u-upload__status__icon"> + <u-icon + v-if="item.status === 'failed'" + name="close-circle" + color="#ffffff" + size="25" + /> + <u-loading-icon + size="22" + mode="circle" + color="#ffffff" + v-else + /> + </view> + <text + v-if="item.message" + class="u-upload__status__message" + >{{ item.message }}</text> + </view> + <view + class="u-upload__deletable" + v-if="item.status !== 'uploading' && (deletable || item.deletable)" + @tap.stop="deleteItem(index)" + > + <view class="u-upload__deletable__icon"> + <u-icon + name="close" + color="#ffffff" + size="10" + ></u-icon> + </view> + </view> + <view + class="u-upload__success" + v-if="item.status === 'success'" + > + <!-- #ifdef APP-NVUE --> + <image + :src="successIcon" + class="u-upload__success__icon" + ></image> + <!-- #endif --> + <!-- #ifndef APP-NVUE --> + <view class="u-upload__success__icon"> + <u-icon + name="checkmark" + color="#ffffff" + size="12" + ></u-icon> + </view> + <!-- #endif --> + </view> + </view> + + </template> + + <template v-if="isInCount"> + <view + v-if="$slots.default || $slots.$default" + @tap="chooseFile" + > + <slot /> + </view> + <view + v-else + class="u-upload__button" + :hover-class="!disabled ? 'u-upload__button--hover' : ''" + hover-stay-time="150" + @tap="chooseFile" + :class="[disabled && 'u-upload__button--disabled']" + :style="[{ + width: $u.addUnit(width), + height: $u.addUnit(height) + }]" + > + <u-icon + :name="uploadIcon" + size="26" + :color="uploadIconColor" + ></u-icon> + <text + v-if="uploadText" + class="u-upload__button__text" + >{{ uploadText }}</text> + </view> + </template> + </view> + + </view> +</template> + +<script> + import { + chooseFile + } from './utils'; + import mixin from './mixin.js'; + import props from './props.js'; + + /** + * upload 上传 + * @description 该组件用于上传图片场景 + * @tutorial https://uviewui.com/components/upload.html + * @property {String} accept 接受的文件类型, 可选值为all media image file video (默认 'image' ) + * @property {String | Array} capture 图片或视频拾取模式,当accept为image类型时设置capture可选额外camera可以直接调起摄像头(默认 ['album', 'camera'] ) + * @property {Boolean} compressed 当accept为video时生效,是否压缩视频,默认为true(默认 true ) + * @property {String} camera 当accept为video时生效,可选值为back或front(默认 'back' ) + * @property {Number} maxDuration 当accept为video时生效,拍摄视频最长拍摄时间,单位秒(默认 60 ) + * @property {String} uploadIcon 上传区域的图标,只能内置图标(默认 'camera-fill' ) + * @property {String} uploadIconColor 上传区域的图标的字体颜色,只能内置图标(默认 #D3D4D6 ) + * @property {Boolean} useBeforeRead 是否开启文件读取前事件(默认 false ) + * @property {Boolean} previewFullImage 是否显示组件自带的图片预览功能(默认 true ) + * @property {String | Number} maxCount 最大上传数量(默认 52 ) + * @property {Boolean} disabled 是否启用(默认 false ) + * @property {String} imageMode 预览上传的图片时的裁剪模式,和image组件mode属性一致(默认 'aspectFill' ) + * @property {String} name 标识符,可以在回调函数的第二项参数中获取 + * @property {Array} sizeType 所选的图片的尺寸, 可选值为original compressed(默认 ['original', 'compressed'] ) + * @property {Boolean} multiple 是否开启图片多选,部分安卓机型不支持 (默认 false ) + * @property {Boolean} deletable 是否展示删除按钮(默认 true ) + * @property {String | Number} maxSize 文件大小限制,单位为byte (默认 Number.MAX_VALUE ) + * @property {Array} fileList 显示已上传的文件列表 + * @property {String} uploadText 上传区域的提示文字 + * @property {String | Number} width 内部预览图片区域和选择图片按钮的区域宽度(默认 80 ) + * @property {String | Number} height 内部预览图片区域和选择图片按钮的区域高度(默认 80 ) + * @property {Object} customStyle 组件的样式,对象形式 + * @event {Function} afterRead 读取后的处理函数 + * @event {Function} beforeRead 读取前的处理函数 + * @event {Function} oversize 文件超出大小限制 + * @event {Function} clickPreview 点击预览图片 + * @event {Function} delete 删除图片 + * @example <u-upload :action="action" :fileList="fileList" ></u-upload> + */ + export default { + name: "u-upload", + mixins: [uni.$u.mpMixin, uni.$u.mixin, mixin,props], + data() { + return { + // #ifdef APP-NVUE + successIcon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAKKADAAQAAAABAAAAKAAAAAB65masAAACP0lEQVRYCc3YXygsURwH8K/dpcWyG3LF5u/6/+dKVylSypuUl6uUPMifKMWL8oKEB1EUT1KeUPdR3uTNUsSLxb2udG/cbvInNuvf2rVnazZ/ZndmZ87snjM1Z+Z3zpzfp9+Z5mEAhlvjRtZgCKs+gnPAOcAkkMOR4jEHfItjDvgRxxSQD8cM0BuOCaAvXNCBQrigAsXgggYUiwsK0B9cwIH+4gIKlIILGFAqLiBAOTjFgXJxigJp4BQD0sIpAqSJow6kjSNAFTnRaHJwLenD6Mud52VQAcrBfTd2oyq+HtGaGGWAcnAVcXWoM3bCZrdi+ncPfaAcXE5UKVpdW/vitGPqqAtn98d0gXJwX7Qp6MmegUYVhvmTIezdmHlxJCjpHRTCFerLkRRu4k0aqdajN3sWOo0BK//msHa+xDuPC/oNFMKRhTtM4xjIX0SCNpXL4+7VIaHuyiWEp2L7ahWLf8fejfPdqPmC3mJicORZUp1CQzm+GiphvljGk+PBvWRbxii+xVTj5M6CiZ/tsDufvaXyxEUDxeLIyvu3m0iOyEFWVAkydcVYdyFrE9tQk9iMq6f/GNlvwt3LjQfh60LUrw9/cFyyMJUW/XkLSNMV4Mi6C5ML+ui4x5ClAX9sB9w0wV6wglJwJCv5fOxcr6EstgbGiEw4XcfUry4cWrcEUW8n+ARKxXEJHhw2WG43UKSvwI/TSZgvl7kh0b3XLZaLEy0QmMgLZAVH7J+ALOE+AVnDvQOyiPMAWcW5gSzjCPAV+78S5WE0GrQAAAAASUVORK5CYII=', + // #endif + lists: [], + isInCount: true, + } + }, + watch: { + // 监听文件列表的变化,重新整理内部数据 + fileList: { + immediate: true, + handler() { + this.formatFileList() + } + }, + }, + methods: { + formatFileList() { + const { + fileList = [], maxCount + } = this; + const lists = fileList.map((item) => + Object.assign(Object.assign({}, item), { + // 如果item.url为本地选择的blob文件的话,无法判断其为video还是image,此处优先通过accept做判断处理 + isImage: this.accept === 'image' || uni.$u.test.image(item.url || item.thumb), + isVideo: this.accept === 'video' || uni.$u.test.video(item.url || item.thumb), + deletable: typeof(item.deletable) === 'boolean' ? item.deletable : this.deletable, + }) + ); + this.lists = lists + this.isInCount = lists.length < maxCount + }, + chooseFile() { + const { + maxCount, + multiple, + lists, + disabled + } = this; + if (disabled) return; + // 如果用户传入的是字符串,需要格式化成数组 + let capture; + try { + capture = uni.$u.test.array(this.capture) ? this.capture : this.capture.split(','); + }catch(e) { + capture = []; + } + chooseFile( + Object.assign({ + accept: this.accept, + multiple: this.multiple, + capture: capture, + compressed: this.compressed, + maxDuration: this.maxDuration, + sizeType: this.sizeType, + camera: this.camera, + }, { + maxCount: maxCount - lists.length, + }) + ) + .then((res) => { + this.onBeforeRead(multiple ? res : res[0]); + }) + .catch((error) => { + this.$emit('error', error); + }); + }, + // 文件读取之前 + onBeforeRead(file) { + const { + beforeRead, + useBeforeRead, + } = this; + let res = true + // beforeRead是否为一个方法 + if (uni.$u.test.func(beforeRead)) { + // 如果用户定义了此方法,则去执行此方法,并传入读取的文件回调 + res = beforeRead(file, this.getDetail()); + } + if (useBeforeRead) { + res = new Promise((resolve, reject) => { + this.$emit( + 'beforeRead', + Object.assign(Object.assign({ + file + }, this.getDetail()), { + callback: (ok) => { + ok ? resolve() : reject(); + }, + }) + ); + }); + } + if (!res) { + return; + } + if (uni.$u.test.promise(res)) { + res.then((data) => this.onAfterRead(data || file)); + } else { + this.onAfterRead(file); + } + }, + getDetail(index) { + return { + name: this.name, + index: index == null ? this.fileList.length : index, + }; + }, + onAfterRead(file) { + const { + maxSize, + afterRead + } = this; + const oversize = Array.isArray(file) ? + file.some((item) => item.size > maxSize) : + file.size > maxSize; + if (oversize) { + this.$emit('oversize', Object.assign({ + file + }, this.getDetail())); + return; + } + if (typeof afterRead === 'function') { + afterRead(file, this.getDetail()); + } + this.$emit('afterRead', Object.assign({ + file + }, this.getDetail())); + }, + deleteItem(index) { + this.$emit( + 'delete', + Object.assign(Object.assign({}, this.getDetail(index)), { + file: this.fileList[index], + }) + ); + }, + // 预览图片 + onPreviewImage(item) { + if (!item.isImage || !this.previewFullImage) return + uni.previewImage({ + // 先filter找出为图片的item,再返回filter结果中的图片url + urls: this.lists.filter((item) => this.accept === 'image' || uni.$u.test.image(item.url || item.thumb)).map((item) => item.url || item.thumb), + current: item.url || item.thumb, + fail() { + uni.$u.toast('预览图片失败') + }, + }); + }, + onPreviewVideo(event) { + if (!this.data.previewFullImage) return; + const { + index + } = event.currentTarget.dataset; + const { + lists + } = this.data; + wx.previewMedia({ + sources: lists + .filter((item) => isVideoFile(item)) + .map((item) => + Object.assign(Object.assign({}, item), { + type: 'video' + }) + ), + current: index, + fail() { + uni.$u.toast('预览视频失败') + }, + }); + }, + onClickPreview(event) { + const { + index + } = event.currentTarget.dataset; + const item = this.data.lists[index]; + this.$emit( + 'clickPreview', + Object.assign(Object.assign({}, item), this.getDetail(index)) + ); + } + } + } +</script> + +<style lang="scss" scoped> + @import '../../libs/css/components.scss'; + $u-upload-preview-border-radius: 2px !default; + $u-upload-preview-margin: 0 8px 8px 0 !default; + $u-upload-image-width:80px !default; + $u-upload-image-height:$u-upload-image-width; + $u-upload-other-bgColor: rgb(242, 242, 242) !default; + $u-upload-other-flex:1 !default; + $u-upload-text-font-size:11px !default; + $u-upload-text-color:$u-tips-color !default; + $u-upload-text-margin-top:2px !default; + $u-upload-deletable-right:0 !default; + $u-upload-deletable-top:0 !default; + $u-upload-deletable-bgColor:rgb(55, 55, 55) !default; + $u-upload-deletable-height:14px !default; + $u-upload-deletable-width:$u-upload-deletable-height; + $u-upload-deletable-boder-bottom-left-radius:100px !default; + $u-upload-deletable-zIndex:3 !default; + $u-upload-success-bottom:0 !default; + $u-upload-success-right:0 !default; + $u-upload-success-border-style:solid !default; + $u-upload-success-border-top-color:transparent !default; + $u-upload-success-border-left-color:transparent !default; + $u-upload-success-border-bottom-color: $u-success !default; + $u-upload-success-border-right-color:$u-upload-success-border-bottom-color; + $u-upload-success-border-width:9px !default; + $u-upload-icon-top:0px !default; + $u-upload-icon-right:0px !default; + $u-upload-icon-h5-top:1px !default; + $u-upload-icon-h5-right:0 !default; + $u-upload-icon-width:16px !default; + $u-upload-icon-height:$u-upload-icon-width; + $u-upload-success-icon-bottom:-10px !default; + $u-upload-success-icon-right:-10px !default; + $u-upload-status-right:0 !default; + $u-upload-status-left:0 !default; + $u-upload-status-bottom:0 !default; + $u-upload-status-top:0 !default; + $u-upload-status-bgColor:rgba(0, 0, 0, 0.5) !default; + $u-upload-status-icon-Zindex:1 !default; + $u-upload-message-font-size:12px !default; + $u-upload-message-color:#FFFFFF !default; + $u-upload-message-margin-top:5px !default; + $u-upload-button-width:80px !default; + $u-upload-button-height:$u-upload-button-width; + $u-upload-button-bgColor:rgb(244, 245, 247) !default; + $u-upload-button-border-radius:2px !default; + $u-upload-botton-margin: 0 8px 8px 0 !default; + $u-upload-text-font-size:11px !default; + $u-upload-text-color:$u-tips-color !default; + $u-upload-text-margin-top: 2px !default; + $u-upload-hover-bgColor:rgb(230, 231, 233) !default; + $u-upload-disabled-opacity:.5 !default; + + .u-upload { + @include flex(column); + flex: 1; + + &__wrap { + @include flex; + flex-wrap: wrap; + flex: 1; + + &__preview { + border-radius: $u-upload-preview-border-radius; + margin: $u-upload-preview-margin; + position: relative; + overflow: hidden; + @include flex; + + &__image { + width: $u-upload-image-width; + height: $u-upload-image-height; + } + + &__other { + width: $u-upload-image-width; + height: $u-upload-image-height; + background-color: $u-upload-other-bgColor; + flex: $u-upload-other-flex; + @include flex(column); + justify-content: center; + align-items: center; + + &__text { + font-size: $u-upload-text-font-size; + color: $u-upload-text-color; + margin-top: $u-upload-text-margin-top; + } + } + } + } + + &__deletable { + position: absolute; + top: $u-upload-deletable-top; + right: $u-upload-deletable-right; + background-color: $u-upload-deletable-bgColor; + height: $u-upload-deletable-height; + width: $u-upload-deletable-width; + @include flex; + border-bottom-left-radius: $u-upload-deletable-boder-bottom-left-radius; + align-items: center; + justify-content: center; + z-index: $u-upload-deletable-zIndex; + + &__icon { + position: absolute; + transform: scale(0.7); + top: $u-upload-icon-top; + right: $u-upload-icon-right; + /* #ifdef H5 */ + top: $u-upload-icon-h5-top; + right: $u-upload-icon-h5-right; + /* #endif */ + } + } + + &__success { + position: absolute; + bottom: $u-upload-success-bottom; + right: $u-upload-success-right; + @include flex; + // 由于weex(nvue)为阿里巴巴的KPI(部门业绩考核)的laji产物,不支持css绘制三角形 + // 所以在nvue下使用图片,非nvue下使用css实现 + /* #ifndef APP-NVUE */ + border-style: $u-upload-success-border-style; + border-top-color: $u-upload-success-border-top-color; + border-left-color: $u-upload-success-border-left-color; + border-bottom-color: $u-upload-success-border-bottom-color; + border-right-color: $u-upload-success-border-right-color; + border-width: $u-upload-success-border-width; + align-items: center; + justify-content: center; + /* #endif */ + + &__icon { + /* #ifndef APP-NVUE */ + position: absolute; + transform: scale(0.7); + bottom: $u-upload-success-icon-bottom; + right: $u-upload-success-icon-right; + /* #endif */ + /* #ifdef APP-NVUE */ + width: $u-upload-icon-width; + height: $u-upload-icon-height; + /* #endif */ + } + } + + &__status { + position: absolute; + top: $u-upload-status-top; + bottom: $u-upload-status-bottom; + left: $u-upload-status-left; + right: $u-upload-status-right; + background-color: $u-upload-status-bgColor; + @include flex(column); + align-items: center; + justify-content: center; + + &__icon { + position: relative; + z-index: $u-upload-status-icon-Zindex; + } + + &__message { + font-size: $u-upload-message-font-size; + color: $u-upload-message-color; + margin-top: $u-upload-message-margin-top; + } + } + + &__button { + @include flex(column); + align-items: center; + justify-content: center; + width: $u-upload-button-width; + height: $u-upload-button-height; + background-color: $u-upload-button-bgColor; + border-radius: $u-upload-button-border-radius; + margin: $u-upload-botton-margin; + /* #ifndef APP-NVUE */ + box-sizing: border-box; + /* #endif */ + + &__text { + font-size: $u-upload-text-font-size; + color: $u-upload-text-color; + margin-top: $u-upload-text-margin-top; + } + + &--hover { + background-color: $u-upload-hover-bgColor; + } + + &--disabled { + opacity: $u-upload-disabled-opacity; + } + } + } +</style> diff --git a/uni_modules/uview-ui/components/u-upload/utils.js b/uni_modules/uview-ui/components/u-upload/utils.js new file mode 100644 index 0000000..88cb602 --- /dev/null +++ b/uni_modules/uview-ui/components/u-upload/utils.js @@ -0,0 +1,151 @@ +function pickExclude(obj, keys) { + // 某些情况下,type可能会为 + if (!['[object Object]', '[object File]'].includes(Object.prototype.toString.call(obj))) { + return {} + } + return Object.keys(obj).reduce((prev, key) => { + if (!keys.includes(key)) { + prev[key] = obj[key] + } + return prev + }, {}) +} + +function formatImage(res) { + return res.tempFiles.map((item) => ({ + ...pickExclude(item, ['path']), + type: 'image', + url: item.path, + thumb: item.path, + size: item.size, + // #ifdef H5 + name: item.name + // #endif + })) +} + +function formatVideo(res) { + return [ + { + ...pickExclude(res, ['tempFilePath', 'thumbTempFilePath', 'errMsg']), + type: 'video', + url: res.tempFilePath, + thumb: res.thumbTempFilePath, + size: res.size, + // #ifdef H5 + name: res.name + // #endif + } + ] +} + +function formatMedia(res) { + return res.tempFiles.map((item) => ({ + ...pickExclude(item, ['fileType', 'thumbTempFilePath', 'tempFilePath']), + type: res.type, + url: item.tempFilePath, + thumb: res.type === 'video' ? item.thumbTempFilePath : item.tempFilePath, + size: item.size + })) +} + +function formatFile(res) { + return res.tempFiles.map((item) => ({ + ...pickExclude(item, ['path']), + url: item.path, + size:item.size, + // #ifdef H5 + name: item.name, + type: item.type + // #endif + })) +} +export function chooseFile({ + accept, + multiple, + capture, + compressed, + maxDuration, + sizeType, + camera, + maxCount +}) { + return new Promise((resolve, reject) => { + switch (accept) { + case 'image': + uni.chooseImage({ + count: multiple ? Math.min(maxCount, 9) : 1, + sourceType: capture, + sizeType, + success: (res) => resolve(formatImage(res)), + fail: reject + }) + break + // #ifdef MP-WEIXIN + // 只有微信小程序才支持chooseMedia接口 + case 'media': + wx.chooseMedia({ + count: multiple ? Math.min(maxCount, 9) : 1, + sourceType: capture, + maxDuration, + sizeType, + camera, + success: (res) => resolve(formatMedia(res)), + fail: reject + }) + break + // #endif + case 'video': + uni.chooseVideo({ + sourceType: capture, + compressed, + maxDuration, + camera, + success: (res) => resolve(formatVideo(res)), + fail: reject + }) + break + // #ifdef MP-WEIXIN || H5 + // 只有微信小程序才支持chooseMessageFile接口 + case 'file': + // #ifdef MP-WEIXIN + wx.chooseMessageFile({ + count: multiple ? maxCount : 1, + type: accept, + success: (res) => resolve(formatFile(res)), + fail: reject + }) + // #endif + // #ifdef H5 + // 需要hx2.9.9以上才支持uni.chooseFile + uni.chooseFile({ + count: multiple ? maxCount : 1, + type: accept, + success: (res) => resolve(formatFile(res)), + fail: reject + }) + // #endif + break + // #endif + default: + // 此为保底选项,在accept不为上面任意一项的时候选取全部文件 + // #ifdef MP-WEIXIN + wx.chooseMessageFile({ + count: multiple ? maxCount : 1, + type: 'all', + success: (res) => resolve(formatFile(res)), + fail: reject + }) + // #endif + // #ifdef H5 + // 需要hx2.9.9以上才支持uni.chooseFile + uni.chooseFile({ + count: multiple ? maxCount : 1, + type: 'all', + success: (res) => resolve(formatFile(res)), + fail: reject + }) + // #endif + } + }) +} diff --git a/uni_modules/uview-ui/components/uview-ui/uview-ui.vue b/uni_modules/uview-ui/components/uview-ui/uview-ui.vue new file mode 100644 index 0000000..bcd3662 --- /dev/null +++ b/uni_modules/uview-ui/components/uview-ui/uview-ui.vue @@ -0,0 +1,15 @@ +<template> +</template> + +<template> + <view></view> +</template> + +<script> + export default { + + } +</script> + +<style> +</style> diff --git a/uni_modules/uview-ui/index.js b/uni_modules/uview-ui/index.js new file mode 100644 index 0000000..651c090 --- /dev/null +++ b/uni_modules/uview-ui/index.js @@ -0,0 +1,79 @@ +// 看到此报错,是因为没有配置vue.config.js的【transpileDependencies】,详见:https://www.uviewui.com/components/npmSetting.html#_5-cli模式额外配置 +const pleaseSetTranspileDependencies = {}, babelTest = pleaseSetTranspileDependencies?.test + + + +// 引入全局mixin +import mixin from './libs/mixin/mixin.js' +// 小程序特有的mixin +import mpMixin from './libs/mixin/mpMixin.js' +// 全局挂载引入http相关请求拦截插件 +import Request from './libs/luch-request' + +// 路由封装 +import route from './libs/util/route.js' +// 颜色渐变相关,colorGradient-颜色渐变,hexToRgb-十六进制颜色转rgb颜色,rgbToHex-rgb转十六进制 +import colorGradient from './libs/function/colorGradient.js' + +// 规则检验 +import test from './libs/function/test.js' +// 防抖方法 +import debounce from './libs/function/debounce.js' +// 节流方法 +import throttle from './libs/function/throttle.js' +// 公共文件写入的方法 +import index from './libs/function/index.js' + +// 配置信息 +import config from './libs/config/config.js' +// props配置信息 +import props from './libs/config/props.js' +// 各个需要fixed的地方的z-index配置文件 +import zIndex from './libs/config/zIndex.js' +// 关于颜色的配置,特殊场景使用 +import color from './libs/config/color.js' +// 平台 +import platform from './libs/function/platform' + +const $u = { + route, + date: index.timeFormat, // 另名date + colorGradient: colorGradient.colorGradient, + hexToRgb: colorGradient.hexToRgb, + rgbToHex: colorGradient.rgbToHex, + colorToRgba: colorGradient.colorToRgba, + test, + type: ['primary', 'success', 'error', 'warning', 'info'], + http: new Request(), + config, // uView配置信息相关,比如版本号 + zIndex, + debounce, + throttle, + mixin, + mpMixin, + props, + ...index, + color, + platform +} + +// $u挂载到uni对象上 +uni.$u = $u + +const install = (Vue) => { + // 时间格式化,同时两个名称,date和timeFormat + Vue.filter('timeFormat', (timestamp, format) => uni.$u.timeFormat(timestamp, format)) + Vue.filter('date', (timestamp, format) => uni.$u.timeFormat(timestamp, format)) + // 将多久以前的方法,注入到全局过滤器 + Vue.filter('timeFrom', (timestamp, format) => uni.$u.timeFrom(timestamp, format)) + // 同时挂载到uni和Vue.prototype中 + // #ifndef APP-NVUE + // 只有vue,挂载到Vue.prototype才有意义,因为nvue中全局Vue.prototype和Vue.mixin是无效的 + Vue.prototype.$u = $u + Vue.mixin(mixin) + // #endif +} + +export default { + install +} diff --git a/uni_modules/uview-ui/index.scss b/uni_modules/uview-ui/index.scss new file mode 100644 index 0000000..8fcfa83 --- /dev/null +++ b/uni_modules/uview-ui/index.scss @@ -0,0 +1,23 @@ +// 引入公共基础类 +@import "./libs/css/common.scss"; +@import "./libs/css/color.scss"; + +// 非nvue的样式 +/* #ifndef APP-NVUE */ +@import "./libs/css/vue.scss"; +/* #endif */ + +// nvue的特有样式 +/* #ifdef APP-NVUE */ +@import "./libs/css/nvue.scss"; +/* #endif */ + +// 小程序特有的样式 +/* #ifdef MP */ +@import "./libs/css/mp.scss"; +/* #endif */ + +// H5特有的样式 +/* #ifdef H5 */ +@import "./libs/css/h5.scss"; +/* #endif */ \ No newline at end of file diff --git a/uni_modules/uview-ui/libs/config/color.js b/uni_modules/uview-ui/libs/config/color.js new file mode 100644 index 0000000..56b4187 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/color.js @@ -0,0 +1,17 @@ +// 为了让用户能够自定义主题,会逐步弃用此文件,各颜色通过css提供 +// 为了给某些特殊场景使用和向后兼容,无需删除此文件(2020-06-20) +const color = { + primary: '#3c9cff', + info: '#909399', + default: '#909399', + warning: '#f9ae3d', + error: '#f56c6c', + success: '#5ac725', + mainColor: '#303133', + contentColor: '#606266', + tipsColor: '#909399', + lightColor: '#c0c4cc', + borderColor: '#e4e7ed' +} + +export default color diff --git a/uni_modules/uview-ui/libs/config/config.js b/uni_modules/uview-ui/libs/config/config.js new file mode 100644 index 0000000..484aa03 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/config.js @@ -0,0 +1,34 @@ +// 此版本发布于2022-06-17 +const version = '2.0.33' + +// 开发环境才提示,生产环境不会提示 +if (process.env.NODE_ENV === 'development') { + //console.log(`\n %c uView V${version} %c https://www.uviewui.com/ \n\n`, 'color: #ffffff; background: #3c9cff; padding:5px 0;', 'color: #3c9cff;background: #ffffff; padding:5px 0;'); +} + +export default { + v: version, + version, + // 主题名称 + type: [ + 'primary', + 'success', + 'info', + 'error', + 'warning' + ], + // 颜色部分,本来可以通过scss的:export导出供js使用,但是奈何nvue不支持 + color: { + 'u-primary': '#2979ff', + 'u-warning': '#ff9900', + 'u-success': '#19be6b', + 'u-error': '#fa3534', + 'u-info': '#909399', + 'u-main-color': '#303133', + 'u-content-color': '#606266', + 'u-tips-color': '#909399', + 'u-light-color': '#c0c4cc' + }, + // 默认单位,可以通过配置为rpx,那么在用于传入组件大小参数为数值时,就默认为rpx + unit: 'px' +} diff --git a/uni_modules/uview-ui/libs/config/props.js b/uni_modules/uview-ui/libs/config/props.js new file mode 100644 index 0000000..6930d48 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props.js @@ -0,0 +1,190 @@ +/** + * 此文件的作用为统一配置所有组件的props参数 + * 借此用户可以全局覆盖组件的props默认值 + * 无需在每个引入组件的页面中都配置一次 + */ +import config from './config' + +import actionSheet from './props/actionSheet.js' +import album from './props/album.js' +import alert from './props/alert.js' +import avatar from './props/avatar' +import avatarGroup from './props/avatarGroup' +import backtop from './props/backtop' +import badge from './props/badge' +import button from './props/button' +import calendar from './props/calendar' +import carKeyboard from './props/carKeyboard' +import cell from './props/cell' +import cellGroup from './props/cellGroup' +import checkbox from './props/checkbox' +import checkboxGroup from './props/checkboxGroup' +import circleProgress from './props/circleProgress' +import code from './props/code' +import codeInput from './props/codeInput' +import col from './props/col' +import collapse from './props/collapse' +import collapseItem from './props/collapseItem' +import columnNotice from './props/columnNotice' +import countDown from './props/countDown' +import countTo from './props/countTo' +import datetimePicker from './props/datetimePicker' +import divider from './props/divider' +import empty from './props/empty' +import form from './props/form' +import formItem from './props/formItem' +import gap from './props/gap' +import grid from './props/grid' +import gridItem from './props/gridItem' +import icon from './props/icon' +import image from './props/image' +import indexAnchor from './props/indexAnchor' +import indexList from './props/indexList' +import input from './props/input' +import keyboard from './props/keyboard' +import line from './props/line' +import lineProgress from './props/lineProgress' +import link from './props/link' +import list from './props/list' +import listItem from './props/listItem' +import loadingIcon from './props/loadingIcon' +import loadingPage from './props/loadingPage' +import loadmore from './props/loadmore' +import modal from './props/modal' +import navbar from './props/navbar' +import noNetwork from './props/noNetwork' +import noticeBar from './props/noticeBar' +import notify from './props/notify' +import numberBox from './props/numberBox' +import numberKeyboard from './props/numberKeyboard' +import overlay from './props/overlay' +import parse from './props/parse' +import picker from './props/picker' +import popup from './props/popup' +import radio from './props/radio' +import radioGroup from './props/radioGroup' +import rate from './props/rate' +import readMore from './props/readMore' +import row from './props/row' +import rowNotice from './props/rowNotice' +import scrollList from './props/scrollList' +import search from './props/search' +import section from './props/section' +import skeleton from './props/skeleton' +import slider from './props/slider' +import statusBar from './props/statusBar' +import steps from './props/steps' +import stepsItem from './props/stepsItem' +import sticky from './props/sticky' +import subsection from './props/subsection' +import swipeAction from './props/swipeAction' +import swipeActionItem from './props/swipeActionItem' +import swiper from './props/swiper' +import swipterIndicator from './props/swipterIndicator' +import _switch from './props/switch' +import tabbar from './props/tabbar' +import tabbarItem from './props/tabbarItem' +import tabs from './props/tabs' +import tag from './props/tag' +import text from './props/text' +import textarea from './props/textarea' +import toast from './props/toast' +import toolbar from './props/toolbar' +import tooltip from './props/tooltip' +import transition from './props/transition' +import upload from './props/upload' + +const { + color +} = config + +export default { + ...actionSheet, + ...album, + ...alert, + ...avatar, + ...avatarGroup, + ...backtop, + ...badge, + ...button, + ...calendar, + ...carKeyboard, + ...cell, + ...cellGroup, + ...checkbox, + ...checkboxGroup, + ...circleProgress, + ...code, + ...codeInput, + ...col, + ...collapse, + ...collapseItem, + ...columnNotice, + ...countDown, + ...countTo, + ...datetimePicker, + ...divider, + ...empty, + ...form, + ...formItem, + ...gap, + ...grid, + ...gridItem, + ...icon, + ...image, + ...indexAnchor, + ...indexList, + ...input, + ...keyboard, + ...line, + ...lineProgress, + ...link, + ...list, + ...listItem, + ...loadingIcon, + ...loadingPage, + ...loadmore, + ...modal, + ...navbar, + ...noNetwork, + ...noticeBar, + ...notify, + ...numberBox, + ...numberKeyboard, + ...overlay, + ...parse, + ...picker, + ...popup, + ...radio, + ...radioGroup, + ...rate, + ...readMore, + ...row, + ...rowNotice, + ...scrollList, + ...search, + ...section, + ...skeleton, + ...slider, + ...statusBar, + ...steps, + ...stepsItem, + ...sticky, + ...subsection, + ...swipeAction, + ...swipeActionItem, + ...swiper, + ...swipterIndicator, + ..._switch, + ...tabbar, + ...tabbarItem, + ...tabs, + ...tag, + ...text, + ...textarea, + ...toast, + ...toolbar, + ...tooltip, + ...transition, + ...upload +} diff --git a/uni_modules/uview-ui/libs/config/props/actionSheet.js b/uni_modules/uview-ui/libs/config/props/actionSheet.js new file mode 100644 index 0000000..d8061a7 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/actionSheet.js @@ -0,0 +1,25 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:44:35 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/actionSheet.js + */ +export default { + // action-sheet组件 + actionSheet: { + show: false, + title: '', + description: '', + actions: () => [], + index: '', + cancelText: '', + closeOnClickAction: true, + safeAreaInsetBottom: true, + openType: '', + closeOnClickOverlay: true, + round: 0 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/album.js b/uni_modules/uview-ui/libs/config/props/album.js new file mode 100644 index 0000000..8877326 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/album.js @@ -0,0 +1,25 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:47:24 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/album.js + */ +export default { + // album 组件 + album: { + urls: () => [], + keyName: '', + singleSize: 180, + multipleSize: 70, + space: 6, + singleMode: 'scaleToFill', + multipleMode: 'aspectFill', + maxCount: 9, + previewFullImage: true, + rowCount: 3, + showMore: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/alert.js b/uni_modules/uview-ui/libs/config/props/alert.js new file mode 100644 index 0000000..8f8182c --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/alert.js @@ -0,0 +1,22 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:48:53 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/alert.js + */ +export default { + // alert警告组件 + alert: { + title: '', + type: 'warning', + description: '', + closable: false, + showIcon: false, + effect: 'light', + center: false, + fontSize: 14 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/avatar.js b/uni_modules/uview-ui/libs/config/props/avatar.js new file mode 100644 index 0000000..c097d4e --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/avatar.js @@ -0,0 +1,28 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:49:22 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/avatar.js + */ +export default { + // avatar 组件 + avatar: { + src: '', + shape: 'circle', + size: 40, + mode: 'scaleToFill', + text: '', + bgColor: '#c0c4cc', + color: '#ffffff', + fontSize: 18, + icon: '', + mpAvatar: false, + randomBgColor: false, + defaultUrl: '', + colorIndex: '', + name: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/avatarGroup.js b/uni_modules/uview-ui/libs/config/props/avatarGroup.js new file mode 100644 index 0000000..f4a66c3 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/avatarGroup.js @@ -0,0 +1,23 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:49:55 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/avatarGroup.js + */ +export default { + // avatarGroup 组件 + avatarGroup: { + urls: () => [], + maxCount: 5, + shape: 'circle', + mode: 'scaleToFill', + showMore: true, + size: 40, + keyName: '', + gap: 0.5, + extraValue: 0 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/backtop.js b/uni_modules/uview-ui/libs/config/props/backtop.js new file mode 100644 index 0000000..80f17d0 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/backtop.js @@ -0,0 +1,27 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:50:18 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/backtop.js + */ +export default { + // backtop组件 + backtop: { + mode: 'circle', + icon: 'arrow-upward', + text: '', + duration: 100, + scrollTop: 0, + top: 400, + bottom: 100, + right: 20, + zIndex: 9, + iconStyle: () => ({ + color: '#909399', + fontSize: '19px' + }) + } +} diff --git a/uni_modules/uview-ui/libs/config/props/badge.js b/uni_modules/uview-ui/libs/config/props/badge.js new file mode 100644 index 0000000..44ee7cc --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/badge.js @@ -0,0 +1,27 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-23 19:51:50 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/badge.js + */ +export default { + // 徽标数组件 + badge: { + isDot: false, + value: '', + show: true, + max: 999, + type: 'error', + showZero: false, + bgColor: null, + color: null, + shape: 'circle', + numberType: 'overflow', + offset: () => [], + inverted: false, + absolute: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/button.js b/uni_modules/uview-ui/libs/config/props/button.js new file mode 100644 index 0000000..acd65fc --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/button.js @@ -0,0 +1,42 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:51:27 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/button.js + */ +export default { + // button组件 + button: { + hairline: false, + type: 'info', + size: 'normal', + shape: 'square', + plain: false, + disabled: false, + loading: false, + loadingText: '', + loadingMode: 'spinner', + loadingSize: 15, + openType: '', + formType: '', + appParameter: '', + hoverStopPropagation: true, + lang: 'en', + sessionFrom: '', + sendMessageTitle: '', + sendMessagePath: '', + sendMessageImg: '', + showMessageCard: false, + dataName: '', + throttleTime: 0, + hoverStartTime: 0, + hoverStayTime: 200, + text: '', + icon: '', + iconColor: '', + color: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/calendar.js b/uni_modules/uview-ui/libs/config/props/calendar.js new file mode 100644 index 0000000..bfd2bd6 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/calendar.js @@ -0,0 +1,42 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:52:43 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/calendar.js + */ +export default { + // calendar 组件 + calendar: { + title: '日期选择', + showTitle: true, + showSubtitle: true, + mode: 'single', + startText: '开始', + endText: '结束', + customList: () => [], + color: '#3c9cff', + minDate: 0, + maxDate: 0, + defaultDate: null, + maxCount: Number.MAX_SAFE_INTEGER, // Infinity + rowHeight: 56, + formatter: null, + showLunar: false, + showMark: true, + confirmText: '确定', + confirmDisabledText: '确定', + show: false, + closeOnClickOverlay: false, + readonly: false, + showConfirm: true, + maxRange: Number.MAX_SAFE_INTEGER, // Infinity + rangePrompt: '', + showRangePrompt: true, + allowSameDay: false, + round: 0, + monthNum: 3 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/carKeyboard.js b/uni_modules/uview-ui/libs/config/props/carKeyboard.js new file mode 100644 index 0000000..af1baa0 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/carKeyboard.js @@ -0,0 +1,15 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:53:20 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/carKeyboard.js + */ +export default { + // 车牌号键盘 + carKeyboard: { + random: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/cell.js b/uni_modules/uview-ui/libs/config/props/cell.js new file mode 100644 index 0000000..425ea3f --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/cell.js @@ -0,0 +1,35 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-23 20:53:09 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/cell.js + */ +export default { + // cell组件的props + cell: { + customClass: '', + title: '', + label: '', + value: '', + icon: '', + disabled: false, + border: true, + center: false, + url: '', + linkType: 'navigateTo', + clickable: false, + isLink: false, + required: false, + arrowDirection: '', + iconStyle: {}, + rightIconStyle: {}, + rightIcon: 'arrow-right', + titleStyle: {}, + size: '', + stop: true, + name: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/cellGroup.js b/uni_modules/uview-ui/libs/config/props/cellGroup.js new file mode 100644 index 0000000..d48a9cd --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/cellGroup.js @@ -0,0 +1,17 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:54:16 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/cellGroup.js + */ +export default { + // cell-group组件的props + cellGroup: { + title: '', + border: true, + customStyle: {} + } +} diff --git a/uni_modules/uview-ui/libs/config/props/checkbox.js b/uni_modules/uview-ui/libs/config/props/checkbox.js new file mode 100644 index 0000000..2310901 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/checkbox.js @@ -0,0 +1,27 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-23 21:06:59 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/checkbox.js + */ +export default { + // checkbox组件 + checkbox: { + name: '', + shape: '', + size: '', + checkbox: false, + disabled: '', + activeColor: '', + inactiveColor: '', + iconSize: '', + iconColor: '', + label: '', + labelSize: '', + labelColor: '', + labelDisabled: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/checkboxGroup.js b/uni_modules/uview-ui/libs/config/props/checkboxGroup.js new file mode 100644 index 0000000..8798fa4 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/checkboxGroup.js @@ -0,0 +1,29 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:54:47 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/checkboxGroup.js + */ +export default { + // checkbox-group组件 + checkboxGroup: { + name: '', + value: () => [], + shape: 'square', + disabled: false, + activeColor: '#2979ff', + inactiveColor: '#c8c9cc', + size: 18, + placement: 'row', + labelSize: 14, + labelColor: '#303133', + labelDisabled: false, + iconColor: '#ffffff', + iconSize: 12, + iconPlacement: 'left', + borderBottom: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/circleProgress.js b/uni_modules/uview-ui/libs/config/props/circleProgress.js new file mode 100644 index 0000000..b3a9b43 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/circleProgress.js @@ -0,0 +1,15 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:55:02 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/circleProgress.js + */ +export default { + // circleProgress 组件 + circleProgress: { + percentage: 30 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/code.js b/uni_modules/uview-ui/libs/config/props/code.js new file mode 100644 index 0000000..693417a --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/code.js @@ -0,0 +1,21 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:55:27 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/code.js + */ + +export default { + // code 组件 + code: { + seconds: 60, + startText: '获取验证码', + changeText: 'X秒重新获取', + endText: '重新获取', + keepRunning: false, + uniqueKey: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/codeInput.js b/uni_modules/uview-ui/libs/config/props/codeInput.js new file mode 100644 index 0000000..cac9265 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/codeInput.js @@ -0,0 +1,29 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:55:58 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/codeInput.js + */ +export default { + // codeInput 组件 + codeInput: { + adjustPosition: true, + maxlength: 6, + dot: false, + mode: 'box', + hairline: false, + space: 10, + value: '', + focus: false, + bold: false, + color: '#606266', + fontSize: 18, + size: 35, + disabledKeyboard: false, + borderColor: '#c9cacc', + disabledDot: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/col.js b/uni_modules/uview-ui/libs/config/props/col.js new file mode 100644 index 0000000..7621653 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/col.js @@ -0,0 +1,19 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:56:12 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/col.js + */ +export default { + // col 组件 + col: { + span: 12, + offset: 0, + justify: 'start', + align: 'stretch', + textAlign: 'left' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/collapse.js b/uni_modules/uview-ui/libs/config/props/collapse.js new file mode 100644 index 0000000..c2b9fdd --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/collapse.js @@ -0,0 +1,17 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:56:30 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/collapse.js + */ +export default { + // collapse 组件 + collapse: { + value: null, + accordion: false, + border: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/collapseItem.js b/uni_modules/uview-ui/libs/config/props/collapseItem.js new file mode 100644 index 0000000..74ce682 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/collapseItem.js @@ -0,0 +1,25 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:56:42 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/collapseItem.js + */ +export default { + // collapseItem 组件 + collapseItem: { + title: '', + value: '', + label: '', + disabled: false, + isLink: true, + clickable: true, + border: true, + align: 'left', + name: '', + icon: '', + duration: 300 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/columnNotice.js b/uni_modules/uview-ui/libs/config/props/columnNotice.js new file mode 100644 index 0000000..147c0aa --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/columnNotice.js @@ -0,0 +1,24 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:57:16 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/columnNotice.js + */ +export default { + // columnNotice 组件 + columnNotice: { + text: '', + icon: 'volume', + mode: '', + color: '#f9ae3d', + bgColor: '#fdf6ec', + fontSize: 14, + speed: 80, + step: false, + duration: 1500, + disableTouch: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/countDown.js b/uni_modules/uview-ui/libs/config/props/countDown.js new file mode 100644 index 0000000..81e33b1 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/countDown.js @@ -0,0 +1,18 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:11:29 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/countDown.js + */ +export default { + // u-count-down 计时器组件 + countDown: { + time: 0, + format: 'HH:mm:ss', + autoStart: true, + millisecond: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/countTo.js b/uni_modules/uview-ui/libs/config/props/countTo.js new file mode 100644 index 0000000..a536cde --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/countTo.js @@ -0,0 +1,25 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:57:32 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/countTo.js + */ +export default { + // countTo 组件 + countTo: { + startVal: 0, + endVal: 0, + duration: 2000, + autoplay: true, + decimals: 0, + useEasing: true, + decimal: '.', + color: '#606266', + fontSize: 22, + bold: false, + separator: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/datetimePicker.js b/uni_modules/uview-ui/libs/config/props/datetimePicker.js new file mode 100644 index 0000000..4f90966 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/datetimePicker.js @@ -0,0 +1,36 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:57:48 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/datetimePicker.js + */ +export default { + // datetimePicker 组件 + datetimePicker: { + show: false, + showToolbar: true, + value: '', + title: '', + mode: 'datetime', + maxDate: new Date(new Date().getFullYear() + 10, 0, 1).getTime(), + minDate: new Date(new Date().getFullYear() - 10, 0, 1).getTime(), + minHour: 0, + maxHour: 23, + minMinute: 0, + maxMinute: 59, + filter: null, + formatter: null, + loading: false, + itemHeight: 44, + cancelText: '取消', + confirmText: '确认', + cancelColor: '#909193', + confirmColor: '#3c9cff', + visibleItemCount: 5, + closeOnClickOverlay: false, + defaultIndex: () => [] + } +} diff --git a/uni_modules/uview-ui/libs/config/props/divider.js b/uni_modules/uview-ui/libs/config/props/divider.js new file mode 100644 index 0000000..55a8ce4 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/divider.js @@ -0,0 +1,23 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:58:03 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/divider.js + */ +export default { + // divider组件 + divider: { + dashed: false, + hairline: true, + dot: false, + textPosition: 'center', + text: '', + textSize: 14, + textColor: '#909399', + lineColor: '#dcdfe6' + } + +} diff --git a/uni_modules/uview-ui/libs/config/props/empty.js b/uni_modules/uview-ui/libs/config/props/empty.js new file mode 100644 index 0000000..fe20445 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/empty.js @@ -0,0 +1,26 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:03:27 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/empty.js + */ +export default { + // empty组件 + empty: { + icon: '', + text: '', + textColor: '#c0c4cc', + textSize: 14, + iconColor: '#c0c4cc', + iconSize: 90, + mode: 'data', + width: 160, + height: 160, + show: true, + marginTop: 0 + } + +} diff --git a/uni_modules/uview-ui/libs/config/props/form.js b/uni_modules/uview-ui/libs/config/props/form.js new file mode 100644 index 0000000..41b122e --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/form.js @@ -0,0 +1,22 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:03:49 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/form.js + */ +export default { + // form 组件 + form: { + model: () => ({}), + rules: () => ({}), + errorType: 'message', + borderBottom: true, + labelPosition: 'left', + labelWidth: 45, + labelAlign: 'left', + labelStyle: () => ({}) + } +} diff --git a/uni_modules/uview-ui/libs/config/props/formItem.js b/uni_modules/uview-ui/libs/config/props/formItem.js new file mode 100644 index 0000000..dc94973 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/formItem.js @@ -0,0 +1,22 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:04:32 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/formItem.js + */ +export default { + // formItem 组件 + formItem: { + label: '', + prop: '', + borderBottom: '', + labelWidth: '', + rightIcon: '', + leftIcon: '', + required: false, + leftIconStyle: '', + } +} diff --git a/uni_modules/uview-ui/libs/config/props/gap.js b/uni_modules/uview-ui/libs/config/props/gap.js new file mode 100644 index 0000000..60a21af --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/gap.js @@ -0,0 +1,19 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:05:25 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/gap.js + */ +export default { + // gap组件 + gap: { + bgColor: 'transparent', + height: 20, + marginTop: 0, + marginBottom: 0, + customStyle: {} + } +} diff --git a/uni_modules/uview-ui/libs/config/props/grid.js b/uni_modules/uview-ui/libs/config/props/grid.js new file mode 100644 index 0000000..60abeb7 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/grid.js @@ -0,0 +1,17 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:05:57 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/grid.js + */ +export default { + // grid组件 + grid: { + col: 3, + border: false, + align: 'left' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/gridItem.js b/uni_modules/uview-ui/libs/config/props/gridItem.js new file mode 100644 index 0000000..1b747f4 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/gridItem.js @@ -0,0 +1,16 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:06:13 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/gridItem.js + */ +export default { + // grid-item组件 + gridItem: { + name: null, + bgColor: 'transparent' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/icon.js b/uni_modules/uview-ui/libs/config/props/icon.js new file mode 100644 index 0000000..1d81d2d --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/icon.js @@ -0,0 +1,36 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 18:00:14 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/icon.js + */ +import config from '../config' + +const { + color +} = config +export default { + // icon组件 + icon: { + name: '', + color: color['u-content-color'], + size: '16px', + bold: false, + index: '', + hoverClass: '', + customPrefix: 'uicon', + label: '', + labelPos: 'right', + labelSize: '15px', + labelColor: color['u-content-color'], + space: '3px', + imgMode: '', + width: '', + height: '', + top: 0, + stop: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/image.js b/uni_modules/uview-ui/libs/config/props/image.js new file mode 100644 index 0000000..2552db6 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/image.js @@ -0,0 +1,30 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:01:51 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/image.js + */ +export default { + // image组件 + image: { + src: '', + mode: 'aspectFill', + width: '300', + height: '225', + shape: 'square', + radius: 0, + lazyLoad: true, + showMenuByLongpress: true, + loadingIcon: 'photo', + errorIcon: 'error-circle', + showLoading: true, + showError: true, + fade: true, + webp: false, + duration: 500, + bgColor: '#f3f4f6' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/indexAnchor.js b/uni_modules/uview-ui/libs/config/props/indexAnchor.js new file mode 100644 index 0000000..bb20d46 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/indexAnchor.js @@ -0,0 +1,19 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:13:15 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/indexAnchor.js + */ +export default { + // indexAnchor 组件 + indexAnchor: { + text: '', + color: '#606266', + size: 14, + bgColor: '#dedede', + height: 32 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/indexList.js b/uni_modules/uview-ui/libs/config/props/indexList.js new file mode 100644 index 0000000..dc6ce94 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/indexList.js @@ -0,0 +1,19 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:13:35 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/indexList.js + */ +export default { + // indexList 组件 + indexList: { + inactiveColor: '#606266', + activeColor: '#5677fc', + indexList: () => [], + sticky: true, + customNavHeight: 0 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/input.js b/uni_modules/uview-ui/libs/config/props/input.js new file mode 100644 index 0000000..4f0edc6 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/input.js @@ -0,0 +1,48 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:13:55 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/input.js + */ +export default { + // index 组件 + input: { + value: '', + type: 'text', + fixed: false, + disabled: false, + disabledColor: '#f5f7fa', + clearable: false, + password: false, + maxlength: -1, + placeholder: null, + placeholderClass: 'input-placeholder', + placeholderStyle: 'color: #c0c4cc', + showWordLimit: false, + confirmType: 'done', + confirmHold: false, + holdKeyboard: false, + focus: false, + autoBlur: false, + disableDefaultPadding: false, + cursor: -1, + cursorSpacing: 30, + selectionStart: -1, + selectionEnd: -1, + adjustPosition: true, + inputAlign: 'left', + fontSize: '15px', + color: '#303133', + prefixIcon: '', + prefixIconStyle: '', + suffixIcon: '', + suffixIconStyle: '', + border: 'surround', + readonly: false, + shape: 'square', + formatter: null + } +} diff --git a/uni_modules/uview-ui/libs/config/props/keyboard.js b/uni_modules/uview-ui/libs/config/props/keyboard.js new file mode 100644 index 0000000..57182bd --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/keyboard.js @@ -0,0 +1,30 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:07:49 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/keyboard.js + */ +export default { + // 键盘组件 + keyboard: { + mode: 'number', + dotDisabled: false, + tooltip: true, + showTips: true, + tips: '', + showCancel: true, + showConfirm: true, + random: false, + safeAreaInsetBottom: true, + closeOnClickOverlay: true, + show: false, + overlay: true, + zIndex: 10075, + cancelText: '取消', + confirmText: '确定', + autoChange: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/line.js b/uni_modules/uview-ui/libs/config/props/line.js new file mode 100644 index 0000000..2c87af2 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/line.js @@ -0,0 +1,20 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:04:49 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/line.js + */ +export default { + // line组件 + line: { + color: '#d6d7d9', + length: '100%', + direction: 'row', + hairline: true, + margin: 0, + dashed: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/lineProgress.js b/uni_modules/uview-ui/libs/config/props/lineProgress.js new file mode 100644 index 0000000..cdfcb0e --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/lineProgress.js @@ -0,0 +1,19 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:14:11 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/lineProgress.js + */ +export default { + // lineProgress 组件 + lineProgress: { + activeColor: '#19be6b', + inactiveColor: '#ececec', + percentage: 0, + showText: true, + height: 12 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/link.js b/uni_modules/uview-ui/libs/config/props/link.js new file mode 100644 index 0000000..6c4c883 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/link.js @@ -0,0 +1,26 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:45:36 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/link.js + */ +import config from '../config' + +const { + color +} = config +export default { + // link超链接组件props参数 + link: { + color: color['u-primary'], + fontSize: 15, + underLine: false, + href: '', + mpTips: '链接已复制,请在浏览器打开', + lineColor: '', + text: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/list.js b/uni_modules/uview-ui/libs/config/props/list.js new file mode 100644 index 0000000..a830c32 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/list.js @@ -0,0 +1,28 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:14:53 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/list.js + */ +export default { + // list 组件 + list: { + showScrollbar: false, + lowerThreshold: 50, + upperThreshold: 0, + scrollTop: 0, + offsetAccuracy: 10, + enableFlex: false, + pagingEnabled: false, + scrollable: true, + scrollIntoView: '', + scrollWithAnimation: false, + enableBackToTop: false, + height: 0, + width: 0, + preLoadScreen: 1 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/listItem.js b/uni_modules/uview-ui/libs/config/props/listItem.js new file mode 100644 index 0000000..7fe2166 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/listItem.js @@ -0,0 +1,15 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:15:40 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/listItem.js + */ +export default { + // listItem 组件 + listItem: { + anchor: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/loadingIcon.js b/uni_modules/uview-ui/libs/config/props/loadingIcon.js new file mode 100644 index 0000000..f4739c4 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/loadingIcon.js @@ -0,0 +1,30 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:45:47 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/loadingIcon.js + */ +import config from '../config' + +const { + color +} = config +export default { + // loading-icon加载中图标组件 + loadingIcon: { + show: true, + color: color['u-tips-color'], + textColor: color['u-tips-color'], + vertical: false, + mode: 'spinner', + size: 24, + textSize: 15, + text: '', + timingFunction: 'ease-in-out', + duration: 1200, + inactiveColor: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/loadingPage.js b/uni_modules/uview-ui/libs/config/props/loadingPage.js new file mode 100644 index 0000000..dc53109 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/loadingPage.js @@ -0,0 +1,23 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:00:23 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/loadingPage.js + */ +export default { + // loading-page组件 + loadingPage: { + loadingText: '正在加载', + image: '', + loadingMode: 'circle', + loading: false, + bgColor: '#ffffff', + color: '#C8C8C8', + fontSize: 19, + iconSize: 28, + loadingColor: '#C8C8C8' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/loadmore.js b/uni_modules/uview-ui/libs/config/props/loadmore.js new file mode 100644 index 0000000..67c1160 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/loadmore.js @@ -0,0 +1,32 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:15:26 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/loadmore.js + */ +export default { + // loadmore 组件 + loadmore: { + status: 'loadmore', + bgColor: 'transparent', + icon: true, + fontSize: 14, + iconSize: 17, + color: '#606266', + loadingIcon: 'spinner', + loadmoreText: '加载更多', + loadingText: '正在加载...', + nomoreText: '没有更多了', + isDot: false, + iconColor: '#b7b7b7', + marginTop: 10, + marginBottom: 10, + height: 'auto', + line: false, + lineColor: '#E6E8EB', + dashed: false, + } +} diff --git a/uni_modules/uview-ui/libs/config/props/modal.js b/uni_modules/uview-ui/libs/config/props/modal.js new file mode 100644 index 0000000..2ae3fff --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/modal.js @@ -0,0 +1,30 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:15:59 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/modal.js + */ +export default { + // modal 组件 + modal: { + show: false, + title: '', + content: '', + confirmText: '确认', + cancelText: '取消', + showConfirmButton: true, + showCancelButton: false, + confirmColor: '#2979ff', + cancelColor: '#606266', + buttonReverse: false, + zoom: true, + asyncClose: false, + closeOnClickOverlay: false, + negativeTop: 0, + width: '650rpx', + confirmButtonShape: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/navbar.js b/uni_modules/uview-ui/libs/config/props/navbar.js new file mode 100644 index 0000000..614a99d --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/navbar.js @@ -0,0 +1,32 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:16:18 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/navbar.js + */ +import color from '../color' +export default { + // navbar 组件 + navbar: { + safeAreaInsetTop: true, + placeholder: false, + fixed: true, + border: false, + leftIcon: 'arrow-left', + leftText: '', + rightText: '', + rightIcon: '', + title: '', + bgColor: '#ffffff', + titleWidth: '400rpx', + height: '44px', + leftIconSize: 20, + leftIconColor: color.mainColor, + autoBack: false, + titleStyle: '' + } + +} diff --git a/uni_modules/uview-ui/libs/config/props/noNetwork.js b/uni_modules/uview-ui/libs/config/props/noNetwork.js new file mode 100644 index 0000000..74dba1b --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/noNetwork.js @@ -0,0 +1,18 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:16:39 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/noNetwork.js + */ +export default { + // noNetwork + noNetwork: { + tips: '哎呀,网络信号丢失', + zIndex: '', + image: 'data:image/png;base64,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' + } + +} diff --git a/uni_modules/uview-ui/libs/config/props/noticeBar.js b/uni_modules/uview-ui/libs/config/props/noticeBar.js new file mode 100644 index 0000000..02c660a --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/noticeBar.js @@ -0,0 +1,27 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:17:13 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/noticeBar.js + */ +export default { + // noticeBar + noticeBar: { + text: () => [], + direction: 'row', + step: false, + icon: 'volume', + mode: '', + color: '#f9ae3d', + bgColor: '#fdf6ec', + speed: 80, + fontSize: 14, + duration: 2000, + disableTouch: true, + url: '', + linkType: 'navigateTo' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/notify.js b/uni_modules/uview-ui/libs/config/props/notify.js new file mode 100644 index 0000000..1042d2a --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/notify.js @@ -0,0 +1,22 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:10:21 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/notify.js + */ +export default { + // notify组件 + notify: { + top: 0, + type: 'primary', + color: '#ffffff', + bgColor: '', + message: '', + duration: 3000, + fontSize: 15, + safeAreaInsetTop: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/numberBox.js b/uni_modules/uview-ui/libs/config/props/numberBox.js new file mode 100644 index 0000000..424f0ca --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/numberBox.js @@ -0,0 +1,35 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:11:46 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/numberBox.js + */ +export default { + // 步进器组件 + numberBox: { + name: '', + value: 0, + min: 1, + max: Number.MAX_SAFE_INTEGER, + step: 1, + integer: false, + disabled: false, + disabledInput: false, + asyncChange: false, + inputWidth: 35, + showMinus: true, + showPlus: true, + decimalLength: null, + longPress: true, + color: '#323233', + buttonSize: 30, + bgColor: '#EBECEE', + cursorSpacing: 100, + disableMinus: false, + disablePlus: false, + iconStyle: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/numberKeyboard.js b/uni_modules/uview-ui/libs/config/props/numberKeyboard.js new file mode 100644 index 0000000..7b45065 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/numberKeyboard.js @@ -0,0 +1,17 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:08:05 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/numberKeyboard.js + */ +export default { + // 数字键盘 + numberKeyboard: { + mode: 'number', + dotDisabled: false, + random: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/overlay.js b/uni_modules/uview-ui/libs/config/props/overlay.js new file mode 100644 index 0000000..c26d068 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/overlay.js @@ -0,0 +1,18 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:06:50 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/overlay.js + */ +export default { + // overlay组件 + overlay: { + show: false, + zIndex: 10070, + duration: 300, + opacity: 0.5 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/parse.js b/uni_modules/uview-ui/libs/config/props/parse.js new file mode 100644 index 0000000..feb22b9 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/parse.js @@ -0,0 +1,22 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:17:33 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/parse.js + */ +export default { + // parse + parse: { + copyLink: true, + errorImg: '', + lazyLoad: false, + loadingImg: '', + pauseVideo: true, + previewImg: true, + setTitle: true, + showImgMenu: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/picker.js b/uni_modules/uview-ui/libs/config/props/picker.js new file mode 100644 index 0000000..f06b321 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/picker.js @@ -0,0 +1,29 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:18:20 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/picker.js + */ +export default { + // picker + picker: { + show: false, + showToolbar: true, + title: '', + columns: () => [], + loading: false, + itemHeight: 44, + cancelText: '取消', + confirmText: '确定', + cancelColor: '#909193', + confirmColor: '#3c9cff', + visibleItemCount: 5, + keyName: 'text', + closeOnClickOverlay: false, + defaultIndex: () => [], + immediateChange: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/popup.js b/uni_modules/uview-ui/libs/config/props/popup.js new file mode 100644 index 0000000..0cc1bc0 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/popup.js @@ -0,0 +1,29 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:06:33 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/popup.js + */ +export default { + // popup组件 + popup: { + show: false, + overlay: true, + mode: 'bottom', + duration: 300, + closeable: false, + overlayStyle: () => {}, + closeOnClickOverlay: true, + zIndex: 10075, + safeAreaInsetBottom: true, + safeAreaInsetTop: false, + closeIconPos: 'top-right', + round: 0, + zoom: true, + bgColor: '', + overlayOpacity: 0.5 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/radio.js b/uni_modules/uview-ui/libs/config/props/radio.js new file mode 100644 index 0000000..4df200f --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/radio.js @@ -0,0 +1,27 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:02:34 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/radio.js + */ +export default { + // radio组件 + radio: { + name: '', + shape: '', + disabled: '', + labelDisabled: '', + activeColor: '', + inactiveColor: '', + iconSize: '', + labelSize: '', + label: '', + labelColor: '', + size: '', + iconColor: '', + placement: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/radioGroup.js b/uni_modules/uview-ui/libs/config/props/radioGroup.js new file mode 100644 index 0000000..728e9db --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/radioGroup.js @@ -0,0 +1,30 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:03:12 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/radioGroup.js + */ +export default { + // radio-group组件 + radioGroup: { + value: '', + disabled: false, + shape: 'circle', + activeColor: '#2979ff', + inactiveColor: '#c8c9cc', + name: '', + size: 18, + placement: 'row', + label: '', + labelColor: '#303133', + labelSize: 14, + labelDisabled: false, + iconColor: '#ffffff', + iconSize: 12, + borderBottom: false, + iconPlacement: 'left' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/rate.js b/uni_modules/uview-ui/libs/config/props/rate.js new file mode 100644 index 0000000..d31c61a --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/rate.js @@ -0,0 +1,26 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:05:09 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/rate.js + */ +export default { + // rate组件 + rate: { + value: 1, + count: 5, + disabled: false, + size: 18, + inactiveColor: '#b2b2b2', + activeColor: '#FA3534', + gutter: 4, + minCount: 1, + allowHalf: false, + activeIcon: 'star-fill', + inactiveIcon: 'star', + touchable: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/readMore.js b/uni_modules/uview-ui/libs/config/props/readMore.js new file mode 100644 index 0000000..09b11cc --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/readMore.js @@ -0,0 +1,22 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:18:41 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/readMore.js + */ +export default { + // readMore + readMore: { + showHeight: 400, + toggle: false, + closeText: '展开阅读全文', + openText: '收起', + color: '#2979ff', + fontSize: 14, + textIndent: '2em', + name: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/row.js b/uni_modules/uview-ui/libs/config/props/row.js new file mode 100644 index 0000000..573a431 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/row.js @@ -0,0 +1,17 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:18:58 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/row.js + */ +export default { + // row + row: { + gutter: 0, + justify: 'start', + align: 'center' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/rowNotice.js b/uni_modules/uview-ui/libs/config/props/rowNotice.js new file mode 100644 index 0000000..cd9d0a0 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/rowNotice.js @@ -0,0 +1,21 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:19:13 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/rowNotice.js + */ +export default { + // rowNotice + rowNotice: { + text: '', + icon: 'volume', + mode: '', + color: '#f9ae3d', + bgColor: '#fdf6ec', + fontSize: 14, + speed: 80 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/scrollList.js b/uni_modules/uview-ui/libs/config/props/scrollList.js new file mode 100644 index 0000000..441e63a --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/scrollList.js @@ -0,0 +1,20 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:19:28 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/scrollList.js + */ +export default { + // scrollList + scrollList: { + indicatorWidth: 50, + indicatorBarWidth: 20, + indicator: true, + indicatorColor: '#f2f2f2', + indicatorActiveColor: '#3c9cff', + indicatorStyle: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/search.js b/uni_modules/uview-ui/libs/config/props/search.js new file mode 100644 index 0000000..2699954 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/search.js @@ -0,0 +1,37 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:19:45 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/search.js + */ +export default { + // search + search: { + shape: 'round', + bgColor: '#f2f2f2', + placeholder: '请输入关键字', + clearabled: true, + focus: false, + showAction: true, + actionStyle: () => ({}), + actionText: '搜索', + inputAlign: 'left', + inputStyle: () => ({}), + disabled: false, + borderColor: 'transparent', + searchIconColor: '#909399', + searchIconSize: 22, + color: '#606266', + placeholderColor: '#909399', + searchIcon: 'search', + margin: '0', + animation: false, + value: '', + maxlength: '-1', + height: 32, + label: null + } +} diff --git a/uni_modules/uview-ui/libs/config/props/section.js b/uni_modules/uview-ui/libs/config/props/section.js new file mode 100644 index 0000000..f432648 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/section.js @@ -0,0 +1,24 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:07:33 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/section.js + */ +export default { + // u-section组件 + section: { + title: '', + subTitle: '更多', + right: true, + fontSize: 15, + bold: true, + color: '#303133', + subColor: '#909399', + showLine: true, + lineColor: '', + arrow: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/skeleton.js b/uni_modules/uview-ui/libs/config/props/skeleton.js new file mode 100644 index 0000000..83b777d --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/skeleton.js @@ -0,0 +1,25 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:20:14 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/skeleton.js + */ +export default { + // skeleton + skeleton: { + loading: true, + animate: true, + rows: 0, + rowsWidth: '100%', + rowsHeight: 18, + title: true, + titleWidth: '50%', + titleHeight: 18, + avatar: false, + avatarSize: 32, + avatarShape: 'circle' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/slider.js b/uni_modules/uview-ui/libs/config/props/slider.js new file mode 100644 index 0000000..50cc37f --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/slider.js @@ -0,0 +1,25 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:08:25 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/slider.js + */ +export default { + // slider组件 + slider: { + value: 0, + blockSize: 18, + min: 0, + max: 100, + step: 1, + activeColor: '#2979ff', + inactiveColor: '#c0c4cc', + blockColor: '#ffffff', + showValue: false, + disabled:false, + blockStyle: () => {} + } +} diff --git a/uni_modules/uview-ui/libs/config/props/statusBar.js b/uni_modules/uview-ui/libs/config/props/statusBar.js new file mode 100644 index 0000000..d237a83 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/statusBar.js @@ -0,0 +1,15 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:20:39 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/statusBar.js + */ +export default { + // statusBar + statusBar: { + bgColor: 'transparent' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/steps.js b/uni_modules/uview-ui/libs/config/props/steps.js new file mode 100644 index 0000000..881c39e --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/steps.js @@ -0,0 +1,21 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:12:37 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/steps.js + */ +export default { + // steps组件 + steps: { + direction: 'row', + current: 0, + activeColor: '#3c9cff', + inactiveColor: '#969799', + activeIcon: '', + inactiveIcon: '', + dot: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/stepsItem.js b/uni_modules/uview-ui/libs/config/props/stepsItem.js new file mode 100644 index 0000000..5dba8f4 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/stepsItem.js @@ -0,0 +1,18 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:12:55 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/stepsItem.js + */ +export default { + // steps-item组件 + stepsItem: { + title: '', + desc: '', + iconSize: 17, + error: false + } +} diff --git a/uni_modules/uview-ui/libs/config/props/sticky.js b/uni_modules/uview-ui/libs/config/props/sticky.js new file mode 100644 index 0000000..b034604 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/sticky.js @@ -0,0 +1,20 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:01:30 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/sticky.js + */ +export default { + // sticky组件 + sticky: { + offsetTop: 0, + customNavHeight: 0, + disabled: false, + bgColor: 'transparent', + zIndex: '', + index: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/subsection.js b/uni_modules/uview-ui/libs/config/props/subsection.js new file mode 100644 index 0000000..9a165ff --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/subsection.js @@ -0,0 +1,23 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:12:20 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/subsection.js + */ +export default { + // subsection组件 + subsection: { + list: [], + current: 0, + activeColor: '#3c9cff', + inactiveColor: '#303133', + mode: 'button', + fontSize: 12, + bold: true, + bgColor: '#eeeeef', + keyName: 'name' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/swipeAction.js b/uni_modules/uview-ui/libs/config/props/swipeAction.js new file mode 100644 index 0000000..25051b8 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/swipeAction.js @@ -0,0 +1,15 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:00:42 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/swipeAction.js + */ +export default { + // swipe-action组件 + swipeAction: { + autoClose: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/swipeActionItem.js b/uni_modules/uview-ui/libs/config/props/swipeActionItem.js new file mode 100644 index 0000000..40ef27c --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/swipeActionItem.js @@ -0,0 +1,21 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:01:13 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/swipeActionItem.js + */ +export default { + // swipeActionItem 组件 + swipeActionItem: { + show: false, + name: '', + disabled: false, + threshold: 20, + autoClose: true, + options: [], + duration: 300 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/swiper.js b/uni_modules/uview-ui/libs/config/props/swiper.js new file mode 100644 index 0000000..0e6a3b7 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/swiper.js @@ -0,0 +1,39 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:21:38 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/swiper.js + */ +export default { + // swiper 组件 + swiper: { + list: () => [], + indicator: false, + indicatorActiveColor: '#FFFFFF', + indicatorInactiveColor: 'rgba(255, 255, 255, 0.35)', + indicatorStyle: '', + indicatorMode: 'line', + autoplay: true, + current: 0, + currentItemId: '', + interval: 3000, + duration: 300, + circular: false, + previousMargin: 0, + nextMargin: 0, + acceleration: false, + displayMultipleItems: 1, + easingFunction: 'default', + keyName: 'url', + imgMode: 'aspectFill', + height: 130, + bgColor: '#f3f4f6', + radius: 4, + loading: false, + showTitle: false + } + +} diff --git a/uni_modules/uview-ui/libs/config/props/swipterIndicator.js b/uni_modules/uview-ui/libs/config/props/swipterIndicator.js new file mode 100644 index 0000000..4b59e6e --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/swipterIndicator.js @@ -0,0 +1,19 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:22:07 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/swiperIndicator.js + */ +export default { + // swiperIndicator 组件 + swiperIndicator: { + length: 0, + current: 0, + indicatorActiveColor: '', + indicatorInactiveColor: '', + indicatorMode: 'line' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/switch.js b/uni_modules/uview-ui/libs/config/props/switch.js new file mode 100644 index 0000000..e6400b4 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/switch.js @@ -0,0 +1,24 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:22:24 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/switch.js + */ +export default { + // switch + switch: { + loading: false, + disabled: false, + size: 25, + activeColor: '#2979ff', + inactiveColor: '#ffffff', + value: false, + activeValue: true, + inactiveValue: false, + asyncChange: false, + space: 0 + } +} diff --git a/uni_modules/uview-ui/libs/config/props/tabbar.js b/uni_modules/uview-ui/libs/config/props/tabbar.js new file mode 100644 index 0000000..187112d --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/tabbar.js @@ -0,0 +1,22 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:22:40 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/tabbar.js + */ +export default { + // tabbar + tabbar: { + value: null, + safeAreaInsetBottom: true, + border: true, + zIndex: 1, + activeColor: '#1989fa', + inactiveColor: '#7d7e80', + fixed: true, + placeholder: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/tabbarItem.js b/uni_modules/uview-ui/libs/config/props/tabbarItem.js new file mode 100644 index 0000000..d036ce5 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/tabbarItem.js @@ -0,0 +1,20 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:22:55 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/tabbarItem.js + */ +export default { + // + tabbarItem: { + name: null, + icon: '', + badge: null, + dot: false, + text: '', + badgeStyle: 'top: 6px;right:2px;' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/tabs.js b/uni_modules/uview-ui/libs/config/props/tabs.js new file mode 100644 index 0000000..81c794a --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/tabs.js @@ -0,0 +1,32 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:23:14 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/tabs.js + */ +export default { + // + tabs: { + duration: 300, + list: () => [], + lineColor: '#3c9cff', + activeStyle: () => ({ + color: '#303133' + }), + inactiveStyle: () => ({ + color: '#606266' + }), + lineWidth: 20, + lineHeight: 3, + lineBgSize: 'cover', + itemStyle: () => ({ + height: '44px' + }), + scrollable: true, + current: 0, + keyName: 'name' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/tag.js b/uni_modules/uview-ui/libs/config/props/tag.js new file mode 100644 index 0000000..125ce94 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/tag.js @@ -0,0 +1,29 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:23:37 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/tag.js + */ +export default { + // tag 组件 + tag: { + type: 'primary', + disabled: false, + size: 'medium', + shape: 'square', + text: '', + bgColor: '', + color: '', + borderColor: '', + closeColor: '#C6C7CB', + name: '', + plainFill: false, + plain: false, + closable: false, + show: true, + icon: '' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/text.js b/uni_modules/uview-ui/libs/config/props/text.js new file mode 100644 index 0000000..7e73606 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/text.js @@ -0,0 +1,38 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:23:58 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/text.js + */ +export default { + // text 组件 + text: { + type: '', + show: true, + text: '', + prefixIcon: '', + suffixIcon: '', + mode: '', + href: '', + format: '', + call: false, + openType: '', + bold: false, + block: false, + lines: '', + color: '#303133', + size: 15, + iconStyle: () => ({ + fontSize: '15px' + }), + decoration: 'none', + margin: 0, + lineHeight: '', + align: 'left', + wordWrap: 'normal' + } + +} diff --git a/uni_modules/uview-ui/libs/config/props/textarea.js b/uni_modules/uview-ui/libs/config/props/textarea.js new file mode 100644 index 0000000..d803662 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/textarea.js @@ -0,0 +1,36 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:24:32 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/textarea.js + */ +export default { + // textarea 组件 + textarea: { + value: '', + placeholder: '', + placeholderClass: 'textarea-placeholder', + placeholderStyle: 'color: #c0c4cc', + height: 70, + confirmType: '', + disabled: false, + count: false, + focus: false, + autoHeight: false, + fixed: false, + cursorSpacing: 0, + cursor: '', + showConfirmBar: true, + selectionStart: -1, + selectionEnd: -1, + adjustPosition: true, + disableDefaultPadding: false, + holdKeyboard: false, + maxlength: 140, + border: 'surround', + formatter: null + } +} diff --git a/uni_modules/uview-ui/libs/config/props/toast.js b/uni_modules/uview-ui/libs/config/props/toast.js new file mode 100644 index 0000000..a50134b --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/toast.js @@ -0,0 +1,30 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:07:07 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/toast.js + */ +export default { + // toast组件 + toast: { + zIndex: 10090, + loading: false, + text: '', + icon: '', + type: '', + loadingMode: '', + show: '', + overlay: false, + position: 'center', + params: () => {}, + duration: 2000, + isTab: false, + url: '', + callback: null, + back: false + } + +} diff --git a/uni_modules/uview-ui/libs/config/props/toolbar.js b/uni_modules/uview-ui/libs/config/props/toolbar.js new file mode 100644 index 0000000..3289967 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/toolbar.js @@ -0,0 +1,21 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:24:55 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/toolbar.js + */ +export default { + // toolbar 组件 + toolbar: { + show: true, + cancelText: '取消', + confirmText: '确认', + cancelColor: '#909193', + confirmColor: '#3c9cff', + title: '' + } + +} diff --git a/uni_modules/uview-ui/libs/config/props/tooltip.js b/uni_modules/uview-ui/libs/config/props/tooltip.js new file mode 100644 index 0000000..115e030 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/tooltip.js @@ -0,0 +1,25 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:25:14 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/tooltip.js + */ +export default { + // tooltip 组件 + tooltip: { + text: '', + copyText: '', + size: 14, + color: '#606266', + bgColor: 'transparent', + direction: 'top', + zIndex: 10071, + showCopy: true, + buttons: () => [], + overlay: true, + showToast: true + } +} diff --git a/uni_modules/uview-ui/libs/config/props/transition.js b/uni_modules/uview-ui/libs/config/props/transition.js new file mode 100644 index 0000000..0fad118 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/transition.js @@ -0,0 +1,18 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 16:59:00 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/transition.js + */ +export default { + // transition动画组件的props + transition: { + show: false, + mode: 'fade', + duration: '300', + timingFunction: 'ease-out' + } +} diff --git a/uni_modules/uview-ui/libs/config/props/upload.js b/uni_modules/uview-ui/libs/config/props/upload.js new file mode 100644 index 0000000..fc7ca92 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/props/upload.js @@ -0,0 +1,36 @@ +/* + * @Author : LQ + * @Description : + * @version : 1.0 + * @Date : 2021-08-20 16:44:21 + * @LastAuthor : LQ + * @lastTime : 2021-08-20 17:09:50 + * @FilePath : /u-view2.0/uview-ui/libs/config/props/upload.js + */ +export default { + // upload组件 + upload: { + accept: 'image', + capture: () => ['album', 'camera'], + compressed: true, + camera: 'back', + maxDuration: 60, + uploadIcon: 'camera-fill', + uploadIconColor: '#D3D4D6', + useBeforeRead: false, + previewFullImage: true, + maxCount: 52, + disabled: false, + imageMode: 'aspectFill', + name: '', + sizeType: () => ['original', 'compressed'], + multiple: false, + deletable: true, + maxSize: Number.MAX_VALUE, + fileList: () => [], + uploadText: '', + width: 80, + height: 80, + previewImage: true + } +} diff --git a/uni_modules/uview-ui/libs/config/zIndex.js b/uni_modules/uview-ui/libs/config/zIndex.js new file mode 100644 index 0000000..5fc3682 --- /dev/null +++ b/uni_modules/uview-ui/libs/config/zIndex.js @@ -0,0 +1,20 @@ +// uniapp在H5中各API的z-index值如下: +/** + * actionsheet: 999 + * modal: 999 + * navigate: 998 + * tabbar: 998 + * toast: 999 + */ + +export default { + toast: 10090, + noNetwork: 10080, + // popup包含popup,actionsheet,keyboard,picker的值 + popup: 10075, + mask: 10070, + navbar: 980, + topTips: 975, + sticky: 970, + indexListSticky: 965 +} diff --git a/uni_modules/uview-ui/libs/css/color.scss b/uni_modules/uview-ui/libs/css/color.scss new file mode 100644 index 0000000..3237ba4 --- /dev/null +++ b/uni_modules/uview-ui/libs/css/color.scss @@ -0,0 +1,155 @@ +.u-primary-light { + color: $u-primary-light; +} + +.u-warning-light { + color: $u-warning-light; +} + +.u-success-light { + color: $u-success-light; +} + +.u-error-light { + color: $u-error-light; +} + +.u-info-light { + color: $u-info-light; +} + +.u-primary-light-bg { + background-color: $u-primary-light; +} + +.u-warning-light-bg { + background-color: $u-warning-light; +} + +.u-success-light-bg { + background-color: $u-success-light; +} + +.u-error-light-bg { + background-color: $u-error-light; +} + +.u-info-light-bg { + background-color: $u-info-light; +} + +.u-primary-dark { + color: $u-primary-dark; +} + +.u-warning-dark { + color: $u-warning-dark; +} + +.u-success-dark { + color: $u-success-dark; +} + +.u-error-dark { + color: $u-error-dark; +} + +.u-info-dark { + color: $u-info-dark; +} + +.u-primary-dark-bg { + background-color: $u-primary-dark; +} + +.u-warning-dark-bg { + background-color: $u-warning-dark; +} + +.u-success-dark-bg { + background-color: $u-success-dark; +} + +.u-error-dark-bg { + background-color: $u-error-dark; +} + +.u-info-dark-bg { + background-color: $u-info-dark; +} + +.u-primary-disabled { + color: $u-primary-disabled; +} + +.u-warning-disabled { + color: $u-warning-disabled; +} + +.u-success-disabled { + color: $u-success-disabled; +} + +.u-error-disabled { + color: $u-error-disabled; +} + +.u-info-disabled { + color: $u-info-disabled; +} + +.u-primary { + color: $u-primary; +} + +.u-warning { + color: $u-warning; +} + +.u-success { + color: $u-success; +} + +.u-error { + color: $u-error; +} + +.u-info { + color: $u-info; +} + +.u-primary-bg { + background-color: $u-primary; +} + +.u-warning-bg { + background-color: $u-warning; +} + +.u-success-bg { + background-color: $u-success; +} + +.u-error-bg { + background-color: $u-error; +} + +.u-info-bg { + background-color: $u-info; +} + +.u-main-color { + color: $u-main-color; +} + +.u-content-color { + color: $u-content-color; +} + +.u-tips-color { + color: $u-tips-color; +} + +.u-light-color { + color: $u-light-color; +} diff --git a/uni_modules/uview-ui/libs/css/common.scss b/uni_modules/uview-ui/libs/css/common.scss new file mode 100644 index 0000000..11f1e53 --- /dev/null +++ b/uni_modules/uview-ui/libs/css/common.scss @@ -0,0 +1,97 @@ +// 超出行数,自动显示行尾省略号,最多5行 +// 来自uView的温馨提示:当您在控制台看到此报错,说明需要在App.vue的style标签加上【lang="scss"】 +@for $i from 1 through 5 { + .u-line-#{$i} { + /* #ifdef APP-NVUE */ + // nvue下,可以直接使用lines属性,这是weex特有样式 + lines: $i; + text-overflow: ellipsis; + overflow: hidden; + flex: 1; + /* #endif */ + + /* #ifndef APP-NVUE */ + // vue下,单行和多行显示省略号需要单独处理 + @if $i == '1' { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } @else { + display: -webkit-box!important; + overflow: hidden; + text-overflow: ellipsis; + word-break: break-all; + -webkit-line-clamp: $i; + -webkit-box-orient: vertical!important; + } + /* #endif */ + } +} + + +// 此处加上!important并非随意乱用,而是因为目前*.nvue页面编译到H5时, +// App.vue的样式会被uni-app的view元素的自带border属性覆盖,导致无效 +// 综上,这是uni-app的缺陷导致我们为了多端兼容,而必须要加上!important +// 移动端兼容性较好,直接使用0.5px去实现细边框,不使用伪元素形式实现 +.u-border { + border-width: 0.5px!important; + border-color: $u-border-color!important; + border-style: solid; +} + +.u-border-top { + border-top-width: 0.5px!important; + border-color: $u-border-color!important; + border-top-style: solid; +} + +.u-border-left { + border-left-width: 0.5px!important; + border-color: $u-border-color!important; + border-left-style: solid; +} + +.u-border-right { + border-right-width: 0.5px!important; + border-color: $u-border-color!important; + border-right-style: solid; +} + +.u-border-bottom { + border-bottom-width: 0.5px!important; + border-color: $u-border-color!important; + border-bottom-style: solid; +} + +.u-border-top-bottom { + border-top-width: 0.5px!important; + border-bottom-width: 0.5px!important; + border-color: $u-border-color!important; + border-top-style: solid; + border-bottom-style: solid; +} + +// 去除button的所有默认样式,让其表现跟普通的view、text元素一样 +.u-reset-button { + padding: 0; + background-color: transparent; + /* #ifndef APP-PLUS */ + font-size: inherit; + line-height: inherit; + color: inherit; + /* #endif */ + /* #ifdef APP-NVUE */ + border-width: 0; + /* #endif */ +} + +/* #ifndef APP-NVUE */ +.u-reset-button::after { + border: none; +} +/* #endif */ + +.u-hover-class { + opacity: 0.7; +} + diff --git a/uni_modules/uview-ui/libs/css/components.scss b/uni_modules/uview-ui/libs/css/components.scss new file mode 100644 index 0000000..766679e --- /dev/null +++ b/uni_modules/uview-ui/libs/css/components.scss @@ -0,0 +1,15 @@ +@import "./mixin.scss"; + +/* #ifndef APP-NVUE */ +// 由于uView是基于nvue环境进行开发的,此环境中普通元素默认为flex-direction: column; +// 所以在非nvue中,需要对元素进行重置为flex-direction: column; 否则可能会表现异常 +view, scroll-view, swiper-item { + display: flex; + flex-direction: column; + flex-shrink: 0; + flex-grow: 0; + flex-basis: auto; + align-items: stretch; + align-content: flex-start; +} +/* #endif */ diff --git a/uni_modules/uview-ui/libs/css/flex.scss b/uni_modules/uview-ui/libs/css/flex.scss new file mode 100644 index 0000000..6d61be9 --- /dev/null +++ b/uni_modules/uview-ui/libs/css/flex.scss @@ -0,0 +1,257 @@ +// .u-flex { +// @include vue-flex(row); +// } + +// .u-flex-x { +// @include vue-flex(row); +// } + +// .u-flex-y { +// @include vue-flex(column); +// } + +// .u-flex-xy-center { +// @include vue-flex(row); +// justify-content: center; +// align-items: center; +// } + +// .u-flex-x-center { +// @include vue-flex(row); +// justify-content: center; +// } + +// .u-flex-y-center { +// @include vue-flex(column); +// justify-content: center; +// } + + +// flex布局 +.u-flex, +.u-flex-row, +.u-flex-x { + @include flex; +} + +.u-flex-y, +.u-flex-column { + @include flex(column); +} + +.u-flex-x-center { + @include flex; + justify-content: center; +} + +.u-flex-xy-center { + @include flex; + justify-content: center; + align-items: center; +} + +.u-flex-y-center { + @include flex; + align-items: center; +} + +.u-flex-x-left { + @include flex; +} + +.u-flex-x-reverse, +.u-flex-row-reverse { + flex-direction: row-reverse; +} + +.u-flex-y-reverse, +.u-flex-column-reverse { + flex-direction: column-reverse; +} + +/* #ifndef APP-NVUE */ +// 此处为vue版本的简写,因为nvue不支持同时作用于两个类名的样式写法 +// nvue下只能写成class="u-flex-x u-flex-x-reverse的形式" +.u-flex.u-flex-reverse, +.u-flex-row.u-flex-reverse, +.u-flex-x.u-flex-reverse { + flex-direction: row-reverse; +} + +.u-flex-column.u-flex-reverse, +.u-flex-y.u-flex-reverse { + flex-direction: column-reverse; +} + +// 自动伸缩 +.u-flex-fill { + flex: 1 1 auto +} + +// 边界自动伸缩 +.u-margin-top-auto, +.u-m-t-auto { + margin-top: auto !important +} + +.u-margin-right-auto, +.u-m-r-auto { + margin-right: auto !important +} + +.u-margin-bottom-auto, +.u-m-b-auto { + margin-bottom: auto !important +} + +.u-margin-left-auto, +.u-m-l-auto { + margin-left: auto !important +} + +.u-margin-center-auto, +.u-m-c-auto { + margin-left: auto !important; + margin-right: auto !important +} + +.u-margin-middle-auto, +.u-m-m-auto { + margin-top: auto !important; + margin-bottom: auto !important +} +/* #endif */ + +// 换行 +.u-flex-wrap { + flex-wrap: wrap; +} + +// 反向换行 +.u-flex-wrap-reverse { + flex-wrap: wrap-reverse; +} + +// 主轴起点对齐 +.u-flex-start { + justify-content: flex-start +} + +// 主轴中间对齐 +.u-flex-center { + justify-content: center +} + +// 主轴终点对齐 +.u-flex-end { + justify-content: flex-end +} + +// 主轴等比间距 +.u-flex-between { + justify-content: space-between +} + +// 主轴均分间距 +.u-flex-around { + justify-content: space-around +} + +// 交叉轴起点对齐 +.u-flex-items-start { + align-items: flex-start +} + +// 交叉轴中间对齐 +.u-flex-items-center { + align-items: center +} + +// 交叉轴终点对齐 +.u-flex-items-end { + align-items: flex-end +} + +// 交叉轴第一行文字基线对齐 +.u-flex-items-baseline { + align-items: baseline +} + +// 交叉轴方向拉伸对齐 +.u-flex-items-stretch { + align-items: stretch +} + + +// 以下属于项目(子元素)的类 + +// 子元素交叉轴起点对齐 +.u-flex-self-start { + align-self: flex-start +} + +// 子元素交叉轴居中对齐 +.u-flex-self-center { + align-self: center +} + +// 子元素交叉轴终点对齐 +.u-flex-self-end { + align-self: flex-end +} + +// 子元素交叉轴第一行文字基线对齐 +.u-flex-self-baseline { + align-self: baseline +} + +// 子元素交叉轴方向拉伸对齐 +.u-flex-self-stretch { + align-self: stretch +} + +// 多轴交叉时的对齐方式 + +// 起点对齐 +.u-flex-content-start { + align-content: flex-start +} + +// 居中对齐 +.u-flex-content-center { + align-content: center +} + +// 终点对齐 +.u-flex-content-end { + align-content: flex-end +} + +// 两端对齐 +.u-flex-content-between { + align-content: space-between +} + +// 均分间距 +.u-flex-content-around { + align-content: space-around +} + +// 全部居中对齐 +.u-flex-middle { + justify-content: center; + align-items: center; + align-self: center; + align-content: center +} + +// 是否可以放大 +.u-flex-grow { + flex-grow: 1 +} + +// 是否可以缩小 +.u-flex-shrink { + flex-shrink: 1 +} + diff --git a/uni_modules/uview-ui/libs/css/h5.scss b/uni_modules/uview-ui/libs/css/h5.scss new file mode 100644 index 0000000..e69de29 diff --git a/uni_modules/uview-ui/libs/css/mixin.scss b/uni_modules/uview-ui/libs/css/mixin.scss new file mode 100644 index 0000000..7e35b3b --- /dev/null +++ b/uni_modules/uview-ui/libs/css/mixin.scss @@ -0,0 +1,8 @@ +// 通过scss的mixin功能,把原来需要写4行的css,变成一行 +// 目的是保持代码干净整洁,不至于在nvue下,到处都要写display:flex的条件编译 +@mixin flex($direction: row) { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: $direction; +} diff --git a/uni_modules/uview-ui/libs/css/mp.scss b/uni_modules/uview-ui/libs/css/mp.scss new file mode 100644 index 0000000..e69de29 diff --git a/uni_modules/uview-ui/libs/css/nvue.scss b/uni_modules/uview-ui/libs/css/nvue.scss new file mode 100644 index 0000000..e69de29 diff --git a/uni_modules/uview-ui/libs/css/vue.scss b/uni_modules/uview-ui/libs/css/vue.scss new file mode 100644 index 0000000..3ae4d29 --- /dev/null +++ b/uni_modules/uview-ui/libs/css/vue.scss @@ -0,0 +1,27 @@ +// 历遍生成4个方向的底部安全区 +@each $d in top, right, bottom, left { + .u-safe-area-inset-#{$d} { + padding-#{$d}: 0; + padding-#{$d}: constant(safe-area-inset-#{$d}); + padding-#{$d}: env(safe-area-inset-#{$d}); + } +} + +//提升H5端uni.toast()的层级,避免被uView的modal等遮盖 +/* #ifdef H5 */ +uni-toast { + z-index: 10090; +} +uni-toast .uni-toast { + z-index: 10090; +} +/* #endif */ + +// 隐藏scroll-view的滚动条 +::-webkit-scrollbar { + display: none; + width: 0 !important; + height: 0 !important; + -webkit-appearance: none; + background: transparent; +} \ No newline at end of file diff --git a/uni_modules/uview-ui/libs/function/colorGradient.js b/uni_modules/uview-ui/libs/function/colorGradient.js new file mode 100644 index 0000000..9727732 --- /dev/null +++ b/uni_modules/uview-ui/libs/function/colorGradient.js @@ -0,0 +1,134 @@ +/** + * 求两个颜色之间的渐变值 + * @param {string} startColor 开始的颜色 + * @param {string} endColor 结束的颜色 + * @param {number} step 颜色等分的份额 + * */ +function colorGradient(startColor = 'rgb(0, 0, 0)', endColor = 'rgb(255, 255, 255)', step = 10) { + const startRGB = hexToRgb(startColor, false) // 转换为rgb数组模式 + const startR = startRGB[0] + const startG = startRGB[1] + const startB = startRGB[2] + + const endRGB = hexToRgb(endColor, false) + const endR = endRGB[0] + const endG = endRGB[1] + const endB = endRGB[2] + + const sR = (endR - startR) / step // 总差值 + const sG = (endG - startG) / step + const sB = (endB - startB) / step + const colorArr = [] + for (let i = 0; i < step; i++) { + // 计算每一步的hex值 + let hex = rgbToHex(`rgb(${Math.round((sR * i + startR))},${Math.round((sG * i + startG))},${Math.round((sB + * i + startB))})`) + // 确保第一个颜色值为startColor的值 + if (i === 0) hex = rgbToHex(startColor) + // 确保最后一个颜色值为endColor的值 + if (i === step - 1) hex = rgbToHex(endColor) + colorArr.push(hex) + } + return colorArr +} + +// 将hex表示方式转换为rgb表示方式(这里返回rgb数组模式) +function hexToRgb(sColor, str = true) { + const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/ + sColor = String(sColor).toLowerCase() + if (sColor && reg.test(sColor)) { + if (sColor.length === 4) { + let sColorNew = '#' + for (let i = 1; i < 4; i += 1) { + sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1)) + } + sColor = sColorNew + } + // 处理六位的颜色值 + const sColorChange = [] + for (let i = 1; i < 7; i += 2) { + sColorChange.push(parseInt(`0x${sColor.slice(i, i + 2)}`)) + } + if (!str) { + return sColorChange + } + return `rgb(${sColorChange[0]},${sColorChange[1]},${sColorChange[2]})` + } if (/^(rgb|RGB)/.test(sColor)) { + const arr = sColor.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',') + return arr.map((val) => Number(val)) + } + return sColor +} + +// 将rgb表示方式转换为hex表示方式 +function rgbToHex(rgb) { + const _this = rgb + const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/ + if (/^(rgb|RGB)/.test(_this)) { + const aColor = _this.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',') + let strHex = '#' + for (let i = 0; i < aColor.length; i++) { + let hex = Number(aColor[i]).toString(16) + hex = String(hex).length == 1 ? `${0}${hex}` : hex // 保证每个rgb的值为2位 + if (hex === '0') { + hex += hex + } + strHex += hex + } + if (strHex.length !== 7) { + strHex = _this + } + return strHex + } if (reg.test(_this)) { + const aNum = _this.replace(/#/, '').split('') + if (aNum.length === 6) { + return _this + } if (aNum.length === 3) { + let numHex = '#' + for (let i = 0; i < aNum.length; i += 1) { + numHex += (aNum[i] + aNum[i]) + } + return numHex + } + } else { + return _this + } +} + +/** +* JS颜色十六进制转换为rgb或rgba,返回的格式为 rgba(255,255,255,0.5)字符串 +* sHex为传入的十六进制的色值 +* alpha为rgba的透明度 +*/ +function colorToRgba(color, alpha) { + color = rgbToHex(color) + // 十六进制颜色值的正则表达式 + const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/ + /* 16进制颜色转为RGB格式 */ + let sColor = String(color).toLowerCase() + if (sColor && reg.test(sColor)) { + if (sColor.length === 4) { + let sColorNew = '#' + for (let i = 1; i < 4; i += 1) { + sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1)) + } + sColor = sColorNew + } + // 处理六位的颜色值 + const sColorChange = [] + for (let i = 1; i < 7; i += 2) { + sColorChange.push(parseInt(`0x${sColor.slice(i, i + 2)}`)) + } + // return sColorChange.join(',') + return `rgba(${sColorChange.join(',')},${alpha})` + } + + return sColor +} + +export default { + colorGradient, + hexToRgb, + rgbToHex, + colorToRgba +} diff --git a/uni_modules/uview-ui/libs/function/debounce.js b/uni_modules/uview-ui/libs/function/debounce.js new file mode 100644 index 0000000..ad3996b --- /dev/null +++ b/uni_modules/uview-ui/libs/function/debounce.js @@ -0,0 +1,29 @@ +let timeout = null + +/** + * 防抖原理:一定时间内,只有最后一次操作,再过wait毫秒后才执行函数 + * + * @param {Function} func 要执行的回调函数 + * @param {Number} wait 延时的时间 + * @param {Boolean} immediate 是否立即执行 + * @return null + */ +function debounce(func, wait = 500, immediate = false) { + // 清除定时器 + if (timeout !== null) clearTimeout(timeout) + // 立即执行,此类情况一般用不到 + if (immediate) { + const callNow = !timeout + timeout = setTimeout(() => { + timeout = null + }, wait) + if (callNow) typeof func === 'function' && func() + } else { + // 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法 + timeout = setTimeout(() => { + typeof func === 'function' && func() + }, wait) + } +} + +export default debounce diff --git a/uni_modules/uview-ui/libs/function/digit.js b/uni_modules/uview-ui/libs/function/digit.js new file mode 100644 index 0000000..c8260a0 --- /dev/null +++ b/uni_modules/uview-ui/libs/function/digit.js @@ -0,0 +1,167 @@ +let _boundaryCheckingState = true; // 是否进行越界检查的全局开关 + +/** + * 把错误的数据转正 + * @private + * @example strip(0.09999999999999998)=0.1 + */ +function strip(num, precision = 15) { + return +parseFloat(Number(num).toPrecision(precision)); +} + +/** + * Return digits length of a number + * @private + * @param {*number} num Input number + */ +function digitLength(num) { + // Get digit length of e + const eSplit = num.toString().split(/[eE]/); + const len = (eSplit[0].split('.')[1] || '').length - +(eSplit[1] || 0); + return len > 0 ? len : 0; +} + +/** + * 把小数转成整数,如果是小数则放大成整数 + * @private + * @param {*number} num 输入数 + */ +function float2Fixed(num) { + if (num.toString().indexOf('e') === -1) { + return Number(num.toString().replace('.', '')); + } + const dLen = digitLength(num); + return dLen > 0 ? strip(Number(num) * Math.pow(10, dLen)) : Number(num); +} + +/** + * 检测数字是否越界,如果越界给出提示 + * @private + * @param {*number} num 输入数 + */ +function checkBoundary(num) { + if (_boundaryCheckingState) { + if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) { + console.warn(`${num} 超出了精度限制,结果可能不正确`); + } + } +} + +/** + * 把递归操作扁平迭代化 + * @param {number[]} arr 要操作的数字数组 + * @param {function} operation 迭代操作 + * @private + */ +function iteratorOperation(arr, operation) { + const [num1, num2, ...others] = arr; + let res = operation(num1, num2); + + others.forEach((num) => { + res = operation(res, num); + }); + + return res; +} + +/** + * 高精度乘法 + * @export + */ +export function times(...nums) { + if (nums.length > 2) { + return iteratorOperation(nums, times); + } + + const [num1, num2] = nums; + const num1Changed = float2Fixed(num1); + const num2Changed = float2Fixed(num2); + const baseNum = digitLength(num1) + digitLength(num2); + const leftValue = num1Changed * num2Changed; + + checkBoundary(leftValue); + + return leftValue / Math.pow(10, baseNum); +} + +/** + * 高精度加法 + * @export + */ +export function plus(...nums) { + if (nums.length > 2) { + return iteratorOperation(nums, plus); + } + + const [num1, num2] = nums; + // 取最大的小数位 + const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2))); + // 把小数都转为整数然后再计算 + return (times(num1, baseNum) + times(num2, baseNum)) / baseNum; +} + +/** + * 高精度减法 + * @export + */ +export function minus(...nums) { + if (nums.length > 2) { + return iteratorOperation(nums, minus); + } + + const [num1, num2] = nums; + const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2))); + return (times(num1, baseNum) - times(num2, baseNum)) / baseNum; +} + +/** + * 高精度除法 + * @export + */ +export function divide(...nums) { + if (nums.length > 2) { + return iteratorOperation(nums, divide); + } + + const [num1, num2] = nums; + const num1Changed = float2Fixed(num1); + const num2Changed = float2Fixed(num2); + checkBoundary(num1Changed); + checkBoundary(num2Changed); + // 重要,这里必须用strip进行修正 + return times(num1Changed / num2Changed, strip(Math.pow(10, digitLength(num2) - digitLength(num1)))); +} + +/** + * 四舍五入 + * @export + */ +export function round(num, ratio) { + const base = Math.pow(10, ratio); + let result = divide(Math.round(Math.abs(times(num, base))), base); + if (num < 0 && result !== 0) { + result = times(result, -1); + } + // 位数不足则补0 + return result; +} + +/** + * 是否进行边界检查,默认开启 + * @param flag 标记开关,true 为开启,false 为关闭,默认为 true + * @export + */ +export function enableBoundaryChecking(flag = true) { + _boundaryCheckingState = flag; +} + + +export default { + times, + plus, + minus, + divide, + round, + enableBoundaryChecking, +}; + diff --git a/uni_modules/uview-ui/libs/function/index.js b/uni_modules/uview-ui/libs/function/index.js new file mode 100644 index 0000000..5a07755 --- /dev/null +++ b/uni_modules/uview-ui/libs/function/index.js @@ -0,0 +1,705 @@ +import test from './test.js' +import { round } from './digit.js' +/** + * @description 如果value小于min,取min;如果value大于max,取max + * @param {number} min + * @param {number} max + * @param {number} value + */ +function range(min = 0, max = 0, value = 0) { + return Math.max(min, Math.min(max, Number(value))) +} + +/** + * @description 用于获取用户传递值的px值 如果用户传递了"xxpx"或者"xxrpx",取出其数值部分,如果是"xxxrpx"还需要用过uni.upx2px进行转换 + * @param {number|string} value 用户传递值的px值 + * @param {boolean} unit + * @returns {number|string} + */ +function getPx(value, unit = false) { + if (test.number(value)) { + return unit ? `${value}px` : Number(value) + } + // 如果带有rpx,先取出其数值部分,再转为px值 + if (/(rpx|upx)$/.test(value)) { + return unit ? `${uni.upx2px(parseInt(value))}px` : Number(uni.upx2px(parseInt(value))) + } + return unit ? `${parseInt(value)}px` : parseInt(value) +} + +/** + * @description 进行延时,以达到可以简写代码的目的 比如: await uni.$u.sleep(20)将会阻塞20ms + * @param {number} value 堵塞时间 单位ms 毫秒 + * @returns {Promise} 返回promise + */ +function sleep(value = 30) { + return new Promise((resolve) => { + setTimeout(() => { + resolve() + }, value) + }) +} +/** + * @description 运行期判断平台 + * @returns {string} 返回所在平台(小写) + * @link 运行期判断平台 https://uniapp.dcloud.io/frame?id=判断平台 + */ +function os() { + return uni.getSystemInfoSync().platform.toLowerCase() +} +/** + * @description 获取系统信息同步接口 + * @link 获取系统信息同步接口 https://uniapp.dcloud.io/api/system/info?id=getsysteminfosync + */ +function sys() { + return uni.getSystemInfoSync() +} + +/** + * @description 取一个区间数 + * @param {Number} min 最小值 + * @param {Number} max 最大值 + */ +function random(min, max) { + if (min >= 0 && max > 0 && max >= min) { + const gab = max - min + 1 + return Math.floor(Math.random() * gab + min) + } + return 0 +} + +/** + * @param {Number} len uuid的长度 + * @param {Boolean} firstU 将返回的首字母置为"u" + * @param {Nubmer} radix 生成uuid的基数(意味着返回的字符串都是这个基数),2-二进制,8-八进制,10-十进制,16-十六进制 + */ +function guid(len = 32, firstU = true, radix = null) { + const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('') + const uuid = [] + radix = radix || chars.length + + if (len) { + // 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位 + for (let i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix] + } else { + let r + // rfc4122标准要求返回的uuid中,某些位为固定的字符 + uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-' + uuid[14] = '4' + + for (let i = 0; i < 36; i++) { + if (!uuid[i]) { + r = 0 | Math.random() * 16 + uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r] + } + } + } + // 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class + if (firstU) { + uuid.shift() + return `u${uuid.join('')}` + } + return uuid.join('') +} + +/** +* @description 获取父组件的参数,因为支付宝小程序不支持provide/inject的写法 + this.$parent在非H5中,可以准确获取到父组件,但是在H5中,需要多次this.$parent.$parent.xxx + 这里默认值等于undefined有它的含义,因为最顶层元素(组件)的$parent就是undefined,意味着不传name + 值(默认为undefined),就是查找最顶层的$parent +* @param {string|undefined} name 父组件的参数名 +*/ +function $parent(name = undefined) { + let parent = this.$parent + // 通过while历遍,这里主要是为了H5需要多层解析的问题 + while (parent) { + // 父组件 + if (parent.$options && parent.$options.name !== name) { + // 如果组件的name不相等,继续上一级寻找 + parent = parent.$parent + } else { + return parent + } + } + return false +} + +/** + * @description 样式转换 + * 对象转字符串,或者字符串转对象 + * @param {object | string} customStyle 需要转换的目标 + * @param {String} target 转换的目的,object-转为对象,string-转为字符串 + * @returns {object|string} + */ +function addStyle(customStyle, target = 'object') { + // 字符串转字符串,对象转对象情形,直接返回 + if (test.empty(customStyle) || typeof(customStyle) === 'object' && target === 'object' || target === 'string' && + typeof(customStyle) === 'string') { + return customStyle + } + // 字符串转对象 + if (target === 'object') { + // 去除字符串样式中的两端空格(中间的空格不能去掉,比如padding: 20px 0如果去掉了就错了),空格是无用的 + customStyle = trim(customStyle) + // 根据";"将字符串转为数组形式 + const styleArray = customStyle.split(';') + const style = {} + // 历遍数组,拼接成对象 + for (let i = 0; i < styleArray.length; i++) { + // 'font-size:20px;color:red;',如此最后字符串有";"的话,会导致styleArray最后一个元素为空字符串,这里需要过滤 + if (styleArray[i]) { + const item = styleArray[i].split(':') + style[trim(item[0])] = trim(item[1]) + } + } + return style + } + // 这里为对象转字符串形式 + let string = '' + for (const i in customStyle) { + // 驼峰转为中划线的形式,否则css内联样式,无法识别驼峰样式属性名 + const key = i.replace(/([A-Z])/g, '-$1').toLowerCase() + string += `${key}:${customStyle[i]};` + } + // 去除两端空格 + return trim(string) +} + +/** + * @description 添加单位,如果有rpx,upx,%,px等单位结尾或者值为auto,直接返回,否则加上px单位结尾 + * @param {string|number} value 需要添加单位的值 + * @param {string} unit 添加的单位名 比如px + */ +function addUnit(value = 'auto', unit = uni?.$u?.config?.unit ?? 'px') { + value = String(value) + // 用uView内置验证规则中的number判断是否为数值 + return test.number(value) ? `${value}${unit}` : value +} + +/** + * @description 深度克隆 + * @param {object} obj 需要深度克隆的对象 + * @returns {*} 克隆后的对象或者原值(不是对象) + */ +function deepClone(obj) { + // 对常见的“非”值,直接返回原来值 + if ([null, undefined, NaN, false].includes(obj)) return obj + if (typeof obj !== 'object' && typeof obj !== 'function') { + // 原始类型直接返回 + return obj + } + const o = test.array(obj) ? [] : {} + for (const i in obj) { + if (obj.hasOwnProperty(i)) { + o[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i] + } + } + return o +} + +/** + * @description JS对象深度合并 + * @param {object} target 需要拷贝的对象 + * @param {object} source 拷贝的来源对象 + * @returns {object|boolean} 深度合并后的对象或者false(入参有不是对象) + */ +function deepMerge(target = {}, source = {}) { + target = deepClone(target) + if (typeof target !== 'object' || typeof source !== 'object') return false + for (const prop in source) { + if (!source.hasOwnProperty(prop)) continue + if (prop in target) { + if (typeof target[prop] !== 'object') { + target[prop] = source[prop] + } else if (typeof source[prop] !== 'object') { + target[prop] = source[prop] + } else if (target[prop].concat && source[prop].concat) { + target[prop] = target[prop].concat(source[prop]) + } else { + target[prop] = deepMerge(target[prop], source[prop]) + } + } else { + target[prop] = source[prop] + } + } + return target +} + +/** + * @description error提示 + * @param {*} err 错误内容 + */ +function error(err) { + // 开发环境才提示,生产环境不会提示 + if (process.env.NODE_ENV === 'development') { + console.error(`uView提示:${err}`) + } +} + +/** + * @description 打乱数组 + * @param {array} array 需要打乱的数组 + * @returns {array} 打乱后的数组 + */ +function randomArray(array = []) { + // 原理是sort排序,Math.random()产生0<= x < 1之间的数,会导致x-0.05大于或者小于0 + return array.sort(() => Math.random() - 0.5) +} + +// padStart 的 polyfill,因为某些机型或情况,还无法支持es7的padStart,比如电脑版的微信小程序 +// 所以这里做一个兼容polyfill的兼容处理 +if (!String.prototype.padStart) { + // 为了方便表示这里 fillString 用了ES6 的默认参数,不影响理解 + String.prototype.padStart = function(maxLength, fillString = ' ') { + if (Object.prototype.toString.call(fillString) !== '[object String]') { + throw new TypeError( + 'fillString must be String' + ) + } + const str = this + // 返回 String(str) 这里是为了使返回的值是字符串字面量,在控制台中更符合直觉 + if (str.length >= maxLength) return String(str) + + const fillLength = maxLength - str.length + let times = Math.ceil(fillLength / fillString.length) + while (times >>= 1) { + fillString += fillString + if (times === 1) { + fillString += fillString + } + } + return fillString.slice(0, fillLength) + str + } +} + +/** + * @description 格式化时间 + * @param {String|Number} dateTime 需要格式化的时间戳 + * @param {String} fmt 格式化规则 yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合 默认yyyy-mm-dd + * @returns {string} 返回格式化后的字符串 + */ + function timeFormat(dateTime = null, formatStr = 'yyyy-mm-dd') { + let date + // 若传入时间为假值,则取当前时间 + if (!dateTime) { + date = new Date() + } + // 若为unix秒时间戳,则转为毫秒时间戳(逻辑有点奇怪,但不敢改,以保证历史兼容) + else if (/^\d{10}$/.test(dateTime?.toString().trim())) { + date = new Date(dateTime * 1000) + } + // 若用户传入字符串格式时间戳,new Date无法解析,需做兼容 + else if (typeof dateTime === 'string' && /^\d+$/.test(dateTime.trim())) { + date = new Date(Number(dateTime)) + } + // 其他都认为符合 RFC 2822 规范 + else { + // 处理平台性差异,在Safari/Webkit中,new Date仅支持/作为分割符的字符串时间 + date = new Date( + typeof dateTime === 'string' + ? dateTime.replace(/-/g, '/') + : dateTime + ) + } + + const timeSource = { + 'y': date.getFullYear().toString(), // 年 + 'm': (date.getMonth() + 1).toString().padStart(2, '0'), // 月 + 'd': date.getDate().toString().padStart(2, '0'), // 日 + 'h': date.getHours().toString().padStart(2, '0'), // 时 + 'M': date.getMinutes().toString().padStart(2, '0'), // 分 + 's': date.getSeconds().toString().padStart(2, '0') // 秒 + // 有其他格式化字符需求可以继续添加,必须转化成字符串 + } + + for (const key in timeSource) { + const [ret] = new RegExp(`${key}+`).exec(formatStr) || [] + if (ret) { + // 年可能只需展示两位 + const beginIndex = key === 'y' && ret.length === 2 ? 2 : 0 + formatStr = formatStr.replace(ret, timeSource[key].slice(beginIndex)) + } + } + + return formatStr +} + +/** + * @description 时间戳转为多久之前 + * @param {String|Number} timestamp 时间戳 + * @param {String|Boolean} format + * 格式化规则如果为时间格式字符串,超出一定时间范围,返回固定的时间格式; + * 如果为布尔值false,无论什么时间,都返回多久以前的格式 + * @returns {string} 转化后的内容 + */ +function timeFrom(timestamp = null, format = 'yyyy-mm-dd') { + if (timestamp == null) timestamp = Number(new Date()) + timestamp = parseInt(timestamp) + // 判断用户输入的时间戳是秒还是毫秒,一般前端js获取的时间戳是毫秒(13位),后端传过来的为秒(10位) + if (timestamp.toString().length == 10) timestamp *= 1000 + let timer = (new Date()).getTime() - timestamp + timer = parseInt(timer / 1000) + // 如果小于5分钟,则返回"刚刚",其他以此类推 + let tips = '' + switch (true) { + case timer < 300: + tips = '刚刚' + break + case timer >= 300 && timer < 3600: + tips = `${parseInt(timer / 60)}分钟前` + break + case timer >= 3600 && timer < 86400: + tips = `${parseInt(timer / 3600)}小时前` + break + case timer >= 86400 && timer < 2592000: + tips = `${parseInt(timer / 86400)}天前` + break + default: + // 如果format为false,则无论什么时间戳,都显示xx之前 + if (format === false) { + if (timer >= 2592000 && timer < 365 * 86400) { + tips = `${parseInt(timer / (86400 * 30))}个月前` + } else { + tips = `${parseInt(timer / (86400 * 365))}年前` + } + } else { + tips = timeFormat(timestamp, format) + } + } + return tips +} + +/** + * @description 去除空格 + * @param String str 需要去除空格的字符串 + * @param String pos both(左右)|left|right|all 默认both + */ +function trim(str, pos = 'both') { + str = String(str) + if (pos == 'both') { + return str.replace(/^\s+|\s+$/g, '') + } + if (pos == 'left') { + return str.replace(/^\s*/, '') + } + if (pos == 'right') { + return str.replace(/(\s*$)/g, '') + } + if (pos == 'all') { + return str.replace(/\s+/g, '') + } + return str +} + +/** + * @description 对象转url参数 + * @param {object} data,对象 + * @param {Boolean} isPrefix,是否自动加上"?" + * @param {string} arrayFormat 规则 indices|brackets|repeat|comma + */ +function queryParams(data = {}, isPrefix = true, arrayFormat = 'brackets') { + const prefix = isPrefix ? '?' : '' + const _result = [] + if (['indices', 'brackets', 'repeat', 'comma'].indexOf(arrayFormat) == -1) arrayFormat = 'brackets' + for (const key in data) { + const value = data[key] + // 去掉为空的参数 + if (['', undefined, null].indexOf(value) >= 0) { + continue + } + // 如果值为数组,另行处理 + if (value.constructor === Array) { + // e.g. {ids: [1, 2, 3]} + switch (arrayFormat) { + case 'indices': + // 结果: ids[0]=1&ids[1]=2&ids[2]=3 + for (let i = 0; i < value.length; i++) { + _result.push(`${key}[${i}]=${value[i]}`) + } + break + case 'brackets': + // 结果: ids[]=1&ids[]=2&ids[]=3 + value.forEach((_value) => { + _result.push(`${key}[]=${_value}`) + }) + break + case 'repeat': + // 结果: ids=1&ids=2&ids=3 + value.forEach((_value) => { + _result.push(`${key}=${_value}`) + }) + break + case 'comma': + // 结果: ids=1,2,3 + let commaStr = '' + value.forEach((_value) => { + commaStr += (commaStr ? ',' : '') + _value + }) + _result.push(`${key}=${commaStr}`) + break + default: + value.forEach((_value) => { + _result.push(`${key}[]=${_value}`) + }) + } + } else { + _result.push(`${key}=${value}`) + } + } + return _result.length ? prefix + _result.join('&') : '' +} + +/** + * 显示消息提示框 + * @param {String} title 提示的内容,长度与 icon 取值有关。 + * @param {Number} duration 提示的延迟时间,单位毫秒,默认:2000 + */ +function toast(title, duration = 2000) { + uni.showToast({ + title: String(title), + icon: 'none', + duration + }) +} + +/** + * @description 根据主题type值,获取对应的图标 + * @param {String} type 主题名称,primary|info|error|warning|success + * @param {boolean} fill 是否使用fill填充实体的图标 + */ +function type2icon(type = 'success', fill = false) { + // 如果非预置值,默认为success + if (['primary', 'info', 'error', 'warning', 'success'].indexOf(type) == -1) type = 'success' + let iconName = '' + // 目前(2019-12-12),info和primary使用同一个图标 + switch (type) { + case 'primary': + iconName = 'info-circle' + break + case 'info': + iconName = 'info-circle' + break + case 'error': + iconName = 'close-circle' + break + case 'warning': + iconName = 'error-circle' + break + case 'success': + iconName = 'checkmark-circle' + break + default: + iconName = 'checkmark-circle' + } + // 是否是实体类型,加上-fill,在icon组件库中,实体的类名是后面加-fill的 + if (fill) iconName += '-fill' + return iconName +} + +/** + * @description 数字格式化 + * @param {number|string} number 要格式化的数字 + * @param {number} decimals 保留几位小数 + * @param {string} decimalPoint 小数点符号 + * @param {string} thousandsSeparator 千分位符号 + * @returns {string} 格式化后的数字 + */ +function priceFormat(number, decimals = 0, decimalPoint = '.', thousandsSeparator = ',') { + number = (`${number}`).replace(/[^0-9+-Ee.]/g, '') + const n = !isFinite(+number) ? 0 : +number + const prec = !isFinite(+decimals) ? 0 : Math.abs(decimals) + const sep = (typeof thousandsSeparator === 'undefined') ? ',' : thousandsSeparator + const dec = (typeof decimalPoint === 'undefined') ? '.' : decimalPoint + let s = '' + + s = (prec ? round(n, prec) + '' : `${Math.round(n)}`).split('.') + const re = /(-?\d+)(\d{3})/ + while (re.test(s[0])) { + s[0] = s[0].replace(re, `$1${sep}$2`) + } + + if ((s[1] || '').length < prec) { + s[1] = s[1] || '' + s[1] += new Array(prec - s[1].length + 1).join('0') + } + return s.join(dec) +} + +/** + * @description 获取duration值 + * 如果带有ms或者s直接返回,如果大于一定值,认为是ms单位,小于一定值,认为是s单位 + * 比如以30位阈值,那么300大于30,可以理解为用户想要的是300ms,而不是想花300s去执行一个动画 + * @param {String|number} value 比如: "1s"|"100ms"|1|100 + * @param {boolean} unit 提示: 如果是false 默认返回number + * @return {string|number} + */ +function getDuration(value, unit = true) { + const valueNum = parseInt(value) + if (unit) { + if (/s$/.test(value)) return value + return value > 30 ? `${value}ms` : `${value}s` + } + if (/ms$/.test(value)) return valueNum + if (/s$/.test(value)) return valueNum > 30 ? valueNum : valueNum * 1000 + return valueNum +} + +/** + * @description 日期的月或日补零操作 + * @param {String} value 需要补零的值 + */ +function padZero(value) { + return `00${value}`.slice(-2) +} + +/** + * @description 在u-form的子组件内容发生变化,或者失去焦点时,尝试通知u-form执行校验方法 + * @param {*} instance + * @param {*} event + */ +function formValidate(instance, event) { + const formItem = uni.$u.$parent.call(instance, 'u-form-item') + const form = uni.$u.$parent.call(instance, 'u-form') + // 如果发生变化的input或者textarea等,其父组件中有u-form-item或者u-form等,就执行form的validate方法 + // 同时将form-item的pros传递给form,让其进行精确对象验证 + if (formItem && form) { + form.validateField(formItem.prop, () => {}, event) + } +} + +/** + * @description 获取某个对象下的属性,用于通过类似'a.b.c'的形式去获取一个对象的的属性的形式 + * @param {object} obj 对象 + * @param {string} key 需要获取的属性字段 + * @returns {*} + */ +function getProperty(obj, key) { + if (!obj) { + return + } + if (typeof key !== 'string' || key === '') { + return '' + } + if (key.indexOf('.') !== -1) { + const keys = key.split('.') + let firstObj = obj[keys[0]] || {} + + for (let i = 1; i < keys.length; i++) { + if (firstObj) { + firstObj = firstObj[keys[i]] + } + } + return firstObj + } + return obj[key] +} + +/** + * @description 设置对象的属性值,如果'a.b.c'的形式进行设置 + * @param {object} obj 对象 + * @param {string} key 需要设置的属性 + * @param {string} value 设置的值 + */ +function setProperty(obj, key, value) { + if (!obj) { + return + } + // 递归赋值 + const inFn = function(_obj, keys, v) { + // 最后一个属性key + if (keys.length === 1) { + _obj[keys[0]] = v + return + } + // 0~length-1个key + while (keys.length > 1) { + const k = keys[0] + if (!_obj[k] || (typeof _obj[k] !== 'object')) { + _obj[k] = {} + } + const key = keys.shift() + // 自调用判断是否存在属性,不存在则自动创建对象 + inFn(_obj[k], keys, v) + } + } + + if (typeof key !== 'string' || key === '') { + + } else if (key.indexOf('.') !== -1) { // 支持多层级赋值操作 + const keys = key.split('.') + inFn(obj, keys, value) + } else { + obj[key] = value + } +} + +/** + * @description 获取当前页面路径 + */ +function page() { + const pages = getCurrentPages() + // 某些特殊情况下(比如页面进行redirectTo时的一些时机),pages可能为空数组 + return `/${pages[pages.length - 1]?.route ?? ''}` +} + +/** + * @description 获取当前路由栈实例数组 + */ +function pages() { + const pages = getCurrentPages() + return pages +} + +/** + * @description 修改uView内置属性值 + * @param {object} props 修改内置props属性 + * @param {object} config 修改内置config属性 + * @param {object} color 修改内置color属性 + * @param {object} zIndex 修改内置zIndex属性 + */ +function setConfig({ + props = {}, + config = {}, + color = {}, + zIndex = {} +}) { + const { + deepMerge, + } = uni.$u + uni.$u.config = deepMerge(uni.$u.config, config) + uni.$u.props = deepMerge(uni.$u.props, props) + uni.$u.color = deepMerge(uni.$u.color, color) + uni.$u.zIndex = deepMerge(uni.$u.zIndex, zIndex) +} + +export default { + range, + getPx, + sleep, + os, + sys, + random, + guid, + $parent, + addStyle, + addUnit, + deepClone, + deepMerge, + error, + randomArray, + timeFormat, + timeFrom, + trim, + queryParams, + toast, + type2icon, + priceFormat, + getDuration, + padZero, + formValidate, + getProperty, + setProperty, + page, + pages, + setConfig +} diff --git a/uni_modules/uview-ui/libs/function/platform.js b/uni_modules/uview-ui/libs/function/platform.js new file mode 100644 index 0000000..d6b926e --- /dev/null +++ b/uni_modules/uview-ui/libs/function/platform.js @@ -0,0 +1,75 @@ +/** + * 注意: + * 此部分内容,在vue-cli模式下,需要在vue.config.js加入如下内容才有效: + * module.exports = { + * transpileDependencies: ['uview-v2'] + * } + */ + +let platform = 'none' + +// #ifdef VUE3 +platform = 'vue3' +// #endif + +// #ifdef VUE2 +platform = 'vue2' +// #endif + +// #ifdef APP-PLUS +platform = 'plus' +// #endif + +// #ifdef APP-NVUE +platform = 'nvue' +// #endif + +// #ifdef H5 +platform = 'h5' +// #endif + +// #ifdef MP-WEIXIN +platform = 'weixin' +// #endif + +// #ifdef MP-ALIPAY +platform = 'alipay' +// #endif + +// #ifdef MP-BAIDU +platform = 'baidu' +// #endif + +// #ifdef MP-TOUTIAO +platform = 'toutiao' +// #endif + +// #ifdef MP-QQ +platform = 'qq' +// #endif + +// #ifdef MP-KUAISHOU +platform = 'kuaishou' +// #endif + +// #ifdef MP-360 +platform = '360' +// #endif + +// #ifdef MP +platform = 'mp' +// #endif + +// #ifdef QUICKAPP-WEBVIEW +platform = 'quickapp-webview' +// #endif + +// #ifdef QUICKAPP-WEBVIEW-HUAWEI +platform = 'quickapp-webview-huawei' +// #endif + +// #ifdef QUICKAPP-WEBVIEW-UNION +platform = 'quckapp-webview-union' +// #endif + +export default platform diff --git a/uni_modules/uview-ui/libs/function/test.js b/uni_modules/uview-ui/libs/function/test.js new file mode 100644 index 0000000..ae4a1b3 --- /dev/null +++ b/uni_modules/uview-ui/libs/function/test.js @@ -0,0 +1,288 @@ +/** + * 验证电子邮箱格式 + */ +function email(value) { + return /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/.test(value) +} + +/** + * 验证手机格式 + */ +function mobile(value) { + return /^1[23456789]\d{9}$/.test(value) +} + +/** + * 验证URL格式 + */ +function url(value) { + return /^((https|http|ftp|rtsp|mms):\/\/)(([0-9a-zA-Z_!~*'().&=+$%-]+: )?[0-9a-zA-Z_!~*'().&=+$%-]+@)?(([0-9]{1,3}.){3}[0-9]{1,3}|([0-9a-zA-Z_!~*'()-]+.)*([0-9a-zA-Z][0-9a-zA-Z-]{0,61})?[0-9a-zA-Z].[a-zA-Z]{2,6})(:[0-9]{1,4})?((\/?)|(\/[0-9a-zA-Z_!~*'().;?:@&=+$,%#-]+)+\/?)$/ + .test(value) +} + +/** + * 验证日期格式 + */ +function date(value) { + if (!value) return false + // 判断是否数值或者字符串数值(意味着为时间戳),转为数值,否则new Date无法识别字符串时间戳 + if (number(value)) value = +value + return !/Invalid|NaN/.test(new Date(value).toString()) +} + +/** + * 验证ISO类型的日期格式 + */ +function dateISO(value) { + return /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(value) +} + +/** + * 验证十进制数字 + */ +function number(value) { + return /^[\+-]?(\d+\.?\d*|\.\d+|\d\.\d+e\+\d+)$/.test(value) +} + +/** + * 验证字符串 + */ +function string(value) { + return typeof value === 'string' +} + +/** + * 验证整数 + */ +function digits(value) { + return /^\d+$/.test(value) +} + +/** + * 验证身份证号码 + */ +function idCard(value) { + return /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test( + value + ) +} + +/** + * 是否车牌号 + */ +function carNo(value) { + // 新能源车牌 + const xreg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))/ + // 旧车牌 + const creg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1}$/ + if (value.length === 7) { + return creg.test(value) + } if (value.length === 8) { + return xreg.test(value) + } + return false +} + +/** + * 金额,只允许2位小数 + */ +function amount(value) { + // 金额,只允许保留两位小数 + return /^[1-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/.test(value) +} + +/** + * 中文 + */ +function chinese(value) { + const reg = /^[\u4e00-\u9fa5]+$/gi + return reg.test(value) +} + +/** + * 只能输入字母 + */ +function letter(value) { + return /^[a-zA-Z]*$/.test(value) +} + +/** + * 只能是字母或者数字 + */ +function enOrNum(value) { + // 英文或者数字 + const reg = /^[0-9a-zA-Z]*$/g + return reg.test(value) +} + +/** + * 验证是否包含某个值 + */ +function contains(value, param) { + return value.indexOf(param) >= 0 +} + +/** + * 验证一个值范围[min, max] + */ +function range(value, param) { + return value >= param[0] && value <= param[1] +} + +/** + * 验证一个长度范围[min, max] + */ +function rangeLength(value, param) { + return value.length >= param[0] && value.length <= param[1] +} + +/** + * 是否固定电话 + */ +function landline(value) { + const reg = /^\d{3,4}-\d{7,8}(-\d{3,4})?$/ + return reg.test(value) +} + +/** + * 判断是否为空 + */ +function empty(value) { + switch (typeof value) { + case 'undefined': + return true + case 'string': + if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length == 0) return true + break + case 'boolean': + if (!value) return true + break + case 'number': + if (value === 0 || isNaN(value)) return true + break + case 'object': + if (value === null || value.length === 0) return true + for (const i in value) { + return false + } + return true + } + return false +} + +/** + * 是否json字符串 + */ +function jsonString(value) { + if (typeof value === 'string') { + try { + const obj = JSON.parse(value) + if (typeof obj === 'object' && obj) { + return true + } + return false + } catch (e) { + return false + } + } + return false +} + +/** + * 是否数组 + */ +function array(value) { + if (typeof Array.isArray === 'function') { + return Array.isArray(value) + } + return Object.prototype.toString.call(value) === '[object Array]' +} + +/** + * 是否对象 + */ +function object(value) { + return Object.prototype.toString.call(value) === '[object Object]' +} + +/** + * 是否短信验证码 + */ +function code(value, len = 6) { + return new RegExp(`^\\d{${len}}$`).test(value) +} + +/** + * 是否函数方法 + * @param {Object} value + */ +function func(value) { + return typeof value === 'function' +} + +/** + * 是否promise对象 + * @param {Object} value + */ +function promise(value) { + return object(value) && func(value.then) && func(value.catch) +} + +/** 是否图片格式 + * @param {Object} value + */ +function image(value) { + const newValue = value.split('?')[0] + const IMAGE_REGEXP = /\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg)/i + return IMAGE_REGEXP.test(newValue) +} + +/** + * 是否视频格式 + * @param {Object} value + */ +function video(value) { + const VIDEO_REGEXP = /\.(mp4|mpg|mpeg|dat|asf|avi|rm|rmvb|mov|wmv|flv|mkv|m3u8)/i + return VIDEO_REGEXP.test(value) +} + +/** + * 是否为正则对象 + * @param {Object} + * @return {Boolean} + */ +function regExp(o) { + return o && Object.prototype.toString.call(o) === '[object RegExp]' +} + +export default { + email, + mobile, + url, + date, + dateISO, + number, + digits, + idCard, + carNo, + amount, + chinese, + letter, + enOrNum, + contains, + range, + rangeLength, + empty, + isEmpty: empty, + jsonString, + landline, + object, + array, + code, + func, + promise, + video, + image, + regExp, + string +} diff --git a/uni_modules/uview-ui/libs/function/throttle.js b/uni_modules/uview-ui/libs/function/throttle.js new file mode 100644 index 0000000..2f33611 --- /dev/null +++ b/uni_modules/uview-ui/libs/function/throttle.js @@ -0,0 +1,30 @@ +let timer; let + flag +/** + * 节流原理:在一定时间内,只能触发一次 + * + * @param {Function} func 要执行的回调函数 + * @param {Number} wait 延时的时间 + * @param {Boolean} immediate 是否立即执行 + * @return null + */ +function throttle(func, wait = 500, immediate = true) { + if (immediate) { + if (!flag) { + flag = true + // 如果是立即执行,则在wait毫秒内开始时执行 + typeof func === 'function' && func() + timer = setTimeout(() => { + flag = false + }, wait) + } + } else if (!flag) { + flag = true + // 如果是非立即执行,则在wait毫秒内的结束处执行 + timer = setTimeout(() => { + flag = false + typeof func === 'function' && func() + }, wait) + } +} +export default throttle diff --git a/uni_modules/uview-ui/libs/luch-request/adapters/index.js b/uni_modules/uview-ui/libs/luch-request/adapters/index.js new file mode 100644 index 0000000..e03cf5f --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/adapters/index.js @@ -0,0 +1,97 @@ +import buildURL from '../helpers/buildURL' +import buildFullPath from '../core/buildFullPath' +import settle from '../core/settle' +import { isUndefined } from '../utils' + +/** + * 返回可选值存在的配置 + * @param {Array} keys - 可选值数组 + * @param {Object} config2 - 配置 + * @return {{}} - 存在的配置项 + */ +const mergeKeys = (keys, config2) => { + const config = {} + keys.forEach((prop) => { + if (!isUndefined(config2[prop])) { + config[prop] = config2[prop] + } + }) + return config +} +export default (config) => new Promise((resolve, reject) => { + const fullPath = buildURL(buildFullPath(config.baseURL, config.url), config.params) + const _config = { + url: fullPath, + header: config.header, + complete: (response) => { + config.fullPath = fullPath + response.config = config + try { + // 对可能字符串不是json 的情况容错 + if (typeof response.data === 'string') { + response.data = JSON.parse(response.data) + } + // eslint-disable-next-line no-empty + } catch (e) { + } + settle(resolve, reject, response) + } + } + let requestTask + if (config.method === 'UPLOAD') { + delete _config.header['content-type'] + delete _config.header['Content-Type'] + const otherConfig = { + // #ifdef MP-ALIPAY + fileType: config.fileType, + // #endif + filePath: config.filePath, + name: config.name + } + const optionalKeys = [ + // #ifdef APP-PLUS || H5 + 'files', + // #endif + // #ifdef H5 + 'file', + // #endif + // #ifdef H5 || APP-PLUS + 'timeout', + // #endif + 'formData' + ] + requestTask = uni.uploadFile({ ..._config, ...otherConfig, ...mergeKeys(optionalKeys, config) }) + } else if (config.method === 'DOWNLOAD') { + // #ifdef H5 || APP-PLUS + if (!isUndefined(config.timeout)) { + _config.timeout = config.timeout + } + // #endif + requestTask = uni.downloadFile(_config) + } else { + const optionalKeys = [ + 'data', + 'method', + // #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN + 'timeout', + // #endif + 'dataType', + // #ifndef MP-ALIPAY + 'responseType', + // #endif + // #ifdef APP-PLUS + 'sslVerify', + // #endif + // #ifdef H5 + 'withCredentials', + // #endif + // #ifdef APP-PLUS + 'firstIpv4' + // #endif + ] + requestTask = uni.request({ ..._config, ...mergeKeys(optionalKeys, config) }) + } + if (config.getTask) { + config.getTask(requestTask, config) + } +}) diff --git a/uni_modules/uview-ui/libs/luch-request/core/InterceptorManager.js b/uni_modules/uview-ui/libs/luch-request/core/InterceptorManager.js new file mode 100644 index 0000000..3e8728d --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/core/InterceptorManager.js @@ -0,0 +1,50 @@ +'use strict' + +function InterceptorManager() { + this.handlers = [] +} + +/** + * Add a new interceptor to the stack + * + * @param {Function} fulfilled The function to handle `then` for a `Promise` + * @param {Function} rejected The function to handle `reject` for a `Promise` + * + * @return {Number} An ID used to remove interceptor later + */ +InterceptorManager.prototype.use = function use(fulfilled, rejected) { + this.handlers.push({ + fulfilled, + rejected + }) + return this.handlers.length - 1 +} + +/** + * Remove an interceptor from the stack + * + * @param {Number} id The ID that was returned by `use` + */ +InterceptorManager.prototype.eject = function eject(id) { + if (this.handlers[id]) { + this.handlers[id] = null + } +} + +/** + * Iterate over all the registered interceptors + * + * This method is particularly useful for skipping over any + * interceptors that may have become `null` calling `eject`. + * + * @param {Function} fn The function to call for each interceptor + */ +InterceptorManager.prototype.forEach = function forEach(fn) { + this.handlers.forEach((h) => { + if (h !== null) { + fn(h) + } + }) +} + +export default InterceptorManager diff --git a/uni_modules/uview-ui/libs/luch-request/core/Request.js b/uni_modules/uview-ui/libs/luch-request/core/Request.js new file mode 100644 index 0000000..cc48566 --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/core/Request.js @@ -0,0 +1,198 @@ +/** + * @Class Request + * @description luch-request http请求插件 + * @version 3.0.7 + * @Author lu-ch + * @Date 2021-09-04 + * @Email webwork.s@qq.com + * 文档: https://www.quanzhan.co/luch-request/ + * github: https://github.com/lei-mu/luch-request + * DCloud: http://ext.dcloud.net.cn/plugin?id=392 + * HBuilderX: beat-3.0.4 alpha-3.0.4 + */ + +import dispatchRequest from './dispatchRequest' +import InterceptorManager from './InterceptorManager' +import mergeConfig from './mergeConfig' +import defaults from './defaults' +import { isPlainObject } from '../utils' +import clone from '../utils/clone' + +export default class Request { + /** + * @param {Object} arg - 全局配置 + * @param {String} arg.baseURL - 全局根路径 + * @param {Object} arg.header - 全局header + * @param {String} arg.method = [GET|POST|PUT|DELETE|CONNECT|HEAD|OPTIONS|TRACE] - 全局默认请求方式 + * @param {String} arg.dataType = [json] - 全局默认的dataType + * @param {String} arg.responseType = [text|arraybuffer] - 全局默认的responseType。支付宝小程序不支持 + * @param {Object} arg.custom - 全局默认的自定义参数 + * @param {Number} arg.timeout - 全局默认的超时时间,单位 ms。默认60000。H5(HBuilderX 2.9.9+)、APP(HBuilderX 2.9.9+)、微信小程序(2.10.0)、支付宝小程序 + * @param {Boolean} arg.sslVerify - 全局默认的是否验证 ssl 证书。默认true.仅App安卓端支持(HBuilderX 2.3.3+) + * @param {Boolean} arg.withCredentials - 全局默认的跨域请求时是否携带凭证(cookies)。默认false。仅H5支持(HBuilderX 2.6.15+) + * @param {Boolean} arg.firstIpv4 - 全DNS解析时优先使用ipv4。默认false。仅 App-Android 支持 (HBuilderX 2.8.0+) + * @param {Function(statusCode):Boolean} arg.validateStatus - 全局默认的自定义验证器。默认statusCode >= 200 && statusCode < 300 + */ + constructor(arg = {}) { + if (!isPlainObject(arg)) { + arg = {} + console.warn('设置全局参数必须接收一个Object') + } + this.config = clone({ ...defaults, ...arg }) + this.interceptors = { + request: new InterceptorManager(), + response: new InterceptorManager() + } + } + + /** + * @Function + * @param {Request~setConfigCallback} f - 设置全局默认配置 + */ + setConfig(f) { + this.config = f(this.config) + } + + middleware(config) { + config = mergeConfig(this.config, config) + const chain = [dispatchRequest, undefined] + let promise = Promise.resolve(config) + + this.interceptors.request.forEach((interceptor) => { + chain.unshift(interceptor.fulfilled, interceptor.rejected) + }) + + this.interceptors.response.forEach((interceptor) => { + chain.push(interceptor.fulfilled, interceptor.rejected) + }) + + while (chain.length) { + promise = promise.then(chain.shift(), chain.shift()) + } + + return promise + } + + /** + * @Function + * @param {Object} config - 请求配置项 + * @prop {String} options.url - 请求路径 + * @prop {Object} options.data - 请求参数 + * @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型 + * @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse + * @prop {Object} [options.header = config.header] - 请求header + * @prop {Object} [options.method = config.method] - 请求方法 + * @returns {Promise<unknown>} + */ + request(config = {}) { + return this.middleware(config) + } + + get(url, options = {}) { + return this.middleware({ + url, + method: 'GET', + ...options + }) + } + + post(url, data, options = {}) { + return this.middleware({ + url, + data, + method: 'POST', + ...options + }) + } + + // #ifndef MP-ALIPAY + put(url, data, options = {}) { + return this.middleware({ + url, + data, + method: 'PUT', + ...options + }) + } + + // #endif + + // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU + delete(url, data, options = {}) { + return this.middleware({ + url, + data, + method: 'DELETE', + ...options + }) + } + + // #endif + + // #ifdef H5 || MP-WEIXIN + connect(url, data, options = {}) { + return this.middleware({ + url, + data, + method: 'CONNECT', + ...options + }) + } + + // #endif + + // #ifdef H5 || MP-WEIXIN || MP-BAIDU + head(url, data, options = {}) { + return this.middleware({ + url, + data, + method: 'HEAD', + ...options + }) + } + + // #endif + + // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU + options(url, data, options = {}) { + return this.middleware({ + url, + data, + method: 'OPTIONS', + ...options + }) + } + + // #endif + + // #ifdef H5 || MP-WEIXIN + trace(url, data, options = {}) { + return this.middleware({ + url, + data, + method: 'TRACE', + ...options + }) + } + + // #endif + + upload(url, config = {}) { + config.url = url + config.method = 'UPLOAD' + return this.middleware(config) + } + + download(url, config = {}) { + config.url = url + config.method = 'DOWNLOAD' + return this.middleware(config) + } +} + +/** + * setConfig回调 + * @return {Object} - 返回操作后的config + * @callback Request~setConfigCallback + * @param {Object} config - 全局默认config + */ diff --git a/uni_modules/uview-ui/libs/luch-request/core/buildFullPath.js b/uni_modules/uview-ui/libs/luch-request/core/buildFullPath.js new file mode 100644 index 0000000..5eb8a17 --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/core/buildFullPath.js @@ -0,0 +1,20 @@ +'use strict' + +import isAbsoluteURL from '../helpers/isAbsoluteURL' +import combineURLs from '../helpers/combineURLs' + +/** + * Creates a new URL by combining the baseURL with the requestedURL, + * only when the requestedURL is not already an absolute URL. + * If the requestURL is absolute, this function returns the requestedURL untouched. + * + * @param {string} baseURL The base URL + * @param {string} requestedURL Absolute or relative URL to combine + * @returns {string} The combined full path + */ +export default function buildFullPath(baseURL, requestedURL) { + if (baseURL && !isAbsoluteURL(requestedURL)) { + return combineURLs(baseURL, requestedURL) + } + return requestedURL +} diff --git a/uni_modules/uview-ui/libs/luch-request/core/defaults.js b/uni_modules/uview-ui/libs/luch-request/core/defaults.js new file mode 100644 index 0000000..be375a9 --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/core/defaults.js @@ -0,0 +1,29 @@ +/** + * 默认的全局配置 + */ + +export default { + baseURL: '', + header: {}, + method: 'GET', + dataType: 'json', + // #ifndef MP-ALIPAY + responseType: 'text', + // #endif + custom: {}, + // #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN + timeout: 60000, + // #endif + // #ifdef APP-PLUS + sslVerify: true, + // #endif + // #ifdef H5 + withCredentials: false, + // #endif + // #ifdef APP-PLUS + firstIpv4: false, + // #endif + validateStatus: function validateStatus(status) { + return status >= 200 && status < 300 + } +} diff --git a/uni_modules/uview-ui/libs/luch-request/core/dispatchRequest.js b/uni_modules/uview-ui/libs/luch-request/core/dispatchRequest.js new file mode 100644 index 0000000..724545c --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/core/dispatchRequest.js @@ -0,0 +1,3 @@ +import adapter from '../adapters/index' + +export default (config) => adapter(config) diff --git a/uni_modules/uview-ui/libs/luch-request/core/mergeConfig.js b/uni_modules/uview-ui/libs/luch-request/core/mergeConfig.js new file mode 100644 index 0000000..08f8b9b --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/core/mergeConfig.js @@ -0,0 +1,103 @@ +import { deepMerge, isUndefined } from '../utils' + +/** + * 合并局部配置优先的配置,如果局部有该配置项则用局部,如果全局有该配置项则用全局 + * @param {Array} keys - 配置项 + * @param {Object} globalsConfig - 当前的全局配置 + * @param {Object} config2 - 局部配置 + * @return {{}} + */ +const mergeKeys = (keys, globalsConfig, config2) => { + const config = {} + keys.forEach((prop) => { + if (!isUndefined(config2[prop])) { + config[prop] = config2[prop] + } else if (!isUndefined(globalsConfig[prop])) { + config[prop] = globalsConfig[prop] + } + }) + return config +} +/** + * + * @param globalsConfig - 当前实例的全局配置 + * @param config2 - 当前的局部配置 + * @return - 合并后的配置 + */ +export default (globalsConfig, config2 = {}) => { + const method = config2.method || globalsConfig.method || 'GET' + let config = { + baseURL: globalsConfig.baseURL || '', + method, + url: config2.url || '', + params: config2.params || {}, + custom: { ...(globalsConfig.custom || {}), ...(config2.custom || {}) }, + header: deepMerge(globalsConfig.header || {}, config2.header || {}) + } + const defaultToConfig2Keys = ['getTask', 'validateStatus'] + config = { ...config, ...mergeKeys(defaultToConfig2Keys, globalsConfig, config2) } + + // eslint-disable-next-line no-empty + if (method === 'DOWNLOAD') { + // #ifdef H5 || APP-PLUS + if (!isUndefined(config2.timeout)) { + config.timeout = config2.timeout + } else if (!isUndefined(globalsConfig.timeout)) { + config.timeout = globalsConfig.timeout + } + // #endif + } else if (method === 'UPLOAD') { + delete config.header['content-type'] + delete config.header['Content-Type'] + const uploadKeys = [ + // #ifdef APP-PLUS || H5 + 'files', + // #endif + // #ifdef MP-ALIPAY + 'fileType', + // #endif + // #ifdef H5 + 'file', + // #endif + 'filePath', + 'name', + // #ifdef H5 || APP-PLUS + 'timeout', + // #endif + 'formData' + ] + uploadKeys.forEach((prop) => { + if (!isUndefined(config2[prop])) { + config[prop] = config2[prop] + } + }) + // #ifdef H5 || APP-PLUS + if (isUndefined(config.timeout) && !isUndefined(globalsConfig.timeout)) { + config.timeout = globalsConfig.timeout + } + // #endif + } else { + const defaultsKeys = [ + 'data', + // #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN + 'timeout', + // #endif + 'dataType', + // #ifndef MP-ALIPAY + 'responseType', + // #endif + // #ifdef APP-PLUS + 'sslVerify', + // #endif + // #ifdef H5 + 'withCredentials', + // #endif + // #ifdef APP-PLUS + 'firstIpv4' + // #endif + ] + config = { ...config, ...mergeKeys(defaultsKeys, globalsConfig, config2) } + } + + return config +} diff --git a/uni_modules/uview-ui/libs/luch-request/core/settle.js b/uni_modules/uview-ui/libs/luch-request/core/settle.js new file mode 100644 index 0000000..8d3638f --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/core/settle.js @@ -0,0 +1,16 @@ +/** + * Resolve or reject a Promise based on response status. + * + * @param {Function} resolve A function that resolves the promise. + * @param {Function} reject A function that rejects the promise. + * @param {object} response The response. + */ +export default function settle(resolve, reject, response) { + const { validateStatus } = response.config + const status = response.statusCode + if (status && (!validateStatus || validateStatus(status))) { + resolve(response) + } else { + reject(response) + } +} diff --git a/uni_modules/uview-ui/libs/luch-request/helpers/buildURL.js b/uni_modules/uview-ui/libs/luch-request/helpers/buildURL.js new file mode 100644 index 0000000..472ad6a --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/helpers/buildURL.js @@ -0,0 +1,69 @@ +'use strict' + +import * as utils from '../utils' + +function encode(val) { + return encodeURIComponent(val) + .replace(/%40/gi, '@') + .replace(/%3A/gi, ':') + .replace(/%24/g, '$') + .replace(/%2C/gi, ',') + .replace(/%20/g, '+') + .replace(/%5B/gi, '[') + .replace(/%5D/gi, ']') +} + +/** + * Build a URL by appending params to the end + * + * @param {string} url The base of the url (e.g., http://www.google.com) + * @param {object} [params] The params to be appended + * @returns {string} The formatted url + */ +export default function buildURL(url, params) { + /* eslint no-param-reassign:0 */ + if (!params) { + return url + } + + let serializedParams + if (utils.isURLSearchParams(params)) { + serializedParams = params.toString() + } else { + const parts = [] + + utils.forEach(params, (val, key) => { + if (val === null || typeof val === 'undefined') { + return + } + + if (utils.isArray(val)) { + key = `${key}[]` + } else { + val = [val] + } + + utils.forEach(val, (v) => { + if (utils.isDate(v)) { + v = v.toISOString() + } else if (utils.isObject(v)) { + v = JSON.stringify(v) + } + parts.push(`${encode(key)}=${encode(v)}`) + }) + }) + + serializedParams = parts.join('&') + } + + if (serializedParams) { + const hashmarkIndex = url.indexOf('#') + if (hashmarkIndex !== -1) { + url = url.slice(0, hashmarkIndex) + } + + url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams + } + + return url +} diff --git a/uni_modules/uview-ui/libs/luch-request/helpers/combineURLs.js b/uni_modules/uview-ui/libs/luch-request/helpers/combineURLs.js new file mode 100644 index 0000000..ac7c124 --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/helpers/combineURLs.js @@ -0,0 +1,14 @@ +'use strict' + +/** + * Creates a new URL by combining the specified URLs + * + * @param {string} baseURL The base URL + * @param {string} relativeURL The relative URL + * @returns {string} The combined URL + */ +export default function combineURLs(baseURL, relativeURL) { + return relativeURL + ? `${baseURL.replace(/\/+$/, '')}/${relativeURL.replace(/^\/+/, '')}` + : baseURL +} diff --git a/uni_modules/uview-ui/libs/luch-request/helpers/isAbsoluteURL.js b/uni_modules/uview-ui/libs/luch-request/helpers/isAbsoluteURL.js new file mode 100644 index 0000000..63c6647 --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/helpers/isAbsoluteURL.js @@ -0,0 +1,14 @@ +'use strict' + +/** + * Determines whether the specified URL is absolute + * + * @param {string} url The URL to test + * @returns {boolean} True if the specified URL is absolute, otherwise false + */ +export default function isAbsoluteURL(url) { + // A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL). + // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed + // by any combination of letters, digits, plus, period, or hyphen. + return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url) +} diff --git a/uni_modules/uview-ui/libs/luch-request/index.d.ts b/uni_modules/uview-ui/libs/luch-request/index.d.ts new file mode 100644 index 0000000..e939ce1 --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/index.d.ts @@ -0,0 +1,116 @@ +type AnyObject = Record<string | number | symbol, any> +type HttpPromise<T> = Promise<HttpResponse<T>>; +type Tasks = UniApp.RequestTask | UniApp.UploadTask | UniApp.DownloadTask +export interface RequestTask { + abort: () => void; + offHeadersReceived: () => void; + onHeadersReceived: () => void; +} +export interface HttpRequestConfig<T = Tasks> { + /** 请求基地址 */ + baseURL?: string; + /** 请求服务器接口地址 */ + url?: string; + + /** 请求查询参数,自动拼接为查询字符串 */ + params?: AnyObject; + /** 请求体参数 */ + data?: AnyObject; + + /** 文件对应的 key */ + name?: string; + /** HTTP 请求中其他额外的 form data */ + formData?: AnyObject; + /** 要上传文件资源的路径。 */ + filePath?: string; + /** 需要上传的文件列表。使用 files 时,filePath 和 name 不生效,App、H5( 2.6.15+) */ + files?: Array<{ + name?: string; + file?: File; + uri: string; + }>; + /** 要上传的文件对象,仅H5(2.6.15+)支持 */ + file?: File; + + /** 请求头信息 */ + header?: AnyObject; + /** 请求方式 */ + method?: "GET" | "POST" | "PUT" | "DELETE" | "CONNECT" | "HEAD" | "OPTIONS" | "TRACE" | "UPLOAD" | "DOWNLOAD"; + /** 如果设为 json,会尝试对返回的数据做一次 JSON.parse */ + dataType?: string; + /** 设置响应的数据类型,支付宝小程序不支持 */ + responseType?: "text" | "arraybuffer"; + /** 自定义参数 */ + custom?: AnyObject; + /** 超时时间,仅微信小程序(2.10.0)、支付宝小程序支持 */ + timeout?: number; + /** DNS解析时优先使用ipv4,仅 App-Android 支持 (HBuilderX 2.8.0+) */ + firstIpv4?: boolean; + /** 验证 ssl 证书 仅5+App安卓端支持(HBuilderX 2.3.3+) */ + sslVerify?: boolean; + /** 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+) */ + withCredentials?: boolean; + + /** 返回当前请求的task, options。请勿在此处修改options。 */ + getTask?: (task: T, options: HttpRequestConfig<T>) => void; + /** 全局自定义验证器 */ + validateStatus?: (statusCode: number) => boolean | void; +} +export interface HttpResponse<T = any> { + config: HttpRequestConfig; + statusCode: number; + cookies: Array<string>; + data: T; + errMsg: string; + header: AnyObject; +} +export interface HttpUploadResponse<T = any> { + config: HttpRequestConfig; + statusCode: number; + data: T; + errMsg: string; +} +export interface HttpDownloadResponse extends HttpResponse { + tempFilePath: string; +} +export interface HttpError { + config: HttpRequestConfig; + statusCode?: number; + cookies?: Array<string>; + data?: any; + errMsg: string; + header?: AnyObject; +} +export interface HttpInterceptorManager<V, E = V> { + use( + onFulfilled?: (config: V) => Promise<V> | V, + onRejected?: (config: E) => Promise<E> | E + ): void; + eject(id: number): void; +} +export abstract class HttpRequestAbstract { + constructor(config?: HttpRequestConfig); + config: HttpRequestConfig; + interceptors: { + request: HttpInterceptorManager<HttpRequestConfig, HttpRequestConfig>; + response: HttpInterceptorManager<HttpResponse, HttpError>; + } + middleware<T = any>(config: HttpRequestConfig): HttpPromise<T>; + request<T = any>(config: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>; + get<T = any>(url: string, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>; + upload<T = any>(url: string, config?: HttpRequestConfig<UniApp.UploadTask>): HttpPromise<T>; + delete<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>; + head<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>; + post<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>; + put<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>; + connect<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>; + options<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>; + trace<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>; + + download(url: string, config?: HttpRequestConfig<UniApp.DownloadTask>): Promise<HttpDownloadResponse>; + + setConfig(onSend: (config: HttpRequestConfig) => HttpRequestConfig): void; +} + +declare class HttpRequest extends HttpRequestAbstract { } +export default HttpRequest; diff --git a/uni_modules/uview-ui/libs/luch-request/index.js b/uni_modules/uview-ui/libs/luch-request/index.js new file mode 100644 index 0000000..8fb2b44 --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/index.js @@ -0,0 +1,3 @@ +import Request from './core/Request' + +export default Request diff --git a/uni_modules/uview-ui/libs/luch-request/utils.js b/uni_modules/uview-ui/libs/luch-request/utils.js new file mode 100644 index 0000000..847283d --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/utils.js @@ -0,0 +1,131 @@ +'use strict' + +// utils is a library of generic helper functions non-specific to axios + +const { toString } = Object.prototype + +/** + * Determine if a value is an Array + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an Array, otherwise false + */ +export function isArray(val) { + return toString.call(val) === '[object Array]' +} + +/** + * Determine if a value is an Object + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an Object, otherwise false + */ +export function isObject(val) { + return val !== null && typeof val === 'object' +} + +/** + * Determine if a value is a Date + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Date, otherwise false + */ +export function isDate(val) { + return toString.call(val) === '[object Date]' +} + +/** + * Determine if a value is a URLSearchParams object + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a URLSearchParams object, otherwise false + */ +export function isURLSearchParams(val) { + return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams +} + +/** + * Iterate over an Array or an Object invoking a function for each item. + * + * If `obj` is an Array callback will be called passing + * the value, index, and complete array for each item. + * + * If 'obj' is an Object callback will be called passing + * the value, key, and complete object for each property. + * + * @param {Object|Array} obj The object to iterate + * @param {Function} fn The callback to invoke for each item + */ +export function forEach(obj, fn) { + // Don't bother if no value provided + if (obj === null || typeof obj === 'undefined') { + return + } + + // Force an array if not already something iterable + if (typeof obj !== 'object') { + /* eslint no-param-reassign:0 */ + obj = [obj] + } + + if (isArray(obj)) { + // Iterate over array values + for (let i = 0, l = obj.length; i < l; i++) { + fn.call(null, obj[i], i, obj) + } + } else { + // Iterate over object keys + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + fn.call(null, obj[key], key, obj) + } + } + } +} + +/** + * 是否为boolean 值 + * @param val + * @returns {boolean} + */ +export function isBoolean(val) { + return typeof val === 'boolean' +} + +/** + * 是否为真正的对象{} new Object + * @param {any} obj - 检测的对象 + * @returns {boolean} + */ +export function isPlainObject(obj) { + return Object.prototype.toString.call(obj) === '[object Object]' +} + +/** + * Function equal to merge with the difference being that no reference + * to original objects is kept. + * + * @see merge + * @param {Object} obj1 Object to merge + * @returns {Object} Result of all merge properties + */ +export function deepMerge(/* obj1, obj2, obj3, ... */) { + const result = {} + function assignValue(val, key) { + if (typeof result[key] === 'object' && typeof val === 'object') { + result[key] = deepMerge(result[key], val) + } else if (typeof val === 'object') { + result[key] = deepMerge({}, val) + } else { + result[key] = val + } + } + for (let i = 0, l = arguments.length; i < l; i++) { + forEach(arguments[i], assignValue) + } + return result +} + +export function isUndefined(val) { + return typeof val === 'undefined' +} diff --git a/uni_modules/uview-ui/libs/luch-request/utils/clone.js b/uni_modules/uview-ui/libs/luch-request/utils/clone.js new file mode 100644 index 0000000..2fee704 --- /dev/null +++ b/uni_modules/uview-ui/libs/luch-request/utils/clone.js @@ -0,0 +1,264 @@ +/* eslint-disable */ +var clone = (function() { + 'use strict'; + + function _instanceof(obj, type) { + return type != null && obj instanceof type; + } + + var nativeMap; + try { + nativeMap = Map; + } catch(_) { + // maybe a reference error because no `Map`. Give it a dummy value that no + // value will ever be an instanceof. + nativeMap = function() {}; + } + + var nativeSet; + try { + nativeSet = Set; + } catch(_) { + nativeSet = function() {}; + } + + var nativePromise; + try { + nativePromise = Promise; + } catch(_) { + nativePromise = function() {}; + } + + /** + * Clones (copies) an Object using deep copying. + * + * This function supports circular references by default, but if you are certain + * there are no circular references in your object, you can save some CPU time + * by calling clone(obj, false). + * + * Caution: if `circular` is false and `parent` contains circular references, + * your program may enter an infinite loop and crash. + * + * @param `parent` - the object to be cloned + * @param `circular` - set to true if the object to be cloned may contain + * circular references. (optional - true by default) + * @param `depth` - set to a number if the object is only to be cloned to + * a particular depth. (optional - defaults to Infinity) + * @param `prototype` - sets the prototype to be used when cloning an object. + * (optional - defaults to parent prototype). + * @param `includeNonEnumerable` - set to true if the non-enumerable properties + * should be cloned as well. Non-enumerable properties on the prototype + * chain will be ignored. (optional - false by default) + */ + function clone(parent, circular, depth, prototype, includeNonEnumerable) { + if (typeof circular === 'object') { + depth = circular.depth; + prototype = circular.prototype; + includeNonEnumerable = circular.includeNonEnumerable; + circular = circular.circular; + } + // maintain two arrays for circular references, where corresponding parents + // and children have the same index + var allParents = []; + var allChildren = []; + + var useBuffer = typeof Buffer != 'undefined'; + + if (typeof circular == 'undefined') + circular = true; + + if (typeof depth == 'undefined') + depth = Infinity; + + // recurse this function so we don't reset allParents and allChildren + function _clone(parent, depth) { + // cloning null always returns null + if (parent === null) + return null; + + if (depth === 0) + return parent; + + var child; + var proto; + if (typeof parent != 'object') { + return parent; + } + + if (_instanceof(parent, nativeMap)) { + child = new nativeMap(); + } else if (_instanceof(parent, nativeSet)) { + child = new nativeSet(); + } else if (_instanceof(parent, nativePromise)) { + child = new nativePromise(function (resolve, reject) { + parent.then(function(value) { + resolve(_clone(value, depth - 1)); + }, function(err) { + reject(_clone(err, depth - 1)); + }); + }); + } else if (clone.__isArray(parent)) { + child = []; + } else if (clone.__isRegExp(parent)) { + child = new RegExp(parent.source, __getRegExpFlags(parent)); + if (parent.lastIndex) child.lastIndex = parent.lastIndex; + } else if (clone.__isDate(parent)) { + child = new Date(parent.getTime()); + } else if (useBuffer && Buffer.isBuffer(parent)) { + if (Buffer.from) { + // Node.js >= 5.10.0 + child = Buffer.from(parent); + } else { + // Older Node.js versions + child = new Buffer(parent.length); + parent.copy(child); + } + return child; + } else if (_instanceof(parent, Error)) { + child = Object.create(parent); + } else { + if (typeof prototype == 'undefined') { + proto = Object.getPrototypeOf(parent); + child = Object.create(proto); + } + else { + child = Object.create(prototype); + proto = prototype; + } + } + + if (circular) { + var index = allParents.indexOf(parent); + + if (index != -1) { + return allChildren[index]; + } + allParents.push(parent); + allChildren.push(child); + } + + if (_instanceof(parent, nativeMap)) { + parent.forEach(function(value, key) { + var keyChild = _clone(key, depth - 1); + var valueChild = _clone(value, depth - 1); + child.set(keyChild, valueChild); + }); + } + if (_instanceof(parent, nativeSet)) { + parent.forEach(function(value) { + var entryChild = _clone(value, depth - 1); + child.add(entryChild); + }); + } + + for (var i in parent) { + var attrs = Object.getOwnPropertyDescriptor(parent, i); + if (attrs) { + child[i] = _clone(parent[i], depth - 1); + } + + try { + var objProperty = Object.getOwnPropertyDescriptor(parent, i); + if (objProperty.set === 'undefined') { + // no setter defined. Skip cloning this property + continue; + } + child[i] = _clone(parent[i], depth - 1); + } catch(e){ + if (e instanceof TypeError) { + // when in strict mode, TypeError will be thrown if child[i] property only has a getter + // we can't do anything about this, other than inform the user that this property cannot be set. + continue + } else if (e instanceof ReferenceError) { + //this may happen in non strict mode + continue + } + } + + } + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(parent); + for (var i = 0; i < symbols.length; i++) { + // Don't need to worry about cloning a symbol because it is a primitive, + // like a number or string. + var symbol = symbols[i]; + var descriptor = Object.getOwnPropertyDescriptor(parent, symbol); + if (descriptor && !descriptor.enumerable && !includeNonEnumerable) { + continue; + } + child[symbol] = _clone(parent[symbol], depth - 1); + Object.defineProperty(child, symbol, descriptor); + } + } + + if (includeNonEnumerable) { + var allPropertyNames = Object.getOwnPropertyNames(parent); + for (var i = 0; i < allPropertyNames.length; i++) { + var propertyName = allPropertyNames[i]; + var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName); + if (descriptor && descriptor.enumerable) { + continue; + } + child[propertyName] = _clone(parent[propertyName], depth - 1); + Object.defineProperty(child, propertyName, descriptor); + } + } + + return child; + } + + return _clone(parent, depth); + } + + /** + * Simple flat clone using prototype, accepts only objects, usefull for property + * override on FLAT configuration object (no nested props). + * + * USE WITH CAUTION! This may not behave as you wish if you do not know how this + * works. + */ + clone.clonePrototype = function clonePrototype(parent) { + if (parent === null) + return null; + + var c = function () {}; + c.prototype = parent; + return new c(); + }; + +// private utility functions + + function __objToStr(o) { + return Object.prototype.toString.call(o); + } + clone.__objToStr = __objToStr; + + function __isDate(o) { + return typeof o === 'object' && __objToStr(o) === '[object Date]'; + } + clone.__isDate = __isDate; + + function __isArray(o) { + return typeof o === 'object' && __objToStr(o) === '[object Array]'; + } + clone.__isArray = __isArray; + + function __isRegExp(o) { + return typeof o === 'object' && __objToStr(o) === '[object RegExp]'; + } + clone.__isRegExp = __isRegExp; + + function __getRegExpFlags(re) { + var flags = ''; + if (re.global) flags += 'g'; + if (re.ignoreCase) flags += 'i'; + if (re.multiline) flags += 'm'; + return flags; + } + clone.__getRegExpFlags = __getRegExpFlags; + + return clone; +})(); + +export default clone diff --git a/uni_modules/uview-ui/libs/mixin/button.js b/uni_modules/uview-ui/libs/mixin/button.js new file mode 100644 index 0000000..0c019c2 --- /dev/null +++ b/uni_modules/uview-ui/libs/mixin/button.js @@ -0,0 +1,13 @@ +export default { + props: { + lang: String, + sessionFrom: String, + sendMessageTitle: String, + sendMessagePath: String, + sendMessageImg: String, + showMessageCard: Boolean, + appParameter: String, + formType: String, + openType: String + } +} diff --git a/uni_modules/uview-ui/libs/mixin/mixin.js b/uni_modules/uview-ui/libs/mixin/mixin.js new file mode 100644 index 0000000..f41a178 --- /dev/null +++ b/uni_modules/uview-ui/libs/mixin/mixin.js @@ -0,0 +1,160 @@ +module.exports = { + // 定义每个组件都可能需要用到的外部样式以及类名 + props: { + // 每个组件都有的父组件传递的样式,可以为字符串或者对象形式 + customStyle: { + type: [Object, String], + default: () => ({}) + }, + customClass: { + type: String, + default: '' + }, + // 跳转的页面路径 + url: { + type: String, + default: '' + }, + // 页面跳转的类型 + linkType: { + type: String, + default: 'navigateTo' + } + }, + data() { + return {} + }, + onLoad() { + // getRect挂载到$u上,因为这方法需要使用in(this),所以无法把它独立成一个单独的文件导出 + this.$u.getRect = this.$uGetRect + }, + created() { + // 组件当中,只有created声明周期,为了能在组件使用,故也在created中将方法挂载到$u + this.$u.getRect = this.$uGetRect + }, + computed: { + // 在2.x版本中,将会把$u挂载到uni对象下,导致在模板中无法使用uni.$u.xxx形式 + // 所以这里通过computed计算属性将其附加到this.$u上,就可以在模板或者js中使用uni.$u.xxx + // 只在nvue环境通过此方式引入完整的$u,其他平台会出现性能问题,非nvue则按需引入(主要原因是props过大) + $u() { + // #ifndef APP-NVUE + // 在非nvue端,移除props,http,mixin等对象,避免在小程序setData时数据过大影响性能 + return uni.$u.deepMerge(uni.$u, { + props: undefined, + http: undefined, + mixin: undefined + }) + // #endif + // #ifdef APP-NVUE + return uni.$u + // #endif + }, + /** + * 生成bem规则类名 + * 由于微信小程序,H5,nvue之间绑定class的差异,无法通过:class="[bem()]"的形式进行同用 + * 故采用如下折中做法,最后返回的是数组(一般平台)或字符串(支付宝和字节跳动平台),类似['a', 'b', 'c']或'a b c'的形式 + * @param {String} name 组件名称 + * @param {Array} fixed 一直会存在的类名 + * @param {Array} change 会根据变量值为true或者false而出现或者隐藏的类名 + * @returns {Array|string} + */ + bem() { + return function (name, fixed, change) { + // 类名前缀 + const prefix = `u-${name}--` + const classes = {} + if (fixed) { + fixed.map((item) => { + // 这里的类名,会一直存在 + classes[prefix + this[item]] = true + }) + } + if (change) { + change.map((item) => { + // 这里的类名,会根据this[item]的值为true或者false,而进行添加或者移除某一个类 + this[item] ? (classes[prefix + item] = this[item]) : (delete classes[prefix + item]) + }) + } + return Object.keys(classes) + // 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效 + // #ifdef MP-ALIPAY || MP-TOUTIAO || MP-LARK + .join(' ') + // #endif + } + } + }, + methods: { + // 跳转某一个页面 + openPage(urlKey = 'url') { + const url = this[urlKey] + if (url) { + // 执行类似uni.navigateTo的方法 + uni[this.linkType]({ + url + }) + } + }, + // 查询节点信息 + // 目前此方法在支付宝小程序中无法获取组件跟接点的尺寸,为支付宝的bug(2020-07-21) + // 解决办法为在组件根部再套一个没有任何作用的view元素 + $uGetRect(selector, all) { + return new Promise((resolve) => { + uni.createSelectorQuery() + .in(this)[all ? 'selectAll' : 'select'](selector) + .boundingClientRect((rect) => { + if (all && Array.isArray(rect) && rect.length) { + resolve(rect) + } + if (!all && rect) { + resolve(rect) + } + }) + .exec() + }) + }, + getParentData(parentName = '') { + // 避免在created中去定义parent变量 + if (!this.parent) this.parent = {} + // 这里的本质原理是,通过获取父组件实例(也即类似u-radio的父组件u-radio-group的this) + // 将父组件this中对应的参数,赋值给本组件(u-radio的this)的parentData对象中对应的属性 + // 之所以需要这么做,是因为所有端中,头条小程序不支持通过this.parent.xxx去监听父组件参数的变化 + // 此处并不会自动更新子组件的数据,而是依赖父组件u-radio-group去监听data的变化,手动调用更新子组件的方法去重新获取 + this.parent = uni.$u.$parent.call(this, parentName) + if (this.parent.children) { + // 如果父组件的children不存在本组件的实例,才将本实例添加到父组件的children中 + this.parent.children.indexOf(this) === -1 && this.parent.children.push(this) + } + if (this.parent && this.parentData) { + // 历遍parentData中的属性,将parent中的同名属性赋值给parentData + Object.keys(this.parentData).map((key) => { + this.parentData[key] = this.parent[key] + }) + } + }, + // 阻止事件冒泡 + preventEvent(e) { + e && typeof (e.stopPropagation) === 'function' && e.stopPropagation() + }, + // 空操作 + noop(e) { + this.preventEvent(e) + } + }, + onReachBottom() { + uni.$emit('uOnReachBottom') + }, + beforeDestroy() { + // 判断当前页面是否存在parent和chldren,一般在checkbox和checkbox-group父子联动的场景会有此情况 + // 组件销毁时,移除子组件在父组件children数组中的实例,释放资源,避免数据混乱 + if (this.parent && uni.$u.test.array(this.parent.children)) { + // 组件销毁时,移除父组件中的children数组中对应的实例 + const childrenList = this.parent.children + childrenList.map((child, index) => { + // 如果相等,则移除 + if (child === this) { + childrenList.splice(index, 1) + } + }) + } + } +} diff --git a/uni_modules/uview-ui/libs/mixin/mpMixin.js b/uni_modules/uview-ui/libs/mixin/mpMixin.js new file mode 100644 index 0000000..29e7e65 --- /dev/null +++ b/uni_modules/uview-ui/libs/mixin/mpMixin.js @@ -0,0 +1,8 @@ +export default { + // #ifdef MP-WEIXIN + // 将自定义节点设置成虚拟的,更加接近Vue组件的表现,能更好的使用flex属性 + options: { + virtualHost: true + } + // #endif +} diff --git a/uni_modules/uview-ui/libs/mixin/mpShare.js b/uni_modules/uview-ui/libs/mixin/mpShare.js new file mode 100644 index 0000000..b07bbd3 --- /dev/null +++ b/uni_modules/uview-ui/libs/mixin/mpShare.js @@ -0,0 +1,13 @@ +module.exports = { + onLoad() { + // 设置默认的转发参数 + uni.$u.mpShare = { + title: '', // 默认为小程序名称 + path: '', // 默认为当前页面路径 + imageUrl: '' // 默认为当前页面的截图 + } + }, + onShareAppMessage() { + return uni.$u.mpShare + } +} diff --git a/uni_modules/uview-ui/libs/mixin/openType.js b/uni_modules/uview-ui/libs/mixin/openType.js new file mode 100644 index 0000000..1216181 --- /dev/null +++ b/uni_modules/uview-ui/libs/mixin/openType.js @@ -0,0 +1,25 @@ +export default { + props: { + openType: String + }, + methods: { + onGetUserInfo(event) { + this.$emit('getuserinfo', event.detail) + }, + onContact(event) { + this.$emit('contact', event.detail) + }, + onGetPhoneNumber(event) { + this.$emit('getphonenumber', event.detail) + }, + onError(event) { + this.$emit('error', event.detail) + }, + onLaunchApp(event) { + this.$emit('launchapp', event.detail) + }, + onOpenSetting(event) { + this.$emit('opensetting', event.detail) + } + } +} diff --git a/uni_modules/uview-ui/libs/mixin/style.js b/uni_modules/uview-ui/libs/mixin/style.js new file mode 100644 index 0000000..2660180 --- /dev/null +++ b/uni_modules/uview-ui/libs/mixin/style.js @@ -0,0 +1,228 @@ +export default { + props: { + // flex排列方式 + flexDirection: { + type: String, + default: '' + }, + // flex-direction的简写 + fd: { + type: String, + default: '' + }, + // 展示类型 + display: { + type: String, + default: '' + }, + // display简写 + d: { + type: String, + default: '' + }, + // 主轴排列方式 + justifyContent: { + type: String, + default: '' + }, + // justifyContent的简写 + jc: { + type: String, + default: '' + }, + // 纵轴排列方式 + alignItems: { + type: String, + default: '' + }, + // align-items的简写 + ai: { + type: String, + default: '' + }, + color: { + type: String, + default: '' + }, + // color简写 + c: { + type: String, + default: '' + }, + // 字体大小 + fontSize: { + type: [String, Number], + default: 0 + }, + // font-size简写 + fs: { + type: [String, Number], + default: '' + }, + margin: { + type: [String, Number], + default: 0 + }, + // margin简写 + m: { + type: [String, Number], + default: 0 + }, + // margin-top + marginTop: { + type: [String, Number], + default: 0 + }, + // margin-top简写 + mt: { + type: [String, Number], + default: 0 + }, + // margin-right + marginRight: { + type: [String, Number], + default: 0 + }, + // margin-right简写 + mr: { + type: [String, Number], + default: 0 + }, + // margin-bottom + marginBottom: { + type: [String, Number], + default: 0 + }, + // margin-bottom简写 + mb: { + type: [String, Number], + default: 0 + }, + // margin-left + marginLeft: { + type: [String, Number], + default: 0 + }, + // margin-left简写 + ml: { + type: [String, Number], + default: 0 + }, + // padding-left + paddingLeft: { + type: [String, Number], + default: 0 + }, + // padding-left简写 + pl: { + type: [String, Number], + default: 0 + }, + // padding-top + paddingTop: { + type: [String, Number], + default: 0 + }, + // padding-top简写 + pt: { + type: [String, Number], + default: 0 + }, + // padding-right + paddingRight: { + type: [String, Number], + default: 0 + }, + // padding-right简写 + pr: { + type: [String, Number], + default: 0 + }, + // padding-bottom + paddingBottom: { + type: [String, Number], + default: 0 + }, + // padding-bottom简写 + pb: { + type: [String, Number], + default: 0 + }, + // border-radius + borderRadius: { + type: [String, Number], + default: 0 + }, + // border-radius简写 + radius: { + type: [String, Number], + default: 0 + }, + // transform + transform: { + type: String, + default: '' + }, + // 定位 + position: { + type: String, + default: '' + }, + // position简写 + pos: { + type: String, + default: '' + }, + // 宽度 + width: { + type: [String, Number], + default: null + }, + // width简写 + w: { + type: [String, Number], + default: null + }, + // 高度 + height: { + type: [String, Number], + default: null + }, + // height简写 + h: { + type: [String, Number], + default: null + }, + top: { + type: [String, Number], + default: 0 + }, + right: { + type: [String, Number], + default: 0 + }, + bottom: { + type: [String, Number], + default: 0 + }, + left: { + type: [String, Number], + default: 0 + } + }, + computed: { + viewStyle() { + const style = {} + const addStyle = uni.$u.addStyle(this.width || this.w) && (style.width = addStyle(this.width || this.w))(this.height || this.h) && (style.height = addStyle(this.height || this.h))(this.margin || this.m) && (style.margin = addStyle(this.margin || this.m))(this.marginTop || this.mt) && (style.marginTop = addStyle(this.marginTop || this.mt))(this.marginRight || this.mr) && (style.marginRight = addStyle(this.marginRight || this.mr))(this.marginBottom || this.mb) && (style.marginBottom = addStyle(this.marginBottom || this.mb))(this.marginLeft || this.ml) && (style.marginLeft = addStyle(this.marginLeft || this.ml))(this.padding || this.p) && (style.padding = addStyle(this.padding || this.p))(this.paddingTop || this.pt) && (style.paddingTop = addStyle(this.paddingTop || this.pt))(this.paddingRight || this.pr) && (style.paddingRight = addStyle(this.paddingRight || this.pr))(this.paddingBottom || this.pb) && (style.paddingBottom = addStyle(this.paddingBottom || this.pb))(this.paddingLeft || this.pl) && (style.paddingLeft = addStyle(this.paddingLeft || this.pl))(this.color || this.c) && (style.color = this.color || this.c)(this.fontSize || this.fs) && (style.fontSize = this.fontSize || this.fs)(this.borderRadius || this.radius) && (style.borderRadius = this.borderRadius || this.radius)(this.position || this.pos) && (this.position = this.position || this.pos)(this.flexDirection || this.fd) && (this.flexDirection = this.flexDirection || this.fd)(this.justifyContent || jc) && (this.justifyContent = this.justifyContent || jc)(this.alignItems || ai) && (this.alignItems = this.alignItems || ai) + + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) + } + }, + methods: { + // 获取margin或者padding的单位,比如padding: 0 20转为padding: 0 20px + getUnit(unit = '') { + // 取出两端空格,分隔成数组,再对数组的每个元素添加单位,最后再合并成字符串 + return uni.$u.trim(unit).split(' ').map((item) => uni.$u.addUnit(item)).join(' ') + } + } +} diff --git a/uni_modules/uview-ui/libs/mixin/touch.js b/uni_modules/uview-ui/libs/mixin/touch.js new file mode 100644 index 0000000..0ecbd88 --- /dev/null +++ b/uni_modules/uview-ui/libs/mixin/touch.js @@ -0,0 +1,59 @@ +const MIN_DISTANCE = 10 + +function getDirection(x, y) { + if (x > y && x > MIN_DISTANCE) { + return 'horizontal' + } + if (y > x && y > MIN_DISTANCE) { + return 'vertical' + } + return '' +} + +export default { + methods: { + getTouchPoint(e) { + if (!e) { + return { + x: 0, + y: 0 + } + } if (e.touches && e.touches[0]) { + return { + x: e.touches[0].pageX, + y: e.touches[0].pageY + } + } if (e.changedTouches && e.changedTouches[0]) { + return { + x: e.changedTouches[0].pageX, + y: e.changedTouches[0].pageY + } + } + return { + x: e.clientX || 0, + y: e.clientY || 0 + } + }, + resetTouchStatus() { + this.direction = '' + this.deltaX = 0 + this.deltaY = 0 + this.offsetX = 0 + this.offsetY = 0 + }, + touchStart(event) { + this.resetTouchStatus() + const touch = this.getTouchPoint(event) + this.startX = touch.x + this.startY = touch.y + }, + touchMove(event) { + const touch = this.getTouchPoint(event) + this.deltaX = touch.x - this.startX + this.deltaY = touch.y - this.startY + this.offsetX = Math.abs(this.deltaX) + this.offsetY = Math.abs(this.deltaY) + this.direction = this.direction || getDirection(this.offsetX, this.offsetY) + } + } +} diff --git a/uni_modules/uview-ui/libs/util/async-validator.js b/uni_modules/uview-ui/libs/util/async-validator.js new file mode 100644 index 0000000..9e114df --- /dev/null +++ b/uni_modules/uview-ui/libs/util/async-validator.js @@ -0,0 +1,1343 @@ +function _extends() { + _extends = Object.assign || function (target) { + for (let i = 1; i < arguments.length; i++) { + const source = arguments[i] + + for (const key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key] + } + } + } + + return target + } + + return _extends.apply(this, arguments) +} + +/* eslint no-console:0 */ +const formatRegExp = /%[sdj%]/g +let warning = function warning() {} // don't print warning message when in production env or node runtime + +if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV !== 'production' && typeof window + !== 'undefined' && typeof document !== 'undefined') { + warning = function warning(type, errors) { + if (typeof console !== 'undefined' && console.warn) { + if (errors.every((e) => typeof e === 'string')) { + console.warn(type, errors) + } + } + } +} + +function convertFieldsError(errors) { + if (!errors || !errors.length) return null + const fields = {} + errors.forEach((error) => { + const { field } = error + fields[field] = fields[field] || [] + fields[field].push(error) + }) + return fields +} + +function format() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key] + } + + let i = 1 + const f = args[0] + const len = args.length + + if (typeof f === 'function') { + return f.apply(null, args.slice(1)) + } + + if (typeof f === 'string') { + let str = String(f).replace(formatRegExp, (x) => { + if (x === '%%') { + return '%' + } + + if (i >= len) { + return x + } + + switch (x) { + case '%s': + return String(args[i++]) + + case '%d': + return Number(args[i++]) + + case '%j': + try { + return JSON.stringify(args[i++]) + } catch (_) { + return '[Circular]' + } + + break + + default: + return x + } + }) + + for (let arg = args[i]; i < len; arg = args[++i]) { + str += ` ${arg}` + } + + return str + } + + return f +} + +function isNativeStringType(type) { + return type === 'string' || type === 'url' || type === 'hex' || type === 'email' || type === 'pattern' +} + +function isEmptyValue(value, type) { + if (value === undefined || value === null) { + return true + } + + if (type === 'array' && Array.isArray(value) && !value.length) { + return true + } + + if (isNativeStringType(type) && typeof value === 'string' && !value) { + return true + } + + return false +} + +function asyncParallelArray(arr, func, callback) { + const results = [] + let total = 0 + const arrLength = arr.length + + function count(errors) { + results.push.apply(results, errors) + total++ + + if (total === arrLength) { + callback(results) + } + } + + arr.forEach((a) => { + func(a, count) + }) +} + +function asyncSerialArray(arr, func, callback) { + let index = 0 + const arrLength = arr.length + + function next(errors) { + if (errors && errors.length) { + callback(errors) + return + } + + const original = index + index += 1 + + if (original < arrLength) { + func(arr[original], next) + } else { + callback([]) + } + } + + next([]) +} + +function flattenObjArr(objArr) { + const ret = [] + Object.keys(objArr).forEach((k) => { + ret.push.apply(ret, objArr[k]) + }) + return ret +} + +function asyncMap(objArr, option, func, callback) { + if (option.first) { + const _pending = new Promise((resolve, reject) => { + const next = function next(errors) { + callback(errors) + return errors.length ? reject({ + errors, + fields: convertFieldsError(errors) + }) : resolve() + } + + const flattenArr = flattenObjArr(objArr) + asyncSerialArray(flattenArr, func, next) + }) + + _pending.catch((e) => e) + + return _pending + } + + let firstFields = option.firstFields || [] + + if (firstFields === true) { + firstFields = Object.keys(objArr) + } + + const objArrKeys = Object.keys(objArr) + const objArrLength = objArrKeys.length + let total = 0 + const results = [] + const pending = new Promise((resolve, reject) => { + const next = function next(errors) { + results.push.apply(results, errors) + total++ + + if (total === objArrLength) { + callback(results) + return results.length ? reject({ + errors: results, + fields: convertFieldsError(results) + }) : resolve() + } + } + + if (!objArrKeys.length) { + callback(results) + resolve() + } + + objArrKeys.forEach((key) => { + const arr = objArr[key] + + if (firstFields.indexOf(key) !== -1) { + asyncSerialArray(arr, func, next) + } else { + asyncParallelArray(arr, func, next) + } + }) + }) + pending.catch((e) => e) + return pending +} + +function complementError(rule) { + return function (oe) { + if (oe && oe.message) { + oe.field = oe.field || rule.fullField + return oe + } + + return { + message: typeof oe === 'function' ? oe() : oe, + field: oe.field || rule.fullField + } + } +} + +function deepMerge(target, source) { + if (source) { + for (const s in source) { + if (source.hasOwnProperty(s)) { + const value = source[s] + + if (typeof value === 'object' && typeof target[s] === 'object') { + target[s] = { ...target[s], ...value } + } else { + target[s] = value + } + } + } + } + + return target +} + +/** + * Rule for validating required fields. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param source The source object being validated. + * @param errors An array of errors that this rule may add + * validation errors to. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function required(rule, value, source, errors, options, type) { + if (rule.required && (!source.hasOwnProperty(rule.field) || isEmptyValue(value, type || rule.type))) { + errors.push(format(options.messages.required, rule.fullField)) + } +} + +/** + * Rule for validating whitespace. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param source The source object being validated. + * @param errors An array of errors that this rule may add + * validation errors to. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function whitespace(rule, value, source, errors, options) { + if (/^\s+$/.test(value) || value === '') { + errors.push(format(options.messages.whitespace, rule.fullField)) + } +} + +/* eslint max-len:0 */ + +const pattern = { + // http://emailregex.com/ + email: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, + 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' + ), + hex: /^#?([a-f0-9]{6}|[a-f0-9]{3})$/i +} +var types = { + integer: function integer(value) { + return /^(-)?\d+$/.test(value); + }, + float: function float(value) { + return /^(-)?\d+(\.\d+)?$/.test(value); + }, + array: function array(value) { + return Array.isArray(value) + }, + regexp: function regexp(value) { + if (value instanceof RegExp) { + return true + } + + try { + return !!new RegExp(value) + } catch (e) { + return false + } + }, + date: function date(value) { + return typeof value.getTime === 'function' && typeof value.getMonth === 'function' && typeof value.getYear + === 'function' + }, + number: function number(value) { + if (isNaN(value)) { + return false + } + + // 修改源码,将字符串数值先转为数值 + return typeof +value === 'number' + }, + object: function object(value) { + return typeof value === 'object' && !types.array(value) + }, + method: function method(value) { + return typeof value === 'function' + }, + email: function email(value) { + return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255 + }, + url: function url(value) { + return typeof value === 'string' && !!value.match(pattern.url) + }, + hex: function hex(value) { + return typeof value === 'string' && !!value.match(pattern.hex) + } +} +/** + * Rule for validating the type of a value. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param source The source object being validated. + * @param errors An array of errors that this rule may add + * validation errors to. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function type(rule, value, source, errors, options) { + if (rule.required && value === undefined) { + required(rule, value, source, errors, options) + return + } + + const custom = ['integer', 'float', 'array', 'regexp', 'object', 'method', 'email', 'number', 'date', 'url', 'hex'] + const ruleType = rule.type + + if (custom.indexOf(ruleType) > -1) { + if (!types[ruleType](value)) { + errors.push(format(options.messages.types[ruleType], rule.fullField, rule.type)) + } // straight typeof check + } else if (ruleType && typeof value !== rule.type) { + errors.push(format(options.messages.types[ruleType], rule.fullField, rule.type)) + } +} + +/** + * Rule for validating minimum and maximum allowed values. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param source The source object being validated. + * @param errors An array of errors that this rule may add + * validation errors to. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function range(rule, value, source, errors, options) { + const len = typeof rule.len === 'number' + const min = typeof rule.min === 'number' + const max = typeof rule.max === 'number' // 正则匹配码点范围从U+010000一直到U+10FFFF的文字(补充平面Supplementary Plane) + + const spRegexp = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g + let val = value + let key = null + const num = typeof value === 'number' + const str = typeof value === 'string' + const arr = Array.isArray(value) + + if (num) { + key = 'number' + } else if (str) { + key = 'string' + } else if (arr) { + key = 'array' + } // if the value is not of a supported type for range validation + // the validation rule rule should use the + // type property to also test for a particular type + + if (!key) { + return false + } + + if (arr) { + val = value.length + } + + if (str) { + // 处理码点大于U+010000的文字length属性不准确的bug,如"𠮷𠮷𠮷".lenght !== 3 + val = value.replace(spRegexp, '_').length + } + + if (len) { + if (val !== rule.len) { + errors.push(format(options.messages[key].len, rule.fullField, rule.len)) + } + } else if (min && !max && val < rule.min) { + errors.push(format(options.messages[key].min, rule.fullField, rule.min)) + } else if (max && !min && val > rule.max) { + errors.push(format(options.messages[key].max, rule.fullField, rule.max)) + } else if (min && max && (val < rule.min || val > rule.max)) { + errors.push(format(options.messages[key].range, rule.fullField, rule.min, rule.max)) + } +} + +const ENUM = 'enum' +/** + * Rule for validating a value exists in an enumerable list. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param source The source object being validated. + * @param errors An array of errors that this rule may add + * validation errors to. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function enumerable(rule, value, source, errors, options) { + rule[ENUM] = Array.isArray(rule[ENUM]) ? rule[ENUM] : [] + + if (rule[ENUM].indexOf(value) === -1) { + errors.push(format(options.messages[ENUM], rule.fullField, rule[ENUM].join(', '))) + } +} + +/** + * Rule for validating a regular expression pattern. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param source The source object being validated. + * @param errors An array of errors that this rule may add + * validation errors to. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function pattern$1(rule, value, source, errors, options) { + if (rule.pattern) { + if (rule.pattern instanceof RegExp) { + // if a RegExp instance is passed, reset `lastIndex` in case its `global` + // flag is accidentally set to `true`, which in a validation scenario + // is not necessary and the result might be misleading + rule.pattern.lastIndex = 0 + + if (!rule.pattern.test(value)) { + errors.push(format(options.messages.pattern.mismatch, rule.fullField, value, rule.pattern)) + } + } else if (typeof rule.pattern === 'string') { + const _pattern = new RegExp(rule.pattern) + + if (!_pattern.test(value)) { + errors.push(format(options.messages.pattern.mismatch, rule.fullField, value, rule.pattern)) + } + } + } +} + +const rules = { + required, + whitespace, + type, + range, + enum: enumerable, + pattern: pattern$1 +} + +/** + * Performs validation for string types. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function string(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value, 'string') && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options, 'string') + + if (!isEmptyValue(value, 'string')) { + rules.type(rule, value, source, errors, options) + rules.range(rule, value, source, errors, options) + rules.pattern(rule, value, source, errors, options) + + if (rule.whitespace === true) { + rules.whitespace(rule, value, source, errors, options) + } + } + } + + callback(errors) +} + +/** + * Validates a function. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function method(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (value !== undefined) { + rules.type(rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Validates a number. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function number(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (value === '') { + value = undefined + } + + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (value !== undefined) { + rules.type(rule, value, source, errors, options) + rules.range(rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Validates a boolean. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function _boolean(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (value !== undefined) { + rules.type(rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Validates the regular expression type. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function regexp(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (!isEmptyValue(value)) { + rules.type(rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Validates a number is an integer. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function integer(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (value !== undefined) { + rules.type(rule, value, source, errors, options) + rules.range(rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Validates a number is a floating point number. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function floatFn(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (value !== undefined) { + rules.type(rule, value, source, errors, options) + rules.range(rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Validates an array. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function array(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value, 'array') && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options, 'array') + + if (!isEmptyValue(value, 'array')) { + rules.type(rule, value, source, errors, options) + rules.range(rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Validates an object. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function object(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (value !== undefined) { + rules.type(rule, value, source, errors, options) + } + } + + callback(errors) +} + +const ENUM$1 = 'enum' +/** + * Validates an enumerable list. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function enumerable$1(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (value !== undefined) { + rules[ENUM$1](rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Validates a regular expression pattern. + * + * Performs validation when a rule only contains + * a pattern property but is not declared as a string type. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function pattern$2(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value, 'string') && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (!isEmptyValue(value, 'string')) { + rules.pattern(rule, value, source, errors, options) + } + } + + callback(errors) +} + +function date(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + + if (!isEmptyValue(value)) { + let dateObject + + if (typeof value === 'number') { + dateObject = new Date(value) + } else { + dateObject = value + } + + rules.type(rule, dateObject, source, errors, options) + + if (dateObject) { + rules.range(rule, dateObject.getTime(), source, errors, options) + } + } + } + + callback(errors) +} + +function required$1(rule, value, callback, source, options) { + const errors = [] + const type = Array.isArray(value) ? 'array' : typeof value + rules.required(rule, value, source, errors, options, type) + callback(errors) +} + +function type$1(rule, value, callback, source, options) { + const ruleType = rule.type + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value, ruleType) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options, ruleType) + + if (!isEmptyValue(value, ruleType)) { + rules.type(rule, value, source, errors, options) + } + } + + callback(errors) +} + +/** + * Performs validation for any type. + * + * @param rule The validation rule. + * @param value The value of the field on the source object. + * @param callback The callback function. + * @param source The source object being validated. + * @param options The validation options. + * @param options.messages The validation messages. + */ + +function any(rule, value, callback, source, options) { + const errors = [] + const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) + + if (validate) { + if (isEmptyValue(value) && !rule.required) { + return callback() + } + + rules.required(rule, value, source, errors, options) + } + + callback(errors) +} + +const validators = { + string, + method, + number, + boolean: _boolean, + regexp, + integer, + float: floatFn, + array, + object, + enum: enumerable$1, + pattern: pattern$2, + date, + url: type$1, + hex: type$1, + email: type$1, + required: required$1, + any +} + +function newMessages() { + return { + default: 'Validation error on field %s', + required: '%s is required', + enum: '%s must be one of %s', + whitespace: '%s cannot be empty', + date: { + format: '%s date %s is invalid for format %s', + parse: '%s date could not be parsed, %s is invalid ', + invalid: '%s date %s is invalid' + }, + types: { + string: '%s is not a %s', + method: '%s is not a %s (function)', + array: '%s is not an %s', + object: '%s is not an %s', + number: '%s is not a %s', + date: '%s is not a %s', + boolean: '%s is not a %s', + integer: '%s is not an %s', + float: '%s is not a %s', + regexp: '%s is not a valid %s', + email: '%s is not a valid %s', + url: '%s is not a valid %s', + hex: '%s is not a valid %s' + }, + string: { + len: '%s must be exactly %s characters', + min: '%s must be at least %s characters', + max: '%s cannot be longer than %s characters', + range: '%s must be between %s and %s characters' + }, + number: { + len: '%s must equal %s', + min: '%s cannot be less than %s', + max: '%s cannot be greater than %s', + range: '%s must be between %s and %s' + }, + array: { + len: '%s must be exactly %s in length', + min: '%s cannot be less than %s in length', + max: '%s cannot be greater than %s in length', + range: '%s must be between %s and %s in length' + }, + pattern: { + mismatch: '%s value %s does not match pattern %s' + }, + clone: function clone() { + const cloned = JSON.parse(JSON.stringify(this)) + cloned.clone = this.clone + return cloned + } + } +} +const messages = newMessages() + +/** + * Encapsulates a validation schema. + * + * @param descriptor An object declaring validation rules + * for this schema. + */ + +function Schema(descriptor) { + this.rules = null + this._messages = messages + this.define(descriptor) +} + +Schema.prototype = { + messages: function messages(_messages) { + if (_messages) { + this._messages = deepMerge(newMessages(), _messages) + } + + return this._messages + }, + define: function define(rules) { + if (!rules) { + throw new Error('Cannot configure a schema with no rules') + } + + if (typeof rules !== 'object' || Array.isArray(rules)) { + throw new Error('Rules must be an object') + } + + this.rules = {} + let z + let item + + for (z in rules) { + if (rules.hasOwnProperty(z)) { + item = rules[z] + this.rules[z] = Array.isArray(item) ? item : [item] + } + } + }, + validate: function validate(source_, o, oc) { + const _this = this + + if (o === void 0) { + o = {} + } + + if (oc === void 0) { + oc = function oc() {} + } + + let source = source_ + let options = o + let callback = oc + + if (typeof options === 'function') { + callback = options + options = {} + } + + if (!this.rules || Object.keys(this.rules).length === 0) { + if (callback) { + callback() + } + + return Promise.resolve() + } + + function complete(results) { + let i + let errors = [] + let fields = {} + + function add(e) { + if (Array.isArray(e)) { + let _errors + + errors = (_errors = errors).concat.apply(_errors, e) + } else { + errors.push(e) + } + } + + for (i = 0; i < results.length; i++) { + add(results[i]) + } + + if (!errors.length) { + errors = null + fields = null + } else { + fields = convertFieldsError(errors) + } + + callback(errors, fields) + } + + if (options.messages) { + let messages$1 = this.messages() + + if (messages$1 === messages) { + messages$1 = newMessages() + } + + deepMerge(messages$1, options.messages) + options.messages = messages$1 + } else { + options.messages = this.messages() + } + + let arr + let value + const series = {} + const keys = options.keys || Object.keys(this.rules) + keys.forEach((z) => { + arr = _this.rules[z] + value = source[z] + arr.forEach((r) => { + let rule = r + + if (typeof rule.transform === 'function') { + if (source === source_) { + source = { ...source } + } + + value = source[z] = rule.transform(value) + } + + if (typeof rule === 'function') { + rule = { + validator: rule + } + } else { + rule = { ...rule } + } + + rule.validator = _this.getValidationMethod(rule) + rule.field = z + rule.fullField = rule.fullField || z + rule.type = _this.getType(rule) + + if (!rule.validator) { + return + } + + series[z] = series[z] || [] + series[z].push({ + rule, + value, + source, + field: z + }) + }) + }) + const errorFields = {} + return asyncMap(series, options, (data, doIt) => { + const { rule } = data + let deep = (rule.type === 'object' || rule.type === 'array') && (typeof rule.fields === 'object' || typeof rule.defaultField + === 'object') + deep = deep && (rule.required || !rule.required && data.value) + rule.field = data.field + + function addFullfield(key, schema) { + return { ...schema, fullField: `${rule.fullField}.${key}` } + } + + function cb(e) { + if (e === void 0) { + e = [] + } + + let errors = e + + if (!Array.isArray(errors)) { + errors = [errors] + } + + if (!options.suppressWarning && errors.length) { + Schema.warning('async-validator:', errors) + } + + if (errors.length && rule.message) { + errors = [].concat(rule.message) + } + + errors = errors.map(complementError(rule)) + + if (options.first && errors.length) { + errorFields[rule.field] = 1 + return doIt(errors) + } + + if (!deep) { + doIt(errors) + } else { + // if rule is required but the target object + // does not exist fail at the rule level and don't + // go deeper + if (rule.required && !data.value) { + if (rule.message) { + errors = [].concat(rule.message).map(complementError(rule)) + } else if (options.error) { + errors = [options.error(rule, format(options.messages.required, rule.field))] + } else { + errors = [] + } + + return doIt(errors) + } + + let fieldsSchema = {} + + if (rule.defaultField) { + for (const k in data.value) { + if (data.value.hasOwnProperty(k)) { + fieldsSchema[k] = rule.defaultField + } + } + } + + fieldsSchema = { ...fieldsSchema, ...data.rule.fields } + + for (const f in fieldsSchema) { + if (fieldsSchema.hasOwnProperty(f)) { + const fieldSchema = Array.isArray(fieldsSchema[f]) ? fieldsSchema[f] : [fieldsSchema[f]] + fieldsSchema[f] = fieldSchema.map(addFullfield.bind(null, f)) + } + } + + const schema = new Schema(fieldsSchema) + schema.messages(options.messages) + + if (data.rule.options) { + data.rule.options.messages = options.messages + data.rule.options.error = options.error + } + + schema.validate(data.value, data.rule.options || options, (errs) => { + const finalErrors = [] + + if (errors && errors.length) { + finalErrors.push.apply(finalErrors, errors) + } + + if (errs && errs.length) { + finalErrors.push.apply(finalErrors, errs) + } + + doIt(finalErrors.length ? finalErrors : null) + }) + } + } + + let res + + if (rule.asyncValidator) { + res = rule.asyncValidator(rule, data.value, cb, data.source, options) + } else if (rule.validator) { + res = rule.validator(rule, data.value, cb, data.source, options) + + if (res === true) { + cb() + } else if (res === false) { + cb(rule.message || `${rule.field} fails`) + } else if (res instanceof Array) { + cb(res) + } else if (res instanceof Error) { + cb(res.message) + } + } + + if (res && res.then) { + res.then(() => cb(), (e) => cb(e)) + } + }, (results) => { + complete(results) + }) + }, + getType: function getType(rule) { + if (rule.type === undefined && rule.pattern instanceof RegExp) { + rule.type = 'pattern' + } + + if (typeof rule.validator !== 'function' && rule.type && !validators.hasOwnProperty(rule.type)) { + throw new Error(format('Unknown rule type %s', rule.type)) + } + + return rule.type || 'string' + }, + getValidationMethod: function getValidationMethod(rule) { + if (typeof rule.validator === 'function') { + return rule.validator + } + + const keys = Object.keys(rule) + const messageIndex = keys.indexOf('message') + + if (messageIndex !== -1) { + keys.splice(messageIndex, 1) + } + + if (keys.length === 1 && keys[0] === 'required') { + return validators.required + } + + return validators[this.getType(rule)] || false + } +} + +Schema.register = function register(type, validator) { + if (typeof validator !== 'function') { + throw new Error('Cannot register a validator by type, validator is not a function') + } + + validators[type] = validator +} + +Schema.warning = warning +Schema.messages = messages + +export default Schema +// # sourceMappingURL=index.js.map diff --git a/uni_modules/uview-ui/libs/util/calendar.js b/uni_modules/uview-ui/libs/util/calendar.js new file mode 100644 index 0000000..e006dea --- /dev/null +++ b/uni_modules/uview-ui/libs/util/calendar.js @@ -0,0 +1,546 @@ +/** +* @1900-2100区间内的公历、农历互转 +* @charset UTF-8 +* @github https://github.com/jjonline/calendar.js +* @Author Jea杨(JJonline@JJonline.Cn) +* @Time 2014-7-21 +* @Time 2016-8-13 Fixed 2033hex、Attribution Annals +* @Time 2016-9-25 Fixed lunar LeapMonth Param Bug +* @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year +* @Version 1.0.3 +* @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0] +* @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0] +*/ +/* eslint-disable */ +var calendar = { + + /** + * 农历1900-2100的润大小信息表 + * @Array Of Property + * @return Hex + */ + lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909 + 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919 + 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929 + 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939 + 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949 + 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959 + 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969 + 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979 + 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989 + 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999 + 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009 + 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019 + 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029 + 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039 + 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049 + /** Add By JJonline@JJonline.Cn**/ + 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059 + 0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069 + 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079 + 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089 + 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099 + 0x0d520], // 2100 + + /** + * 公历每个月份的天数普通表 + * @Array Of Property + * @return Number + */ + solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + + /** + * 天干地支之天干速查表 + * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"] + * @return Cn string + */ + Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'], + + /** + * 天干地支之地支速查表 + * @Array Of Property + * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"] + * @return Cn string + */ + Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'], + + /** + * 天干地支之地支速查表<=>生肖 + * @Array Of Property + * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"] + * @return Cn string + */ + Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'], + + /** + * 24节气速查表 + * @Array Of Property + * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"] + * @return Cn string + */ + solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'], + + /** + * 1900-2100各年的24节气日期速查表 + * @Array Of Property + * @return 0x string For splice + */ + sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', + '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', + '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', + 'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f', + '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa', + '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', + '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f', + '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', + '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', + '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', + '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722', + '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f', + '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e', + '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', + '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722', + '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', + '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', + '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', + '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e', + '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', + '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', + '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721', + '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2', + '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', + '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', + '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', + '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721', + '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5', + '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722', + '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', + '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721', + '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd', + '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35', + '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', + '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721', + '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5', + '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35', + '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35', + '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'], + + /** + * 数字转中文速查表 + * @Array Of Property + * @trans ['日','一','二','三','四','五','六','七','八','九','十'] + * @return Cn string + */ + nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'], + + /** + * 日期转农历称呼速查表 + * @Array Of Property + * @trans ['初','十','廿','卅'] + * @return Cn string + */ + nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'], + + /** + * 月份转农历称呼速查表 + * @Array Of Property + * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊'] + * @return Cn string + */ + nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'], + + /** + * 返回农历y年一整年的总天数 + * @param lunar Year + * @return Number + * @eg:var count = calendar.lYearDays(1987) ;//count=387 + */ + lYearDays: function (y) { + var i; var sum = 348 + for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 } + return (sum + this.leapDays(y)) + }, + + /** + * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0 + * @param lunar Year + * @return Number (0-12) + * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6 + */ + leapMonth: function (y) { // 闰字编码 \u95f0 + return (this.lunarInfo[y - 1900] & 0xf) + }, + + /** + * 返回农历y年闰月的天数 若该年没有闰月则返回0 + * @param lunar Year + * @return Number (0、29、30) + * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29 + */ + leapDays: function (y) { + if (this.leapMonth(y)) { + return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29) + } + return (0) + }, + + /** + * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法 + * @param lunar Year + * @return Number (-1、29、30) + * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29 + */ + monthDays: function (y, m) { + if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1 + return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29) + }, + + /** + * 返回公历(!)y年m月的天数 + * @param solar Year + * @return Number (-1、28、29、30、31) + * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30 + */ + solarDays: function (y, m) { + if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1 + var ms = m - 1 + if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29 + return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28) + } else { + return (this.solarMonth[ms]) + } + }, + + /** + * 农历年份转换为干支纪年 + * @param lYear 农历年的年份数 + * @return Cn string + */ + toGanZhiYear: function (lYear) { + var ganKey = (lYear - 3) % 10 + var zhiKey = (lYear - 3) % 12 + if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干 + if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支 + return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1] + }, + + /** + * 公历月、日判断所属星座 + * @param cMonth [description] + * @param cDay [description] + * @return Cn string + */ + toAstro: function (cMonth, cDay) { + var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf' + var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22] + return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座 + }, + + /** + * 传入offset偏移量返回干支 + * @param offset 相对甲子的偏移量 + * @return Cn string + */ + toGanZhi: function (offset) { + return this.Gan[offset % 10] + this.Zhi[offset % 12] + }, + + /** + * 传入公历(!)y年获得该年第n个节气的公历日期 + * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起 + * @return day Number + * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春 + */ + getTerm: function (y, n) { + if (y < 1900 || y > 2100) { return -1 } + if (n < 1 || n > 24) { return -1 } + var _table = this.sTermInfo[y - 1900] + var _info = [ + parseInt('0x' + _table.substr(0, 5)).toString(), + parseInt('0x' + _table.substr(5, 5)).toString(), + parseInt('0x' + _table.substr(10, 5)).toString(), + parseInt('0x' + _table.substr(15, 5)).toString(), + parseInt('0x' + _table.substr(20, 5)).toString(), + parseInt('0x' + _table.substr(25, 5)).toString() + ] + var _calday = [ + _info[0].substr(0, 1), + _info[0].substr(1, 2), + _info[0].substr(3, 1), + _info[0].substr(4, 2), + + _info[1].substr(0, 1), + _info[1].substr(1, 2), + _info[1].substr(3, 1), + _info[1].substr(4, 2), + + _info[2].substr(0, 1), + _info[2].substr(1, 2), + _info[2].substr(3, 1), + _info[2].substr(4, 2), + + _info[3].substr(0, 1), + _info[3].substr(1, 2), + _info[3].substr(3, 1), + _info[3].substr(4, 2), + + _info[4].substr(0, 1), + _info[4].substr(1, 2), + _info[4].substr(3, 1), + _info[4].substr(4, 2), + + _info[5].substr(0, 1), + _info[5].substr(1, 2), + _info[5].substr(3, 1), + _info[5].substr(4, 2) + ] + return parseInt(_calday[n - 1]) + }, + + /** + * 传入农历数字月份返回汉语通俗表示法 + * @param lunar month + * @return Cn string + * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月' + */ + toChinaMonth: function (m) { // 月 => \u6708 + if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1 + var s = this.nStr3[m - 1] + s += '\u6708'// 加上月字 + return s + }, + + /** + * 传入农历日期数字返回汉字表示法 + * @param lunar day + * @return Cn string + * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一' + */ + toChinaDay: function (d) { // 日 => \u65e5 + var s + switch (d) { + case 10: + s = '\u521d\u5341'; break + case 20: + s = '\u4e8c\u5341'; break + break + case 30: + s = '\u4e09\u5341'; break + break + default: + s = this.nStr2[Math.floor(d / 10)] + s += this.nStr1[d % 10] + } + return (s) + }, + + /** + * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春” + * @param y year + * @return Cn string + * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔' + */ + getAnimal: function (y) { + return this.Animals[(y - 4) % 12] + }, + + /** + * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON + * @param y solar year + * @param m solar month + * @param d solar day + * @return JSON object + * @eg:console.log(calendar.solar2lunar(1987,11,01)); + */ + solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31 + // 年份限定、上限 + if (y < 1900 || y > 2100) { + return -1// undefined转换为数字变为NaN + } + // 公历传参最下限 + if (y == 1900 && m == 1 && d < 31) { + return -1 + } + // 未传参 获得当天 + if (!y) { + var objDate = new Date() + } else { + var objDate = new Date(y, parseInt(m) - 1, d) + } + var i; var leap = 0; var temp = 0 + // 修正ymd参数 + var y = objDate.getFullYear() + var m = objDate.getMonth() + 1 + var d = objDate.getDate() + var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000 + for (i = 1900; i < 2101 && offset > 0; i++) { + temp = this.lYearDays(i) + offset -= temp + } + if (offset < 0) { + offset += temp; i-- + } + + // 是否今天 + var isTodayObj = new Date() + var isToday = false + if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) { + isToday = true + } + // 星期几 + var nWeek = objDate.getDay() + var cWeek = this.nStr1[nWeek] + // 数字表示周几顺应天朝周一开始的惯例 + if (nWeek == 0) { + nWeek = 7 + } + // 农历年 + var year = i + var leap = this.leapMonth(i) // 闰哪个月 + var isLeap = false + + // 效验闰月 + for (i = 1; i < 13 && offset > 0; i++) { + // 闰月 + if (leap > 0 && i == (leap + 1) && isLeap == false) { + --i + isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数 + } else { + temp = this.monthDays(year, i)// 计算农历普通月天数 + } + // 解除闰月 + if (isLeap == true && i == (leap + 1)) { isLeap = false } + offset -= temp + } + // 闰月导致数组下标重叠取反 + if (offset == 0 && leap > 0 && i == leap + 1) { + if (isLeap) { + isLeap = false + } else { + isLeap = true; --i + } + } + if (offset < 0) { + offset += temp; --i + } + // 农历月 + var month = i + // 农历日 + var day = offset + 1 + // 天干地支处理 + var sm = m - 1 + var gzY = this.toGanZhiYear(year) + + // 当月的两个节气 + // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year` + var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始 + var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始 + + // 依据12节气修正干支月 + var gzM = this.toGanZhi((y - 1900) * 12 + m + 11) + if (d >= firstNode) { + gzM = this.toGanZhi((y - 1900) * 12 + m + 12) + } + + // 传入的日期的节气与否 + var isTerm = false + var Term = null + if (firstNode == d) { + isTerm = true + Term = this.solarTerm[m * 2 - 2] + } + if (secondNode == d) { + isTerm = true + Term = this.solarTerm[m * 2 - 1] + } + // 日柱 当月一日与 1900/1/1 相差天数 + var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10 + var gzD = this.toGanZhi(dayCyclical + d - 1) + // 该日期所属的星座 + var astro = this.toAstro(m, d) + + return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro } + }, + + /** + * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON + * @param y lunar year + * @param m lunar month + * @param d lunar day + * @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可] + * @return JSON object + * @eg:console.log(calendar.lunar2solar(1987,9,10)); + */ + lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1 + var isLeapMonth = !!isLeapMonth + var leapOffset = 0 + var leapMonth = this.leapMonth(y) + var leapDay = this.leapDays(y) + if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同 + if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值 + var day = this.monthDays(y, m) + var _day = day + // bugFix 2016-9-25 + // if month is leap, _day use leapDays method + if (isLeapMonth) { + _day = this.leapDays(y, m) + } + if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验 + + // 计算农历的时间差 + var offset = 0 + for (var i = 1900; i < y; i++) { + offset += this.lYearDays(i) + } + var leap = 0; var isAdd = false + for (var i = 1; i < m; i++) { + leap = this.leapMonth(y) + if (!isAdd) { // 处理闰月 + if (leap <= i && leap > 0) { + offset += this.leapDays(y); isAdd = true + } + } + offset += this.monthDays(y, i) + } + // 转换闰月农历 需补充该年闰月的前一个月的时差 + if (isLeapMonth) { offset += day } + // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点) + var stmap = Date.UTC(1900, 1, 30, 0, 0, 0) + var calObj = new Date((offset + d - 31) * 86400000 + stmap) + var cY = calObj.getUTCFullYear() + var cM = calObj.getUTCMonth() + 1 + var cD = calObj.getUTCDate() + + return this.solar2lunar(cY, cM, cD) + } +} + +export default calendar diff --git a/uni_modules/uview-ui/libs/util/dayjs.js b/uni_modules/uview-ui/libs/util/dayjs.js new file mode 100644 index 0000000..c4efea0 --- /dev/null +++ b/uni_modules/uview-ui/libs/util/dayjs.js @@ -0,0 +1,308 @@ +!(function (t, e) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = e() : typeof define === 'function' + && define.amd ? define(e) : t.dayjs = e() +}(this, () => { + 'use strict' + + const t = 'millisecond' + const e = 'second' + const n = 'minute' + const r = 'hour' + const i = 'day' + const s = 'week' + const u = 'month' + const a = 'quarter' + const o = 'year' + const f = 'date' + const h = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?.?(\d+)?$/ + const c = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g + const d = { + name: 'en', + weekdays: 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), + months: 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_') + } + const $ = function (t, e, n) { + const r = String(t) + return !r || r.length >= e ? t : `${Array(e + 1 - r.length).join(n)}${t}` + } + const l = { + s: $, + z(t) { + const e = -t.utcOffset() + const n = Math.abs(e) + const r = Math.floor(n / 60) + const i = n % 60 + return `${(e <= 0 ? '+' : '-') + $(r, 2, '0')}:${$(i, 2, '0')}` + }, + m: function t(e, n) { + if (e.date() < n.date()) return -t(n, e) + const r = 12 * (n.year() - e.year()) + (n.month() - e.month()) + const i = e.clone().add(r, u) + const s = n - i < 0 + const a = e.clone().add(r + (s ? -1 : 1), u) + return +(-(r + (n - i) / (s ? i - a : a - i)) || 0) + }, + a(t) { + return t < 0 ? Math.ceil(t) || 0 : Math.floor(t) + }, + p(h) { + return { + M: u, + y: o, + w: s, + d: i, + D: f, + h: r, + m: n, + s: e, + ms: t, + Q: a + }[h] || String(h || '').toLowerCase().replace(/s$/, '') + }, + u(t) { + return void 0 === t + } + } + let y = 'en' + const M = {} + M[y] = d + const m = function (t) { + return t instanceof S + } + const D = function (t, e, n) { + let r + if (!t) return y + if (typeof t === 'string') M[t] && (r = t), e && (M[t] = e, r = t) + else { + const i = t.name + M[i] = t, r = i + } + return !n && r && (y = r), r || !n && y + } + const v = function (t, e) { + if (m(t)) return t.clone() + const n = typeof e === 'object' ? e : {} + return n.date = t, n.args = arguments, new S(n) + } + const g = l + g.l = D, g.i = m, g.w = function (t, e) { + return v(t, { + locale: e.$L, + utc: e.$u, + x: e.$x, + $offset: e.$offset + }) + } + var S = (function () { + function d(t) { + this.$L = D(t.locale, null, !0), this.parse(t) + } + const $ = d.prototype + return $.parse = function (t) { + this.$d = (function (t) { + const e = t.date + const n = t.utc + if (e === null) return new Date(NaN) + if (g.u(e)) return new Date() + if (e instanceof Date) return new Date(e) + if (typeof e === 'string' && !/Z$/i.test(e)) { + const r = e.match(h) + if (r) { + const i = r[2] - 1 || 0 + const s = (r[7] || '0').substring(0, 3) + return n ? new Date(Date.UTC(r[1], i, r[3] || 1, r[4] || 0, r[5] || 0, r[6] || 0, s)) : new Date(r[1], i, r[3] + || 1, r[4] || 0, r[5] || 0, r[6] || 0, s) + } + } + return new Date(e) + }(t)), this.$x = t.x || {}, this.init() + }, $.init = function () { + const t = this.$d + this.$y = t.getFullYear(), this.$M = t.getMonth(), this.$D = t.getDate(), this.$W = t.getDay(), this.$H = t.getHours(), + this.$m = t.getMinutes(), this.$s = t.getSeconds(), this.$ms = t.getMilliseconds() + }, $.$utils = function () { + return g + }, $.isValid = function () { + return !(this.$d.toString() === 'Invalid Date') + }, $.isSame = function (t, e) { + const n = v(t) + return this.startOf(e) <= n && n <= this.endOf(e) + }, $.isAfter = function (t, e) { + return v(t) < this.startOf(e) + }, $.isBefore = function (t, e) { + return this.endOf(e) < v(t) + }, $.$g = function (t, e, n) { + return g.u(t) ? this[e] : this.set(n, t) + }, $.unix = function () { + return Math.floor(this.valueOf() / 1e3) + }, $.valueOf = function () { + return this.$d.getTime() + }, $.startOf = function (t, a) { + const h = this + const c = !!g.u(a) || a + const d = g.p(t) + const $ = function (t, e) { + const n = g.w(h.$u ? Date.UTC(h.$y, e, t) : new Date(h.$y, e, t), h) + return c ? n : n.endOf(i) + } + const l = function (t, e) { + return g.w(h.toDate()[t].apply(h.toDate('s'), (c ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(e)), h) + } + const y = this.$W + const M = this.$M + const m = this.$D + const D = `set${this.$u ? 'UTC' : ''}` + switch (d) { + case o: + return c ? $(1, 0) : $(31, 11) + case u: + return c ? $(1, M) : $(0, M + 1) + case s: + var v = this.$locale().weekStart || 0 + var S = (y < v ? y + 7 : y) - v + return $(c ? m - S : m + (6 - S), M) + case i: + case f: + return l(`${D}Hours`, 0) + case r: + return l(`${D}Minutes`, 1) + case n: + return l(`${D}Seconds`, 2) + case e: + return l(`${D}Milliseconds`, 3) + default: + return this.clone() + } + }, $.endOf = function (t) { + return this.startOf(t, !1) + }, $.$set = function (s, a) { + let h; const c = g.p(s) + const d = `set${this.$u ? 'UTC' : ''}` + const $ = (h = {}, h[i] = `${d}Date`, h[f] = `${d}Date`, h[u] = `${d}Month`, h[o] = `${d}FullYear`, h[r] = `${d}Hours`, + h[n] = `${d}Minutes`, h[e] = `${d}Seconds`, h[t] = `${d}Milliseconds`, h)[c] + const l = c === i ? this.$D + (a - this.$W) : a + if (c === u || c === o) { + const y = this.clone().set(f, 1) + y.$d[$](l), y.init(), this.$d = y.set(f, Math.min(this.$D, y.daysInMonth())).$d + } else $ && this.$d[$](l) + return this.init(), this + }, $.set = function (t, e) { + return this.clone().$set(t, e) + }, $.get = function (t) { + return this[g.p(t)]() + }, $.add = function (t, a) { + let f; const + h = this + t = Number(t) + const c = g.p(a) + const d = function (e) { + const n = v(h) + return g.w(n.date(n.date() + Math.round(e * t)), h) + } + if (c === u) return this.set(u, this.$M + t) + if (c === o) return this.set(o, this.$y + t) + if (c === i) return d(1) + if (c === s) return d(7) + const $ = (f = {}, f[n] = 6e4, f[r] = 36e5, f[e] = 1e3, f)[c] || 1 + const l = this.$d.getTime() + t * $ + return g.w(l, this) + }, $.subtract = function (t, e) { + return this.add(-1 * t, e) + }, $.format = function (t) { + const e = this + if (!this.isValid()) return 'Invalid Date' + const n = t || 'YYYY-MM-DDTHH:mm:ssZ' + const r = g.z(this) + const i = this.$locale() + const s = this.$H + const u = this.$m + const a = this.$M + const o = i.weekdays + const f = i.months + const h = function (t, r, i, s) { + return t && (t[r] || t(e, n)) || i[r].substr(0, s) + } + const d = function (t) { + return g.s(s % 12 || 12, t, '0') + } + const $ = i.meridiem || function (t, e, n) { + const r = t < 12 ? 'AM' : 'PM' + return n ? r.toLowerCase() : r + } + const l = { + YY: String(this.$y).slice(-2), + YYYY: this.$y, + M: a + 1, + MM: g.s(a + 1, 2, '0'), + MMM: h(i.monthsShort, a, f, 3), + MMMM: h(f, a), + D: this.$D, + DD: g.s(this.$D, 2, '0'), + d: String(this.$W), + dd: h(i.weekdaysMin, this.$W, o, 2), + ddd: h(i.weekdaysShort, this.$W, o, 3), + dddd: o[this.$W], + H: String(s), + HH: g.s(s, 2, '0'), + h: d(1), + hh: d(2), + a: $(s, u, !0), + A: $(s, u, !1), + m: String(u), + mm: g.s(u, 2, '0'), + s: String(this.$s), + ss: g.s(this.$s, 2, '0'), + SSS: g.s(this.$ms, 3, '0'), + Z: r + } + return n.replace(c, (t, e) => e || l[t] || r.replace(':', '')) + }, $.utcOffset = function () { + return 15 * -Math.round(this.$d.getTimezoneOffset() / 15) + }, $.diff = function (t, f, h) { + let c; const d = g.p(f) + const $ = v(t) + const l = 6e4 * ($.utcOffset() - this.utcOffset()) + const y = this - $ + let M = g.m(this, $) + return M = (c = {}, c[o] = M / 12, c[u] = M, c[a] = M / 3, c[s] = (y - l) / 6048e5, c[i] = (y - l) / 864e5, c[r] = y / 36e5, c[n] = y / 6e4, c[e] = y / 1e3, c)[d] || y, h ? M : g.a(M) + }, $.daysInMonth = function () { + return this.endOf(u).$D + }, $.$locale = function () { + return M[this.$L] + }, $.locale = function (t, e) { + if (!t) return this.$L + const n = this.clone() + const r = D(t, e, !0) + return r && (n.$L = r), n + }, $.clone = function () { + return g.w(this.$d, this) + }, $.toDate = function () { + return new Date(this.valueOf()) + }, $.toJSON = function () { + return this.isValid() ? this.toISOString() : null + }, $.toISOString = function () { + return this.$d.toISOString() + }, $.toString = function () { + return this.$d.toUTCString() + }, d + }()) + const p = S.prototype + return v.prototype = p, [ + ['$ms', t], + ['$s', e], + ['$m', n], + ['$H', r], + ['$W', i], + ['$M', u], + ['$y', o], + ['$D', f] + ].forEach((t) => { + p[t[1]] = function (e) { + return this.$g(e, t[0], t[1]) + } + }), v.extend = function (t, e) { + return t.$i || (t(e, S, v), t.$i = !0), v + }, v.locale = D, v.isDayjs = m, v.unix = function (t) { + return v(1e3 * t) + }, v.en = M[y], v.Ls = M, v.p = {}, v +})) diff --git a/uni_modules/uview-ui/libs/util/emitter.js b/uni_modules/uview-ui/libs/util/emitter.js new file mode 100644 index 0000000..1e64044 --- /dev/null +++ b/uni_modules/uview-ui/libs/util/emitter.js @@ -0,0 +1,51 @@ +/** + * 递归使用 call 方式this指向 + * @param componentName // 需要找的组件的名称 + * @param eventName // 事件名称 + * @param params // 需要传递的参数 + */ +function broadcast(componentName, eventName, params) { + // 循环子节点找到名称一样的子节点 否则 递归 当前子节点 + this.$children.map((child) => { + if (componentName === child.$options.name) { + child.$emit.apply(child, [eventName].concat(params)) + } else { + broadcast.apply(child, [componentName, eventName].concat(params)) + } + }) +} +export default { + methods: { + /** + * 派发 (向上查找) (一个) + * @param componentName // 需要找的组件的名称 + * @param eventName // 事件名称 + * @param params // 需要传递的参数 + */ + dispatch(componentName, eventName, params) { + let parent = this.$parent || this.$root// $parent 找到最近的父节点 $root 根节点 + let { name } = parent.$options // 获取当前组件实例的name + // 如果当前有节点 && 当前没名称 且 当前名称等于需要传进来的名称的时候就去查找当前的节点 + // 循环出当前名称的一样的组件实例 + while (parent && (!name || name !== componentName)) { + parent = parent.$parent + if (parent) { + name = parent.$options.name + } + } + // 有节点表示当前找到了name一样的实例 + if (parent) { + parent.$emit.apply(parent, [eventName].concat(params)) + } + }, + /** + * 广播 (向下查找) (广播多个) + * @param componentName // 需要找的组件的名称 + * @param eventName // 事件名称 + * @param params // 需要传递的参数 + */ + broadcast(componentName, eventName, params) { + broadcast.call(this, componentName, eventName, params) + } + } +} diff --git a/uni_modules/uview-ui/libs/util/route.js b/uni_modules/uview-ui/libs/util/route.js new file mode 100644 index 0000000..112f853 --- /dev/null +++ b/uni_modules/uview-ui/libs/util/route.js @@ -0,0 +1,124 @@ +/** + * 路由跳转方法,该方法相对于直接使用uni.xxx的好处是使用更加简单快捷 + * 并且带有路由拦截功能 + */ + +class Router { + constructor() { + // 原始属性定义 + this.config = { + type: 'navigateTo', + url: '', + delta: 1, // navigateBack页面后退时,回退的层数 + params: {}, // 传递的参数 + animationType: 'pop-in', // 窗口动画,只在APP有效 + animationDuration: 300, // 窗口动画持续时间,单位毫秒,只在APP有效 + intercept: false // 是否需要拦截 + } + // 因为route方法是需要对外赋值给另外的对象使用,同时route内部有使用this,会导致route失去上下文 + // 这里在构造函数中进行this绑定 + this.route = this.route.bind(this) + } + + // 判断url前面是否有"/",如果没有则加上,否则无法跳转 + addRootPath(url) { + return url[0] === '/' ? url : `/${url}` + } + + // 整合路由参数 + mixinParam(url, params) { + url = url && this.addRootPath(url) + + // 使用正则匹配,主要依据是判断是否有"/","?","="等,如“/page/index/index?name=mary" + // 如果有url中有get参数,转换后无需带上"?" + let query = '' + if (/.*\/.*\?.*=.*/.test(url)) { + // object对象转为get类型的参数 + query = uni.$u.queryParams(params, false) + // 因为已有get参数,所以后面拼接的参数需要带上"&"隔开 + return url += `&${query}` + } + // 直接拼接参数,因为此处url中没有后面的query参数,也就没有"?/&"之类的符号 + query = uni.$u.queryParams(params) + return url += query + } + + // 对外的方法名称 + async route(options = {}, params = {}) { + // 合并用户的配置和内部的默认配置 + let mergeConfig = {} + + if (typeof options === 'string') { + // 如果options为字符串,则为route(url, params)的形式 + mergeConfig.url = this.mixinParam(options, params) + mergeConfig.type = 'navigateTo' + } else { + mergeConfig = uni.$u.deepClone(options, this.config) + // 否则正常使用mergeConfig中的url和params进行拼接 + mergeConfig.url = this.mixinParam(options.url, options.params) + } + + // 如果本次跳转的路径和本页面路径一致,不执行跳转,防止用户快速点击跳转按钮,造成多次跳转同一个页面的问题 + if (mergeConfig.url === uni.$u.page()) return + + if (params.intercept) { + this.config.intercept = params.intercept + } + // params参数也带给拦截器 + mergeConfig.params = params + // 合并内外部参数 + mergeConfig = uni.$u.deepMerge(this.config, mergeConfig) + // 判断用户是否定义了拦截器 + if (typeof uni.$u.routeIntercept === 'function') { + // 定一个promise,根据用户执行resolve(true)或者resolve(false)来决定是否进行路由跳转 + const isNext = await new Promise((resolve, reject) => { + uni.$u.routeIntercept(mergeConfig, resolve) + }) + // 如果isNext为true,则执行路由跳转 + isNext && this.openPage(mergeConfig) + } else { + this.openPage(mergeConfig) + } + } + + // 执行路由跳转 + openPage(config) { + // 解构参数 + const { + url, + type, + delta, + animationType, + animationDuration + } = config + if (config.type == 'navigateTo' || config.type == 'to') { + uni.navigateTo({ + url, + animationType, + animationDuration + }) + } + if (config.type == 'redirectTo' || config.type == 'redirect') { + uni.redirectTo({ + url + }) + } + if (config.type == 'switchTab' || config.type == 'tab') { + uni.switchTab({ + url + }) + } + if (config.type == 'reLaunch' || config.type == 'launch') { + uni.reLaunch({ + url + }) + } + if (config.type == 'navigateBack' || config.type == 'back') { + uni.navigateBack({ + delta + }) + } + } +} + +export default (new Router()).route diff --git a/uni_modules/uview-ui/package.json b/uni_modules/uview-ui/package.json new file mode 100644 index 0000000..fee4372 --- /dev/null +++ b/uni_modules/uview-ui/package.json @@ -0,0 +1,87 @@ +{ + "id": "uview-ui", + "name": "uview-ui", + "displayName": "uView2.0重磅发布,利剑出鞘,一统江湖", + "version": "2.0.33", + "description": "uView UI已完美兼容nvue,全面的组件和便捷的工具会让您信手拈来,如鱼得水", + "keywords": [ + "uview", + "uview", + "ui", + "ui", + "uni-app", + "uni-app", + "ui" + ], + "repository": "https://github.com/umicro/uView2.0", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "1416956117" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/uview-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "Vue": { + "vue2": "y", + "vue3": "n" + }, + "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" + } + } + } + } +} diff --git a/uni_modules/uview-ui/theme.scss b/uni_modules/uview-ui/theme.scss new file mode 100644 index 0000000..331b30f --- /dev/null +++ b/uni_modules/uview-ui/theme.scss @@ -0,0 +1,44 @@ +// 此文件为uView的主题变量,这些变量目前只能通过uni.scss引入才有效,另外由于 +// uni.scss中引入的样式会同时混入到全局样式文件和单独每一个页面的样式中,造成微信程序包太大, +// 故uni.scss只建议放scss变量名相关样式,其他的样式可以通过main.js或者App.vue引入 + +$u-main-color: #303133; +$u-content-color: #606266; +$u-tips-color: #909193; +$u-light-color: #c0c4cc; +$u-border-color: #dadbde; +$u-bg-color: #f3f4f6; +$u-disabled-color: #c8c9cc; + +$u-primary: #3c9cff; +$u-primary-dark: #398ade; +$u-primary-disabled: #9acafc; +$u-primary-light: #ecf5ff; + +$u-warning: #f9ae3d; +$u-warning-dark: #f1a532; +$u-warning-disabled: #f9d39b; +$u-warning-light: #fdf6ec; + +$u-success: #5ac725; +$u-success-dark: #53c21d; +$u-success-disabled: #a9e08f; +$u-success-light: #f5fff0; + +$u-error: #f56c6c; +$u-error-dark: #e45656; +$u-error-disabled: #f7b2b2; +$u-error-light: #fef0f0; + +$u-info: #909399; +$u-info-dark: #767a82; +$u-info-disabled: #c4c6c9; +$u-info-light: #f4f4f5; + +// scss混入,为了少写几行#ifndef +@mixin flex($direction: row) { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: $direction; +}