diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..78df13555fa2898860ecf3ca7fd7762a11bace49 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +unpackage/ +.hbuilderx +node_modules +.DS_Store +uni_modules_tools/copy +package-lock.json \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000000000000000000000000000000000000..d680f3a8fab20d1ee32074de82995f24141fb7e8 --- /dev/null +++ b/.npmignore @@ -0,0 +1,8 @@ +unpackage/ +.hbuilderx +node_modules +.DS_Store +uni_modules_tools/copy +package-lock.json +uni_modules_tools +uni_modules.config.json \ No newline at end of file diff --git a/App.vue b/App.vue new file mode 100644 index 0000000000000000000000000000000000000000..f112766b2fe467674ada5b877819c78ec56423c7 --- /dev/null +++ b/App.vue @@ -0,0 +1,57 @@ + + + diff --git a/LICENSE b/LICENSE index 4470915dccf274f474dd8e4f3894fe0dcb99ad2e..261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,201 @@ -MIT License - -Copyright (c) 2021 崔红保 - -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. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.en.md b/README.en.md deleted file mode 100644 index d2b51efd1f79ee8b1b03427f533ffa29c37d708b..0000000000000000000000000000000000000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# base-app - -#### Description -{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index 75b7769160ef6de01451814f253085adf8970668..0dfbde60824c22bd06481d487ed9f94e9c7b9978 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,567 @@ -# base-app +#### 视频介绍 + + 腾讯课堂uniCloud视频教程 + -#### 介绍 -{**以下是 Gitee 平台说明,您可以替换此简介** -Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} +注:视频会有滞后问题,入门为主。最新的完整功能请看以下文档 -#### 软件架构 -软件架构说明 +#### 简介 +uni-starter是一个集成了大量商用项目常见功能的,云端一体应用快速开发基本项目模版。 +APP有很多通用的功能,比如登录注册、头像、设置、banner、... uni-starter将这些功能都已经集成好。 -#### 安装教程 +直接在`hbuilderx`新建项目选择`uni-starter`模板,即可在此基础上快速开发自己的特色业务。 -1. xxxx -2. xxxx -3. xxxx +有了`uni-starter`,再加上`schema2code`生成前端页面,一个简单应用就可以快速完成。 -#### 使用说明 +如果说[uniCloud admin](https://uniapp.dcloud.io/uniCloud/admin)是管理端项目的基本项目模版,那么uni-starter则是用户端、尤其是移动端的基础项目模板。 -1. xxxx -2. xxxx -3. xxxx +`uni-starter` + `uniCloud admin` 提供了用户端和管理端的基本项目模版,应用开发从未如此简单快捷! -#### 参与贡献 +##### 扫码体验:h5版演示效果(链接:[https://uni-starter.dcloud.net.cn](https://uni-starter.dcloud.net.cn)) + -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +### uni-starter集成包括: +1. 用户管理: + - 登录注册(用户名密码登录、手机号验证码登录、APP一键登录、微信登录、Apple登录、微信小程序登录) + - 修改密码、忘记密码、头像更换(集成图片裁剪)、昵称修改、积分查看、退出登录 +2. 系统设置: + - App更新(整包升级、wgt升级、强制升级,后台搭配uniCloud admin的升级中心插件管理) + - 推送开关(app)、清除缓存(app) + - 指纹解锁(app)、人脸解锁(app) + - 多语言切换 + - 账号注销(正在完善中...) +3. 隐私权限:内置Android先弹出隐私协议对话框,然后再向用户申请设备权限 +4. 权限引导:当应用拒绝授权某些权限,但在后续使用中又需要这个权限;此时实现:引导用户可“一键跳转至系统设置”中开启。 + - 而不是报错让用户自己去找解决方案(更好的用户体验)。 + - 采用高内聚低耦合的设计结构,直接在应用启动时,应用拦截器中实现。免去在每个业务代码中处理这类问题,更优雅更方便。 + - 已实现项目:摄像头、相册、获取GPS定位、网络2/3/4/5G和Wi-Fi。你可以参考这些实现,处理更多该类场景的处理。uni-starter也会持续更新完善。 +5. 实用功能 + - 问题与反馈、关于、隐私政策、用户服务协议 + - banner(后台搭配uniCloud admin的banner插件管理) + - 新闻的搜索、列表、详情、分享。通过clientDB实现,开发者直接修改定义的表名等参数,即可轻松改为自己的业务 + - 可覆盖原生层的分享菜单 + - h5版在页面顶部(全局悬浮)引导用户点击下载App + - 营销裂变:点击“分销推荐”,生成带用户inviteCode参数的应用下载页(H5),一键分享到微信或微信朋友圈等。被邀请人打开下载页面点击下载,设备剪贴板的内容会被自动设置为邀请者的inviteCode。被邀请人下载app之后通过任何方式登录(含:注册并登录),uni-starter框架会自动获取设备剪切板中的inviteCode提交到服务端绑定关联关系。 +6. 更好的性能:首页采用nvue,fast编译模式,加快App端启动速度 +7. 内置拦截器: + - 页面路由拦截,配置需强制登录的页面;打开时自动检测`token`若无效就自动跳转到登录页 + - 调用云函数(callFunction)拦截器,自动携带必要参数、自动处理响应体。详见9.自动完成1-2 +8. 自动完成: + - 分析uniCloud.callfunction和clientDB操作的响应体,判断code执行对应的操作如跳转到登录页,自动续期token + - 操作注册/登录操作自动获取客户端设备:push_clientid、imei、oaid、idfa新增/更新到数据表uni-id-device + - 异常恢复处理:断网恢复后自动重连“因网络错误导致的”网络请求 + - 为迎合苹果App Store的规则,登录与分享功能项显示之前自动检测是否安装了对应客户端。比如:设备未安装微信则不显示微信快捷登录和微信分享选项 +* 更多功能模块会不断更新,请持续关注本插件 -#### 特技 +## 快速体验部署流程 +#### 1. 开通uniCloud +- 开通`uniCloud`:本项目是云端一体的,它的云端代码需要部署在uniCloud云服务空间里,需要开通uniCloud。在[https://unicloud.dcloud.net.cn/](https://unicloud.dcloud.net.cn/)登录,按云厂商要求进行实名认证。 +- 在uniCloud认证通过后,创建一个服务空间给本项目使用。选择阿里云或腾讯云均可,两种服务空间差异[详情](https://uniapp.dcloud.net.cn/uniCloud/price) -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +#### 2. 运行云服务空间初始化向导 + + + + +## 功能模块介绍 +### 1.登录模块 +|登录类型 |描述 | +|-- |-- | +|smsCode |验证码登录 | +|univerify |读取手机SIM卡一键登录 | +|username |账号密码登录 | +|weixin |微信登录 | +|apple |苹果登录 | + +配置文件:`项目根目录/uni-starter.config.js` +```js +{ + "router":{ + "login":["username","smsCode"] + } +} +``` + +#### 启用登录方式 +如上示例配置为:`["username","smsCode"]` 表示启用:验证码登录、账号密码登录。 + +同理配置为:`["weixin","username","smsCode"]` 则表示启用:微信登录、验证码登录、账号密码登录。 + +总结:需要几项列举几项即可。 + +#### 优先级 +在uni-starter框架中执行`uni.navigateTo({url: "/pages/ucenter/login-page/index/index"})`,会根据配置跳转到相应的登录页面。如果配置内容为:`["username","smsCode"]`会自动切换到"配置的第0项,也就是`username`类型的登录方式对应的页面”,即`账户登录`方式页面,路径:`/pages/ucenter/login-page/pwd-login/pwd-login` + +#### 平台差异性配置 +这里支持用[条件编译](https://uniapp.dcloud.io/platform?id=%e6%9d%a1%e4%bb%b6%e7%bc%96%e8%af%91)因此你可以配置在不同平台下拥有的登录方式。 +如下配置,即表示仅在APP端启用“短信验证码登录” +```js +"login": [ + "username","univerify","weixin","apple" + // #ifdef APP-PLUS + "smsCode", + // #endif +] +``` + +#### 生效策略 +登录方式有如上5种,虽然你希望有几种登录方式就在配置中列举几种。但是有的登录方式可能因为设备环境问题而不被支持; +比如你正确地配置了微信登录,而用户的手机并没有安装微信,这样微信登录功能就无法使用。 +并且如果出现这种情况你的app会被iOS的App Store拒绝上架。 +所以在这里,我们的生效策略在检测:你是否有列举到某个配置项为前提的情况下,增加了检测当前环境是否支持,如果不支持会自动隐藏。 + +#### 在uni-app框架中配置: +在应用模块:`manifest.json` App模块配置 --> OAuth(登录鉴权)--> 勾选并配置你所需要的模块 ++ 一键登录: + [开通配置](https://dev.dcloud.net.cn/uniLogin) + [使用指南](https://uniapp.dcloud.io/univerify) ++ [苹果登录集成指南](https://ask.dcloud.net.cn/article/36651) ++ 短信登录: + 使用本功能需要在[DCloud开发者中心](https://dev.dcloud.net.cn/uniSms)开通并充值 + 教程参考[短信服务开通指南](https://ask.dcloud.net.cn/article/37534) + 修改短信注册/登录发生验证码的模板id,在文件`/uniCloud-aliyun/cloudfunctions/uni-id-cf/index.js` 搜索 `const templateId = '11753'` 替换为自己申请的模板id + +#### 服务端配置 +uni-starter服务端使用[uni-config-center](https://ext.dcloud.net.cn/plugin?id=4425)统一管理这些配置, +文件路径`/uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json` +详情下文[目录结构](#id=catalogue) 和[uni-id配置说明](https://uniapp.dcloud.io/uniCloud/uni-id?id=configjson%e7%9a%84%e8%af%b4%e6%98%8e) + +### 2.路由拦截 +#### 应用场景 +有些页面,限允许已经登录后用户才访问。 +常规的做法是打开这类页面之前,检查(前端校验)uni_id_token的值是否有效,如果无效会自动跳转到登录页面。 +而这样的页面有很多,入口也不少。面向过程的写法会产生大量的代码冗余,且不易维护。 +而uni-starter基于拦截器(`uni.addInterceptor`),提供了仅需简单配置即可实现的路由拦截功能。 +#### 配置方式 +支持两种模式(二选一) +##### 黑名单模式 +列举需要强制登录的页面完整路径(支持正则) +##### 白名单模式 +列举不需要强制登录即可访问的页面完整路径(支持正则) +#### 配置示例 +配置文件:`项目根目录/uni-starter.config.js` + +```js +"router": { + "needLogin" : [ + {pattern:/^\/pages\/list.*/}, //支持正则表达式 + "/uni_modules/uni-news-favorite/pages/uni-news-favorite/list", + "/uni_modules/uni-feedback/pages/uni-feedback/add" + ], +/* + 请注意上下,黑名单(needLogin)、白名单(visitor)两种配置模式二选一不可同时使用 +*/ + "visitor" : [ + "/",//注意入口页必须直接写 "/" + {"pattern":/^\/pages\/list.*/}, //支持正则表达式 + {"pattern":/^\/pages\/ucenter\/login-page.*/}, + "/pages/common/webview/webview", + "/pages/grid/grid", + "/pages/ucenter/ucenter", + "/pages/ucenter/guestbook/guestbook", + "/pages/ucenter/about/about", + "/pages/ucenter/settings/settings" + ] +} +``` + +##### 优势: +传统的路由管理方式是对uni-app框架路由写法的二次封装,自定义的写法不支持ide的代码提示,且不优雅。 +另外不同插件作者封装不同的路由管理方式,这样做出来的插件与用户的项目结合时,路由写法不统一的差异需要去磨平。 +为此`uni-starter`基于`uni.addInterceptor`(拦截器)实现路由管理。 + +##### 注意: +- uni-starter的路由拦截,仅在调用路由API(navigateTo、redirectTo、reLaunch、switchTab)时触发。应用的首页是由系统自动打开,不会触发拦截器。首页需要强制登录才能访问的场景,不由路由控制。但不用担心,如果未登录的用户,打开了需要登录才能访问页面,必定会触发需要携带有效token才能访问的API。此时则会返回相应的响应体,uni-starter监测到token无效这类的响应体也会自动跳转到登录页(这种效果需要前后端都开发完成才体验到)。 +- uni-starter框架不能将登录页`/pages/ucenter/login-page/index/index`设置为首页,否则由拦截器实现的路由管理将生效。 +- 拦截器实现的路由控制,是在路由跳转未完成之前触发。路由切换方式(navigateTo、redirectTo、reLaunch、switchTab)URL参数必须使用绝对路径路 + +### 3.h5版在页面顶部引导用户`点击下载App` +把h5端用户引流到APP端,是一个非常实用的功能。相对于h5,APP端有更高的用户留存和更好的产品体验。 +uni-starter集成了这个功能,你只需直接在`项目根目录/uni-starter.config.js`的"h5"->"openApp"中配置相关内容,即可开启全局悬浮的下载引导。 +这也是一个演示开发者如何在h5端做全局悬浮块的例子。你也可以在`/common/openApp.js`中修改他的样式等代码等,注意他只支持原生js语法。 + +### 4.分享模块 +一个可覆盖原生层分享模块 +- 应用配置:`manifest.json` App模块配置 --> Share --> 勾选并配置你所需要的模块 +- 分享功能配置参数,随着应用的业务场景决定,在各场景调用的时候配置。参考uni-starter的`/pages/list/detail.vue`的`methods -> shareClick` +- 更多`uni-share`的介绍 [详情](https://ext.dcloud.net.cn/plugin?id=4860) + +### 5.升级中心相关 +为了解决开发者维护多个 App 升级繁琐,重复逻辑过多,管理不便的问题,升级中心`uni-upgrade-center`应运而生。 +提供了简单、易用、统一的 App 管理、App 版本管理、安装包发布管理,升级检测更新管理。 +- 升级中心分为两个部分:`uni-upgrade-center` Admin管理后台和`uni-upgrade-center - Admin`前台检测更新。 +- `uni-upgrade-center`的介绍 [详情](https://ext.dcloud.net.cn/plugin?id=4542) +- `uni-upgrade-center - Admin`的介绍 [详情](https://ext.dcloud.net.cn/plugin?id=4470) + +### 6.意见反馈 +- 客户端[详情](https://ext.dcloud.net.cn/plugin?id=50) +- admin端[详情](https://ext.dcloud.net.cn/plugin?id=4992) + +### 7.指纹识别模块 +- `manifest.json` App模块配置 --> `Fingerprint`指纹识别 + +### 8.消息推送模块 +- `manifest.json` App模块配置 --> `push`消息推送 + +### 9.隐私政策弹框 +根据工业和信息化部关于开展APP侵害用户权益专项整治要求,App提交到应用市场必须满足以下条件: +- 应用启动运行时需弹出隐私政策协议,说明应用采集用户数据 +- 应用不能强制要求用户授予权限,即不能“不给权限不让用” ++ 如不希望应用启动时申请“读写手机存储”和“访问设备信息”权限,请参考:https://ask.dcloud.net.cn/article/36549 + +配置弹出“隐私协议和政策”打开项目的manifest.json文件,切换到“源码视图”项 +在`manifest.json` -> `app-plus` -> `privacy` 节点下添加 prompt节点 +```js +"privacy" : { + "prompt" : "template", + "template" : { + "title" : "服务协议和隐私政策", + "message" : "  请你务必审慎阅读、充分理解“服务协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。
  你可阅读《服务协议》《隐私政策》了解详细信息。如果你同意,请点击下面按钮开始接受我们的服务。", + "buttonAccept" : "同意", + "buttonRefuse" : "暂不同意" + } +} +``` +- prompt + 字符串类型,必填,隐私政策提示框配置策略,可取值template、custom、none,默认值为none + + template + 使用原生提示框模板,可自定义标题、内容已经按钮上的文本 + + custom + 自定义隐私政策提示框,uni-app项目中推荐使用nvue页面进行自定义,5+ APP使用html页面进行自定义 + + none + 不弹出隐私政策提示框 +- template + json格式,可选,模板提示框上显示的内容 + + title + 模板提示框上的标题,默认为“服务协议和隐私政策” + + message + 模板提示框上的内容,richtext类型字符串,支持a/font/br等节点,点击a链接会调用内置页面打开其href属性中链接地址。 + **注意:务必配置此提示内容,或参考上面示例内容并修改《服务协议》和《隐私政策》链接地址** + + buttonAccept + 模板提示框上接受按钮的文本,默认值为“我知道了” + + buttonRefuse + 模板提示框上拒绝按钮的文本,默认不显示此按钮 + + second + HBuilderX3.1.12+版本新增支持隐私提示框二次确认提示,用于配置二次确认提示框显示内容,message属性值不为空时弹出二次确认提示框 + + title 二次确认提示框上的标题 + + message 二次确认提示框上的内容,支持richtext类型字符串 + + buttonAccept 二次确认提示框上接受按钮的文本 + + buttonRefuse 二次确认提示框上拒绝按钮的文本 + +> 更多Android平台隐私与政策提示框配置方法,[详情](https://ask.dcloud.net.cn/article/36937) + +##### 注意: +1. 最新的华为应用市场要求,隐私政策提示框上接受按钮的文本,必须为“同意”而不能是其他有歧义的文字。 +2. 配置后提交云端打包后生效。理论上绝大部分和`manifest.json`生效相关的配置均需要提交云打包后生效 + +### 10.拦截器改造后的uniCloud +1. Debug,调试期间开启Debug。接口一旦fail就会弹出真实错误信息。否则将弹出,系统错误请稍后再试! +```js + if(Debug){ + console.log(e); + uni.showModal({ + content: JSON.stringify(e), + showCancel: false + }); + } +``` +2. 断网自动重试,当callFunction为fail时检测是否因断网引起。如果是会提醒用户并且会在恢复网络之后自动重新发起请求 +3. 常规的errCoder自动执行对应程序,如token无效/过期自动跳转到登录页面。 +4. token自动续期。 + +### 11.举例路由控制原理,深入了解拦截器的使用 +比如你希望在打开用户中心等页面之前,都检查一下该用户是否登录,否则就重定向到登录页面。使用拦截器你可以用以下写法在应用入口定义全局生效: + +```js + //定义各个页面,这里为了演示uni-starter框架是把该定义写在全局配置uni-starter.config.js中 + let needLogin = ["/pages/ucenter/userinfo/userinfo", ... ] + + uni.addInterceptor("navigateTo", { + invoke(e) { // 调用前拦截 + //获取用户的token + const token = uni.getStorageSync('uni_id_token') + //获取当前页面路径(即url去掉"?"和"?"后的参数) + const url = e.url.split('?')[0] + //判断要打开的页面是否需要验证登录 + if (needLogin.includes(url) && token == '') { + uni.showToast({ + title: '该页面需要登录才能访问,请先登录', + icon: 'none' + }) + uni.navigateTo({ + url: "/pages/ucenter/login-page/index/index" + }) + return false + } + }, + fail(err) { // 失败回调拦截 + console.log(err); + }, + }) +``` +- 而路由跳转方法不仅有`uni.navigateTo`还有`uni.redirectTo`,`uni.reLaunch`,`uni.switchTab`; +- 另外我们还希望控制直接跳转至哪种登录类型 +所以在uni-starter框架中我们这样定义: +uni-starter/common/appInit.js 的第228-280行 +```js + const {"router": {needLogin,login} } = uniStarterConfig //需要登录的页面 + let list = ["navigateTo", "redirectTo", "reLaunch", "switchTab"]; + list.forEach(item => { //用遍历的方式分别为,uni.navigateTo,uni.redirectTo,uni.reLaunch,uni.switchTab这4个路由方法添加拦截器 + uni.addInterceptor(item, { + invoke(e) { // 调用前拦截 + //获取用户的token + const token = uni.getStorageSync('uni_id_token') + //获取当前页面路径(即url去掉"?"和"?"后的参数) + const url = e.url.split('?')[0] + //判断要打开的页面是否需要验证登录 + if (needLogin.includes(url) && token == '') { + uni.showToast({ + title: '该页面需要登录才能访问,请先登录', + icon: 'none' + }) + uni.navigateTo({ + url: "/pages/ucenter/login-page/index/index" + }) + return false + } + //控制登录优先级 + if (url == '/pages/ucenter/login-page/index/index') { + //一键登录(univerify)、账号(username)、验证码登录(短信smsCode) + if (login[0] == 'univerify') { + if(e.url == url) { e.url += '?' } //添加参数之前判断是否带了`?`号如果没有就补上,因为当开发场景本身有参数的情况下是已经带了`?`号 + e.url += "univerify_first=true" + } else if (login[0] == 'username') { + e.url = "/pages/ucenter/login-page/pwd-login/pwd-login" + } + } + return true + }, + fail(err) { // 失败回调拦截 + console.log(err); + }, + }) + }) +``` + +### 12.关于升级 +- 项目升级 + + uni-starter遵循uni-app的插件模块化规范,即:[uni_modules](https://uniapp.dcloud.io/uni_modules) 。他是个项目类型的插件。在项目的根目录下有一个符合uni_modules规范的package.json文件,在这个文件右键-从插件市场更新即可更新该插件。 + +- 插件升级 + + 非项目类型的uni_modules插件,是项目根目录下的uni_modules目录下。以插件ID为插件文件夹命名,在该目录右键也会看到“从插件市场更新”选项,点击即可更新该插件。 + +### 13.多语言国际化 + uni-starter支持多语言国际化。默认开启,可以在`uni-starter.config.js`->`i18n`->`enable`中配置 + 如果你启用了多语言国际化需要先阅读:[uni-app多语言国际化](https://uniapp.dcloud.io/collocation/i18n?id=%e6%a1%86%e6%9e%b6%e5%86%85%e7%bd%ae%e7%bb%84%e4%bb%b6%e5%92%8capi%e5%9b%bd%e9%99%85%e5%8c%96) + +## 应用启动时序介绍 +文件路径: App.vue +```js + import initApp from '@/common/appInit.js'; + export default { + onLaunch: function() { + initApp(); + } + } +``` +onLaunch生命周期执行了 +1. 全局监听clientDB的err事件, + - 判断是否为token过期失效等需要重新登录的问题。自动跳转到登录页面 + - 检测本地的token是否有效(存在且并未过期)否则跳转到登录页面 +2. 预登录一键登录功能 +3. 执行了initApp()包含以下操作 + 1. 读取uni-starter.config并挂载到globalData的config下 + 2. 读取应用版本号,并存到globalData下 + 3. 检查是否有可更新的应用版本,决定是否启动在线更新版本 + 4. 监听设备的网络变化并以uni.showToast APi的方式提醒用户 + 5. 使用[拦截器](https://uniapp.dcloud.io/api/interceptor?id=addinterceptor) 实现 + - 页面路由拦截,配置需强制登录的页面;打开时检测,如果token无效就自动跳转到登录页 + - 优雅实现:自动引导打开`选择图片`所需要的权限。当调用`uni.chooseImage`时检测到无权限自动开启引导。并不是在每次调用接口时处理这类问题,你可以参考该例子做更多该类场景的处理。uni-starter也会持续完善 + + +## 配置文件 +uni-starter提供了`uni-starter.config.js`,可配置选择登录注册方式及优先级等,可指定该应用是否强制登录才能进入某个页面。配置项内容如下: +```js +module.exports = { + "h5": { + "url": "https://static-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e.bspapp.com", // 前端网页托管的域名 + // 在h5端全局悬浮引导用户下载app的功能 更多自定义要求在/common/openApp.js中修改 + "openApp": { + //点击悬浮下载栏后打开的网页链接 + "openUrl": 'https://sj.qq.com/myapp/detail.htm?apkName=com.tencent.android.qqdownloader&info=6646FD239A6EBA9E2DEE5DFC7E18D867', + //左侧显示的应用名称 + "appname": 'uni-starter', + //应用的图标 + "logo": './static/logo.png', + } + }, + "mp": { + "weixin": { + //微信小程序原始id,微信小程序分享时 + "id": "gh_132465798" + } + }, + "router": { + //配置需要路由拦截的页面地址,在打开这些页面之前会自动检查(无需联网)uni_id_token的值,如果token无效就自动跳转到登录页 + "needLogin": [ + "/pages/ucenter/userinfo/userinfo", + "/uni_modules/uni-news-favorite/pages/uni-news-favorite/list", + ], + "login": ["smsCode","univerify", "username", "weixin", "apple"], + /* + 这里会根据数组的第0项,决定登录方式的第一优先级是哪种登录方式。 + 所有你希望拥有的登录方式这里都需要一一列举,未列举到的或设备环境不支持的登录方式将被隐藏。 + 如果你需要在不同平台有不同的配置,直接用条件编译即可。 + */ + }, + //关于应用 + "about": { + //应用名称 + "appName": "uni-starter", + //应用logo + "logo": "/static/logo.png", + //公司名称 + "company": "数字天堂(北京)网络技术有限公司", + //口号 + "slogan": "为开发而生", + //政策协议 + "agreements": [{ + "title": "用户服务协议", //协议名称 + "url": "https://ask.dcloud.net.cn/protocol.html" //对应的网络链接 + }, + { + "title": "隐私政策", + "url": "https://ask.dcloud.net.cn/protocol.html" + } + ], + //应用的链接,用于分享到第三方平台和生成关于我们页的二维码 + "download": "https://m3w.cn/uniapp" + }, + //用于打开应用市场评分界面 + "marketId":{ + "ios":"id1417078253", + "android":"123456" + }, + //配置多语言国际化。i18n为英文单词 internationalization的首末字符i和n,18为中间的字符数 是“国际化”的简称 + "i18n":{ + "enable":false //默认启用,国际化。如果你不想使用国际化相关功能,请改为false + } +} +``` + +## 目录结构@catalogue +
+uni-starter
+├─uniCloud-aliyun	
+│	├─cloudfunctions 云函数目录
+│	|	├─common 公共模块
+│	│	|	├─uni-config-center		uni-starter的服务端配置中心,项目所有云函数的配置在这里填写 详情
+│	│	|	|	├─index.js			config-center入口文件
+│	│	|	|	└─uni-id			uni-id模块配置目录
+│	│	|	|		├─config.json	uni-id对应的配置数据:微信登录、一键登录、短信验证码登录等key都在这里填写详情
+│	│	|	|		└─file.cert		uni-id依赖的配置文件,假如你使用微信发红包功能,需要的证书文件就是放到这里
+│	|	|	└───uni-id				uni-id用户体系 详情
+│	|	├─uni-analyse-searchhot		云端一体搜索模板依赖的云函数 详情
+│	|	└─uni-id-cf				用户中心云函数,实现用户注册、修改密码、发送验证码、快捷登录(微信、短信、账户、一键登录)
+│	└──database						云数据目录
+│		├─db_init.json				db_init.json初始化数据库文件,其中不再包含schema 详情
+│		├─opendb-app-versions.schema.json		应用版本,表结构文件
+│		├─opendb-banner.schema.json	        	横幅数据表,表结构文件
+│		├─opendb-feedback.schema.json	        意见反馈表,表结构文件
+│		├─opendb-news-articles.schema.json	    新闻文章表,表结构文件
+│		├─opendb-news-categories.schema.json	新闻分类表,表结构文件
+│		├─opendb-news-comments.schema.json		新闻评论表,表结构文件
+│		├─opendb-news-favorite.schema.json		新闻收藏表,表结构文件
+│		├─opendb-search-hot.schema.json			热门搜索表,表结构文件
+│		├─opendb-search-log.schema.json			搜索记录表,表结构文件
+│		├─opendb-verify-codes.schema.json		验证码表,表结构文件
+│		├─uni-id-log.schema.json	        	登录日志表,表结构文件
+│		├─uni-id-scores.schema.json	        	用户积分表,表结构文件
+│		└─uni-id-users.schema.json	        	用户表,表结构文件
+├─pages										业务页面文件存放的目录
+│	├─common						
+│	│	└─webview							webview目录
+│	│		└─webview.vue					webview页面	用于实现跨端的web页面浏览
+│	├─grid
+│	│	└─grid.vue	 						带宫格和banner的示例页面
+│	├─list
+│	│	├─list.vue	 						新闻列表
+│	│	├─search
+│	│	│	└─search						云端一体搜索插件
+│	│	└─detail.vue						新闻详情
+│	├─ucenter
+│	│	├─about								关于我们
+│	│	│	└─about
+│	│	├─login-page						登录模块
+│	|	|	├─common						登录模块公共库
+│	│	│	│	├─login-page.css			公共样式库
+│	│	│	│	├─login-page.mixin.js		公共mixin
+│	│	│	│	└─loginSuccess.js			公共登录成功后操作
+│	|	|	├─index							短信验证码登录,手机号码输入页面
+│	|	|	├─phone-code					短信验证码登录,验证码输入页面
+│	|	|	├─pwd-login						账户密码登录
+│	|	|	├─pwd-retrieve					密码重置
+│	│	│	└─register						注册账户模块
+│	│	│		├─validator.js
+│	│	│		└─register.vue
+│	│	├─read-news-log						新闻阅读记录
+│	│	│	└─read-news-log
+│	│	├─settings						
+│	│	│	├─dc-push
+│	│	│	│	└─push.js					push权限操作SDK
+│	│	│	└─settings.vue					app设置
+│	│	├─userinfo							用户个人信息
+│	│	│	├─bind-mobile
+│	│	│	│	└─bind-mobile.vue			绑定手机号码
+│	│	│	├─limeClipper					图片裁剪插件,来源[limeClipper](https://ext.dcloud.net.cn/plugin?id=3594) @作者: 陌上华年
+│	│	│	│	├─images
+│	│	│	│	│	├─photo.svg
+│	│	│	│	│	└─rotate.svg
+│	│	│	│	├─index.css
+│	│	│	│	├─limeClipper.vue
+│	│	│	│	├─README.md
+│	│	│	│	└─utils.js
+│	│	│	├─main.js
+│	│	│	├─cropImage.vue	引用limeClipper的图片裁剪模块,为了方便二开可能会出现兼容`vue`与`nvue`,所以做成了`页面`而不是`组件`
+│	│	│	└─userinfo.vue
+│	|	└─ucenter.vue						用户中心
+│	|
+├─static	 						存放应用引用的本地静态资源(如图片、视频等)的目录,注意:静态资源只能存放于此
+├─uni_modules						存放[uni_module](/uni_modules)规范的插件。
+├─uni_modules_tools					uni_modules插件上传辅助脚本 详情。
+├─main.js							Vue初始化入口文件
+├─App.vue							应用配置,用来配置App全局样式以及监听 应用生命周期
+├─uni-starter.config				uni-starter的前端的配置文件,项目所有模块的配置在这里填写。详见该文件的代码注释。
+├─manifest.json	 					配置应用名称、appid、logo、版本等打包信息,详见
+└─pages.json						配置页面路由、导航条、选项卡等页面类信息,详见
+
+完整的uni-app目录结构[详情](https://uniapp.dcloud.io/frame?id=%e7%9b%ae%e5%bd%95%e7%bb%93%e6%9e%84) + +## 常见API示范 +1. 判断当前用户是否拥有某角色`uniIDHasRole` 演示页面:`/pages/grid/grid` [API文档详情:](https://uniapp.dcloud.io/api/global?id=uniidhasrole) +2. 指纹解锁、人脸解锁 演示页面:`/pages/ucenter/settings/settings` [API文档详情:](https://uniapp.dcloud.io/api/system/authentication) + +## 注意事项 +1. 真机运行需要制作自定义基座,制作后选择运行到自定义基座 +2. 苹果登录的图标,需要满足苹果应用市场的审核规范请勿随便修改;如需修改请先阅读:[Sign in with Apple Button](https://appleid.apple.com/signinwithapple/button) +3. 应用登录功能,默认不勾选同意隐私权限是响应安卓应用市场的规范;请勿修改该逻辑。 + +## FAQ:常见问题 +1. 提示“公共模块uni-id缺少配置信息”解决方案:在cloudfunctions右键‘上传所有云函数、公共模块及actions’之后,需要在cloudfunctions -> common -> uni-config-center 目录单独上传一次,右键‘上传公共模块’。 +2. 本项目代码可以商用,无需为DCloud付费。但不能把本项目的代码改造用于非uni-app和uniCloud的技术体系。即,不能将后台改成php、java等其他后台,这将违反使用许可协议。 + +## 相关案例 +[ + ![](https://vkceyugu.cdn.bspapp.com/VKCEYUGU-f184e7c3-1912-41b2-b81f-435d1b37c7b4/dd4c366f-6165-46c0-8500-5a679d7e5463.jpg) +](https://ext.dcloud.net.cn/search?q=uni-starter) +(点击跳转到案例列表) + + +## 第三方插件(感谢插件作者,排名不分前后): +1. 图片裁剪 [limeClipper](https://ext.dcloud.net.cn/plugin?id=3594) @作者: 陌上华年 +2. 二维码生成 [Sansnn-uQRCode](https://ext.dcloud.net.cn/plugin?id=1287) @作者: 3snn +3. clipboard.js [clipboard](https://clipboardjs.com/) \ No newline at end of file diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..e87aa746bf63f83e270bdeac65ff5b99a43bac0e --- /dev/null +++ b/changelog.md @@ -0,0 +1,174 @@ +## 1.1.22(2021-11-10) +删除`common/openApp.js`中可选链操作符,解决vue3版本在hbuilderX内置浏览器不兼容的问题 +## 1.1.21(2021-11-10) +新增app端列表页面使用原生list下拉刷新 +## 1.1.20(2021-11-08) +修复vue3版某些情况下i18n报错的问题 +## 1.1.19(2021-11-08) +配置文件`uni-starter.config.js`默认关闭i18n多语言国际化 +## 1.1.18(2021-10-14) +使用2.0版`uni-share`。当显示分享窗口时,监听返回操作(如:物理返回,全面屏手机侧滑)关闭分享窗口,而不是关闭当前页面。 +## 1.1.17(2021-10-12) +- 更新文档 +- 修复list页面where条件中缺少&符,导致的错误 +## 1.1.16(2021-10-05) +在控制台提示:开启多语言国际化,将获取i18n中配置的tabbar的值覆盖pages.json中配置的tabbar的值 +## 1.1.15(2021-10-02) +新增,支持配置是否开启i18n多语言国际化。 +配置文件:`uni-starter.config.js` +` +"i18n":{ + "enable":true //默认启用,国际化。如果你不想使用国际化相关功能,请改为false +} +` +## 1.1.14(2021-09-30) +1. 通过微信小程序登录自动保存`sessionKey`到`uni-id-users`表 +2. 我的-设置-个人资料 点击绑定手机号码,完善账号信息支持以下三种策略: + - APP端,(如果支持)使用"通过运营商一键获取手机号码" + - 微信小程序端,支持"一键获取微信绑定的手机号" + - 其他端,通过手机验证码 +## 1.1.13(2021-09-29) +修复search页面因多语言国际化导致的白屏问题 +## 1.1.12(2021-09-28) +1. 改造微信登录逻辑,直接使用`uni.login`参数`"onlyAuthorize":true`实现 +2. 修复,一键登录弹出层,已勾选“同意隐私政策协议”点击自定义登录按钮,报“你未同意隐私政策协议”的bug +## 1.1.11(2021-09-24) +优化邀请下载app页(`pages/ucenter/invite`)下载按钮闪烁的问题 +## 1.1.10(2021-09-23) +修复获取验证码按钮的文字,在中文模式下显示为英文的问题 +## 1.1.9(2021-09-16) +修复由多语言切换功能引起的隐私政策协议标题链接被重写的问题 +## 1.1.8(2021-09-15) +更新数据表guestbook的schema中更新权限的配置 +## 1.1.7(2021-09-14) +更新数据表opendb-news-articles的schema中的权限配置 +## 1.1.6(2021-09-13) +纠正错误schema权限表达式`doc.uid`为`doc.user_id` +## 1.1.5(2021-09-01) +为了更直观理解路由拦截。移除路由拦截器中,默认过滤登录相关页面拦截的逻辑。确保所有白名单页面均在配置文件router.visitor中体现 +## 1.1.4(2021-08-31) +修改错误的文章表`SChema`的读权限表达式 +## 1.1.3(2021-08-31) +修复在微信小程序端默认语言为英文的问题 +## 1.1.2(2021-08-30) +修复在微信小程序下切换语言报`locale`不存在的问题 +## 1.1.1(2021-08-30) +- 解决3.2.6以下版本hbuilderx,编译的项目报`uni.setLocale`不存在的问题 +## 1.1.0(2021-08-27) +- APP端支持vue3 (hbuilderx 3.2.5+) +- 支持国际化 中英文切换 +- 新增留言板示例 +- 修复签到的时区问题 +## 1.0.48(2021-08-10) +- 修复登录成功后响应体包含`userInfo.password`的问题 +- 修改了`uni-id-users`表的schema中字段username的编辑权限,防止用户通过clientDB绕过用户名不能重复的规则更新用户名的问题 +## 1.0.47(2021-08-09) +- 更新文档快速体验部署流程 +- 修复一键登录优先时报变量找不到的问题 +## 1.0.46(2021-08-05) +清理多余文件 +## 1.0.45(2021-08-05) +默认首页为nvue页面+fast +## 1.0.44(2021-08-05) +解决首页为非nvue页面时白屏的问题。 +- 注意:本次在`common/appInit.js`中修改了路由拦截的逻辑,是个兼容方案;当首页为非nvue页面,路由拦截器逻辑会在加载首页时执行。接下来新版本的hx编译的uni-app项目无论首页是否为nvue都不走拦截器,保持各端逻辑一致。 +## 1.0.43(2021-08-02) +1. 微信小程序端,新增:微信登录成功后,弹出是否"获取微信头像和昵称,完善个人资料"的弹框 +2. APP端,新增逻辑:微信登录成功后,自动获取用户的微信昵称和头像完善用户个人资料 +- 提示:因为微信的头像一旦更换,微信返回的头像url会失效。所以,以上两示例功能将url(客户端:下载到临时目录/服务端:转为Buffer)再上传到uniCloud云存储中再使用。 +## 1.0.42(2021-07-29) +新增绑定手机号码页面前端校验 +## 1.0.41(2021-07-27) +1. 支持vue3.0 +2. 去掉App.vue全局样式,避免与非flex布局的页面样式冲突 +## 1.0.40(2021-07-22) +1. 调整使用正则表达式配置强制登录功能的写法,解决在小程序端的兼容问题。 +2. 新增签到功能(培养用户习惯,提升用户粘性)。支持:每日签到奖励、周期性连续7日签到,奖励翻倍。 +## 1.0.39(2021-07-19) +1. 强制登录配置,新增白名单模式 +2. 强制登录配置,支持正则表达式 +## 1.0.38(2021-07-17) +删除多余文件 +## 1.0.37(2021-07-14) +去掉配置文件:`uni-starter.config.js`,`h5` —> `url`结尾的`/` +## 1.0.36(2021-07-14) +剪切板中的邀请码,添加标识性前缀 `uniInvitationCode:` +## 1.0.35(2021-07-12) +1. H5端默认不开启,隐私权限协议签署页面。因为网页端没有什么隐私权限能被获取,目前全球仅欧盟有要求;如有需要请手动开启 +2. 在列表页演示,如何在onShow生命周期获取设备位置,并在设备或者应用没有权限时自动引导。设置完毕自动重新获取。[更多点此查看插件介绍](https://ext.dcloud.net.cn/plugin?name=json-gps) +## 1.0.34(2021-07-08) +修复,打开登录页时携带参数,导致的快捷登录方式重复的问题 +## 1.0.33(2021-07-06) +修复,点击短信验证码登录打开的页面不正确的问题 +## 1.0.32(2021-07-06) +修复,仅配置一种快捷登录时的错误 +## 1.0.31(2021-07-02) +优化项目文档 +## 1.0.30(2021-07-01) +1. 简化宫格页面写法,方便理解如何控制不同状态角色的用户是否可见哪些元素。 +2. uni-id-cf发送短信验证码api,默认注释掉:虚拟发送短信验证码的代码块。 +3. uni-id-cf统一action名称为驼峰法 +## 1.0.29(2021-06-29) +1. 修复在安卓10以下设备,操作登录获取不到oaid会直接导致登录失败的bug +2. 修复uniCloud版本为阿里云版时删除头像设置失败,腾讯云版删除头像后二次上传失败的问题 +## 1.0.28(2021-06-28) +修复云函数uni-id-cf的resetPwdBySmsCode接口,未注册过的用户也能调用的问题 +## 1.0.27(2021-06-25) +修改文档,新增h5版演示示例 +## 1.0.26(2021-06-24) +升级用户头像上传的裁切功能,app端为原生裁剪其他端保持原来方式。数据表字段改用avatar_file存储file对象方便做图片的回显 +## 1.0.25(2021-06-23) +预置uniCloud admin依赖的uniCloud文件,方便uniCloud admin与uni-starter配套使用时免做文件迁移 +## 1.0.24(2021-06-23) +删除callFunction拦截器中多余的代码 +## 1.0.23(2021-06-22) +更正调试遗留的uni-config-center/uni-id/config.json的tokenExpiresIn=1配置问题,改为默认值7200 +## 1.0.22(2021-06-22) +1. 新增一键登录授权界面的其他快捷登录按钮 +2. 优化uni-quick-login组件代码 +3. 调整隐私政策协议框勾选逻辑:在登录页面已勾选,同步勾选。如果没勾选需要手动勾选(为符合应用市场上架要求) +4. 调整登录页隐私政策协议框位置。 +5. 增强路由拦截,新增判断token是否过期。 +## 1.0.21(2021-06-21) +优化:uni_modules模式使用uni-id-cf,方便uni-starter与uniCloud-admin的uni-id-cf同步更新。 +## 1.0.20(2021-06-18) +1.H5端新增,强制要求用户同意隐私协议 2.兼容ios端自动设置打开下载页用户的剪切板为邀请者的inviteCode 3.成功注册用户,且请求体含邀请码inviteCode自动关联裂变关系 +## 1.0.19(2021-06-17) +1.新增获取邀请码接口getUserInviteCode 2.在邀请用户下载应用页面,自动设置被邀请用户的剪切板为邀请者的code(仅支持安卓端) 3.在注册或登录并注册请求时自动添加剪切板中的请求参数 4.统一接口名称为驼峰法 +## 1.0.18(2021-06-15) +修复,APP端有安装微信客户端但未显示微信登录快捷键的问题 +## 1.0.17(2021-06-09) +修复,非APP端deviceInfo为空引起的登录失败问题 +## 1.0.16(2021-06-08) +新增,操作注册/登录操作自动获取客户端设备:push_clientid、imei、oaid、idfa新增/更新到数据表uni-id-device新增,操作注册/登录操作自动获取客户端设备:push_clientid、imei、oaid、idfa新增/更新到数据表uni-id-device +## 1.0.15(2021-06-07) +为迎合苹果App Store的规则,登录与分享功能项显示之前自动检测是否安装了对应客户端。比如:设备未安装微信则不显示微信快捷登录和微信分享选项。为迎合苹果App Store的规则,登录与分享功能项显示之前自动检测是否安装了对应客户端。比如:设备未安装微信则不显示微信快捷登录和微信分享选项。 +## 1.0.14(2021-06-07) +修改错误的表名称uni-verify为opendb-verify-codes +## 1.0.13(2021-06-04) +新增一键登录界面的第三方快捷登录按钮 +## 1.0.12(2021-05-28) +修复拦截器在ios app端会报错:Unhandled promise...的问题 +## 1.0.10(2021-05-27) +新增callfunction的拦截器废除this.request的写法。为callFunction添加:请求失败是否断网判断并提示、恢复网络自动重新执行、自动处理响应体:token过期自动跳转到登录页面、token自动续期 +## 1.0.9(2021-05-23) +修复变量被重复定义的问题 +## 1.0.8(2021-05-22) +宫格页(/pages/grid/grid),新增根据当前用户是否登录、是否为管理员的角色来决定是否显示的示范 +## 1.0.7(2021-05-22) +删除多余数据 +## 1.0.6(2021-05-22) +修复当username(用户名&密码)为第一优先级的登录方式时。无法切换到smsCode(短信验证码)登录方式 +## 1.0.5(2021-05-20) +改用uni_modules方式处理图片选择api时无权限,引导用户快捷打开系统设置 +## 1.0.4(2021-05-19) +为方便部署,添加空的manifest.json uni-config-center下的uni-id配置 +## 1.0.3(2021-05-18) +重大调整,原云函数名称:user-center改名叫uni-id-cf +修复,绑定手机号码场景。因手机未插SIM导致的一键登录失败后未直接跳到获取短信验证码方式绑定 +## 1.0.2(2021-05-17) +添加 uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json 文件 +## 1.0.1(2021-05-17) +manifest.json 在小程序平台增加了一个配置项 betterScopedSlots,启用新的作用域插槽编译,用于支持作用域插槽内使用复杂表达式。 +## 1.0.0(2021-05-17) +第一版 \ No newline at end of file diff --git a/common/appInit.js b/common/appInit.js new file mode 100644 index 0000000000000000000000000000000000000000..a3d285a963c1c18265e6a8754ac89911474dad72 --- /dev/null +++ b/common/appInit.js @@ -0,0 +1,457 @@ +import uniStarterConfig from '@/uni-starter.config.js'; +import store from '@/store' +//应用初始化页 +// #ifdef APP-PLUS +import checkUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update'; +import callCheckVersion from '@/uni_modules/uni-upgrade-center-app/utils/call-check-version'; +import interceptorChooseImage from '@/uni_modules/json-interceptor-chooseImage/js_sdk/main.js'; +// #endif +const db = uniCloud.database() +export default async function() { + let loginConfig = uniStarterConfig.router.login + //清除有配置但设备环境不支持的登录项 + // #ifdef APP-PLUS + await new Promise((callBack) => { + plus.oauth.getServices(oauthServices => { + loginConfig = loginConfig.filter(item => { + if (["univerify", "weixin", "apple"].includes(item)) { + let index = oauthServices.findIndex(e => e.id == item) + if (index == -1) { + return false + } else { + return oauthServices[index].nativeClient + } + } else { + return true + } + }) + if (loginConfig.includes('univerify')) { //一键登录 功能预登录 + uni.preLogin({ + provider: 'univerify', + complete: e => { + console.log(e); + } + }) + } + callBack() + }, err => { + console.error('获取服务供应商失败:' + JSON.stringify(err)); + }) + }) + // #endif + + //非app移除:一键登录、苹果登录;h5移除微信登录,如果你做微信公众号登录需要将此行移除 + // #ifndef APP-PLUS + loginConfig = loginConfig.filter(item => { + return ![ + 'univerify', + 'apple', + // #ifdef H5 + 'weixin' + // #endif + ].includes(item) + }) + // #endif + + uniStarterConfig.router.login = loginConfig + + // uniStarterConfig挂载到getApp().globalData.config + setTimeout(() => { + getApp({ + allowDefault: true + }).globalData.config = uniStarterConfig; + }, 1) + + + // 初始化appVersion(仅app生效) + initAppVersion(); + + // #ifdef APP-PLUS + // 实现,路由拦截。当应用无访问摄像头/相册权限,引导跳到设置界面 + interceptorChooseImage() + // #endif + + + //clientDB的错误提示 + function onDBError({ + code, // 错误码详见https://uniapp.dcloud.net.cn/uniCloud/clientdb?id=returnvalue + message + }) { + console.log('onDBError', { + code, + message + }); + // 处理错误 + console.error(code, message); + if ([ + 'TOKEN_INVALID_INVALID_CLIENTID', + 'TOKEN_INVALID', + 'TOKEN_INVALID_TOKEN_EXPIRED', + 'TOKEN_INVALID_WRONG_TOKEN', + 'TOKEN_INVALID_ANONYMOUS_USER', + + ].includes(code)) { + uni.navigateTo({ + url: '/pages/ucenter/login-page/index/index' + }) + } + } + // 绑定clientDB错误事件 + db.on('error', onDBError) + + // 解绑clientDB错误事件 + //db.off('error', onDBError) + db.on('refreshToken', function({ + token, + tokenExpired + }) { + console.log('监听到clientDB的refreshToken', { + token, + tokenExpired + }); + store.commit('user/login', { + token, + tokenExpired + }) + }) + + const Debug = false; + //拦截器封装callFunction + let callFunctionOption; + uniCloud.addInterceptor('callFunction', { + async invoke(option) { + // #ifdef APP-PLUS + // 判断如果是执行登录(无论是哪种登录方式),就记录用户的相关设备id + if (option.name == 'uni-id-cf' && + (option.data.action == 'register' || option.data.action.slice(0, 5) == 'login') + ) { + let oaid = await new Promise((callBack, fail) => { + if (uni.getSystemInfoSync().platform == "android") { + plus.device.getOAID({ + success: function(e) { + callBack(e.oaid) + // console.log('getOAID success: '+JSON.stringify(e)); + }, + fail: function(e) { + callBack() + console.log('getOAID failed: ' + JSON.stringify(e)); + } + }); + } else { + callBack() + } + }) + + let imei = await new Promise((callBack, fail) => { + if (uni.getSystemInfoSync().platform == "android") { + plus.device.getInfo({ + success: function(e) { + callBack(e.imei) + // console.log('getOAID success: '+JSON.stringify(e)); + }, + fail: function(e) { + callBack() + console.log('getOAID failed: ' + JSON.stringify(e)); + } + }); + } else { + callBack() + } + }) + + let push_clientid = '', + idfa = plus.storage.getItem('idfa') || ''; //idfa有需要的用户在应用首次启动时自己获取存储到storage中 + + try { + push_clientid = plus.push.getClientInfo().clientid + } catch (e) { + uni.showModal({ + content: '获取推送标识失败。如果你的应用不需要推送功能,请注释掉本代码块', + showCancel: false, + confirmText: "好的" + }); + console.log(e) + } + + let deviceInfo = { + push_clientid, // 获取匿名设备标识符 + imei, + oaid, + idfa + } + console.log("重新登录/注册,获取设备id", deviceInfo); + option.data.deviceInfo = deviceInfo + + // #ifndef H5 + //注册可能不仅仅走register接口,还有登录并注册的接口 + option.data.inviteCode = await new Promise((callBack) => { + uni.getClipboardData({ + success: function(res) { + if (res.data.slice(0, 18) == 'uniInvitationCode:') { + let uniInvitationCode = res.data.slice(18, 38) + console.log('当前用户是其他用户推荐下载的,推荐者的code是:' + + uniInvitationCode); + // uni.showModal({ + // content: '当前用户是其他用户推荐下载的,推荐者的code是:'+uniInvitationCode, + // showCancel: false + // }); + callBack(uniInvitationCode) + //当前用户是其他用户推荐下载的。这里登记他的推荐者id 为当前用户的myInviteCode。判断如果是注册 + } else { + callBack(false) + } + }, + fail() { + callBack(false) + } + }); + }) + // #endif + } + // #endif + // console.log(JSON.stringify(option)); + callFunctionOption = option + }, + complete(e) { + // console.log(JSON.stringify(e)); + }, + fail(e) { // 失败回调拦截 + if (Debug) { + uni.showModal({ + content: JSON.stringify(e), + showCancel: false + }); + console.error(e); + } else { + uni.showModal({ + content: "系统错误请稍后再试!", + showCancel: false, + confirmText: "知道了" + }); + } + //如果执行错误,检查是否断网 + uni.getNetworkType({ + complete: res => { + // console.log(res); + if (res.networkType == 'none') { + uni.showToast({ + title: '手机网络异常', + icon: 'none' + }); + console.log('手机网络异常'); + let callBack = res => { + console.log(res); + if (res.isConnected) { + uni.showToast({ + title: '恢复联网自动重新执行', + icon: 'none' + }); + console.log(res.networkType, "恢复联网自动重新执行"); + uni.offNetworkStatusChange(e => { + console.log("移除监听成功", e); + }) + //恢复联网自动重新执行 + uniCloud.callFunction(callFunctionOption) + uni.offNetworkStatusChange(callBack); + } + } + uni.onNetworkStatusChange(callBack); + } + } + }); + }, + success: (e) => { + // console.log(e); + const { + token, + tokenExpired + } = e.result + if (token && tokenExpired) { + store.commit('user/login', { + token, + tokenExpired + }) + } + switch (e.result.code) { + case 403: + uni.navigateTo({ + url: "/pages/ucenter/login-page/index/index" + }) + break; + case 30203: + uni.navigateTo({ + url: "/pages/ucenter/login-page/index/index" + }) + break; + case 50101: + uni.showToast({ + title: e.result.msg, + icon: 'none', + duration: 2000 + }); + break; + default: + console.log('code的值是:' + e.result.code, '可以在上面添加case,自动处理响应体'); + break; + } + + switch (e.result.errCode) { + case 'uni-id-token-not-exist': + uni.showToast({ + title: '登录信息失效', + icon: 'none' + }); + uni.navigateTo({ + url: "/pages/ucenter/login-page/index/index" + }) + break; + default: + break; + } + } + }) + + //自定义路由拦截 + const { + "router": { + needLogin, + visitor, + login + } + } = uniStarterConfig //需要登录的页面 + + let list = ["navigateTo", "redirectTo", "reLaunch", "switchTab"]; + list.forEach(item => { //用遍历的方式分别为,uni.navigateTo,uni.redirectTo,uni.reLaunch,uni.switchTab这4个路由方法添加拦截器 + uni.addInterceptor(item, { + invoke(e) { // 调用前拦截 + //获取用户的token + const token = uni.getStorageSync('uni_id_token'), + //token是否已失效 + tokenExpired = uni.getStorageSync('uni_id_token_expired') < Date.now(), + //获取要跳转的页面路径(url去掉"?"和"?"后的参数) + url = e.url.split('?')[0]; + //获取要前往的页面路径(即url去掉"?"和"?"后的参数) + const pages = getCurrentPages(); + if (!pages.length) { + console.log("首页启动调用了"); + return e + } + const fromUrl = pages[pages.length - 1].route; + + let inLoginPage = fromUrl.split('/')[2] == 'login-page' + + //控制登录优先级 + if ( //判断当前窗口是否为登录页面,如果是则不重定向路由 + url == '/pages/ucenter/login-page/index/index' && + !inLoginPage + ) { + //一键登录(univerify)、账号(username)、验证码登录(短信smsCode) + if (login[0] == 'username') { + e.url = "/pages/ucenter/login-page/pwd-login/pwd-login" + } else { + if (e.url == url) { + e.url += '?' + } //添加参数之前判断是否带了`?`号如果没有就补上,因为当开发场景本身有参数的情况下是已经带了`?`号 + e.url += "type=" + login[0] + } + } else { + //拦截强制登录页面 + let pass = true + //pattern + if (needLogin) { + pass = needLogin.every((item) => { + if (typeof(item) == 'object' && item.pattern) { + return !item.pattern.test(url) + } + return url != item + }) + // console.log({pass}) + } + if (visitor) { + pass = visitor.some((item) => { + if (typeof(item) == 'object' && item.pattern) { + return item.pattern.test(url) + } + return url == item + }) + // console.log({pass}) + } + + if (!pass && (token == '' || tokenExpired)) { + uni.showToast({ + title: '请先登录', + icon: 'none' + }) + uni.navigateTo({ + url: "/pages/ucenter/login-page/index/index" + }) + return false + } + } + return e + }, + fail(err) { // 失败回调拦截 + console.log(err); + if (Debug) { + console.log(err); + uni.showModal({ + content: JSON.stringify(err), + showCancel: false + }); + } + } + }) + }) + + // #ifdef APP-PLUS + // 监听并提示设备网络状态变化 + uni.onNetworkStatusChange(res => { + console.log(res.isConnected); + console.log(res.networkType); + if (res.networkType != 'none') { + uni.showToast({ + title: '当前网络类型:' + res.networkType, + icon: 'none', + duration: 3000 + }) + } else { + uni.showToast({ + title: '网络类型:' + res.networkType, + icon: 'none', + duration: 3000 + }) + } + }); + // #endif + +} +/** + * // 初始化appVersion + */ +function initAppVersion() { + // #ifdef APP-PLUS + let appid = plus.runtime.appid; + plus.runtime.getProperty(appid, (wgtInfo) => { + let appVersion = plus.runtime; + let currentVersion = appVersion.versionCode > wgtInfo.versionCode ? appVersion : wgtInfo; + getApp({ + allowDefault: true + }).appVersion = { + ...currentVersion, + appid, + hasNew: false + } + // 检查更新小红点 + callCheckVersion().then(res => { + // console.log('检查是否有可以更新的版本', res); + if (res.result.code > 0) { + // 有新版本 + getApp({ + allowDefault: true + }).appVersion.hasNew = true; + } + }) + }); + // 检查更新 + // #endif +} diff --git a/common/openApp.js b/common/openApp.js new file mode 100644 index 0000000000000000000000000000000000000000..6fbbbb44df95b8d3fb630083bacaf694718e432d --- /dev/null +++ b/common/openApp.js @@ -0,0 +1,36 @@ +/* + 创建在h5端全局悬浮引导用户下载app的功能, + 如不需要本功能直接移除配置文件uni-starter.config.js下的h5/openApp即可 +*/ + +import CONFIG from '../uni-starter.config.js'; + +const CONFIG_OPEN = CONFIG.h5.openApp || {}; +// 仅H5端添加"打开APP" +export default function() { + // #ifdef H5 + if (!CONFIG_OPEN.openUrl) return; + + let openLogo = CONFIG_OPEN.logo ? + `` : ''; + let openApp = document.createElement("div"); + openApp.id = 'openApp'; + openApp.style = + 'position: fixed;background:#FFFFFF;box-shadow: #eeeeee 1px 1px 9px; ;top: 0;left: 0;right: 0;z-index: 999;width: 100%;height: 45px;display: flex;flex-direction: row;justify-content: space-between;align-items: center;box-sizing: border-box;padding: 0 0.5rem;' + openApp.innerHTML = ` +
+ ${openLogo} +
${CONFIG_OPEN.appname || ''}
+
+
下载app
+ `; + document.body.insertBefore(openApp, document.body.firstChild); + document.body.style = 'height:calc(100% - 45px); margin-top:45px;'; + openApp.addEventListener('click', e => { + var target = e.target || e.srcElement; + if (target.className.indexOf('openBtn') >= 0) { + window.location.href = CONFIG_OPEN.openUrl; + } + }) + //#endif +} diff --git a/common/uni-ui.scss b/common/uni-ui.scss new file mode 100644 index 0000000000000000000000000000000000000000..de868c7fb9f97825a7c34d61d32381c7185bd7ae --- /dev/null +++ b/common/uni-ui.scss @@ -0,0 +1,118 @@ + +.uni-flex { + display: flex; +} + +.uni-flex-row { + @extend .uni-flex; + flex-direction: row; + box-sizing: border-box; +} + +.uni-flex-column { + @extend .uni-flex; + flex-direction: column; +} + +.uni-color-gary { + color: #3b4144; +} + +/* 标题 */ +.uni-title { + display: flex; + margin-bottom: $uni-spacing-col-base; + font-size: $uni-font-size-lg; + font-weight: bold; + color: #3b4144; +} + +.uni-title-sub { + display: flex; + font-size: $uni-font-size-base; + font-weight: 500; + color: #3b4144; +} + +/* 描述 额外文本 */ +.uni-note { + margin-top: 10px; + color: #999; + font-size: $uni-font-size-sm; +} + +/* 列表内容 */ +.uni-list-box { + @extend .uni-flex-row; + flex: 1; + margin-top: 10px; +} + +/* 略缩图 */ +.uni-thumb { + flex-shrink: 0; + margin-right: $uni-spacing-row-base; + width: 125px; + height: 75px; + border-radius: $uni-border-radius-lg; + overflow: hidden; + border: 1px #f5f5f5 solid; + image { + width: 100%; + height: 100%; + } +} + +.uni-media-box { + @extend .uni-flex-row; + border-radius: $uni-border-radius-lg; + overflow: hidden; + .uni-thumb { + margin: 0; + margin-left: 4px; + flex-shrink: 1; + width: 33%; + border-radius:0; + &:first-child { + margin: 0; + } + } +} + +/* 内容 */ +.uni-content { + @extend .uni-flex-column; + justify-content: space-between; +} + +/* 列表footer */ +.uni-footer { + @extend .uni-flex-row; + justify-content: space-between; + margin-top: $uni-spacing-col-lg; +} +.uni-footer-text { + font-size: $uni-font-size-sm; + color: $uni-text-color-grey; + margin-left: 5px; +} + +/* 标签 */ + +.uni-tag { + flex-shrink: 0; + padding: 0 5px; + border: 1px $uni-border-color solid; + margin-right: $uni-spacing-row-sm; + border-radius: $uni-border-radius-base; + background: $uni-bg-color-grey; + color: $uni-text-color; + font-size: $uni-font-size-sm; +} + +/* 链接 */ +.uni-link { + margin-left: 10px; + color: $uni-text-color; + text-decoration: underline; +} diff --git a/components/Sansnn-uQRCode/README.md b/components/Sansnn-uQRCode/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c99382f8549497838761117878d2720d7f44ced1 --- /dev/null +++ b/components/Sansnn-uQRCode/README.md @@ -0,0 +1,172 @@ +> 插件来源:[https://ext.dcloud.net.cn/plugin?id=1287](https://ext.dcloud.net.cn/plugin?id=1287) +##### 以下是作者写的插件介绍: + +# uQRCode + +### 云函数版二维码生成插件explain-qrcode现已发布,URL化后一句代码即可生成,有网就有二维码,100%生成成功,不会因为平台差异,设备差异导致生成失败,无需在前端做适配和兼容,极力推荐。插件地址:[explain-qrcode云函数二维码生成](https://ext.dcloud.net.cn/plugin?id=3359) + +uQRCode 生成方式简单,可扩展性高,如有复杂需求可通过自定义组件或修改源码完成需求。已测试H5、微信小程序、iPhoneXsMax真机。 + +本示例项目中的自定义组件旨在抛砖引玉,有其他需求的朋友可自行扩展,自定义组件参考 ``/components/uni-qrcode/uni-qrcode.vue`` ,自定义组件使用案例参考 ``/pages/component/qrcode/qrcode.vue`` 。 + +联系方式:QQ540000228。 + +最近一次用于更新代码的 HBuilder X 版本为 2.8.11。 + +### 二维码 +**什么是QR码** + +QR码属于矩阵式二维码中的一个种类,由DENSO(日本电装)公司开发,由JIS和ISO将其标准化。 + +**QR码的特点** + +一是高速读取(QR就是取自“Quick Response”的首字母),通过摄像头从拍摄到解码到显示内容也就三秒左右,对摄像的角度也没有什么要求; + +二是高容量、高密度,理论上内容经过压缩处理后可以存7089个数字,4296个字母和数字混合字符,2953个8位字节数据,1817个汉字; + +三是支持纠错处理,按照QR码的标准文档说明,QR码的纠错分为4个级别,分别是: +- level L : 最大 7% 的错误能够被纠正; +- level M : 最大 15% 的错误能够被纠正; +- level Q : 最大 25% 的错误能够被纠正; +- level H : 最大 30% 的错误能够被纠正; + +四是结构化,看似无规则的图形,其实对区域有严格的定义。 + +更多二维码介绍及原理:[https://blog.csdn.net/jason_ldh/article/details/11801355](https://blog.csdn.net/jason_ldh/article/details/11801355) + +### 使用方式 + +在 ``script`` 中引用组件 + +```javascript +import uQRCode from '@/common/uqrcode.js' +``` + +在 ``template`` 中创建 ```` + +```html + +``` + +在 ``script`` 中调用 ``make()`` 方法 + +```javascript +export default { + methods: { + async make() { + // 回调方式 + uQRCode.make({ + canvasId: 'qrcode', + componentInstance: this, + text: 'uQRCode', + size: 354, + margin: 10, + backgroundColor: '#ffffff', + foregroundColor: '#000000', + fileType: 'jpg', + errorCorrectLevel: uQRCode.errorCorrectLevel.H, + success: res => { + console.log(res) + } + }) + + // Promise + uQRCode.make({ + canvasId: 'qrcode', + componentInstance: this, + text: 'uQRCode', + size: 354, + margin: 10, + backgroundColor: '#ffffff', + foregroundColor: '#000000', + fileType: 'jpg', + errorCorrectLevel: uQRCode.errorCorrectLevel.H + }).then(res => { + console.log(res) + }) + + // 同步等待 + var res = await uQRCode.make({ + canvasId: 'qrcode', + componentInstance: this, + text: 'uQRCode', + size: 354, + margin: 10, + backgroundColor: '#ffffff', + foregroundColor: '#000000', + fileType: 'jpg', + errorCorrectLevel: uQRCode.errorCorrectLevel.H + }) + console.log(res) + } + } +} +``` + +### 属性说明 + +|属性名|说明| +|---|:---| +|errorCorrectLevel|纠错等级,包含 `errorCorrectLevel.L`、`errorCorrectLevel.M`、`errorCorrectLevel.Q`、`errorCorrectLevel.H` 四个级别,`L`: 最大 7% 的错误能够被纠正;`M`: 最大 15% 的错误能够被纠正;`Q`: 最大 25% 的错误能够被纠正;`H`: 最大 30% 的错误能够被纠正。| +|defaults|二维码生成参数的默认值。| + +### 方法说明 + +|方法名|说明| +|---|:---| +|[make](#makeoptions)|生成二维码。| + +### make(options) + +生成二维码 + +**options参数说明:** + +|参数|类型|必填|说明| +|---|---|---|:---| +|canvasId|String|是|画布标识,传入 `` 的 `canvas-id`| +|componentInstance|Object|否|自定义组件实例 `this` ,表示在这个自定义组件下查找拥有 `canvas-id` 的 `` ,如果省略,则不在任何自定义组件内查找| +|text|String|是|二维码内容| +|size|Number|否|画布尺寸大小,请与 `` 所设 `width` , `height` 保持一致(默认:`354`)| +|margin|Number|否|边距,二维码实际尺寸会根据所设边距值进行缩放调整(默认:`0`)| +|backgroundColor|String|否|背景色,若设置为透明背景, `fileType` 需设置为 `'png'` , 然后设置背景色为 `'rgba(255,255,255,0)'` 即可(默认:`'#ffffff'`)| +|foregroundColor|String|否|前景色(默认:`'#000000'`)| +|fileType|String|否|输出图片的类型,只支持 `'jpg'` 和 `'png'`(默认:`'png'`)| +|errorCorrectLevel|Number|否|纠错等级,参考属性说明 `errorCorrectLevel`(默认:`errorCorrectLevel.H`)| + +### 使用建议 +canvas在二维码生成中请当做一个生成工具来看待,它的作用仅是绘制出二维码。应把生成回调得到的资源保存并使用,显示用image图片组件,原因是方便操作,例如调整大小,或是H5端长按保存或识别,所以canvas应将它放在看不见的地方。不能用`display:none;overflow:hidden;`隐藏,否则生成空白。这里推荐canvas的隐藏样式代码 +```html + +``` + +### 常见问题 +**二维码生成不完整** + +canvas宽高必须和size一致,并且size的单位是px,如果canvas的单位是rpx,那么不同设备屏幕分辨率不一样,rpx转换成px后的画布尺寸不足以放下全部内容,实际绘制图案超出,就会出现不完整的情况。 + +**如何扫码跳转指定网页** + +text参数直接放入完整的网页地址即可,例如:`https://ext.dcloud.net.cn/plugin?id=1287`。微信客户端不能是ip地址。 + +**小程序、APP报错** + +canvas不支持放在 `slot` 插槽,请尽量放在模板根节点,也就是第一个 `` 标签里面 + +**H5长按识别** + +canvas无法长按识别,长按识别需要是图片才行,所以只需将回调过来的资源用image组件显示即可。 + +### Tips +- 示例项目中的图片采集于互联网,仅作为案例展示,不作为广告/商业,如有侵权,请告知删除。下载使用的用户,请勿把示例项目中的图片应用到你的项目。 \ No newline at end of file diff --git a/components/Sansnn-uQRCode/Sansnn-uQRCode.vue b/components/Sansnn-uQRCode/Sansnn-uQRCode.vue new file mode 100644 index 0000000000000000000000000000000000000000..0433faee91742247dc38a4363bb74f5775d04eb4 --- /dev/null +++ b/components/Sansnn-uQRCode/Sansnn-uQRCode.vue @@ -0,0 +1,198 @@ + + + diff --git a/components/Sansnn-uQRCode/uqrcode.js b/components/Sansnn-uQRCode/uqrcode.js new file mode 100644 index 0000000000000000000000000000000000000000..311ce1dfacd7052ea3459aa1b142ac5fd65f02d9 --- /dev/null +++ b/components/Sansnn-uQRCode/uqrcode.js @@ -0,0 +1,1438 @@ +//--------------------------------------------------------------------- +// github https://github.com/Sansnn/uQRCode +//--------------------------------------------------------------------- + +let uQRCode = {}; + +(function() { + //--------------------------------------------------------------------- + // QRCode for JavaScript + // + // Copyright (c) 2009 Kazuhiko Arase + // + // URL: http://www.d-project.com/ + // + // Licensed under the MIT license: + // http://www.opensource.org/licenses/mit-license.php + // + // The word "QR Code" is registered trademark of + // DENSO WAVE INCORPORATED + // http://www.denso-wave.com/qrcode/faqpatent-e.html + // + //--------------------------------------------------------------------- + + //--------------------------------------------------------------------- + // QR8bitByte + //--------------------------------------------------------------------- + + function QR8bitByte(data) { + this.mode = QRMode.MODE_8BIT_BYTE; + this.data = data; + } + + QR8bitByte.prototype = { + + getLength: function(buffer) { + return this.data.length; + }, + + write: function(buffer) { + for (var i = 0; i < this.data.length; i++) { + // not JIS ... + buffer.put(this.data.charCodeAt(i), 8); + } + } + }; + + //--------------------------------------------------------------------- + // QRCode + //--------------------------------------------------------------------- + + function QRCode(typeNumber, errorCorrectLevel) { + this.typeNumber = typeNumber; + this.errorCorrectLevel = errorCorrectLevel; + this.modules = null; + this.moduleCount = 0; + this.dataCache = null; + this.dataList = new Array(); + } + + QRCode.prototype = { + + addData: function(data) { + var newData = new QR8bitByte(data); + this.dataList.push(newData); + this.dataCache = null; + }, + + isDark: function(row, col) { + if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) { + throw new Error(row + "," + col); + } + return this.modules[row][col]; + }, + + getModuleCount: function() { + return this.moduleCount; + }, + + make: function() { + // Calculate automatically typeNumber if provided is < 1 + if (this.typeNumber < 1) { + var typeNumber = 1; + for (typeNumber = 1; typeNumber < 40; typeNumber++) { + var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, this.errorCorrectLevel); + + var buffer = new QRBitBuffer(); + var totalDataCount = 0; + for (var i = 0; i < rsBlocks.length; i++) { + totalDataCount += rsBlocks[i].dataCount; + } + + for (var i = 0; i < this.dataList.length; i++) { + var data = this.dataList[i]; + buffer.put(data.mode, 4); + buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber)); + data.write(buffer); + } + if (buffer.getLengthInBits() <= totalDataCount * 8) + break; + } + this.typeNumber = typeNumber; + } + this.makeImpl(false, this.getBestMaskPattern()); + }, + + makeImpl: function(test, maskPattern) { + + this.moduleCount = this.typeNumber * 4 + 17; + this.modules = new Array(this.moduleCount); + + for (var row = 0; row < this.moduleCount; row++) { + + this.modules[row] = new Array(this.moduleCount); + + for (var col = 0; col < this.moduleCount; col++) { + this.modules[row][col] = null; //(col + row) % 3; + } + } + + this.setupPositionProbePattern(0, 0); + this.setupPositionProbePattern(this.moduleCount - 7, 0); + this.setupPositionProbePattern(0, this.moduleCount - 7); + this.setupPositionAdjustPattern(); + this.setupTimingPattern(); + this.setupTypeInfo(test, maskPattern); + + if (this.typeNumber >= 7) { + this.setupTypeNumber(test); + } + + if (this.dataCache == null) { + this.dataCache = QRCode.createData(this.typeNumber, this.errorCorrectLevel, this.dataList); + } + + this.mapData(this.dataCache, maskPattern); + }, + + setupPositionProbePattern: function(row, col) { + + for (var r = -1; r <= 7; r++) { + + if (row + r <= -1 || this.moduleCount <= row + r) continue; + + for (var c = -1; c <= 7; c++) { + + if (col + c <= -1 || this.moduleCount <= col + c) continue; + + if ((0 <= r && r <= 6 && (c == 0 || c == 6)) || + (0 <= c && c <= 6 && (r == 0 || r == 6)) || + (2 <= r && r <= 4 && 2 <= c && c <= 4)) { + this.modules[row + r][col + c] = true; + } else { + this.modules[row + r][col + c] = false; + } + } + } + }, + + getBestMaskPattern: function() { + + var minLostPoint = 0; + var pattern = 0; + + for (var i = 0; i < 8; i++) { + + this.makeImpl(true, i); + + var lostPoint = QRUtil.getLostPoint(this); + + if (i == 0 || minLostPoint > lostPoint) { + minLostPoint = lostPoint; + pattern = i; + } + } + + return pattern; + }, + + createMovieClip: function(target_mc, instance_name, depth) { + + var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth); + var cs = 1; + + this.make(); + + for (var row = 0; row < this.modules.length; row++) { + + var y = row * cs; + + for (var col = 0; col < this.modules[row].length; col++) { + + var x = col * cs; + var dark = this.modules[row][col]; + + if (dark) { + qr_mc.beginFill(0, 100); + qr_mc.moveTo(x, y); + qr_mc.lineTo(x + cs, y); + qr_mc.lineTo(x + cs, y + cs); + qr_mc.lineTo(x, y + cs); + qr_mc.endFill(); + } + } + } + + return qr_mc; + }, + + setupTimingPattern: function() { + + for (var r = 8; r < this.moduleCount - 8; r++) { + if (this.modules[r][6] != null) { + continue; + } + this.modules[r][6] = (r % 2 == 0); + } + + for (var c = 8; c < this.moduleCount - 8; c++) { + if (this.modules[6][c] != null) { + continue; + } + this.modules[6][c] = (c % 2 == 0); + } + }, + + setupPositionAdjustPattern: function() { + + var pos = QRUtil.getPatternPosition(this.typeNumber); + + for (var i = 0; i < pos.length; i++) { + + for (var j = 0; j < pos.length; j++) { + + var row = pos[i]; + var col = pos[j]; + + if (this.modules[row][col] != null) { + continue; + } + + for (var r = -2; r <= 2; r++) { + + for (var c = -2; c <= 2; c++) { + + if (r == -2 || r == 2 || c == -2 || c == 2 || + (r == 0 && c == 0)) { + this.modules[row + r][col + c] = true; + } else { + this.modules[row + r][col + c] = false; + } + } + } + } + } + }, + + setupTypeNumber: function(test) { + + var bits = QRUtil.getBCHTypeNumber(this.typeNumber); + + for (var i = 0; i < 18; i++) { + var mod = (!test && ((bits >> i) & 1) == 1); + this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod; + } + + for (var i = 0; i < 18; i++) { + var mod = (!test && ((bits >> i) & 1) == 1); + this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod; + } + }, + + setupTypeInfo: function(test, maskPattern) { + + var data = (this.errorCorrectLevel << 3) | maskPattern; + var bits = QRUtil.getBCHTypeInfo(data); + + // vertical + for (var i = 0; i < 15; i++) { + + var mod = (!test && ((bits >> i) & 1) == 1); + + if (i < 6) { + this.modules[i][8] = mod; + } else if (i < 8) { + this.modules[i + 1][8] = mod; + } else { + this.modules[this.moduleCount - 15 + i][8] = mod; + } + } + + // horizontal + for (var i = 0; i < 15; i++) { + + var mod = (!test && ((bits >> i) & 1) == 1); + + if (i < 8) { + this.modules[8][this.moduleCount - i - 1] = mod; + } else if (i < 9) { + this.modules[8][15 - i - 1 + 1] = mod; + } else { + this.modules[8][15 - i - 1] = mod; + } + } + + // fixed module + this.modules[this.moduleCount - 8][8] = (!test); + + }, + + mapData: function(data, maskPattern) { + + var inc = -1; + var row = this.moduleCount - 1; + var bitIndex = 7; + var byteIndex = 0; + + for (var col = this.moduleCount - 1; col > 0; col -= 2) { + + if (col == 6) col--; + + while (true) { + + for (var c = 0; c < 2; c++) { + + if (this.modules[row][col - c] == null) { + + var dark = false; + + if (byteIndex < data.length) { + dark = (((data[byteIndex] >>> bitIndex) & 1) == 1); + } + + var mask = QRUtil.getMask(maskPattern, row, col - c); + + if (mask) { + dark = !dark; + } + + this.modules[row][col - c] = dark; + bitIndex--; + + if (bitIndex == -1) { + byteIndex++; + bitIndex = 7; + } + } + } + + row += inc; + + if (row < 0 || this.moduleCount <= row) { + row -= inc; + inc = -inc; + break; + } + } + } + + } + + }; + + QRCode.PAD0 = 0xEC; + QRCode.PAD1 = 0x11; + + QRCode.createData = function(typeNumber, errorCorrectLevel, dataList) { + + var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel); + + var buffer = new QRBitBuffer(); + + for (var i = 0; i < dataList.length; i++) { + var data = dataList[i]; + buffer.put(data.mode, 4); + buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber)); + data.write(buffer); + } + + // calc num max data. + var totalDataCount = 0; + for (var i = 0; i < rsBlocks.length; i++) { + totalDataCount += rsBlocks[i].dataCount; + } + + if (buffer.getLengthInBits() > totalDataCount * 8) { + throw new Error("code length overflow. (" + + buffer.getLengthInBits() + + ">" + + totalDataCount * 8 + + ")"); + } + + // end code + if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) { + buffer.put(0, 4); + } + + // padding + while (buffer.getLengthInBits() % 8 != 0) { + buffer.putBit(false); + } + + // padding + while (true) { + + if (buffer.getLengthInBits() >= totalDataCount * 8) { + break; + } + buffer.put(QRCode.PAD0, 8); + + if (buffer.getLengthInBits() >= totalDataCount * 8) { + break; + } + buffer.put(QRCode.PAD1, 8); + } + + return QRCode.createBytes(buffer, rsBlocks); + } + + QRCode.createBytes = function(buffer, rsBlocks) { + + var offset = 0; + + var maxDcCount = 0; + var maxEcCount = 0; + + var dcdata = new Array(rsBlocks.length); + var ecdata = new Array(rsBlocks.length); + + for (var r = 0; r < rsBlocks.length; r++) { + + var dcCount = rsBlocks[r].dataCount; + var ecCount = rsBlocks[r].totalCount - dcCount; + + maxDcCount = Math.max(maxDcCount, dcCount); + maxEcCount = Math.max(maxEcCount, ecCount); + + dcdata[r] = new Array(dcCount); + + for (var i = 0; i < dcdata[r].length; i++) { + dcdata[r][i] = 0xff & buffer.buffer[i + offset]; + } + offset += dcCount; + + var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount); + var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1); + + var modPoly = rawPoly.mod(rsPoly); + ecdata[r] = new Array(rsPoly.getLength() - 1); + for (var i = 0; i < ecdata[r].length; i++) { + var modIndex = i + modPoly.getLength() - ecdata[r].length; + ecdata[r][i] = (modIndex >= 0) ? modPoly.get(modIndex) : 0; + } + + } + + var totalCodeCount = 0; + for (var i = 0; i < rsBlocks.length; i++) { + totalCodeCount += rsBlocks[i].totalCount; + } + + var data = new Array(totalCodeCount); + var index = 0; + + for (var i = 0; i < maxDcCount; i++) { + for (var r = 0; r < rsBlocks.length; r++) { + if (i < dcdata[r].length) { + data[index++] = dcdata[r][i]; + } + } + } + + for (var i = 0; i < maxEcCount; i++) { + for (var r = 0; r < rsBlocks.length; r++) { + if (i < ecdata[r].length) { + data[index++] = ecdata[r][i]; + } + } + } + + return data; + + } + + //--------------------------------------------------------------------- + // QRMode + //--------------------------------------------------------------------- + + var QRMode = { + MODE_NUMBER: 1 << 0, + MODE_ALPHA_NUM: 1 << 1, + MODE_8BIT_BYTE: 1 << 2, + MODE_KANJI: 1 << 3 + }; + + //--------------------------------------------------------------------- + // QRErrorCorrectLevel + //--------------------------------------------------------------------- + + var QRErrorCorrectLevel = { + L: 1, + M: 0, + Q: 3, + H: 2 + }; + + //--------------------------------------------------------------------- + // QRMaskPattern + //--------------------------------------------------------------------- + + var QRMaskPattern = { + PATTERN000: 0, + PATTERN001: 1, + PATTERN010: 2, + PATTERN011: 3, + PATTERN100: 4, + PATTERN101: 5, + PATTERN110: 6, + PATTERN111: 7 + }; + + //--------------------------------------------------------------------- + // QRUtil + //--------------------------------------------------------------------- + + var QRUtil = { + + PATTERN_POSITION_TABLE: [ + [], + [6, 18], + [6, 22], + [6, 26], + [6, 30], + [6, 34], + [6, 22, 38], + [6, 24, 42], + [6, 26, 46], + [6, 28, 50], + [6, 30, 54], + [6, 32, 58], + [6, 34, 62], + [6, 26, 46, 66], + [6, 26, 48, 70], + [6, 26, 50, 74], + [6, 30, 54, 78], + [6, 30, 56, 82], + [6, 30, 58, 86], + [6, 34, 62, 90], + [6, 28, 50, 72, 94], + [6, 26, 50, 74, 98], + [6, 30, 54, 78, 102], + [6, 28, 54, 80, 106], + [6, 32, 58, 84, 110], + [6, 30, 58, 86, 114], + [6, 34, 62, 90, 118], + [6, 26, 50, 74, 98, 122], + [6, 30, 54, 78, 102, 126], + [6, 26, 52, 78, 104, 130], + [6, 30, 56, 82, 108, 134], + [6, 34, 60, 86, 112, 138], + [6, 30, 58, 86, 114, 142], + [6, 34, 62, 90, 118, 146], + [6, 30, 54, 78, 102, 126, 150], + [6, 24, 50, 76, 102, 128, 154], + [6, 28, 54, 80, 106, 132, 158], + [6, 32, 58, 84, 110, 136, 162], + [6, 26, 54, 82, 110, 138, 166], + [6, 30, 58, 86, 114, 142, 170] + ], + + G15: (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0), + G18: (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0), + G15_MASK: (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1), + + getBCHTypeInfo: function(data) { + var d = data << 10; + while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) { + d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15))); + } + return ((data << 10) | d) ^ QRUtil.G15_MASK; + }, + + getBCHTypeNumber: function(data) { + var d = data << 12; + while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) { + d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18))); + } + return (data << 12) | d; + }, + + getBCHDigit: function(data) { + + var digit = 0; + + while (data != 0) { + digit++; + data >>>= 1; + } + + return digit; + }, + + getPatternPosition: function(typeNumber) { + return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1]; + }, + + getMask: function(maskPattern, i, j) { + + switch (maskPattern) { + + case QRMaskPattern.PATTERN000: + return (i + j) % 2 == 0; + case QRMaskPattern.PATTERN001: + return i % 2 == 0; + case QRMaskPattern.PATTERN010: + return j % 3 == 0; + case QRMaskPattern.PATTERN011: + return (i + j) % 3 == 0; + case QRMaskPattern.PATTERN100: + return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0; + case QRMaskPattern.PATTERN101: + return (i * j) % 2 + (i * j) % 3 == 0; + case QRMaskPattern.PATTERN110: + return ((i * j) % 2 + (i * j) % 3) % 2 == 0; + case QRMaskPattern.PATTERN111: + return ((i * j) % 3 + (i + j) % 2) % 2 == 0; + + default: + throw new Error("bad maskPattern:" + maskPattern); + } + }, + + getErrorCorrectPolynomial: function(errorCorrectLength) { + + var a = new QRPolynomial([1], 0); + + for (var i = 0; i < errorCorrectLength; i++) { + a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0)); + } + + return a; + }, + + getLengthInBits: function(mode, type) { + + if (1 <= type && type < 10) { + + // 1 - 9 + + switch (mode) { + case QRMode.MODE_NUMBER: + return 10; + case QRMode.MODE_ALPHA_NUM: + return 9; + case QRMode.MODE_8BIT_BYTE: + return 8; + case QRMode.MODE_KANJI: + return 8; + default: + throw new Error("mode:" + mode); + } + + } else if (type < 27) { + + // 10 - 26 + + switch (mode) { + case QRMode.MODE_NUMBER: + return 12; + case QRMode.MODE_ALPHA_NUM: + return 11; + case QRMode.MODE_8BIT_BYTE: + return 16; + case QRMode.MODE_KANJI: + return 10; + default: + throw new Error("mode:" + mode); + } + + } else if (type < 41) { + + // 27 - 40 + + switch (mode) { + case QRMode.MODE_NUMBER: + return 14; + case QRMode.MODE_ALPHA_NUM: + return 13; + case QRMode.MODE_8BIT_BYTE: + return 16; + case QRMode.MODE_KANJI: + return 12; + default: + throw new Error("mode:" + mode); + } + + } else { + throw new Error("type:" + type); + } + }, + + getLostPoint: function(qrCode) { + + var moduleCount = qrCode.getModuleCount(); + + var lostPoint = 0; + + // LEVEL1 + + for (var row = 0; row < moduleCount; row++) { + + for (var col = 0; col < moduleCount; col++) { + + var sameCount = 0; + var dark = qrCode.isDark(row, col); + + for (var r = -1; r <= 1; r++) { + + if (row + r < 0 || moduleCount <= row + r) { + continue; + } + + for (var c = -1; c <= 1; c++) { + + if (col + c < 0 || moduleCount <= col + c) { + continue; + } + + if (r == 0 && c == 0) { + continue; + } + + if (dark == qrCode.isDark(row + r, col + c)) { + sameCount++; + } + } + } + + if (sameCount > 5) { + lostPoint += (3 + sameCount - 5); + } + } + } + + // LEVEL2 + + for (var row = 0; row < moduleCount - 1; row++) { + for (var col = 0; col < moduleCount - 1; col++) { + var count = 0; + if (qrCode.isDark(row, col)) count++; + if (qrCode.isDark(row + 1, col)) count++; + if (qrCode.isDark(row, col + 1)) count++; + if (qrCode.isDark(row + 1, col + 1)) count++; + if (count == 0 || count == 4) { + lostPoint += 3; + } + } + } + + // LEVEL3 + + for (var row = 0; row < moduleCount; row++) { + for (var col = 0; col < moduleCount - 6; col++) { + if (qrCode.isDark(row, col) && + !qrCode.isDark(row, col + 1) && + qrCode.isDark(row, col + 2) && + qrCode.isDark(row, col + 3) && + qrCode.isDark(row, col + 4) && + !qrCode.isDark(row, col + 5) && + qrCode.isDark(row, col + 6)) { + lostPoint += 40; + } + } + } + + for (var col = 0; col < moduleCount; col++) { + for (var row = 0; row < moduleCount - 6; row++) { + if (qrCode.isDark(row, col) && + !qrCode.isDark(row + 1, col) && + qrCode.isDark(row + 2, col) && + qrCode.isDark(row + 3, col) && + qrCode.isDark(row + 4, col) && + !qrCode.isDark(row + 5, col) && + qrCode.isDark(row + 6, col)) { + lostPoint += 40; + } + } + } + + // LEVEL4 + + var darkCount = 0; + + for (var col = 0; col < moduleCount; col++) { + for (var row = 0; row < moduleCount; row++) { + if (qrCode.isDark(row, col)) { + darkCount++; + } + } + } + + var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5; + lostPoint += ratio * 10; + + return lostPoint; + } + + }; + + + //--------------------------------------------------------------------- + // QRMath + //--------------------------------------------------------------------- + + var QRMath = { + + glog: function(n) { + + if (n < 1) { + throw new Error("glog(" + n + ")"); + } + + return QRMath.LOG_TABLE[n]; + }, + + gexp: function(n) { + + while (n < 0) { + n += 255; + } + + while (n >= 256) { + n -= 255; + } + + return QRMath.EXP_TABLE[n]; + }, + + EXP_TABLE: new Array(256), + + LOG_TABLE: new Array(256) + + }; + + for (var i = 0; i < 8; i++) { + QRMath.EXP_TABLE[i] = 1 << i; + } + for (var i = 8; i < 256; i++) { + QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ + QRMath.EXP_TABLE[i - 5] ^ + QRMath.EXP_TABLE[i - 6] ^ + QRMath.EXP_TABLE[i - 8]; + } + for (var i = 0; i < 255; i++) { + QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i; + } + + //--------------------------------------------------------------------- + // QRPolynomial + //--------------------------------------------------------------------- + + function QRPolynomial(num, shift) { + + if (num.length == undefined) { + throw new Error(num.length + "/" + shift); + } + + var offset = 0; + + while (offset < num.length && num[offset] == 0) { + offset++; + } + + this.num = new Array(num.length - offset + shift); + for (var i = 0; i < num.length - offset; i++) { + this.num[i] = num[i + offset]; + } + } + + QRPolynomial.prototype = { + + get: function(index) { + return this.num[index]; + }, + + getLength: function() { + return this.num.length; + }, + + multiply: function(e) { + + var num = new Array(this.getLength() + e.getLength() - 1); + + for (var i = 0; i < this.getLength(); i++) { + for (var j = 0; j < e.getLength(); j++) { + num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j))); + } + } + + return new QRPolynomial(num, 0); + }, + + mod: function(e) { + + if (this.getLength() - e.getLength() < 0) { + return this; + } + + var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0)); + + var num = new Array(this.getLength()); + + for (var i = 0; i < this.getLength(); i++) { + num[i] = this.get(i); + } + + for (var i = 0; i < e.getLength(); i++) { + num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio); + } + + // recursive call + return new QRPolynomial(num, 0).mod(e); + } + }; + + //--------------------------------------------------------------------- + // QRRSBlock + //--------------------------------------------------------------------- + + function QRRSBlock(totalCount, dataCount) { + this.totalCount = totalCount; + this.dataCount = dataCount; + } + + QRRSBlock.RS_BLOCK_TABLE = [ + + // L + // M + // Q + // H + + // 1 + [1, 26, 19], + [1, 26, 16], + [1, 26, 13], + [1, 26, 9], + + // 2 + [1, 44, 34], + [1, 44, 28], + [1, 44, 22], + [1, 44, 16], + + // 3 + [1, 70, 55], + [1, 70, 44], + [2, 35, 17], + [2, 35, 13], + + // 4 + [1, 100, 80], + [2, 50, 32], + [2, 50, 24], + [4, 25, 9], + + // 5 + [1, 134, 108], + [2, 67, 43], + [2, 33, 15, 2, 34, 16], + [2, 33, 11, 2, 34, 12], + + // 6 + [2, 86, 68], + [4, 43, 27], + [4, 43, 19], + [4, 43, 15], + + // 7 + [2, 98, 78], + [4, 49, 31], + [2, 32, 14, 4, 33, 15], + [4, 39, 13, 1, 40, 14], + + // 8 + [2, 121, 97], + [2, 60, 38, 2, 61, 39], + [4, 40, 18, 2, 41, 19], + [4, 40, 14, 2, 41, 15], + + // 9 + [2, 146, 116], + [3, 58, 36, 2, 59, 37], + [4, 36, 16, 4, 37, 17], + [4, 36, 12, 4, 37, 13], + + // 10 + [2, 86, 68, 2, 87, 69], + [4, 69, 43, 1, 70, 44], + [6, 43, 19, 2, 44, 20], + [6, 43, 15, 2, 44, 16], + + // 11 + [4, 101, 81], + [1, 80, 50, 4, 81, 51], + [4, 50, 22, 4, 51, 23], + [3, 36, 12, 8, 37, 13], + + // 12 + [2, 116, 92, 2, 117, 93], + [6, 58, 36, 2, 59, 37], + [4, 46, 20, 6, 47, 21], + [7, 42, 14, 4, 43, 15], + + // 13 + [4, 133, 107], + [8, 59, 37, 1, 60, 38], + [8, 44, 20, 4, 45, 21], + [12, 33, 11, 4, 34, 12], + + // 14 + [3, 145, 115, 1, 146, 116], + [4, 64, 40, 5, 65, 41], + [11, 36, 16, 5, 37, 17], + [11, 36, 12, 5, 37, 13], + + // 15 + [5, 109, 87, 1, 110, 88], + [5, 65, 41, 5, 66, 42], + [5, 54, 24, 7, 55, 25], + [11, 36, 12], + + // 16 + [5, 122, 98, 1, 123, 99], + [7, 73, 45, 3, 74, 46], + [15, 43, 19, 2, 44, 20], + [3, 45, 15, 13, 46, 16], + + // 17 + [1, 135, 107, 5, 136, 108], + [10, 74, 46, 1, 75, 47], + [1, 50, 22, 15, 51, 23], + [2, 42, 14, 17, 43, 15], + + // 18 + [5, 150, 120, 1, 151, 121], + [9, 69, 43, 4, 70, 44], + [17, 50, 22, 1, 51, 23], + [2, 42, 14, 19, 43, 15], + + // 19 + [3, 141, 113, 4, 142, 114], + [3, 70, 44, 11, 71, 45], + [17, 47, 21, 4, 48, 22], + [9, 39, 13, 16, 40, 14], + + // 20 + [3, 135, 107, 5, 136, 108], + [3, 67, 41, 13, 68, 42], + [15, 54, 24, 5, 55, 25], + [15, 43, 15, 10, 44, 16], + + // 21 + [4, 144, 116, 4, 145, 117], + [17, 68, 42], + [17, 50, 22, 6, 51, 23], + [19, 46, 16, 6, 47, 17], + + // 22 + [2, 139, 111, 7, 140, 112], + [17, 74, 46], + [7, 54, 24, 16, 55, 25], + [34, 37, 13], + + // 23 + [4, 151, 121, 5, 152, 122], + [4, 75, 47, 14, 76, 48], + [11, 54, 24, 14, 55, 25], + [16, 45, 15, 14, 46, 16], + + // 24 + [6, 147, 117, 4, 148, 118], + [6, 73, 45, 14, 74, 46], + [11, 54, 24, 16, 55, 25], + [30, 46, 16, 2, 47, 17], + + // 25 + [8, 132, 106, 4, 133, 107], + [8, 75, 47, 13, 76, 48], + [7, 54, 24, 22, 55, 25], + [22, 45, 15, 13, 46, 16], + + // 26 + [10, 142, 114, 2, 143, 115], + [19, 74, 46, 4, 75, 47], + [28, 50, 22, 6, 51, 23], + [33, 46, 16, 4, 47, 17], + + // 27 + [8, 152, 122, 4, 153, 123], + [22, 73, 45, 3, 74, 46], + [8, 53, 23, 26, 54, 24], + [12, 45, 15, 28, 46, 16], + + // 28 + [3, 147, 117, 10, 148, 118], + [3, 73, 45, 23, 74, 46], + [4, 54, 24, 31, 55, 25], + [11, 45, 15, 31, 46, 16], + + // 29 + [7, 146, 116, 7, 147, 117], + [21, 73, 45, 7, 74, 46], + [1, 53, 23, 37, 54, 24], + [19, 45, 15, 26, 46, 16], + + // 30 + [5, 145, 115, 10, 146, 116], + [19, 75, 47, 10, 76, 48], + [15, 54, 24, 25, 55, 25], + [23, 45, 15, 25, 46, 16], + + // 31 + [13, 145, 115, 3, 146, 116], + [2, 74, 46, 29, 75, 47], + [42, 54, 24, 1, 55, 25], + [23, 45, 15, 28, 46, 16], + + // 32 + [17, 145, 115], + [10, 74, 46, 23, 75, 47], + [10, 54, 24, 35, 55, 25], + [19, 45, 15, 35, 46, 16], + + // 33 + [17, 145, 115, 1, 146, 116], + [14, 74, 46, 21, 75, 47], + [29, 54, 24, 19, 55, 25], + [11, 45, 15, 46, 46, 16], + + // 34 + [13, 145, 115, 6, 146, 116], + [14, 74, 46, 23, 75, 47], + [44, 54, 24, 7, 55, 25], + [59, 46, 16, 1, 47, 17], + + // 35 + [12, 151, 121, 7, 152, 122], + [12, 75, 47, 26, 76, 48], + [39, 54, 24, 14, 55, 25], + [22, 45, 15, 41, 46, 16], + + // 36 + [6, 151, 121, 14, 152, 122], + [6, 75, 47, 34, 76, 48], + [46, 54, 24, 10, 55, 25], + [2, 45, 15, 64, 46, 16], + + // 37 + [17, 152, 122, 4, 153, 123], + [29, 74, 46, 14, 75, 47], + [49, 54, 24, 10, 55, 25], + [24, 45, 15, 46, 46, 16], + + // 38 + [4, 152, 122, 18, 153, 123], + [13, 74, 46, 32, 75, 47], + [48, 54, 24, 14, 55, 25], + [42, 45, 15, 32, 46, 16], + + // 39 + [20, 147, 117, 4, 148, 118], + [40, 75, 47, 7, 76, 48], + [43, 54, 24, 22, 55, 25], + [10, 45, 15, 67, 46, 16], + + // 40 + [19, 148, 118, 6, 149, 119], + [18, 75, 47, 31, 76, 48], + [34, 54, 24, 34, 55, 25], + [20, 45, 15, 61, 46, 16] + ]; + + QRRSBlock.getRSBlocks = function(typeNumber, errorCorrectLevel) { + + var rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel); + + if (rsBlock == undefined) { + throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + errorCorrectLevel); + } + + var length = rsBlock.length / 3; + + var list = new Array(); + + for (var i = 0; i < length; i++) { + + var count = rsBlock[i * 3 + 0]; + var totalCount = rsBlock[i * 3 + 1]; + var dataCount = rsBlock[i * 3 + 2]; + + for (var j = 0; j < count; j++) { + list.push(new QRRSBlock(totalCount, dataCount)); + } + } + + return list; + } + + QRRSBlock.getRsBlockTable = function(typeNumber, errorCorrectLevel) { + + switch (errorCorrectLevel) { + case QRErrorCorrectLevel.L: + return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0]; + case QRErrorCorrectLevel.M: + return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1]; + case QRErrorCorrectLevel.Q: + return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2]; + case QRErrorCorrectLevel.H: + return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3]; + default: + return undefined; + } + } + + //--------------------------------------------------------------------- + // QRBitBuffer + //--------------------------------------------------------------------- + + function QRBitBuffer() { + this.buffer = new Array(); + this.length = 0; + } + + QRBitBuffer.prototype = { + + get: function(index) { + var bufIndex = Math.floor(index / 8); + return ((this.buffer[bufIndex] >>> (7 - index % 8)) & 1) == 1; + }, + + put: function(num, length) { + for (var i = 0; i < length; i++) { + this.putBit(((num >>> (length - i - 1)) & 1) == 1); + } + }, + + getLengthInBits: function() { + return this.length; + }, + + putBit: function(bit) { + + var bufIndex = Math.floor(this.length / 8); + if (this.buffer.length <= bufIndex) { + this.buffer.push(0); + } + + if (bit) { + this.buffer[bufIndex] |= (0x80 >>> (this.length % 8)); + } + + this.length++; + } + }; + + //--------------------------------------------------------------------- + // Support Chinese + //--------------------------------------------------------------------- + function utf16To8(text) { + var result = ''; + var c; + for (var i = 0; i < text.length; i++) { + c = text.charCodeAt(i); + if (c >= 0x0001 && c <= 0x007F) { + result += text.charAt(i); + } else if (c > 0x07FF) { + result += String.fromCharCode(0xE0 | c >> 12 & 0x0F); + result += String.fromCharCode(0x80 | c >> 6 & 0x3F); + result += String.fromCharCode(0x80 | c >> 0 & 0x3F); + } else { + result += String.fromCharCode(0xC0 | c >> 6 & 0x1F); + result += String.fromCharCode(0x80 | c >> 0 & 0x3F); + } + } + return result; + } + + uQRCode = { + + errorCorrectLevel: QRErrorCorrectLevel, + + defaults: { + size: 354, + margin: 0, + backgroundColor: '#ffffff', + foregroundColor: '#000000', + fileType: 'png', // 'jpg', 'png' + errorCorrectLevel: QRErrorCorrectLevel.H, + typeNumber: -1 + }, + + make: function(options) { + return new Promise((reslove, reject) => { + var defaultOptions = { + canvasId: options.canvasId, + componentInstance: options.componentInstance, + text: options.text, + size: this.defaults.size, + margin: this.defaults.margin, + backgroundColor: this.defaults.backgroundColor, + foregroundColor: this.defaults.foregroundColor, + fileType: this.defaults.fileType, + errorCorrectLevel: this.defaults.errorCorrectLevel, + typeNumber: this.defaults.typeNumber + }; + if (options) { + for (var i in options) { + defaultOptions[i] = options[i]; + } + } + options = defaultOptions; + if (!options.canvasId) { + console.error('uQRCode: Please set canvasId!'); + return; + } + + function createCanvas() { + var qrcode = new QRCode(options.typeNumber, options.errorCorrectLevel); + qrcode.addData(utf16To8(options.text)); + qrcode.make(); + + var ctx = uni.createCanvasContext(options.canvasId, options.componentInstance); + ctx.setFillStyle(options.backgroundColor); + ctx.fillRect(0, 0, options.size, options.size); + + var tileW = (options.size - options.margin * 2) / qrcode.getModuleCount(); + var tileH = tileW; + + for (var row = 0; row < qrcode.getModuleCount(); row++) { + for (var col = 0; col < qrcode.getModuleCount(); col++) { + var style = qrcode.isDark(row, col) ? options.foregroundColor : options.backgroundColor; + ctx.setFillStyle(style); + var x = Math.round(col * tileW) + options.margin; + var y = Math.round(row * tileH) + options.margin; + var w = Math.ceil((col + 1) * tileW) - Math.floor(col * tileW); + var h = Math.ceil((row + 1) * tileW) - Math.floor(row * tileW); + ctx.fillRect(x, y, w, h); + } + } + + setTimeout(function() { + ctx.draw(false, (function() { + setTimeout(function() { + uni.canvasToTempFilePath({ + canvasId: options.canvasId, + fileType: options.fileType, + width: options.size, + height: options.size, + destWidth: options.size, + destHeight: options.size, + success: function(res) { + let resData; // 将统一为base64格式 + let tempFilePath = res.tempFilePath; // H5为base64,其他为相对路径 + + // #ifdef H5 + resData = tempFilePath; + options.success && options.success(resData); + reslove(resData); + // #endif + + // #ifdef APP-PLUS + const path = plus.io.convertLocalFileSystemURL(tempFilePath) // 绝对路径 + let fileReader = new plus.io.FileReader(); + fileReader.readAsDataURL(path); + fileReader.onloadend = res => { + resData = res.target.result; + options.success && options.success(resData); + reslove(resData); + }; + // #endif + + // #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO + uni.getFileSystemManager().readFile({ + filePath: tempFilePath, + encoding: 'base64', + success: res => { + resData = 'data:image/png;base64,' + res.data; + options.success && options.success(resData); + reslove(resData); + } + }) + // #endif + + // #ifndef H5 || APP-PLUS || MP-WEIXIN || MP-QQ || MP-TOUTIAO + if (plus) { + const path = plus.io.convertLocalFileSystemURL(tempFilePath) // 绝对路径 + let fileReader = new plus.io.FileReader(); + fileReader.readAsDataURL(path); + fileReader.onloadend = res => { + resData = res.target.result; + options.success && options.success(resData); + reslove(resData); + }; + } else { + uni.request({ + url: tempFilePath, + method: 'GET', + responseType: 'arraybuffer', + success: res => { + resData = `data:image/png;base64,${uni.arrayBufferToBase64(res.data)}`; // 把arraybuffer转成base64 + options.success && options.success(resData); + reslove(resData); + } + }) + } + // #endif + }, + fail: function(error) { + options.fail && options.fail(error); + reject(error); + }, + complete: function(res) { + options.complete && options.complete(res); + } + }, options.componentInstance); + }, options.text.length + 100); + })()); + }, 150); + } + + createCanvas(); + }); + } + } + +})() + +export default uQRCode diff --git a/components/cloud-image/cloud-image.vue b/components/cloud-image/cloud-image.vue new file mode 100644 index 0000000000000000000000000000000000000000..3f2815937d591835950b9bb99e159a952bb0eedd --- /dev/null +++ b/components/cloud-image/cloud-image.vue @@ -0,0 +1,70 @@ + + + \ No newline at end of file diff --git a/components/refreshBox/refreshBox.nvue b/components/refreshBox/refreshBox.nvue new file mode 100644 index 0000000000000000000000000000000000000000..f41c25cf49ac5b5fd7ca0c0b1ec58c582d932fd9 --- /dev/null +++ b/components/refreshBox/refreshBox.nvue @@ -0,0 +1,95 @@ + + + + diff --git a/components/uni-agreements/uni-agreements.vue b/components/uni-agreements/uni-agreements.vue new file mode 100644 index 0000000000000000000000000000000000000000..2c0af79c1e4b5593604e670f1c244a461c09c91d --- /dev/null +++ b/components/uni-agreements/uni-agreements.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/components/uni-bindMobileByMpWeixin/uni-bindMobileByMpWeixin.vue b/components/uni-bindMobileByMpWeixin/uni-bindMobileByMpWeixin.vue new file mode 100644 index 0000000000000000000000000000000000000000..bceda443e6b7de3d17a9a43f7be6f0aa525b7e91 --- /dev/null +++ b/components/uni-bindMobileByMpWeixin/uni-bindMobileByMpWeixin.vue @@ -0,0 +1,189 @@ + + + + + diff --git a/components/uni-load-state/i18n/en.json b/components/uni-load-state/i18n/en.json new file mode 100644 index 0000000000000000000000000000000000000000..b600ad47c8d868090e2f70ae8472d8eb5bd6e203 --- /dev/null +++ b/components/uni-load-state/i18n/en.json @@ -0,0 +1,6 @@ +{ + "noData": "No Data", + "noNetwork": "Network error", + "toSet": "Go to settings", + "error": "error" +} diff --git a/components/uni-load-state/i18n/index.js b/components/uni-load-state/i18n/index.js new file mode 100644 index 0000000000000000000000000000000000000000..c70a38619a5e2b7f933ec3dda91db5ddc9840231 --- /dev/null +++ b/components/uni-load-state/i18n/index.js @@ -0,0 +1,6 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +export default { + en, + 'zh-Hans': zhHans +} diff --git a/components/uni-load-state/i18n/zh-Hans.json b/components/uni-load-state/i18n/zh-Hans.json new file mode 100644 index 0000000000000000000000000000000000000000..4fa8e1a78267e21eac0fff2c3f52053319fb83f5 --- /dev/null +++ b/components/uni-load-state/i18n/zh-Hans.json @@ -0,0 +1,6 @@ +{ + "noData": "暂无数据", + "noNetwork": "网络异常", + "toSet": "前往设置", + "error": "错误" +} diff --git a/components/uni-load-state/readme.md b/components/uni-load-state/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..07288d69d7a7a687a7d3b5ee6840c5516cdee510 --- /dev/null +++ b/components/uni-load-state/readme.md @@ -0,0 +1,3 @@ +新增uni-load-state组件,这是一个封装数据请求状态的组件。根据uniCloud-db组件提供的参数直接响应对应的效果。 +包括加载中、当前页面为空、没有更多数据、上拉加载更多; +加载错误判断,如果是断网就引导打开系统网络设置页面。恢复联网后自动触发networkResume方法。 \ No newline at end of file diff --git a/components/uni-load-state/uni-load-state.vue b/components/uni-load-state/uni-load-state.vue new file mode 100644 index 0000000000000000000000000000000000000000..640f4a7c3d383fb3dc313b3f536d1e8419d55f59 --- /dev/null +++ b/components/uni-load-state/uni-load-state.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/components/uni-quick-login/uni-quick-login.vue b/components/uni-quick-login/uni-quick-login.vue new file mode 100644 index 0000000000000000000000000000000000000000..bfc75b2b0ca91b90f39a196d0b064b7daa04a398 --- /dev/null +++ b/components/uni-quick-login/uni-quick-login.vue @@ -0,0 +1,396 @@ + + + + \ No newline at end of file diff --git a/components/uni-section/uni-section.vue b/components/uni-section/uni-section.vue new file mode 100644 index 0000000000000000000000000000000000000000..b3fc47c559f0cd4ea7f63d0c798ae342c766e57d --- /dev/null +++ b/components/uni-section/uni-section.vue @@ -0,0 +1,139 @@ + + + + diff --git a/components/uni-send-sms-code/uni-send-sms-code.vue b/components/uni-send-sms-code/uni-send-sms-code.vue new file mode 100644 index 0000000000000000000000000000000000000000..4b59b75e9a2bd34ca2d6215b7ece14d4310b3d04 --- /dev/null +++ b/components/uni-send-sms-code/uni-send-sms-code.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/components/uni-user-profile/uni-user-profile.vue b/components/uni-user-profile/uni-user-profile.vue new file mode 100644 index 0000000000000000000000000000000000000000..62beb86485fa39557e91887f8408d0d1cd5b3d2f --- /dev/null +++ b/components/uni-user-profile/uni-user-profile.vue @@ -0,0 +1,194 @@ + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000000000000000000000000000000000000..bb564510ede52ef9c0dabfd730531233d977de69 --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ + + + + + + + + + + +
+ + + \ No newline at end of file diff --git a/lang/en.js b/lang/en.js new file mode 100644 index 0000000000000000000000000000000000000000..43e1bfbcaeb9c0cc3aa28dfaed417ca93c0b81b1 --- /dev/null +++ b/lang/en.js @@ -0,0 +1,199 @@ +export default { + tabbar:'List,Grid,contacts,Mine', + agreementsTitle:'User service agreement,Privacy policy', + common: { + wechatFriends: "friends", + wechatBbs: "bbs", + weibo: "weibo", + more: "more", + agree:"agree", + copy: "copy", + wechatApplet: "applet", + cancelShare: "cancel sharing", + updateSucceeded: "update succeeded", + phonePlaceholder: "Please enter your mobile phone number", + verifyCodePlaceholder: "Please enter the verification code", + newPasswordPlaceholder: "Please enter a new password", + confirmNewPasswordPlaceholder: "Please confirm the new password", + confirmPassword: "Please confirm the password", + verifyCodeSend: "Verification code has been sent to via SMS", + passwordDigits: "The password is 6 - 20 digits", + getVerifyCode: "Get Code", + noAgree: "You have not agreed to the privacy policy agreement", + gotIt: "got it", + login: "sign in", + error: "error", + complete: "complete", + submit: "Submit", + formatErr: "Incorrect mobile phone number format", + sixDigitCode: "Please enter a 6-digit verification code", + resetNavTitle:"Reset password" + }, + list: { + inputPlaceholder: "Please enter the search content", + }, + search: { + cancelText: "cancel", + searchHistory: "search history", + searchDiscovery: "search discovery", + deleteAll: "delete all", + delete: "delete", + deleteTip: "Are you sure to clear the search history ?", + complete: "complete", + searchHiddenTip: "Current search found hidden", + }, + grid: { + grid: "Grid Assembly", + visibleToAll: "Visible to all", + invisibleToTourists: "Invisible to tourists", + adminVisible: "Admin visible", + clickTip: "Click the", + clickTipGrid: "grid", + }, + mine: { + showText: "Text", + signIn: "Check In Reward", + toEvaluate: "To Evaluate", + readArticles: "Read Articles", + myScore: "My Score", + invite: "Invite Friends", + guestBook: "Guest Book Example", + feedback: "Problems And Feedback", + settings: "Settings", + about: "About", + checkUpdate: "Check for Updates", + clicked: "You Clicked", + checkScore: "Please check your points after logging in", + currentScore: "The current score is ", + noScore: "There are currently no points", + notLogged: "not logged in", + }, + userinfo: { + navigationBarTitle:"My Profile", + ProfilePhoto: "Profile Photo", + nickname: "Nickname", + notSet: "not set", + phoneNumber: "Phone Number", + notSpecified: "Not Specified", + setNickname: "Set Nickname ", + setNicknamePlaceholder: "Please enter a nickname to set", + bindPhoneNumber: "One click binding of local number", + bindOtherLogin: "Other number binding", + noChange: "No change", + uploading: "uploading", + requestFail: "Request for service failed", + setting: "setting", + deleteSucceeded: "Delete succeeded", + setSucceeded: "Set successfully", + }, + smsCode: { + resendVerifyCode: "resend", + phoneErrTip: "Mobile phone number format error", + sendSuccessTip: "SMS verification code sent successfully", + }, + loadMore: { + noData: "No Data", + noNetwork: "Network error", + toSet: "Go to settings", + error: "error", + }, + guestbook: { + navigationBarTitle:"Message board", + msgContent: "post message content", + notAvailable: "not available for visitors who are not logged in", + send: "send", + addSucceeded: "Successfully added", + noPermission: "You don't have permission for this operation", + }, + uniFeedback: { + navigationBarTitle: "Problems and feedback", + msgTitle: "Message content", + imgTitle: "Picture list", + contacts: "contacts", + phone: "contact number", + submit: "submit", + }, + settings: { + navigationBarTitle:"Settings", + userInfo: "Personal Data", + changePassword: "change password", + clearTmp: "clean cache", + pushServer: "push function", + fingerPrint: "fingerprint unlock", + facial: "face unlock", + deactivate: "Deactivate", + logOut: "Logout", + login: "Login", + changeLanguage: "Language", + please: "please", + successText: "success", + failTip: "Authentication failed. Please try again", + authFailed: "authentication failed", + deviceNoOpen: "The device is not turned on", + fail: "fail", + tips: "tips", + exitLogin: "Do you want to log out?", + cancelText: "cancel", + confirmText: "confirm", + clearing: "clearing", + clearedSuccessed: "Cleared successfully", + }, + deactivate: { + cancelText: "cancel", + nextStep: "next step", + navigationBarTitle:"Logout prompt" + }, + about: { + sacnQR: "Scan the QR Code and your friends can also download it", + client: "applCantion", + and: "And", + about: "About", + }, + invite: { + download: "Download", + }, + login: { + phoneLogin: "After logging in, you can show yourself", + phoneLoginTip: "Unregistered mobile phone numbers will be automatically registered after verification", + getVerifyCode: "Get Code", + }, + uniQuickLogin: { + accountLogin: "Account", + SMSLogin: "SMS", + wechatLogin: "wechat", + appleLogin: "Apple", + oneClickLogin: "One click login", + QQLogin: "QQ", + xiaomiLogin: "Xiaomi", + getProviderFail: "Failed to get service provider", + loginErr: "Login service initialization error", + chooseOtherLogin: "Click the third-party login", + }, + pwdLogin: { + pwdLogin: "User name password login", + placeholder: "Please enter mobile number / user name", + passwordPlaceholder: "Please input a password", + verifyCodePlaceholder: "Please enter the verification code", + login: "sign in", + forgetPassword: "Forget password", + register: "Registered account", + }, + register: { + navigationBarTitle:"register", + usernamePlaceholder: "Please enter user name", + nicknamePlaceholder: "Please enter user nickname", + passwordDigitsPlaceholder: "Please enter a 6-20 digit password", + passwordAgain: "Enter the password again", + registerAndLogin: "Register and log in", + }, + listDetail: { + follow: "Click follow", + newsErr: "Error, news ID is empty", + }, + newsLog:{ + navigationBarTitle:"Reading Log" + }, + bindMobile:{ + navigationBarTitle:"Bind Mobile" + } +} diff --git a/lang/i18n.js b/lang/i18n.js new file mode 100644 index 0000000000000000000000000000000000000000..2a3599046457dccedf51380d55496324fccf717a --- /dev/null +++ b/lang/i18n.js @@ -0,0 +1,102 @@ +import langEn from './en' +import zhHans from './zh-Hans' +import uniStarterConfig from '../uni-starter.config.js' +const {i18n:{enable:i18nEnable} }= uniStarterConfig +const messages = { + 'en': langEn, + 'zh-Hans': zhHans +} +let currentLang +if(i18nEnable){ + currentLang = uni.getStorageSync('CURRENT_LANG') +}else{ + currentLang = "zh-Hans" +} +// console.log(uni.getStorageSync('CURRENT_LANG'),currentLang); +if (!currentLang) { + if (uni.getLocale) { + console.log('获取应用语言:', uni.getLocale()); + let language = 'en' + if (uni.getLocale() != 'en') { + language = 'zh-Hans' + } + uni.setStorageSync('CURRENT_LANG', language) + currentLang = language + } else { + uni.getSystemInfo({ + success: function(res) { + console.log('获取设备信息:', res); + let language = 'zh-Hans' + if (res.language == 'en') { + language = 'en' + } + uni.setStorageSync('CURRENT_LANG', language) + currentLang = language + }, + fail: (err) => { + console.error(err) + } + }) + } +} +let i18nConfig = { + locale: currentLang, // set locale + messages // set locale messages +} + +// #ifdef VUE2 +import Vue from 'vue' +import VueI18n from 'vue-i18n' +Vue.use(VueI18n) +const i18n = new VueI18n(i18nConfig) +// #endif + +// #ifdef VUE3 +import { + createI18n +} from 'vue-i18n' +const i18n = createI18n(i18nConfig) +// #endif + +export default i18n + + +if(i18nEnable){ +console.log(` + 你已开启多语言国际化,将自动根据语言获取【lang/en.js】或【lang/en.js】文件中配置的tabbar的值, + 覆盖你在pages.json中的tabbar的值 + 如果你不需要多语言国际化,请打开配置文件uni-starter.config.js找到 -> i18n -> enable把值设置为false +`); + let initLanguageAfter = () => { + function $i18n(e){ + // #ifdef VUE3 + return i18n.global.messages[i18n.global.locale][e] + // #endif + return i18n.messages[i18n.locale][e] + } + setTimeout(function(){ + //底部tabbar更新 + $i18n('tabbar').split(',').forEach((text, index) => { + // console.log(text); + uni.setTabBarItem({ + index, + text, + complete: e => { + // console.log("e: " + JSON.stringify(e)); + } + }) + }) + },1) + //更新 uni-starter.config agreements + let agreementsTitle = $i18n('agreementsTitle').split(',') + let agreements = uniStarterConfig.about.agreements + agreements[0].title = agreementsTitle[0] + agreements[1].title = agreementsTitle[1] + uniStarterConfig.about.agreements = agreements + } + initLanguageAfter() + uni.$on('changeLanguage', e => { + console.log('changeLanguage', e); + initLanguageAfter(e) + }) +} \ No newline at end of file diff --git a/lang/zh-Hans.js b/lang/zh-Hans.js new file mode 100644 index 0000000000000000000000000000000000000000..7cbcb56a1cd76d0ea354f6f19f5b133b925b9c3c --- /dev/null +++ b/lang/zh-Hans.js @@ -0,0 +1,200 @@ +export default { + tabbar:'列表,宫格,通讯录,我的', + agreementsTitle:'用户服务协议,隐私政策', + common:{ + wechatFriends: "微信好友", + wechatBbs: "微信朋友圈", + weibo:"微博", + more: "更多", + agree:"同意", + copy: "复制", + wechatApplet: "微信小程序", + cancelShare: "取消分享", + updateSucceeded: "更新成功", + phonePlaceholder: "请输入手机号", + verifyCodePlaceholder: "请输入验证码", + newPasswordPlaceholder: "请输入新密码", + confirmNewPasswordPlaceholder: "请确认新密码", + confirmPassword: "请确认密码", + verifyCodeSend: "验证码已通过短信发送至", + passwordDigits: "密码为6 - 20位", + getVerifyCode: "获取验证码", + noAgree: "你未同意隐私政策协议", + gotIt: "知道了", + login: "登录", + error: "错误", + complete: "完成", + submit: "提交", + formatErr:"手机号码格式不正确", + sixDigitCode:"请输入6位验证码", + resetNavTitle:"重置密码" + + }, + list: { + inputPlaceholder: "请输入搜索内容", + }, + search:{ + cancelText: '取消', + searchHistory: "搜索历史", + searchDiscovery: "搜索发现", + deleteAll: "全部删除", + delete: "删除", + deleteTip: "确认清空搜索历史吗?", + complete: "完成", + searchHiddenTip: "当前搜索发现已隐藏", + }, + grid:{ + grid: "宫格组件", + visibleToAll: "所有人可见", + invisibleToTourists: "游客不可见", + adminVisible: "管理员可见", + clickTip: "点击第", + clickTipGrid: "个宫格", + }, + mine:{ + showText: "文字", + signIn: "签到有奖", + toEvaluate: "去评分", + readArticles: "阅读过的文章", + myScore: "我的积分", + invite: "分销推荐", + guestBook: "留言板示例", + feedback: "问题与反馈", + settings: "设置", + checkUpdate: "检查更新", + about: "关于", + clicked: "你点击了", + checkScore: "请登录后查看积分", + currentScore: "当前积分为", + noScore: "当前无积分", + notLogged: "未登录", + }, + userinfo:{ + navigationBarTitle:"个人资料", + ProfilePhoto: "头像", + nickname: "昵称", + notSet: "未设置", + phoneNumber: "手机号", + notSpecified: "未绑定", + setNickname: "设置昵称", + setNicknamePlaceholder: "请输入要设置的昵称", + bindPhoneNumber: "本机号码一键绑定", + bindOtherLogin: "其他号码绑定", + noChange: "没有变化", + uploading: "正在上传", + requestFail: "请求服务失败", + setting: "设置中", + deleteSucceeded: "删除成功", + setSucceeded: "设置成功", + }, + smsCode:{ + resendVerifyCode: "重新发送", + phoneErrTip: "手机号格式错误", + sendSuccessTip: "短信验证码发送成功", + }, + loadMore:{ + noData: "暂无数据", + noNetwork: "网络异常", + toSet: "前往设置", + error: "错误", + }, + guestbook:{ + navigationBarTitle:"留言板", + msgContent: "发表留言内容", + notAvailable: "未登录游客不可用", + send: "发送", + addSucceeded: "新增成功", + noPermission: "你没有该操作权限", + }, + uniFeedback:{ + navigationBarTitle:"问题与反馈", + msgTitle: "留言内容", + imgTitle: "图片列表", + contacts: "联系人", + phone: "联系电话", + submit: "提交", + }, + settings:{ + navigationBarTitle:"设置", + userInfo: "个人资料", + changePassword: "修改密码", + clearTmp: "清理缓存", + pushServer: "推送功能", + fingerPrint: "指纹解锁", + facial: "人脸解锁", + deactivate: "注销账号", + logOut: "退出登录", + login: "登录", + failTip: "认证失败请重试", + authFailed: "认证失败", + changeLanguage: "切换语言", + please: "请用", + successText: "成功", + deviceNoOpen: "设备未开启", + fail: "失败", + tips: "提示", + exitLogin: "是否退出登录?", + clearing: "清除中", + clearedSuccessed: "清除成功", + confirmText: "确定", + cancelText: '取消', + }, + deactivate:{ + cancelText: '取消', + nextStep: "下一步", + navigationBarTitle:"注销提示" + }, + about:{ + sacnQR: "扫描二维码,您的朋友也可以下载", + client: "客户端", + and: "和", + about: "关于", + }, + invite:{ + download: "下载", + }, + login:{ + phoneLogin: "登录后即可展示自己", + phoneLoginTip: "未注册的手机号验证通过后将自动注册", + getVerifyCode: "获取验证码", + }, + uniQuickLogin:{ + accountLogin: "账号登录", + SMSLogin: "短信验证码", + wechatLogin: "微信登录", + appleLogin: "苹果登录", + oneClickLogin: "一键登录", + QQLogin: "QQ登录", + xiaomiLogin: "小米登录", + getProviderFail: "获取服务供应商失败", + loginErr: "登录服务初始化错误", + chooseOtherLogin: "点击了第三方登录", + }, + pwdLogin:{ + pwdLogin: "用户名密码登录", + placeholder: "请输入手机号/用户名", + passwordPlaceholder: "请输入密码", + verifyCodePlaceholder: "请输入验证码", + login: "登录", + forgetPassword: "忘记密码", + register: "注册账号", + }, + register:{ + navigationBarTitle:"注册", + usernamePlaceholder: "请输入用户名", + nicknamePlaceholder: "请输入用户昵称", + registerAndLogin: "注册并登录", + passwordDigitsPlaceholder: "请输入6-20位密码", + passwordAgain: "再次输入密码", + }, + listDetail:{ + follow: "点击关注", + newsErr: "出错了,新闻ID为空", + }, + newsLog:{ + navigationBarTitle:"阅读记录" + }, + bindMobile:{ + navigationBarTitle:"绑定手机号码" + } +} diff --git a/main.js b/main.js new file mode 100644 index 0000000000000000000000000000000000000000..a0b835c22b61a21a8932abb6c6de7ebef6dbbd13 --- /dev/null +++ b/main.js @@ -0,0 +1,29 @@ +import App from './App' +import store from './store' +import i18n from './lang/i18n' + + +// #ifndef VUE3 +import Vue from 'vue' +Vue.config.productionTip = false +Vue.prototype.$store = store +App.mpType = 'app' +const app = new Vue({ + i18n, + store, + ...App +}) +app.$mount() +// #endif + + +// #ifdef VUE3 +import {createSSRApp} from 'vue' + +export function createApp() { + const app = createSSRApp(App) + app.use(i18n) + app.use(store) + return {app} +} +// #endif diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000000000000000000000000000000000000..0c95e9f1158aac5301218c696f50c11c865476d3 --- /dev/null +++ b/manifest.json @@ -0,0 +1,229 @@ +{ + "name": "统一应用基本项目", + "appid": "请点击重新获取", + "description": "云端一体应用快速开发模版", + "versionName": "1.0.0", + "versionCode": "100", + "transformPx": false, + "app-plus": { + "locales": { + "en": { + // 英文 + "name": "uni-starter", // 应用名称 + "android": { + "strings": { + //Android平台自定义字符串 + "CustomKey": "CustomValue" + } + }, + "ios": { + "privacyDescription": { + //iOS平台隐私访问描述信息 + "NSPhotoLibraryUsageDescription": "access to the user’s photo library(read)" + }, + "infoPlist": { + //iOS平台自定义InfoPlist.strings + "CustomKey": "CustomValue" + } + } + }, + "zh": { + // 中文(简体) + "name": "统一应用基本项目" // 应用名称 + } + }, + "privacy": { + "prompt": "template", + "template": { + "title": "服务协议和隐私政策", + "message": "  请你务必审慎阅读、充分理解“服务协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。
  你可阅读《服务协议》《隐私政策》了解详细信息。如果你同意,请点击下面按钮开始接受我们的服务。", + "buttonAccept": "同意", + "buttonRefuse": "暂不同意" + } + }, + "compatible": { + "ignoreVersion": true + }, + "usingComponents": true, + "nvueStyleCompiler": "uni-app", + "compilerVersion": 3, + "splashscreen": { + "alwaysShowBeforeRender": false, + "waiting": true, + "autoclose": true, + "delay": 0 + }, + "modules": { + "Fingerprint": { + }, + "Share": { + }, + "OAuth": { + }, + "FaceID": { + }, + "Geolocation": { + }, + "Push": { + }, + "Bluetooth": { + } + }, + "distribute": { + "android": { + "permissions": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "abiFilters": [ + "armeabi-v7a", + "arm64-v8a", + "x86" + ] + }, + "ios": { + "capabilities": { + "entitlements": { + "com.apple.developer.associated-domains": [ + "applinks:static-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e.bspapp.com" + ] + } + } + }, + "sdkConfigs": { + "oauth": { + "apple": { + }, + "weixin": { + "appid": "", + "appsecret": "", + "UniversalLinks": "" + }, + "univerify": { + } + }, + "ad": { + }, + "share": { + "weixin": { + "appid": "", + "UniversalLinks": "" + } + }, + "geolocation": { + "baidu": { + "__platform__": [ + "ios", + "android" + ], + "appkey_ios": "请填写地图的key", + "appkey_android": "请填写地图的key" + } + }, + "push": { + "unipush": { + } + }, + "payment": { + } + }, + "icons": { + "android": { + "hdpi": "", + "xhdpi": "", + "xxhdpi": "", + "xxxhdpi": "" + }, + "ios": { + "appstore": "", + "ipad": { + "app": "", + "app@2x": "", + "notification": "", + "notification@2x": "", + "proapp@2x": "", + "settings": "", + "settings@2x": "", + "spotlight": "", + "spotlight@2x": "" + }, + "iphone": { + "app@2x": "", + "app@3x": "", + "notification@2x": "", + "notification@3x": "", + "settings@2x": "", + "settings@3x": "", + "spotlight@2x": "", + "spotlight@3x": "" + } + } + }, + "splashscreen": { + "iosStyle": "common", + "androidStyle": "common" + } + }, + "nvueLaunchMode": "" + }, + "quickapp": { + }, + "mp-weixin": { + "appid": "", + "setting": { + "urlCheck": false, + "es6": false + }, + "usingComponents": true, + "betterScopedSlots": true, + "permission": { + "scope.userLocation": { + "desc": "演示在onShow生命周期获取地理位置" + } + } + }, + "mp-alipay": { + "usingComponents": true + }, + "mp-baidu": { + "usingComponents": true + }, + "mp-toutiao": { + "usingComponents": true + }, + "uniStatistics": { + "enable": false + }, + "h5": { + "template": "", + "sdkConfigs": { + "maps": { + "qqmap": { + "key": "" + } + } + }, + "router": { + "base": "" + } + }, + "_spaceID": "", + "vueVersion": "2" +} +//... +// 中文(简体) +// 应用名称 diff --git a/package.json b/package.json new file mode 100644 index 0000000000000000000000000000000000000000..c0099edbd8f7aa3b393a3ff13ba7b5b81b0cb6d6 --- /dev/null +++ b/package.json @@ -0,0 +1,85 @@ +{ + "id": "uni-starter", + "displayName": "uni-starter", + "version": "1.1.22", + "description": "云端一体应用快速开发基本项目模版", + "keywords": [ + "login", + "登录", + "搜索", + "uni-id例子", + "留言板" + ], + "repository": "https://codechina.csdn.net/dcloud/uni-starter.git", + "engines": { + "HBuilderX": "^3.2.6" + }, + "dcloudext": { + "category": [ + "uniCloud", + "云端一体项目模板" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "u", + "Android Browser": "u", + "微信浏览器(Android)": "u", + "QQ浏览器(Android)": "u" + }, + "H5-pc": { + "Chrome": "y", + "IE": "n", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "y", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + }, + "dependencies": {} +} diff --git a/pages.json b/pages.json new file mode 100644 index 0000000000000000000000000000000000000000..ebd08303990dbcfdb950556618a0575f169a43b6 --- /dev/null +++ b/pages.json @@ -0,0 +1,240 @@ +{ + "pages": [{ + "path": "pages/list/list", + "style": { + // #ifndef APP-PLUS + "enablePullDownRefresh": true, + // #endif + "navigationStyle": "custom" + } + }, + { + "path": "pages/grid/grid", + "style": { + //#ifndef MP + "navigationStyle": "custom" + //#endif + } + }, { + "path": "pages/ucenter/login-page/index/index", + "style": { + "navigationBarTitleText": "", + "app-plus": { + "animationType": "none", + "popGesture": "none" + } + } + }, { + "path": "pages/list/search/search", + "style": { + "navigationBarTitleText":"搜索" + } + }, { + "path": "pages/list/detail", + "style": { + "app-plus": { + "titleNView": { + "buttons": [{ + "type": "share" + }], + "type": "transparent" + } + }, + "h5": { + "titleNView": { + "type": "transparent" + } + }, + "navigationBarTitleText": "文章详情" + } + }, { + "path": "pages/ucenter/userinfo/bind-mobile/bind-mobile", + "style": { + "navigationBarTitleText": "绑定手机号码" + } + }, + { + "path": "pages/ucenter/ucenter", + "style": { + "navigationStyle": "custom" + } + }, { + "path": "pages/ucenter/about/about", + "style": { + "navigationBarTitleText": "关于" + // #ifdef APP-PLUS + , + "app-plus": { + "titleNView": { + "buttons": [{ + "type": "share" + }] + } + } + // #endif + } + + }, + { + "path": "uni_modules/uni-upgrade-center-app/pages/upgrade-popup", + "style": { + "disableScroll": true, + "app-plus": { + "backgroundColorTop": "transparent", + "background": "transparent", + "titleNView": false, + "scrollIndicator": false, + "popGesture": "none", + "animationType": "fade-in", + "animationDuration": 200 + + } + } + }, { + "path": "pages/uni-agree/uni-agree", + "style": { + "navigationStyle": "custom", + "app-plus": { + "popGesture": "none" + } + } + }, { + "path": "pages/ucenter/settings/settings", + "style": { + "navigationBarTitleText": "设置" + } + + }, { + "path": "pages/ucenter/userinfo/userinfo", + "style": { + "navigationBarTitleText": "个人资料" + } + }, { + "path": "pages/ucenter/userinfo/cropImage", + "style": { + "navigationStyle": "custom" + } + }, { + "path": "pages/ucenter/login-page/pwd-login/pwd-login", + "style": { + "navigationBarTitleText": "" + } + }, { + "path": "pages/ucenter/login-page/pwd-retrieve/pwd-retrieve", + "style": { + "navigationBarTitleText": "重置密码" + } + }, { + "path": "pages/ucenter/login-page/phone-code/phone-code", + "style": { + "navigationBarTitleText": "" + } + + }, { + "path": "pages/common/webview/webview", + "style": { + "navigationBarTitleText": "", + "enablePullDownRefresh": false + } + + }, { + "path": "pages/ucenter/login-page/register/register", + "style": { + "navigationBarTitleText": "注册", + "enablePullDownRefresh": false + } + }, + { + "path": "pages/ucenter/read-news-log/read-news-log", + "style": { + "navigationBarTitleText": "阅读记录", + "enablePullDownRefresh": true + } + }, { + "path": "pages/ucenter/invite/invite", + "style": { + "navigationStyle": "custom", + "enablePullDownRefresh": false + } + + }, { + "path": "pages/ucenter/settings/deactivate/deactivate", + "style": { + "navigationBarTitleText": "注销提醒", + "enablePullDownRefresh": false + } + }, { + "path": "uni_modules/uni-feedback/pages/opendb-feedback/opendb-feedback", + "style": { + "navigationBarTitleText": "意见反馈", + "enablePullDownRefresh": false + } + }, { + "path": "pages/ucenter/guestbook/guestbook", + "style": { + "navigationBarTitleText": "留言板", + "enablePullDownRefresh": false, + "app-plus": { + "titleNView": { + "buttons": [{ + "text": "切换", + "fontSize": "12px" + }, + { + "text": "注销", + "fontSize": "12px" + } + ] + } + } + } + } + ], + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "uni-starter", + "navigationBarBackgroundColor": "#FFFFFF", + "backgroundColor": "#F8F8F8", + "enablePullDownRefresh": false + }, + "condition": { + "list": [{ + "path": "pages/list/detail" + }, { + "path": "pages/list/list" + }, + { + "path": "pages/ucenter/login-page/index/index" + }, { + "path": "pages/ucenter/userinfo/userinfo" + }, + { + "path": "pages/ucenter/settings/settings" + } + ], + "current": 1 + }, + "tabBar": { + "color": "#7A7E83", + "selectedColor": "#007AFF", + "borderStyle": "black", + "backgroundColor": "#FFFFFF", + "list": [{ + "pagePath": "pages/list/list", + "iconPath": "static/tabbar/list.png", + "selectedIconPath": "static/tabbar/list_active.png", + "text": "列表" + }, { + "pagePath": "pages/grid/grid", + "iconPath": "static/tabbar/grid.png", + "selectedIconPath": "static/tabbar/grid_active.png", + "text": "宫格" + } + , { + "pagePath": "pages/ucenter/ucenter", + "iconPath": "static/tabbar/me.png", + "selectedIconPath": "static/tabbar/me_active.png", + "text": "我的" + }] + } +} diff --git a/pages/common/webview/webview.vue b/pages/common/webview/webview.vue new file mode 100644 index 0000000000000000000000000000000000000000..5fa64b98d363de76140e0d24fe11ac6e43ce61da --- /dev/null +++ b/pages/common/webview/webview.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/pages/grid/grid.vue b/pages/grid/grid.vue new file mode 100644 index 0000000000000000000000000000000000000000..0358fbc097ff85e1cd16520d12d4d36fcf46a0f0 --- /dev/null +++ b/pages/grid/grid.vue @@ -0,0 +1,222 @@ + + + + + diff --git a/pages/list/detail.vue b/pages/list/detail.vue new file mode 100644 index 0000000000000000000000000000000000000000..ee1ca77d803f5aad8c7195ad1589d7bb66213eed --- /dev/null +++ b/pages/list/detail.vue @@ -0,0 +1,368 @@ + + + + + diff --git a/pages/list/list.nvue b/pages/list/list.nvue new file mode 100644 index 0000000000000000000000000000000000000000..dec5cb44ae2105be3c79802a8685e0bd43c2764f --- /dev/null +++ b/pages/list/list.nvue @@ -0,0 +1,238 @@ + + + + + diff --git a/pages/list/search/search.nvue b/pages/list/search/search.nvue new file mode 100644 index 0000000000000000000000000000000000000000..12ab77042bec049036e79227fbab7a68bf283d51 --- /dev/null +++ b/pages/list/search/search.nvue @@ -0,0 +1,506 @@ + + + + + + + diff --git a/pages/ucenter/about/about.vue b/pages/ucenter/about/about.vue new file mode 100644 index 0000000000000000000000000000000000000000..beab4a57e7ec695940bd94e86ffbda80569356f0 --- /dev/null +++ b/pages/ucenter/about/about.vue @@ -0,0 +1,203 @@ + + + diff --git a/pages/ucenter/guestbook/guestbook.vue b/pages/ucenter/guestbook/guestbook.vue new file mode 100644 index 0000000000000000000000000000000000000000..0edc841db09a8c151d1d9fdfa03cea80703d5c92 --- /dev/null +++ b/pages/ucenter/guestbook/guestbook.vue @@ -0,0 +1,223 @@ + + + + + \ No newline at end of file diff --git a/pages/ucenter/invite/invite.vue b/pages/ucenter/invite/invite.vue new file mode 100644 index 0000000000000000000000000000000000000000..5bb4df85a7e9dc21a70e183a392fe5fd3d3daeda --- /dev/null +++ b/pages/ucenter/invite/invite.vue @@ -0,0 +1,179 @@ + + + diff --git a/pages/ucenter/login-page/common/login-page.css b/pages/ucenter/login-page/common/login-page.css new file mode 100644 index 0000000000000000000000000000000000000000..34ff42ce28f58158db20c7e9736b27922b84a93d --- /dev/null +++ b/pages/ucenter/login-page/common/login-page.css @@ -0,0 +1,61 @@ +/* #ifndef APP-NVUE */ +view { + display: flex; + box-sizing: border-box; + flex-direction: column; +} + +/* #endif */ + +.content { + padding: 0 50rpx; + width: 750rpx; + flex: 1; +} + +.input-box { + padding: 0 15px; + margin-bottom: 10px; + background-color: #F8F8F8; + border-radius: 6px; + font-size: 28rpx; +} + +.get-code { + margin: 0; + margin-top: 15px; + background-color: #007aff; + color: #FFFFFF; +} + +.input-box, +.get-code { + height: 45px; + line-height: 45px; +} + +.title { + text-align: center; + padding-bottom: 5px; +} + +.tip { + color: #666666; + font-size: 26rpx; + margin: 6px 0; +} + +.easyinput { + background-color: #F8F8F8; + border-radius: 6rpx; +} + +.send-btn { + width: 100%; + margin-top: 15px; + border-radius: 6rpx; +} + +.link { + color: #04498c; +} diff --git a/pages/ucenter/login-page/common/login-page.mixin.js b/pages/ucenter/login-page/common/login-page.mixin.js new file mode 100644 index 0000000000000000000000000000000000000000..6b161253c7fa2ff16062ab1b3577d512b3b00d3c --- /dev/null +++ b/pages/ucenter/login-page/common/login-page.mixin.js @@ -0,0 +1,15 @@ +import {mapMutations} from 'vuex'; +import loginSuccess from './loginSuccess.js'; +let mixin = { + methods:{ + ...mapMutations({ + setUserInfo: 'user/login' + }), + loginSuccess(result){ + loginSuccess(result) + delete result.userInfo.token + this.setUserInfo(result.userInfo) + } + } +} +export default mixin \ No newline at end of file diff --git a/pages/ucenter/login-page/common/loginSuccess.js b/pages/ucenter/login-page/common/loginSuccess.js new file mode 100644 index 0000000000000000000000000000000000000000..db1540f381ae82913c3a555bd411fb49a1568049 --- /dev/null +++ b/pages/ucenter/login-page/common/loginSuccess.js @@ -0,0 +1,20 @@ +export default function(result){ + uni.showToast({ + title: '登录成功', + icon: 'none' + }); + console.log('登录成功',result); + + var delta = 0//判断需要返回几层 + let pages = getCurrentPages(); + // console.log(pages); + pages.forEach((page,index)=>{ + // console.log(pages[pages.length-index-1].route.split('/')[2]); + pages[pages.length-index-1].route.split('/') + if(pages[pages.length-index-1].route.split('/')[2] == 'login-page'){ + delta ++ + } + }) + // console.log('判断需要返回几层',delta); + uni.navigateBack({delta}) +} diff --git a/pages/ucenter/login-page/index/index.vue b/pages/ucenter/login-page/index/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..f30fdfaac1cee50fb74521fb11641da2ac467516 --- /dev/null +++ b/pages/ucenter/login-page/index/index.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/pages/ucenter/login-page/phone-code/phone-code.vue b/pages/ucenter/login-page/phone-code/phone-code.vue new file mode 100644 index 0000000000000000000000000000000000000000..8eb43be8513d78cdcfb6b240e03c155c215a5510 --- /dev/null +++ b/pages/ucenter/login-page/phone-code/phone-code.vue @@ -0,0 +1,72 @@ + + + diff --git a/pages/ucenter/login-page/pwd-login/pwd-login.vue b/pages/ucenter/login-page/pwd-login/pwd-login.vue new file mode 100644 index 0000000000000000000000000000000000000000..4610d99968ea86b041028a109253af3969cdaf0f --- /dev/null +++ b/pages/ucenter/login-page/pwd-login/pwd-login.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/pages/ucenter/login-page/pwd-retrieve/pwd-retrieve.vue b/pages/ucenter/login-page/pwd-retrieve/pwd-retrieve.vue new file mode 100644 index 0000000000000000000000000000000000000000..c28ae7db685698851bab69b64cea56bf9338871c --- /dev/null +++ b/pages/ucenter/login-page/pwd-retrieve/pwd-retrieve.vue @@ -0,0 +1,176 @@ + + + + + diff --git a/pages/ucenter/login-page/register/register.vue b/pages/ucenter/login-page/register/register.vue new file mode 100644 index 0000000000000000000000000000000000000000..81f71d430a07ec986d38f57ee1c6cb24029d97cd --- /dev/null +++ b/pages/ucenter/login-page/register/register.vue @@ -0,0 +1,105 @@ + + + + + diff --git a/pages/ucenter/login-page/register/validator.js b/pages/ucenter/login-page/register/validator.js new file mode 100644 index 0000000000000000000000000000000000000000..d5b4c27c569cd62b36610ddafe97f7f8bfc84b88 --- /dev/null +++ b/pages/ucenter/login-page/register/validator.js @@ -0,0 +1,61 @@ +export default { + "username": { + "rules": [{ + required: true, + errorMessage: '请输入用户名', + }, + { + minLength: 3, + maxLength: 32, + errorMessage: '用户名长度在 {minLength} 到 {maxLength} 个字符', + }, + { + validateFunction:function(rule,value,data,callback){ + console.log(value); + if(/^1\d{10}$/.test(value) || /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/.test(value)){ + callback('用户名不能是:手机号或邮箱') + }; + return true + } + } + ], + "label": "用户名" + }, + "password":{ + "rules": [{ + required: true, + errorMessage: '请输入密码', + }, + { + minLength: 6, + maxLength: 20, + errorMessage: '密码长度在 {minLength} 到 {maxLength} 个字符', + } + ], + "label": "密码" + }, + "pwd2":{ + "rules": [{ + + required: true, + errorMessage: '再次输入密码', + + }, + { + minLength: 6, + maxLength: 20, + errorMessage: '密码长度在 {minLength} 到 {maxLength} 个字符', + }, + { + validateFunction:function(rule,value,data,callback){ + console.log(value); + if(value!=data.password){ + callback('两次输入密码不一致') + }; + return true + } + } + ], + "label": "确认密码" + } +} \ No newline at end of file diff --git a/pages/ucenter/read-news-log/read-news-log.vue b/pages/ucenter/read-news-log/read-news-log.vue new file mode 100644 index 0000000000000000000000000000000000000000..137f58a4649edf60459443f025192c836dabcbaa --- /dev/null +++ b/pages/ucenter/read-news-log/read-news-log.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/pages/ucenter/settings/dc-push/push.js b/pages/ucenter/settings/dc-push/push.js new file mode 100644 index 0000000000000000000000000000000000000000..9ad20d42425d57f4830433799ad4132314df72f2 --- /dev/null +++ b/pages/ucenter/settings/dc-push/push.js @@ -0,0 +1,118 @@ +/** + * 判断Push是否开启 + */ +function isTurnedOnPush(){ + var isOn = undefined; + try{ + if('iOS' == plus.os.name){ + var types = 0; + var app = plus.ios.invoke('UIApplication', 'sharedApplication'); + var settings = plus.ios.invoke(app, 'currentUserNotificationSettings'); + if(settings){ + types = settings.plusGetAttribute('types'); + plus.ios.deleteObject(settings); + }else{ + types = plus.ios.invoke(app, 'enabledRemoteNotificationTypes'); + } + plus.ios.deleteObject(app); + isOn = (0!=types); + }else{ + var main = plus.android.runtimeMainActivity(); + var manager = plus.android.invoke('com.igexin.sdk.PushManager', 'getInstance'); + isOn = plus.android.invoke(manager, 'isPushTurnedOn', main); + } + }catch(e){ + console.error('exception in isTurnedOnPush@dc-push!!'); + } + return isOn; +} + +/** + * 打开Push + * Android平台 - 打开个推(UniPush)的推送通道 + * iOS平台 - 如果开启通知功能,则打开应用的设置页面引导用户开启通知 + */ +function turnOnPush(){ + try{ + if('iOS' == plus.os.name){ + // 如果设置中没有开启通知,则打开应用的设置界面 + if(!isTurnedOnPush()){ + settingInIos(); + } + }else{ + var main = plus.android.runtimeMainActivity(); + var manager = plus.android.invoke('com.igexin.sdk.PushManager', 'getInstance'); + plus.android.invoke(manager, 'turnOnPush', main); + } + }catch(e){ + console.error('exception in turnOnPush@dc-push!!'); + } +} + +/** + * 关闭Push + * Android平台 - 关闭个推(UniPush)的推送通道 + * iOS平台 - 不做任何操作 + */ +function trunOffPush(){ + try{ + if('iOS' == plus.os.name){ + // 这里不做任何操作(不引导用户关闭应用的推送能力),应该通知业务服务器不向此用户下发推送消息 + }else{ + var main = plus.android.runtimeMainActivity(); + var manager = plus.android.invoke('com.igexin.sdk.PushManager', 'getInstance'); + plus.android.invoke(manager, 'turnOffPush', main); + } + }catch(e){ + console.error('exception in trunOffPush@dc-push!!'); + } +} + +/** + * iOS平台打开应用设置界面 + */ +function settingInIos(){ + try{ + if('iOS' == plus.os.name){ + var app = plus.ios.invoke('UIApplication', 'sharedApplication'); + var setting = plus.ios.invoke('NSURL', 'URLWithString:', 'app-settings:'); + plus.ios.invoke(app, 'openURL:', setting); + plus.ios.deleteObject(setting); + plus.ios.deleteObject(app); + } + }catch(e){ + console.error('exception in settingInIos@dc-push!!'); + } +} +/** + * android打开应用设置页面 + */ +function settingInAndroid(){ + if (uni.getSystemInfoSync().platform == "android") { + var main = plus.android.runtimeMainActivity(); + var Intent = plus.android.importClass('android.content.Intent'); + var Settings = plus.android.importClass('android.provider.Settings'); + var intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + // 安卓跳转设置页面详细查看(https://ask.dcloud.net.cn/question/14732) + main.startActivity(intent); + } +} +/** + * 打开应用设置界面 + */ +function setting(){ + if (uni.getSystemInfoSync().platform == "ios") { + settingInIos(); + } + if (uni.getSystemInfoSync().platform == "android") { + settingInAndroid(); + } +} + +export default { + isOn: isTurnedOnPush, + iosSetting: settingInIos, + on: turnOnPush, + off: trunOffPush, + setting:setting +} diff --git a/pages/ucenter/settings/deactivate/deactivate.vue b/pages/ucenter/settings/deactivate/deactivate.vue new file mode 100644 index 0000000000000000000000000000000000000000..58fa9c7b0b0d17e9fec2228a74cb71553b97ee31 --- /dev/null +++ b/pages/ucenter/settings/deactivate/deactivate.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/pages/ucenter/settings/settings.vue b/pages/ucenter/settings/settings.vue new file mode 100644 index 0000000000000000000000000000000000000000..68ad813ceb7de6c5e88709acd8ceb4b6b73cb93a --- /dev/null +++ b/pages/ucenter/settings/settings.vue @@ -0,0 +1,344 @@ + + + + + \ No newline at end of file diff --git a/pages/ucenter/ucenter.vue b/pages/ucenter/ucenter.vue new file mode 100644 index 0000000000000000000000000000000000000000..12e1610d27b00947a5cef1b945e2b347e8c58c9e --- /dev/null +++ b/pages/ucenter/ucenter.vue @@ -0,0 +1,449 @@ + + + + + \ No newline at end of file diff --git a/pages/ucenter/userinfo/bind-mobile/bind-mobile.vue b/pages/ucenter/userinfo/bind-mobile/bind-mobile.vue new file mode 100644 index 0000000000000000000000000000000000000000..29cb05d85f094acd893fe7e7ede6875ecdf7e8dd --- /dev/null +++ b/pages/ucenter/userinfo/bind-mobile/bind-mobile.vue @@ -0,0 +1,115 @@ + + + + diff --git a/pages/ucenter/userinfo/cropImage.vue b/pages/ucenter/userinfo/cropImage.vue new file mode 100644 index 0000000000000000000000000000000000000000..dbec54d65615aba78c4eb62417606835da945ea6 --- /dev/null +++ b/pages/ucenter/userinfo/cropImage.vue @@ -0,0 +1,38 @@ + + + + \ No newline at end of file diff --git a/pages/ucenter/userinfo/limeClipper/README.md b/pages/ucenter/userinfo/limeClipper/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9219f815af48cf5b2c5b999f22af1e608e9970d8 --- /dev/null +++ b/pages/ucenter/userinfo/limeClipper/README.md @@ -0,0 +1,227 @@ +> 插件来源:[https://ext.dcloud.net.cn/plugin?id=3594](https://ext.dcloud.net.cn/plugin?id=3594) +##### 以下是作者写的插件介绍: + +# Clipper 图片裁剪 +> uniapp 图片裁剪,可用于图片头像等裁剪处理 +> [查看更多](http://liangei.gitee.io/limeui/#/clipper)
+> Q群:458377637 + + +## 平台兼容 + +| H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ 小程序 | App | +| --- | ---------- | ------------ | ---------- | ---------- | --------- | --- | +| √ | √ | √ | 未测 | √ | √ | √ | + + +## 代码演示 +### 基本用法 +`@success` 事件点击 👉 **确定** 后会返回生成的图片信息,包含 `url`、`width`、`height` + +```html + + + +``` + +```js +// 非uni_modules引入 +import lClipper from '@/components/lime-clipper/' +// uni_modules引入 +import lClipper from '@/uni_modules/lime-clipper/components/lime-clipper/' +export default { + components: {lClipper}, + data() { + return { + show: false, + url: '', + } + } +} +``` + + +### 传入图片 +`image-url`可传入**相对路径**、**临时路径**、**本地路径**、**网络图片**
+ +* **当为网络地址时** +* H5:👉 需要解决跨域问题。
+* 小程序:👉 需要配置 downloadFile 域名
+ + +```html + + + +``` + +```js +export default { + components: {lClipper}, + data() { + return { + imageUrl: 'https://img12.360buyimg.com/pop/s1180x940_jfs/t1/97205/26/1142/87801/5dbac55aEf795d962/48a4d7a63ff80b8b.jpg', + show: false, + url: '', + } + } +} +``` + + +### 确定按钮颜色 +样式变量名:`--l-clipper-confirm-color` +可放到全局样式的 `page` 里或节点的 `style` +```html + +``` +```css +// css 中为组件设置 CSS 变量 +.clipper { + --l-clipper-confirm-color: linear-gradient(to right, #ff6034, #ee0a24) +} +// 全局 +page { + --l-clipper-confirm-color: linear-gradient(to right, #ff6034, #ee0a24) +} +``` + + +### 使用插槽 +共五个插槽 `cancel` 取消按钮、 `photo` 选择图片按钮、 `rotate` 旋转按钮、 `confirm` 确定按钮和默认插槽。 + +```html + + + + 取消 + 选择图片 + 旋转 + 确定 + + + 显示取消按钮 + + + 显示选择图片按钮 + + + 显示旋转按钮 + + + 显示确定按钮 + + + 锁定裁剪框宽度 + + + 锁定裁剪框高度 + + + 锁定裁剪框比例 + + + 限制移动范围 + + + 禁止缩放 + + + 禁止旋转 + + + + + +``` + +```js +export default { + components: {lClipper}, + data() { + return { + show: false, + url: '', + isLockWidth: false, + isLockHeight: false, + isLockRatio: true, + isLimitMove: false, + isDisableScale: false, + isDisableRotate: false, + isShowCancelBtn: true, + isShowPhotoBtn: true, + isShowRotateBtn: true, + isShowConfirmBtn: true + } + } +} +``` + + +## API + +### Props + +| 参数 | 说明 | 类型 | 默认值 | +| ------------- | ------------ | ---------------- | ------------ | +| image-url | 图片路径 | string | | +| quality | 图片的质量,取值范围为 [0, 1],不在范围内时当作1处理 | number | `1` | +| source | `{album: '从相册中选择'}`key为图片来源类型,value为选项说明 | Object | | +| width | 裁剪框宽度,单位为 `rpx` | number | `400` | +| height | 裁剪框高度 | number | `400` | +| min-width | 裁剪框最小宽度 | number | `200` | +| min-height |裁剪框最小高度 | number | `200` | +| max-width | 裁剪框最大宽度 | number | `600` | +| max-height | 裁剪框最大宽度 | number | `600` | +| min-ratio | 图片最小缩放比 | number | `0.5` | +| max-ratio | 图片最大缩放比 | number | `2` | +| rotate-angle | 旋转按钮每次旋转的角度 | number | `90` | +| scale-ratio | 生成图片相对于裁剪框的比例, **比例越高生成图片越清晰** | number | `1` | +| is-lock-width | 是否锁定裁剪框宽度 | boolean | `false` | +| is-lock-height | 是否锁定裁剪框高度上 | boolean | `false` | +| is-lock-ratio | 是否锁定裁剪框比例 | boolean | `true` | +| is-disable-scale | 是否禁止缩放 | boolean | `false` | +| is-disable-rotate | 是否禁止旋转 | boolean | `false` | +| is-limit-move | 是否限制移动范围 | boolean | `false` | +| is-show-photo-btn | 是否显示选择图片按钮 | boolean | `true` | +| is-show-rotate-btn | 是否显示转按钮 | boolean | `true` | +| is-show-confirm-btn | 是否显示确定按钮 | boolean | `true` | +| is-show-cancel-btn | 是否显示关闭按钮 | boolean | `true` | + + + +### 事件 Events + +| 事件名 | 说明 | 回调 | +| ------- | ------------ | -------------- | +| success | 生成图片成功 | {`width`, `height`, `url`} | +| fail | 生成图片失败 | `error` | +| cancel | 关闭 | `false` | +| ready | 图片加载完成 | {`width`, `height`, `path`, `orientation`, `type`} | +| change | 图片大小改变时触发 | {`width`, `height`} | +| rotate | 图片旋转时触发 | `angle` | + +## 常见问题 +> 1、H5端使用网络图片需要解决跨域问题。
+> 2、小程序使用网络图片需要去公众平台增加下载白名单!二级域名也需要配!
+> 3、H5端生成图片是base64,有时显示只有一半可以使用原生标签``
+> 4、IOS APP 请勿使用HBX2.9.3.20201014的版本!这个版本无法生成图片。
+> 5、APP端无成功反馈、也无失败反馈时,请更新基座和HBX。
+ + +## 打赏 +如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。
+![输入图片说明](https://images.gitee.com/uploads/images/2020/1122/222521_bb543f96_518581.jpeg "微信图片编辑_20201122220352.jpg") \ No newline at end of file diff --git a/pages/ucenter/userinfo/limeClipper/images/photo.svg b/pages/ucenter/userinfo/limeClipper/images/photo.svg new file mode 100644 index 0000000000000000000000000000000000000000..7b4b59058376794d6e6a78035f627678102f96ad --- /dev/null +++ b/pages/ucenter/userinfo/limeClipper/images/photo.svg @@ -0,0 +1,19 @@ + + + + + + + + + diff --git a/pages/ucenter/userinfo/limeClipper/images/rotate.svg b/pages/ucenter/userinfo/limeClipper/images/rotate.svg new file mode 100644 index 0000000000000000000000000000000000000000..0143706ca7adea484ed38339980c2cc23559595e --- /dev/null +++ b/pages/ucenter/userinfo/limeClipper/images/rotate.svg @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/pages/ucenter/userinfo/limeClipper/index.css b/pages/ucenter/userinfo/limeClipper/index.css new file mode 100644 index 0000000000000000000000000000000000000000..ce542bf1193799b39f006d08e71b4fb3cbecf1cc --- /dev/null +++ b/pages/ucenter/userinfo/limeClipper/index.css @@ -0,0 +1,160 @@ +.flex-auto { + flex: auto; +} +.bg-transparent { + background-color: rgba(0,0,0,0.9); + transition-duration: 0.35s; +} +.l-clipper { + width: 100vw; + height: calc(100vh - var(--window-top)); + background-color: rgba(0,0,0,0.9); + position: fixed; + top: var(--window-top); + left: 0; + z-index: 1; +} +.l-clipper-mask { + position: relative; + z-index: 2; + pointer-events: none; +} +.l-clipper__content { + pointer-events: none; + position: absolute; + border: 1rpx solid rgba(255,255,255,0.3); + box-sizing: border-box; + box-shadow: rgba(0,0,0,0.5) 0 0 0 80vh; + background: transparent; +} +.l-clipper__content::before, +.l-clipper__content::after { + content: ''; + position: absolute; + border: 1rpx dashed rgba(255,255,255,0.3); +} +.l-clipper__content::before { + width: 100%; + top: 33.33%; + height: 33.33%; + border-left: none; + border-right: none; +} +.l-clipper__content::after { + width: 33.33%; + left: 33.33%; + height: 100%; + border-top: none; + border-bottom: none; +} +.l-clipper__edge { + position: absolute; + width: 34rpx; + height: 34rpx; + border: 6rpx solid #fff; + pointer-events: auto; +} +.l-clipper__edge::before { + content: ''; + position: absolute; + width: 40rpx; + height: 40rpx; + background-color: transparent; +} +.l-clipper__edge:nth-child(1) { + left: -6rpx; + top: -6rpx; + border-bottom-width: 0 !important; + border-right-width: 0 !important; +} +.l-clipper__edge:nth-child(1):before { + top: -50%; + left: -50%; +} +.l-clipper__edge:nth-child(2) { + right: -6rpx; + top: -6rpx; + border-bottom-width: 0 !important; + border-left-width: 0 !important; +} +.l-clipper__edge:nth-child(2):before { + top: -50%; + left: 50%; +} +.l-clipper__edge:nth-child(3) { + left: -6rpx; + bottom: -6rpx; + border-top-width: 0 !important; + border-right-width: 0 !important; +} +.l-clipper__edge:nth-child(3):before { + bottom: -50%; + left: -50%; +} +.l-clipper__edge:nth-child(4) { + right: -6rpx; + bottom: -6rpx; + border-top-width: 0 !important; + border-left-width: 0 !important; +} +.l-clipper__edge:nth-child(4):before { + bottom: -50%; + left: 50%; +} +.l-clipper-image { + width: 100%; + border-style: none; + position: absolute; + top: 0; + left: 0; + z-index: 1; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + transform-origin: center; +} +.l-clipper-canvas { + position: fixed; + z-index: 10; + left: -200vw; + top: -200vw; + pointer-events: none; +} +.l-clipper-tools { + position: fixed; + left: 0; + bottom: 10px; + width: 100%; + z-index: 99; + color: #fff; +} +.l-clipper-tools__btns { + font-weight: bold; + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + padding: 20rpx 40rpx; + box-sizing: border-box; +} +.l-clipper-tools__btns .cancel { + width: 112rpx; + height: 60rpx; + text-align: center; + line-height: 60rpx; +} +.l-clipper-tools__btns .confirm { + width: 112rpx; + height: 60rpx; + line-height: 60rpx; + background-color: #07c160; + border-radius: 6rpx; + text-align: center; +} +.l-clipper-tools__btns image { + display: block; + width: 60rpx; + height: 60rpx; +} +.l-clipper-tools__btns { + flex-direction: row; +} diff --git a/pages/ucenter/userinfo/limeClipper/limeClipper.vue b/pages/ucenter/userinfo/limeClipper/limeClipper.vue new file mode 100644 index 0000000000000000000000000000000000000000..076354b4dc86a069113b23e87f3eb8bd1c947aa8 --- /dev/null +++ b/pages/ucenter/userinfo/limeClipper/limeClipper.vue @@ -0,0 +1,816 @@ + + + + + \ No newline at end of file diff --git a/pages/ucenter/userinfo/limeClipper/utils.js b/pages/ucenter/userinfo/limeClipper/utils.js new file mode 100644 index 0000000000000000000000000000000000000000..980c43915efa8b5664ca42e439e1e0ef2b28cb4b --- /dev/null +++ b/pages/ucenter/userinfo/limeClipper/utils.js @@ -0,0 +1,244 @@ +/** + * 判断手指触摸位置 + */ +export function determineDirection(clipX, clipY, clipWidth, clipHeight, currentX, currentY) { + /* + * (右下>>1 右上>>2 左上>>3 左下>>4) + */ + let corner; + /** + * 思路:(利用直角坐标系) + * 1.找出裁剪框中心点 + * 2.如点击坐标在上方点与左方点区域内,则点击为左上角 + * 3.如点击坐标在下方点与右方点区域内,则点击为右下角 + * 4.其他角同理 + */ + const mainPoint = [clipX + clipWidth / 2, clipY + clipHeight / 2]; // 中心点 + const currentPoint = [currentX, currentY]; // 触摸点 + + if (currentPoint[0] <= mainPoint[0] && currentPoint[1] <= mainPoint[1]) { + corner = 3; // 左上 + } else if (currentPoint[0] >= mainPoint[0] && currentPoint[1] <= mainPoint[1]) { + corner = 2; // 右上 + } else if (currentPoint[0] <= mainPoint[0] && currentPoint[1] >= mainPoint[1]) { + corner = 4; // 左下 + } else if (currentPoint[0] >= mainPoint[0] && currentPoint[1] >= mainPoint[1]) { + corner = 1; // 右下 + } + + return corner; +} + +/** + * 图片边缘检测检测时,计算图片偏移量 + */ +export function calcImageOffset(data, scale) { + let left = data.imageLeft; + let top = data.imageTop; + scale = scale || data.scale; + + let imageWidth = data.imageWidth; + let imageHeight = data.imageHeight; + if ((data.angle / 90) % 2) { + imageWidth = data.imageHeight; + imageHeight = data.imageWidth; + } + const { + clipX, + clipWidth, + clipY, + clipHeight + } = data; + + // 当前图片宽度/高度 + const currentImageSize = (size) => (size * scale) / 2; + const currentImageWidth = currentImageSize(imageWidth); + const currentImageHeight = currentImageSize(imageHeight); + + left = clipX + currentImageWidth >= left ? left : clipX + currentImageWidth; + left = clipX + clipWidth - currentImageWidth <= left ? left : clipX + clipWidth - currentImageWidth; + top = clipY + currentImageHeight >= top ? top : clipY + currentImageHeight; + top = clipY + clipHeight - currentImageHeight <= top ? top : clipY + clipHeight - currentImageHeight; + return { + left, + top, + scale + }; +} + +/** + * 图片边缘检测时,计算图片缩放比例 + */ +export function calcImageScale(data, scale) { + scale = scale || data.scale; + let { + imageWidth, + imageHeight, + clipWidth, + clipHeight, + angle + } = data + if ((angle / 90) % 2) { + imageWidth = imageHeight; + imageHeight = imageWidth; + } + if (imageWidth * scale < clipWidth) { + scale = clipWidth / imageWidth; + } + if (imageHeight * scale < clipHeight) { + scale = Math.max(scale, clipHeight / imageHeight); + } + return scale; +} + +/** + * 计算图片尺寸 + */ +export function calcImageSize(width, height, data) { + let imageWidth = width, + imageHeight = height; + let { + clipWidth, + clipHeight, + sysinfo, + width: originWidth, + height: originHeight + } = data + if (imageWidth && imageHeight) { + if (imageWidth / imageHeight > (clipWidth || originWidth) / (clipWidth || originHeight)) { + imageHeight = clipHeight || originHeight; + imageWidth = (width / height) * imageHeight; + } else { + imageWidth = clipWidth || originWidth; + imageHeight = (height / width) * imageWidth; + } + } else { + let sys = sysinfo || uni.getSystemInfoSync(); + imageWidth = sys.windowWidth; + imageHeight = 0; + } + return { + imageWidth, + imageHeight + }; +} + +/** + * 勾股定理求斜边 + */ +export function calcPythagoreanTheorem(width, height) { + return Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)); +} + +/** + * 拖动裁剪框时计算 + */ +export function clipTouchMoveOfCalculate(data, event) { + const clientX = event.touches[0].clientX; + const clientY = event.touches[0].clientY; + + let { + clipWidth, + clipHeight, + clipY: oldClipY, + clipX: oldClipX, + clipStart, + isLockRatio, + maxWidth, + minWidth, + maxHeight, + minHeight + } = data; + maxWidth = maxWidth / 2; + minWidth = minWidth / 2; + minHeight = minHeight / 2; + maxHeight = maxHeight / 2; + + let width = clipWidth, + height = clipHeight, + clipY = oldClipY, + clipX = oldClipX, + // 获取裁剪框实际宽度/高度 + // 如果大于最大值则使用最大值 + // 如果小于最小值则使用最小值 + sizecorrect = () => { + width = width <= maxWidth ? (width >= minWidth ? width : minWidth) : maxWidth; + height = height <= maxHeight ? (height >= minHeight ? height : minHeight) : maxHeight; + }, + sizeinspect = () => { + sizecorrect(); + if ((width > maxWidth || width < minWidth || height > maxHeight || height < minHeight) && isLockRatio) { + return false; + } else { + return true; + } + }; + //if (clipStart.corner) { + height = clipStart.height + (clipStart.corner > 1 && clipStart.corner < 4 ? 1 : -1) * (clipStart.y - clientY); + //} + switch (clipStart.corner) { + case 1: + width = clipStart.width - clipStart.x + clientX; + if (isLockRatio) { + height = width / (clipWidth / clipHeight); + } + if (!sizeinspect()) return; + break; + case 2: + width = clipStart.width - clipStart.x + clientX; + if (isLockRatio) { + height = width / (clipWidth / clipHeight); + } + if (!sizeinspect()) { + return; + } else { + clipY = clipStart.clipY - (height - clipStart.height); + } + + break; + case 3: + width = clipStart.width + clipStart.x - clientX; + if (isLockRatio) { + height = width / (clipWidth / clipHeight); + } + if (!sizeinspect()) { + return; + } else { + clipY = clipStart.clipY - (height - clipStart.height); + clipX = clipStart.clipX - (width - clipStart.width); + } + + break; + case 4: + width = clipStart.width + clipStart.x - clientX; + if (isLockRatio) { + height = width / (clipWidth / clipHeight); + } + if (!sizeinspect()) { + return; + } else { + clipX = clipStart.clipX - (width - clipStart.width); + } + break; + default: + break; + } + return { + width, + height, + clipX, + clipY + }; +} + +/** + * 单指拖动图片计算偏移 + */ +export function imageTouchMoveOfCalcOffset(data, clientXForLeft, clientYForLeft) { + let left = clientXForLeft - data.touchRelative[0].x, + top = clientYForLeft - data.touchRelative[0].y; + return { + left, + top + }; +} diff --git a/pages/ucenter/userinfo/userinfo.vue b/pages/ucenter/userinfo/userinfo.vue new file mode 100644 index 0000000000000000000000000000000000000000..7e3f42307fde886b001e9b9847ed198c5cf5eded --- /dev/null +++ b/pages/ucenter/userinfo/userinfo.vue @@ -0,0 +1,284 @@ + + + diff --git a/pages/uni-agree/uni-agree.nvue b/pages/uni-agree/uni-agree.nvue new file mode 100644 index 0000000000000000000000000000000000000000..5befcea554d0b103656b23cf176e3cc895153aa4 --- /dev/null +++ b/pages/uni-agree/uni-agree.nvue @@ -0,0 +1,139 @@ + + + + + \ No newline at end of file diff --git a/pages/uni-agree/utils/uni-agree.js b/pages/uni-agree/utils/uni-agree.js new file mode 100644 index 0000000000000000000000000000000000000000..3d1340c729a973d501d3e4f99d288aa1fe36496d --- /dev/null +++ b/pages/uni-agree/utils/uni-agree.js @@ -0,0 +1,11 @@ +export default function(){ + console.log(uni.getSystemInfoSync().platform) + let userprotocol = uni.getStorageSync('userprotocol'); + console.log('userprotocol',userprotocol); + if(!userprotocol){ + uni.navigateTo({ + url:'/pages/uni-agree/uni-agree', + animationType:"none" + }) + } +} \ No newline at end of file diff --git a/static/app-plus/sharemenu/copyurl.png b/static/app-plus/sharemenu/copyurl.png new file mode 100644 index 0000000000000000000000000000000000000000..270e6aee210a59d179d67ac3e44d169d030a8721 Binary files /dev/null and b/static/app-plus/sharemenu/copyurl.png differ diff --git a/static/app-plus/sharemenu/more.png b/static/app-plus/sharemenu/more.png new file mode 100644 index 0000000000000000000000000000000000000000..bca03ce8015e5317bad3c5fd6cf7d566d6cacea6 Binary files /dev/null and b/static/app-plus/sharemenu/more.png differ diff --git a/static/app-plus/sharemenu/mp_weixin.png b/static/app-plus/sharemenu/mp_weixin.png new file mode 100644 index 0000000000000000000000000000000000000000..5ff5908238fca590df862a3475a627d2692c6cb6 Binary files /dev/null and b/static/app-plus/sharemenu/mp_weixin.png differ diff --git a/static/app-plus/sharemenu/qq.png b/static/app-plus/sharemenu/qq.png new file mode 100644 index 0000000000000000000000000000000000000000..28e745655ba66f66917144ff8ef4f4eb39395a15 Binary files /dev/null and b/static/app-plus/sharemenu/qq.png differ diff --git a/static/app-plus/sharemenu/wechatfriend.png b/static/app-plus/sharemenu/wechatfriend.png new file mode 100644 index 0000000000000000000000000000000000000000..e6af3472fe40c35b6a31e2ceb4a0d64a4f1db3db Binary files /dev/null and b/static/app-plus/sharemenu/wechatfriend.png differ diff --git a/static/app-plus/sharemenu/wechatmoments.png b/static/app-plus/sharemenu/wechatmoments.png new file mode 100644 index 0000000000000000000000000000000000000000..6445df06def179c84852804a4701a5b29efd6e9b Binary files /dev/null and b/static/app-plus/sharemenu/wechatmoments.png differ diff --git a/static/app-plus/sharemenu/weibo.png b/static/app-plus/sharemenu/weibo.png new file mode 100644 index 0000000000000000000000000000000000000000..8affa79cb02b8a2cf880e3df2017c136767f12c7 Binary files /dev/null and b/static/app-plus/sharemenu/weibo.png differ diff --git a/static/grid/c1.png b/static/grid/c1.png new file mode 100644 index 0000000000000000000000000000000000000000..9d38fdc45f54393919608143278902961ebfc03e Binary files /dev/null and b/static/grid/c1.png differ diff --git a/static/grid/c2.png b/static/grid/c2.png new file mode 100644 index 0000000000000000000000000000000000000000..ce956d76c7547cc90469647a2a1f6cd34731dbb7 Binary files /dev/null and b/static/grid/c2.png differ diff --git a/static/grid/c3.png b/static/grid/c3.png new file mode 100644 index 0000000000000000000000000000000000000000..216202ad01c5d093a15c247cf791bf66759cf54d Binary files /dev/null and b/static/grid/c3.png differ diff --git a/static/grid/c4.png b/static/grid/c4.png new file mode 100644 index 0000000000000000000000000000000000000000..fb8b4770c323cc87544a502c4efcd9792ec6047a Binary files /dev/null and b/static/grid/c4.png differ diff --git a/static/grid/c5.png b/static/grid/c5.png new file mode 100644 index 0000000000000000000000000000000000000000..310bfb123ba811240a23883c589557cced22af97 Binary files /dev/null and b/static/grid/c5.png differ diff --git a/static/grid/c6.png b/static/grid/c6.png new file mode 100644 index 0000000000000000000000000000000000000000..c3c45d8814b2657a1c603b1a44c8207da134cb40 Binary files /dev/null and b/static/grid/c6.png differ diff --git a/static/grid/c7.png b/static/grid/c7.png new file mode 100644 index 0000000000000000000000000000000000000000..a1e73908c282c8ce053a037a9b0d0ff2bcdb5f28 Binary files /dev/null and b/static/grid/c7.png differ diff --git a/static/grid/c8.png b/static/grid/c8.png new file mode 100644 index 0000000000000000000000000000000000000000..c32633c0b399d119fe940b19fb48ee0efd986ca1 Binary files /dev/null and b/static/grid/c8.png differ diff --git a/static/grid/c9.png b/static/grid/c9.png new file mode 100644 index 0000000000000000000000000000000000000000..51bcf6a1b39ff986d5e347f16a34c8d3e5dfccec Binary files /dev/null and b/static/grid/c9.png differ diff --git a/static/grid/empty.png b/static/grid/empty.png new file mode 100644 index 0000000000000000000000000000000000000000..c836ffa48680ae45bd5a125f2ca5cd66f5799774 Binary files /dev/null and b/static/grid/empty.png differ diff --git a/static/h5/download-app/android.png b/static/h5/download-app/android.png new file mode 100644 index 0000000000000000000000000000000000000000..cc0981584587371ae4471c51ecf7d4bfc307978f Binary files /dev/null and b/static/h5/download-app/android.png differ diff --git a/static/h5/download-app/ios.png b/static/h5/download-app/ios.png new file mode 100644 index 0000000000000000000000000000000000000000..9f3d7593a18eb804b7052e53a48e231dd602c2f7 Binary files /dev/null and b/static/h5/download-app/ios.png differ diff --git a/static/h5/download-app/openImg.png b/static/h5/download-app/openImg.png new file mode 100644 index 0000000000000000000000000000000000000000..6de7ef2e3564078cd78f111888a9f0c25d65b2a5 Binary files /dev/null and b/static/h5/download-app/openImg.png differ diff --git a/static/limeClipper/photo.svg b/static/limeClipper/photo.svg new file mode 100644 index 0000000000000000000000000000000000000000..7b4b59058376794d6e6a78035f627678102f96ad --- /dev/null +++ b/static/limeClipper/photo.svg @@ -0,0 +1,19 @@ + + + + + + + + + diff --git a/static/limeClipper/rotate.svg b/static/limeClipper/rotate.svg new file mode 100644 index 0000000000000000000000000000000000000000..0143706ca7adea484ed38339980c2cc23559595e --- /dev/null +++ b/static/limeClipper/rotate.svg @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/static/login-index/apple.png b/static/login-index/apple.png new file mode 100644 index 0000000000000000000000000000000000000000..04a3579cbd143037161da2779fb695f731818d81 Binary files /dev/null and b/static/login-index/apple.png differ diff --git a/static/login-index/weixin.png b/static/login-index/weixin.png new file mode 100644 index 0000000000000000000000000000000000000000..913a9d1d6959aef1b0efef8171f74f6878adfd37 Binary files /dev/null and b/static/login-index/weixin.png differ diff --git a/static/logo.png b/static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b5771e209bb677e2ebd5ff766ad5ee11790f305a Binary files /dev/null and b/static/logo.png differ diff --git a/static/tabbar/grid.png b/static/tabbar/grid.png new file mode 100644 index 0000000000000000000000000000000000000000..6bb1d837bc8a3b37d909746eba22b2e2293fe664 Binary files /dev/null and b/static/tabbar/grid.png differ diff --git a/static/tabbar/grid_active.png b/static/tabbar/grid_active.png new file mode 100644 index 0000000000000000000000000000000000000000..81df4c78f5562c303606bd515b928c741e803edf Binary files /dev/null and b/static/tabbar/grid_active.png differ diff --git a/static/tabbar/im-contacts.png b/static/tabbar/im-contacts.png new file mode 100644 index 0000000000000000000000000000000000000000..17bb78fb08cdda17fe1acd7c6c758c33a88f7fa1 Binary files /dev/null and b/static/tabbar/im-contacts.png differ diff --git a/static/tabbar/im-contacts_active.png b/static/tabbar/im-contacts_active.png new file mode 100644 index 0000000000000000000000000000000000000000..b86a8c19b70d11255ff39e3ba3056b5dd621dfbf Binary files /dev/null and b/static/tabbar/im-contacts_active.png differ diff --git a/static/tabbar/list.png b/static/tabbar/list.png new file mode 100644 index 0000000000000000000000000000000000000000..7ddda5e31e93694190259410b1e7eec2dcb35186 Binary files /dev/null and b/static/tabbar/list.png differ diff --git a/static/tabbar/list_active.png b/static/tabbar/list_active.png new file mode 100644 index 0000000000000000000000000000000000000000..195b149b2fbf4f224d390507954635c9fa2a83e7 Binary files /dev/null and b/static/tabbar/list_active.png differ diff --git a/static/tabbar/me.png b/static/tabbar/me.png new file mode 100644 index 0000000000000000000000000000000000000000..1bff84a5047cc57dcb03d76e476bd1fda120c43c Binary files /dev/null and b/static/tabbar/me.png differ diff --git a/static/tabbar/me_active.png b/static/tabbar/me_active.png new file mode 100644 index 0000000000000000000000000000000000000000..c509f7f8ab06f5affa1ea1a8df02def3aedb3035 Binary files /dev/null and b/static/tabbar/me_active.png differ diff --git a/static/uni-center/defaultAvatarUrl.png b/static/uni-center/defaultAvatarUrl.png new file mode 100644 index 0000000000000000000000000000000000000000..c3d33188b94e4843e0311836ffb0fd843195ae4e Binary files /dev/null and b/static/uni-center/defaultAvatarUrl.png differ diff --git a/static/uni-center/grey.png b/static/uni-center/grey.png new file mode 100644 index 0000000000000000000000000000000000000000..2aae15a5d0b9eea8451df6395d53775bcc676f78 Binary files /dev/null and b/static/uni-center/grey.png differ diff --git a/static/uni-center/headers.png b/static/uni-center/headers.png new file mode 100644 index 0000000000000000000000000000000000000000..0a8a4ad2bd55122db45c0de819b85da87df4f50f Binary files /dev/null and b/static/uni-center/headers.png differ diff --git a/static/uni-load-state/disconnection.png b/static/uni-load-state/disconnection.png new file mode 100644 index 0000000000000000000000000000000000000000..a96f200358f26de619c4d071e469460c8fe178f7 Binary files /dev/null and b/static/uni-load-state/disconnection.png differ diff --git a/static/uni-quick-login/apple.png b/static/uni-quick-login/apple.png new file mode 100644 index 0000000000000000000000000000000000000000..1d4b1334ff3c1ee81488d9c8cb05df232ec3b652 Binary files /dev/null and b/static/uni-quick-login/apple.png differ diff --git a/static/uni-quick-login/sms.png b/static/uni-quick-login/sms.png new file mode 100644 index 0000000000000000000000000000000000000000..fee8760911f91afc987871b6229af321c7433630 Binary files /dev/null and b/static/uni-quick-login/sms.png differ diff --git a/static/uni-quick-login/univerify.png b/static/uni-quick-login/univerify.png new file mode 100644 index 0000000000000000000000000000000000000000..00909a84976e151fdfcb7b000e52ed46dc2c6e33 Binary files /dev/null and b/static/uni-quick-login/univerify.png differ diff --git a/static/uni-quick-login/user.png b/static/uni-quick-login/user.png new file mode 100644 index 0000000000000000000000000000000000000000..57153d320f4eec453d8cb7ffe042a95e81347622 Binary files /dev/null and b/static/uni-quick-login/user.png differ diff --git a/static/uni-quick-login/wechat.png b/static/uni-quick-login/wechat.png new file mode 100644 index 0000000000000000000000000000000000000000..a16c17dc7156118c7780558a47706cbb7cae45ce Binary files /dev/null and b/static/uni-quick-login/wechat.png differ diff --git a/static/uni-sign-in/background.png b/static/uni-sign-in/background.png new file mode 100644 index 0000000000000000000000000000000000000000..2d7ba0999a55dea2553a7319e93c8904b51d7810 Binary files /dev/null and b/static/uni-sign-in/background.png differ diff --git a/static/uni.ttf b/static/uni.ttf new file mode 100644 index 0000000000000000000000000000000000000000..60a1968d08cc6056c70b5402b2effac43c6f96a3 Binary files /dev/null and b/static/uni.ttf differ diff --git a/store/index.js b/store/index.js new file mode 100644 index 0000000000000000000000000000000000000000..0282d1430997be1e01927f85255f098ee0349608 --- /dev/null +++ b/store/index.js @@ -0,0 +1,24 @@ +import user from '@/store/modules/user.js' + +// #ifndef VUE3 +import Vue from 'vue' +import Vuex from 'vuex' +Vue.use(Vuex) +const store = new Vuex.Store({ + modules: { + user + }, + strict: true +}) +// #endif + +// #ifdef VUE3 +import {createStore} from 'vuex' +const store = createStore({ + modules: { + user + } +}) +// #endif + +export default store \ No newline at end of file diff --git a/store/modules/user.js b/store/modules/user.js new file mode 100644 index 0000000000000000000000000000000000000000..22d12ba7c140ac33294f5475e8acb626395829ef --- /dev/null +++ b/store/modules/user.js @@ -0,0 +1,48 @@ +// 上次启动时的用户信息 +let userInfoHistory = uni.getStorageSync('userInfo') || {}; +let state = { + //是否已经登录 + hasLogin: Boolean(Object.keys(userInfoHistory).length), + //用户信息 + info: userInfoHistory + }, + getters = { + info(state) { + return state.info; + }, + hasLogin(state){ + return state.hasLogin; + } + }, + mutations = { + login(state, info) { //登录成功后的操作 + //原有的结合传来的参数 + let _info = state.info; + state.info = Object.assign({}, _info, info); + //设置为已经登录 + state.hasLogin = true; + console.log('state.info',state.info); + //存储最新的用户数据到本地持久化存储 + uni.setStorageSync('userInfo', state.info); + uni.setStorageSync('uni_id_token', state.info.token) + uni.setStorageSync('uni_id_token_expired', state.info.tokenExpired) + + }, + logout(state) { + state.info = {}; + state.hasLogin = false; + uni.setStorageSync('userInfo', {}); + uni.setStorageSync('uni_id_token', ''); + uni.setStorageSync('uni_id_token_expired', 0) + } + }, + actions = { + + } +export default { + namespaced: true, + state, + getters, + mutations, + actions +} \ No newline at end of file diff --git a/uni-starter.config.js b/uni-starter.config.js new file mode 100644 index 0000000000000000000000000000000000000000..2b2fd222335fee4c7fdf77c40e8011782340290e --- /dev/null +++ b/uni-starter.config.js @@ -0,0 +1,92 @@ +//这是应用的配置页面,App.vue挂载到getApp().globalData.config +export default { + "h5": { + "url": "https://uni-starter.dcloud.net.cn", // 前端网页托管的域名 + // 在h5端全局悬浮引导用户下载app的功能 更多自定义要求在/common/openApp.js中修改 + // "openApp": { //如不需要本功能直接移除本节点即可 + // //点击悬浮下载栏后打开的网页链接 + // "openUrl": '/#/pages/ucenter/invite/invite', + // //左侧显示的应用名称 + // "appname": 'uni-starter', + // //应用的图标 + // "logo": './static/logo.png', + // } + }, + "mp": { + "weixin": { + //微信小程序原始id,微信小程序分享时 + "id": "gh_33446d7f7a26" + } + }, + "router": { + /* + 名词解释:“强制登录页” + 在打开定义的需强制登录的页面之前会自动检查(前端校验)uni_id_token的值是否有效, + 如果无效会自动跳转到登录页面 + 两种模式: + 1.needLogin:黑名单模式。枚举游客不可访问的页面。 + 2.visitor:白名单模式。枚举游客可访问的页面。 + * 注意:黑名单与白名单模式二选一 + */ + // "needLogin" : [ + // {pattern:/^\/pages\/list.*/}, //支持正则表达式 + // "/uni_modules/uni-news-favorite/pages/uni-news-favorite/list", + // "/uni_modules/uni-feedback/pages/uni-feedback/add" + // ], + "visitor" : [ + "/",//注意入口页必须直接写 "/" + {"pattern":/^\/pages\/list.*/}, //支持正则表达式 + {"pattern":/^\/pages\/ucenter\/login-page.*/}, + "/pages/common/webview/webview", + "/pages/grid/grid", + "/pages/ucenter/ucenter", + "/pages/ucenter/guestbook/guestbook", + "/pages/ucenter/about/about", + "/pages/ucenter/settings/settings" + ], + /* + login:配置登录类型与优先级 + 未列举到的,或设备环境不支持的选项,将被隐藏。如果你需要在不同平台有不同的配置,直接用条件编译即可 + 根据数组的第0项,决定登录方式的第一优先级。 + */ + "login": ["weixin","univerify","username", "smsCode", "apple"] + }, + //关于应用 + "about": { + //应用名称 + "appName": "uni-starter", + //应用logo + "logo": "/static/logo.png", + //公司名称 + "company": "北京xx网络技术有限公司", + //口号 + "slogan": "云端一体应用快速开发模版", + //政策协议 + "agreements": [{ + "title": "用户服务协议", //如果开启了多语言国际化,本配置将失效。请在 lang/en.js 和 lang/zh-Hans.js中配置 + "url": "请填写用户服务协议链接" //对应的网络链接 + }, + { + "title": "隐私政策", //如果开启了多语言国际化,本配置将失效。请在 lang/en.js 和 lang/zh-Hans.js中配置 + "url": "请填写隐私政策链接" //对应的网络链接 + } + ], + //应用的链接,用于分享到第三方平台和生成关于我们页的二维码 + "download": "", + //version + "version":"1.0.0" //用于非app端显示,app端自动获取 + }, + "download":{ //用于生成二合一下载页面 + "ios":"https://itunes.apple.com/cn/app/hello-uni-app/id1417078253?mt=8", + "android":"https://vkceyugu.cdn.bspapp.com/VKCEYUGU-97fca9f2-41f6-449f-a35e-3f135d4c3875/6d754387-a6c3-48ed-8ad2-e8f39b40fc01.apk" + }, + //用于打开应用市场评分界面 + "marketId":{ + "ios":"id1417078253", + "android":"123456" + }, + //配置多语言国际化。i18n为英文单词 internationalization的首末字符i和n,18为中间的字符数 是“国际化”的简称 + "i18n":{ + "enable":false //默认关闭,国际化。如果你想使用国际化相关功能,请改为true + } +} \ No newline at end of file diff --git a/uni.scss b/uni.scss new file mode 100644 index 0000000000000000000000000000000000000000..45ae737e66433b1d6050e07a62833f7146183de2 --- /dev/null +++ b/uni.scss @@ -0,0 +1,76 @@ +/** + * 这里是uni-app内置的常用样式变量 + * + * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 + * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App + * + */ + +/** + * 如果你是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:24rpx; +$uni-font-size-base:28rpx; +$uni-font-size-lg:32rpx; + +/* 图片尺寸 */ +$uni-img-size-sm:40rpx; +$uni-img-size-base:52rpx; +$uni-img-size-lg:80rpx; + +/* Border Radius */ +$uni-border-radius-sm: 4rpx; +$uni-border-radius-base: 6rpx; +$uni-border-radius-lg: 12rpx; +$uni-border-radius-circle: 50%; + +/* 水平间距 */ +$uni-spacing-row-sm: 10px; +$uni-spacing-row-base: 20rpx; +$uni-spacing-row-lg: 30rpx; + +/* 垂直间距 */ +$uni-spacing-col-sm: 8rpx; +$uni-spacing-col-base: 8px; +$uni-spacing-col-lg: 24rpx; + +/* 透明度 */ +$uni-opacity-disabled: 0.3; // 组件禁用态的透明度 + +/* 文章场景相关 */ +$uni-color-title: #2C405A; // 文章标题颜色 +$uni-font-size-title:40rpx; +$uni-color-subtitle: #555555; // 二级标题颜色 +$uni-font-size-subtitle:36rpx; +$uni-color-paragraph: #3F536E; // 文章段落颜色 +$uni-font-size-paragraph:30rpx; \ No newline at end of file diff --git a/uniCloud-aliyun/cloudfunctions/uni-analyse-searchhot/index.js b/uniCloud-aliyun/cloudfunctions/uni-analyse-searchhot/index.js new file mode 100644 index 0000000000000000000000000000000000000000..1f16dc2fe70cfe7a79f8ce4dece68d49ccf51947 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-analyse-searchhot/index.js @@ -0,0 +1,49 @@ +'use strict'; +exports.main = async (event, context) => { + /** + * 根据搜索记录,设定时间间隔来归纳出热搜数据并存储在热搜表中 + */ + const SEARCHHOT = 'opendb-search-hot'; // 热搜数据库名称 + const SEARCHLOG = 'opendb-search-log'; // 搜索记录数据库名称 + const SEARCHLOG_timeZone = 604800000; // 归纳搜索记录时间间隔,毫秒数,默认为最近7天 + const SEARCHHOT_size = 10; // 热搜条数 + + const DB = uniCloud.database(); + const DBCmd = DB.command; + const $ = DB.command.aggregate; + const SEARCHHOT_db = DB.collection(SEARCHHOT); + const SEARCHLOG_db = DB.collection(SEARCHLOG); + const timeEnd = Date.now() - SEARCHLOG_timeZone; + + let { + data: searchHotData + } = await SEARCHLOG_db + .aggregate() + .match({ + create_date: DBCmd.gt(timeEnd) + }) + .group({ + _id: { + 'content': '$content', + }, + count: $.sum(1) + }) + .replaceRoot({ + newRoot: $.mergeObjects(['$_id', '$$ROOT']) + }) + .project({ + _id: false + }) + .sort({ + count: -1 + }) + .end(); + + let now = Date.now(); + searchHotData.map(item => { + item.create_date = now; + return item; + }).slice(0, SEARCHHOT_size); + // searchHotData = searchHotData.sort((a, b) => b.count - a.count).slice(0, SEARCHHOT_size); + return searchHotData.length ? await SEARCHHOT_db.add(searchHotData) : '' +}; diff --git a/uniCloud-aliyun/cloudfunctions/uni-analyse-searchhot/package.json b/uniCloud-aliyun/cloudfunctions/uni-analyse-searchhot/package.json new file mode 100644 index 0000000000000000000000000000000000000000..6005add8563ac879001ba7f5fdeb304768ed8486 --- /dev/null +++ b/uniCloud-aliyun/cloudfunctions/uni-analyse-searchhot/package.json @@ -0,0 +1,14 @@ +{ + "name": "uni-analyse-searchhot", + "version": "1.0.0", + "description": "定时归纳热搜", + "main": "index.js", + "dependencies": {}, + "cloudfunction-config": { + "triggers": [{ + "name": "analyse-searchHot", + "type": "timer", + "config": "0 0 */2 * * * *" + }] + } +} diff --git "a/uniCloud-aliyun/database/JQL\346\225\260\346\215\256\345\272\223\347\256\241\347\220\206.jql" "b/uniCloud-aliyun/database/JQL\346\225\260\346\215\256\345\272\223\347\256\241\347\220\206.jql" new file mode 100644 index 0000000000000000000000000000000000000000..4903b479c888e928fc113d11322ff3411095ab97 --- /dev/null +++ "b/uniCloud-aliyun/database/JQL\346\225\260\346\215\256\345\272\223\347\256\241\347\220\206.jql" @@ -0,0 +1,12 @@ +// 本文件用于,使用JQL语法操作项目关联的uniCloud空间的数据库,方便开发调试和远程数据库管理 +// 编写clientDB的js API(也支持常规js语法,比如var),可以对云数据库进行增删改查操作。不支持uniCloud-db组件写法 +// 可以全部运行,也可以选中部分代码运行。点击工具栏上的运行按钮或者按下【F5】键运行代码 +// 如果文档中存在多条JQL语句,只有最后一条语句生效 +// 如果混写了普通js,最后一条语句需是数据库操作语句 +// 此处代码运行不受DB Schema的权限控制,移植代码到实际业务中注意在schema中配好permission +// 不支持clientDB的action +// 数据库查询有最大返回条数限制,详见:https://uniapp.dcloud.net.cn/uniCloud/cf-database?id=limit +// 详细JQL语法,请参考 https://uniapp.dcloud.net.cn/uniCloud/clientdb?id=jsquery + +// 下面示例查询uni-id-users表的所有数据 +db.collection('guestbook, uni-id-users').where('user_id._id=="610a6daa506cc7000100c07f"').get(); diff --git "a/uniCloud-aliyun/database/JQL\346\237\245\350\257\242.jql" "b/uniCloud-aliyun/database/JQL\346\237\245\350\257\242.jql" new file mode 100644 index 0000000000000000000000000000000000000000..680e8987bb99075ab51c0044f4fd659659f877a2 --- /dev/null +++ "b/uniCloud-aliyun/database/JQL\346\237\245\350\257\242.jql" @@ -0,0 +1,9 @@ +// 本查询文件用于,使用JQL语法查询项目关联的uniCloud空间的数据库,方便开发调试 +// 选中查询代码,点击工具栏上的运行按钮或者【F5】运行查询语句 +// 如果没有选中代码,直接运行,则会执行整个文档的JQL语句。但如果文档中存在多条JQL语句,只有最后一条语句生效 +// 本文档支持简单js,但不支持clientDB的action +// 数据库查询有最大返回条数限制,详见:https://uniapp.dcloud.net.cn/uniCloud/cf-database?id=limit +// 详细JQL语法,请参考 https://uniapp.dcloud.net.cn/uniCloud/clientdb?id=jsquery + +// 下面示例查询uni-id-users表的所有数据 +db.collection('uni-id-users').get(); diff --git a/uniCloud-aliyun/database/db_init.json b/uniCloud-aliyun/database/db_init.json new file mode 100644 index 0000000000000000000000000000000000000000..6c7d6891cea81a29360b087bf2f4029127174a31 --- /dev/null +++ b/uniCloud-aliyun/database/db_init.json @@ -0,0 +1,157 @@ +// 在本文件中可配置云数据库初始化,数据格式见:https://uniapp.dcloud.io/uniCloud/cf-database?id=db_init +// 编写完毕后对本文件点右键,可按配置规则创建表和添加数据 +{ + "opendb-verify-codes": { + "data": [] + }, + "uni-id-roles": { + "data": [] + }, + "uni-id-permissions": { + "data": [] + }, + "uni-id-log": { + "data": [] + }, + "opendb-admin-menus": { + "data": [{ + "menu_id": "system_management", + "name": "系统管理", + "icon": "uni-icons-gear", + "url": "", + "sort": 1000, + "parent_id": "", + "permission": [], + "enable": true, + "create_date": 1602662469396 + }, { + "menu_id": "system_user", + "name": "用户管理", + "icon": "uni-icons-person", + "url": "/pages/system/user/list", + "sort": 1010, + "parent_id": "system_management", + "permission": [], + "enable": true, + "create_date": 1602662469398 + }, { + "menu_id": "system_role", + "name": "角色管理", + "icon": "uni-icons-personadd", + "url": "/pages/system/role/list", + "sort": 1020, + "parent_id": "system_management", + "permission": [], + "enable": true, + "create_date": 1602662469397 + }, { + "menu_id": "system_permission", + "name": "权限管理", + "icon": "uni-icons-locked", + "url": "/pages/system/permission/list", + "sort": 1030, + "parent_id": "system_management", + "permission": [], + "enable": true, + "create_date": 1602662469396 + }, { + "menu_id": "system_menu", + "name": "菜单管理", + "icon": "uni-icons-settings", + "url": "/pages/system/menu/list", + "sort": 1040, + "parent_id": "system_management", + "permission": [], + "enable": true, + "create_date": 1602662469396 + }] + }, + "opendb-news-articles": { + "data": [{ + "title": "阿里小程序IDE官方内嵌uni-app,为开发者提供多端开发服务", + "excerpt": "阿里小程序IDE官方内嵌uni-app,为开发者提供多端开发服务", + "content": "

随着微信、阿里、百度、头条、QQ纷纷推出小程序,开发者的开发维护成本持续上升,负担过重。这点已经成为共识,现在连小程序平台厂商也充分意识到了。

\n

阿里小程序团队,为了减轻开发者的负担,在官方的小程序开发者工具中整合了多端框架。

\n

经过阿里团队仔细评估,uni-app 在产品完成度、跨平台支持度、开发者社区、可持续发展等多方面优势明显,最终选定 uni-app内置于阿里小程序开发工具中,为开发者提供多端开发解决方案。

\n

经过之前1个月的公测,10月10日,阿里小程序正式发布0.70版开发者工具,通过 uni-app 实现多端开发,成为本次版本更新的亮点功能!

\n

如下图,在阿里小程序工具左侧主导航选择 uni-app,创建项目,即可开发。

\n
\n


阿里小程序开发工具更新说明详见:https://docs.alipay.com/mini/ide/0.70-stable

\n

 

\n

集成uni-app,这对于阿里团队而言,并不是一个容易做出的决定。毕竟 uni-app 是一个三方产品,要经过复杂的评审流程。

\n

这一方面突显出阿里团队以开发者需求为本的优秀价值观,另一方面也证明 uni-app的产品确实过硬。

\n

很多开发者都有多端需求,但又没有足够精力去了解、评估 uni-app,而处于观望态度。现在大家可以更放心的使用 uni-app 了,它没有让阿里失望,也不会让你失望。

\n

自从uni-app推出以来,DCloud也取得了高速的发展,目前拥有370万开发者,框架运行在4.6亿手机用户设备上,月活达到1.35亿(仅包括部分接入DCloud统计平台的数据)。并且数据仍在高速增长中,在市场占有率上处于遥遥领先的位置。

\n

本次阿里小程序工具集成 uni-app,会让 uni-app 继续快速爆发,取得更大的成功。

\n

后续DCloud还将深化与阿里的合作,在serverless等领域给开发者提供更多优质服务。

\n

使用多端框架开发各端应用,是多赢的模式。开发者减轻了负担,获得了更多新流量。而小程序平台厂商,也能保证自己平台上的各种应用可以被及时的更新。

\n

DCloud欢迎更多小程序平台厂商,与我们一起合作,为开发者、平台、用户的多赢而努力。

\n

进一步了解uni-app,详见:https://uniapp.dcloud.io

\n

欢迎扫码关注DCloud公众号,转发消息到朋友圈。

", + "avatar": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-aliyun-gacrhzeynhss7c6d04/249516a0-3941-11eb-899d-733ae62bed2f.jpg", + "type": 0, + "user_id": "123", + "comment_count": 0, + "like_count": 0, + "comment_status": 0, + "article_status": 1, + "publish_date": 1616092287006, + "last_modify_date": 1616092303031, + "create_time": "2021-03-19T08:25:06.109Z" + }] + }, + "opendb-app-versions": { + "data": [{ + "is_silently": false, + "is_mandatory": false, + "appid": "__UNI__03B096E", + "name": "uni-starter", + "title": "新增升级中心", + "contents": "新增升级中心", + "platform": [ + "Android" + ], + "version": "1.0.1", + "url": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-3469aac7-a663-4c5d-8ee8-94275f8c09ab/3128d010-01c5-4121-a1d6-f3f919944a23.apk", + "stable_publish": false, + "type": "native_app", + "create_date": 1616771628150 + }] + }, + "uni-id-users": { + "data": [{ + "_id": "123", + "username": "预置用户", + "nickname": "测试", + "avatar": "https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-dc-site/d84c6de0-6080-11eb-bdc1-8bd33eb6adaa.png", + "mobile": "18888888888", + "mobile_confirmed": 1 + }] + }, + "opendb-banner": { + "data": [{ + "status": true, + "bannerfile": { + "name": "094a9dc0-50c0-11eb-b680-7980c8a877b8.jpg", + "extname": "jpg", + "fileType": "image", + "url": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e/b88a7e17-35f0-4d0d-bc32-93f8909baf03.jpg", + "size": 70880, + "image": { + "width": 500, + "height": 333, + "location": "blob:http://localhost:8081/a3bfaab4-7ee6-44d5-a171-dc8225d83598" + }, + "path": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e/b88a7e17-35f0-4d0d-bc32-93f8909baf03.jpg" + }, + "open_url": "https://www.dcloud.io/", + "title": "测试", + "sort": 1, + "category_id": "", + "description": "" + }, + { + "status": true, + "bannerfile": { + "name": "094a9dc0-50c0-11eb-b680-7980c8a877b8.jpg", + "extname": "jpg", + "fileType": "image", + "url": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e/9db94cb4-a5e0-4ed9-b356-b42a392b3112.jpg", + "size": 70880, + "image": { + "width": 500, + "height": 333, + "location": "blob:http://localhost:8081/1a6f718a-4012-476a-9172-590fef2cc518" + }, + "path": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e/9db94cb4-a5e0-4ed9-b356-b42a392b3112.jpg" + }, + "open_url": "https://www.dcloud.io/", + "title": "", + "category_id": "", + "description": "" + }] + } +} \ No newline at end of file diff --git a/uniCloud-aliyun/database/default.jql b/uniCloud-aliyun/database/default.jql new file mode 100644 index 0000000000000000000000000000000000000000..465356a46dcc688b3da922ce65ae05b56a04cc45 --- /dev/null +++ b/uniCloud-aliyun/database/default.jql @@ -0,0 +1,12 @@ +// 本文件用于,使用JQL语法操作项目关联的uniCloud空间的数据库,方便开发调试和远程数据库管理 +// 编写clientDB的js API(也支持常规js语法,比如var),可以对云数据库进行增删改查操作。不支持uniCloud-db组件写法 +// 可以全部运行,也可以选中部分代码运行。点击工具栏上的运行按钮或者按下【F5】键运行代码 +// 如果文档中存在多条JQL语句,只有最后一条语句生效 +// 如果混写了普通js,最后一条语句需是数据库操作语句 +// 此处代码运行不受DB Schema的权限控制,移植代码到实际业务中注意在schema中配好permission +// 不支持clientDB的action +// 数据库查询有最大返回条数限制,详见:https://uniapp.dcloud.net.cn/uniCloud/cf-database?id=limit +// 详细JQL语法,请参考 https://uniapp.dcloud.net.cn/uniCloud/clientdb?id=jsquery + +// 下面示例查询uni-id-users表的所有数据 +db.collection('uni-id-users').get(); diff --git a/uniCloud-aliyun/database/guestbook.schema.json b/uniCloud-aliyun/database/guestbook.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..b4e9bb93d59668768053172b7b1537ed61bdb33b --- /dev/null +++ b/uniCloud-aliyun/database/guestbook.schema.json @@ -0,0 +1,57 @@ +// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema +{ + "bsonType": "object", + "required": [], + "permission": { + "read": "doc.state || auth.uid == doc.user_id || 'AUDITOR' in auth.role", + "create": "auth.uid != null", + "update": "'AUDITOR' in auth.role", + "delete": "auth.uid == doc.user_id" + }, + "properties": { + "_id": { + "description": "ID,系统自动生成", + "permission":{ + "write":false + } + }, + "text": { + "bsonType": "string", + "permission":{ + "write":false + } + }, + "user_id": { + "forceDefaultValue": { + "$env": "uid" + }, + "foreignKey": "uni-id-users._id", + "permission":{ + "write":false + } + }, + "ip": { + "forceDefaultValue": { + "$env": "clientIP" + }, + "permission":{ + "write":false + } + }, + "create_time": { + "forceDefaultValue": { + "$env": "now" + }, + "permission":{ + "write":false + } + }, + "state": { + "bsonType": "bool", + "forceDefaultValue":false, + "permission":{ + "write":true + } + } + } +} \ No newline at end of file diff --git a/uniCloud-aliyun/database/opendb-admin-menus.schema.json b/uniCloud-aliyun/database/opendb-admin-menus.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..c6b9de5383e1ee6488f07604e8a34898b546de67 --- /dev/null +++ b/uniCloud-aliyun/database/opendb-admin-menus.schema.json @@ -0,0 +1,56 @@ +{ + "bsonType": "object", + "required": ["name", "menu_id"], + "permission": { + "read": true + }, + "properties": { + "_id": { + "description": "存储文档 ID,系统自动生成" + }, + "menu_id": { + "bsonType": "string", + "description": "菜单项的ID,不可重复", + "trim": "both" + }, + "name": { + "bsonType": "string", + "description": "菜单名称", + "trim": "both" + }, + "icon": { + "bsonType": "string", + "description": "菜单图标", + "trim": "both" + }, + "url": { + "bsonType": "string", + "description": "菜单url", + "trim": "both" + }, + "sort": { + "bsonType": "int", + "description": "菜单序号(越大越靠后)" + }, + "parent_id": { + "bsonType": "string", + "description": "父级菜单Id", + "parentKey": "menu_id" + }, + "permission": { + "bsonType": "array", + "description": "菜单权限列表" + }, + "enable": { + "bsonType": "bool", + "description": "是否启用菜单,true启用、false禁用" + }, + "create_date": { + "bsonType": "timestamp", + "description": "菜单创建时间", + "forceDefaultValue": { + "$env": "now" + } + } + } +} diff --git a/uniCloud-aliyun/database/opendb-app-versions.schema.json b/uniCloud-aliyun/database/opendb-app-versions.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..a24c2d34d647ce7d19d173c316c152c004764cd1 --- /dev/null +++ b/uniCloud-aliyun/database/opendb-app-versions.schema.json @@ -0,0 +1,124 @@ +{ + "bsonType": "object", + "required": ["appid", "platform", "version", "url", "contents", "type"], + "permission": { + "read": false, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "记录id,自动生成" + }, + "appid": { + "bsonType": "string", + "trim": "both", + "description": "应用的AppID", + "label": "AppID", + "componentForEdit": { + "name": "uni-easyinput", + "props": { + "disabled": true + } + } + }, + "name": { + "bsonType": "string", + "trim": "both", + "description": "应用名称", + "label": "应用名称", + "componentForEdit": { + "name": "uni-easyinput", + "props": { + "disabled": true + } + } + }, + "title": { + "bsonType": "string", + "description": "更新标题", + "label": "更新标题" + }, + "contents": { + "bsonType": "string", + "description": "更新内容", + "label": "更新内容", + "componentForEdit": { + "name": "textarea" + }, + "componentForShow": { + "name": "textarea", + "props": { + "disabled": true + } + } + }, + "platform": { + "bsonType": "array", + "enum": [{ + "value": "Android", + "text": "安卓" + }, { + "value": "iOS", + "text": "苹果" + }], + "description": "更新平台,Android || iOS || [Android, iOS]", + "label": "平台" + }, + "type": { + "bsonType": "string", + "enum": [{ + "value": "native_app", + "text": "原生App安装包" + }, { + "value": "wgt", + "text": "Wgt资源包" + }], + "description": "安装包类型,native_app || wgt", + "label": "安装包类型" + }, + "version": { + "bsonType": "string", + "description": "当前包版本号,必须大于当前线上发行版本号", + "label": "版本号" + }, + "min_uni_version": { + "bsonType": "string", + "description": "原生App最低版本", + "label": "原生App最低版本" + }, + "url": { + "bsonType": "string", + "description": "可下载安装包地址", + "label": "包地址" + }, + "stable_publish": { + "bsonType": "bool", + "description": "是否上线发行", + "label": "上线发行" + }, + "is_silently": { + "bsonType": "bool", + "description": "是否静默更新", + "label": "静默更新", + "defaultValue": false + }, + "is_mandatory": { + "bsonType": "bool", + "description": "是否强制更新", + "label": "强制更新", + "defaultValue": false + }, + "create_date": { + "bsonType": "timestamp", + "label": "上传时间", + "forceDefaultValue": { + "$env": "now" + }, + "componentForEdit": { + "name": "uni-dateformat" + } + } + } +} diff --git a/uniCloud-aliyun/database/opendb-banner.schema.json b/uniCloud-aliyun/database/opendb-banner.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..a027517e79b756cdb0af19666759f54845e1377b --- /dev/null +++ b/uniCloud-aliyun/database/opendb-banner.schema.json @@ -0,0 +1,54 @@ +{ + "bsonType": "object", + "required": ["bannerfile"], + "permission": { + "read": true + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "bannerfile": { + "bsonType": "file", + "fileMediaType": "image", + "title": "图片文件", + "description": "图片文件信息,包括文件名、url等" + }, + "open_url": { + "bsonType": "string", + "description": "点击跳转目标地址。如果是web地址则使用内置web-view打开;如果是本地页面则跳转本地页面;如果是schema地址则打开本地的app", + "title": "点击目标地址", + "format": "url", + "pattern": "^(http:\/\/|https:\/\/|\/|.\/|@\/)\\S", + "trim": "both" + }, + "title": { + "bsonType": "string", + "description": "注意标题文字颜色和背景图靠色导致看不清的问题", + "maxLength": 20, + "title": "标题", + "trim": "both" + }, + "sort": { + "bsonType": "int", + "description": "数字越小,排序越前", + "title": "排序" + }, + "category_id": { + "bsonType": "string", + "description": "多个栏目的banner都存在一个表里时可用这个字段区分", + "title": "分类id" + }, + "status": { + "bsonType": "bool", + "defaultValue": true, + "title": "生效状态" + }, + "description": { + "bsonType": "string", + "description": "维护者自用描述", + "title": "备注", + "trim": "both" + } + } +} diff --git a/uniCloud-aliyun/database/opendb-department.schema.json b/uniCloud-aliyun/database/opendb-department.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..698239b363e88fa4ed2eaa8b199ec1a5edaf7648 --- /dev/null +++ b/uniCloud-aliyun/database/opendb-department.schema.json @@ -0,0 +1,47 @@ +{ + "bsonType": "object", + "required": ["name"], + "permission": { + "read": true, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "parent_id": { + "bsonType": "string", + "description": "父级部门ID", + "parentKey": "_id" + }, + "name": { + "bsonType": "string", + "description": "部门名称", + "title": "部门名称", + "trim": "both" + }, + "level": { + "bsonType": "int", + "description": "部门层级,为提升检索效率而作的冗余设计" + }, + "sort": { + "bsonType": "int", + "description": "部门在当前层级下的顺序,由小到大", + "title": "显示顺序" + }, + "manager_uid": { + "bsonType": "string", + "description": "部门主管的userid, 参考`uni-id-users` 表", + "foreignKey": "uni-id-users._id" + }, + "create_date": { + "bsonType": "timestamp", + "description": "部门创建时间", + "forceDefaultValue": { + "$env": "now" + } + } + } +} diff --git a/uniCloud-aliyun/database/opendb-mall-goods.schema.json b/uniCloud-aliyun/database/opendb-mall-goods.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..b3c24cc6506c60543bd011741f599fcfbbd62c50 --- /dev/null +++ b/uniCloud-aliyun/database/opendb-mall-goods.schema.json @@ -0,0 +1,123 @@ +{ + "bsonType": "object", + "permission": { + "create": false, + "delete": false, + "read": "doc.is_on_sale == true", + "update": false + }, + "properties": { + "_id": { + "description": "存储文档 ID(商品 ID),系统自动生成" + }, + "add_date": { + "bsonType": "timestamp", + "defaultValue": { + "$env": "now" + }, + "description": "上架时间" + }, + "category_id": { + "bsonType": "string", + "description": "分类 id,参考`opendb-mall-categories`表", + "foreignKey": "opendb-mall-categories._id" + }, + "comment_count": { + "bsonType": "int", + "description": "累计评论数" + }, + "goods_banner_imgs": { + "bsonType": "array", + "description": "商品详情页的banner图地址" + }, + "goods_desc": { + "bsonType": "string", + "description": "商品详细描述", + "title": "详细描述", + "trim": "both" + }, + "goods_sn": { + "bsonType": "string", + "description": "商品的唯一货号", + "title": "货号", + "trim": "both" + }, + "goods_thumb": { + "bsonType": "string", + "description": "商品缩略图,用于在列表或搜索结果中预览显示", + "pattern": "^(http:\/\/|https:\/\/|\/|.\/|@\/)\\S", + "title": "缩略图地址", + "trim": "both" + }, + "is_alone_sale": { + "bsonType": "bool", + "description": "是否能单独销售;如果不能单独销售,则只能作为某商品的配件或者赠品销售" + }, + "is_best": { + "bsonType": "bool", + "description": "是否精品" + }, + "is_hot": { + "bsonType": "bool", + "description": "是否热销" + }, + "is_new": { + "bsonType": "bool", + "description": "是否新品", + "title": "是否新品" + }, + "is_on_sale": { + "bsonType": "bool", + "description": "是否上架销售", + "title": "是否上架" + }, + "is_real": { + "bsonType": "bool", + "description": "是否实物", + "title": "是否为实物" + }, + "keywords": { + "bsonType": "string", + "description": "商品关键字,为搜索引擎收录使用", + "title": "关键字", + "trim": "both" + }, + "last_modify_date": { + "bsonType": "timestamp", + "defaultValue": { + "$env": "now" + }, + "description": "最后修改时间" + }, + "month_sell_count": { + "bsonType": "int", + "description": "月销量" + }, + "name": { + "bsonType": "string", + "description": "商品名称", + "title": "名称", + "trim": "both" + }, + "remain_count": { + "bsonType": "int", + "description": "库存数量", + "title": "库存数量" + }, + "seller_note": { + "bsonType": "string", + "description": "商家备注,仅商家可见", + "permission": { + "read": false + }, + "trim": "both" + }, + "total_sell_count": { + "bsonType": "int", + "description": "总销量" + } + }, + "required": ["goods_sn", "name", "remain_count", "month_sell_count", "total_sell_count", "comment_count", "is_real", + "is_on_sale", "is_alone_sale", "is_best", "is_new", "is_hot" + ] +} diff --git a/uniCloud-aliyun/database/opendb-news-articles-detail.schema.json b/uniCloud-aliyun/database/opendb-news-articles-detail.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..325fc9aadd7babd0b8ff08ccbacf848ba633e962 --- /dev/null +++ b/uniCloud-aliyun/database/opendb-news-articles-detail.schema.json @@ -0,0 +1,122 @@ +{ + "bsonType": "object", + "permission": { + "create": "auth.uid != null", + "delete": "doc.user_id == auth.uid", + "read": true, + "update": "doc.user_id == auth.uid" + }, + "properties": { + "_id": { + "description": "存储文档 ID(用户 ID),系统自动生成" + }, + "article_status": { + "bsonType": "int", + "description": "文章状态:0 草稿箱 1 已发布", + "maximum": 1, + "minimum": 0 + }, + "avatar": { + "bsonType": "string", + "description": "缩略图地址", + "label": "封面大图" + }, + "category_id": { + "bsonType": "string", + "description": "分类 id,参考`uni-news-categories`表" + }, + "comment_count": { + "bsonType": "int", + "description": "评论数量", + "permission": { + "write": false + } + }, + "comment_status": { + "bsonType": "int", + "description": "评论状态:0 关闭 1 开放", + "maximum": 1, + "minimum": 0 + }, + "content": { + "bsonType": "string", + "description": "文章内容", + "label": "文章内容" + }, + "excerpt": { + "bsonType": "string", + "description": "文章摘录", + "label": "摘要" + }, + "is_essence": { + "bsonType": "bool", + "description": "阅读加精", + "permission": { + "write": false + } + }, + "is_sticky": { + "bsonType": "bool", + "description": "是否置顶", + "permission": { + "write": false + } + }, + "last_comment_user_id": { + "bsonType": "string", + "description": "最后回复用户 id,参考`uni-id-users` 表" + }, + "last_modify_date": { + "bsonType": "timestamp", + "description": "最后修改时间" + }, + "last_modify_ip": { + "bsonType": "string", + "description": "最后修改时 IP 地址" + }, + "like_count": { + "bsonType": "int", + "description": "喜欢数、点赞数", + "permission": { + "write": false + } + }, + "mode": { + "bsonType": "number", + "description": "排版显示模式" + }, + "publish_date": { + "bsonType": "timestamp", + "defaultValue": { + "$env": "now" + }, + "description": "发表时间" + }, + "publish_ip": { + "bsonType": "string", + "description": "发表时 IP 地址", + "forceDefaultValue": { + "$env": "clientIP" + } + }, + "title": { + "bsonType": "string", + "description": "标题", + "label": "标题" + }, + "user_id": { + "bsonType": "string", + "description": "文章作者ID, 参考`uni-id-users` 表" + }, + "view_count": { + "bsonType": "int", + "description": "阅读数量", + "permission": { + "write": false + } + } + }, + "required": ["user_id", "title", "content", "article_status", "view_count", "like_count", "is_sticky", "is_essence", + "comment_status", "comment_count", "mode" + ] +} diff --git a/uniCloud-aliyun/database/opendb-news-articles.schema.json b/uniCloud-aliyun/database/opendb-news-articles.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..ee4f8cce2c03225b63c9ecd41dfc1b526b7c98ec --- /dev/null +++ b/uniCloud-aliyun/database/opendb-news-articles.schema.json @@ -0,0 +1,165 @@ +{ + "bsonType": "object", + "required": ["user_id", "title", "content"], + "permission": { + "read": "doc.uid == auth.uid && doc.article_status == 0 || doc.article_status == 1", + "create": "auth.uid != null", + "update": "doc.user_id == auth.uid", + "delete": "doc.user_id == auth.uid" + }, + "properties": { + "_id": { + "description": "存储文档 ID(用户 ID),系统自动生成" + }, + "user_id": { + "bsonType": "string", + "description": "文章作者ID, 参考`uni-id-users` 表", + "foreignKey": "uni-id-users._id", + "defaultValue": { + "$env": "uid" + } + }, + "category_id": { + "bsonType": "string", + "title": "分类", + "description": "分类 id,参考`uni-news-categories`表", + "foreignKey": "opendb-news-categories._id", + "enum": { + "collection": "opendb-news-categories", + "field": "name as text, _id as value" + } + }, + "title": { + "bsonType": "string", + "title": "标题", + "description": "标题", + "label": "标题", + "trim": "both" + }, + "content": { + "bsonType": "string", + "title": "文章内容", + "description": "文章内容", + "label": "文章内容", + "trim": "right" + }, + "excerpt": { + "bsonType": "string", + "title": "文章摘录", + "description": "文章摘录", + "label": "摘要", + "trim": "both" + }, + "article_status": { + "bsonType": "int", + "title": "文章状态", + "description": "文章状态:0 草稿箱 1 已发布", + "defaultValue": 0, + "enum": [{ + "value": 0, + "text": "草稿箱" + }, { + "value": 1, + "text": "已发布" + }] + }, + "view_count": { + "bsonType": "int", + "title": "阅读数量", + "description": "阅读数量", + "permission": { + "write": false + } + }, + "like_count": { + "bsonType": "int", + "description": "喜欢数、点赞数", + "permission": { + "write": false + } + }, + "is_sticky": { + "bsonType": "bool", + "title": "是否置顶", + "description": "是否置顶", + "permission": { + "write": false + } + }, + "is_essence": { + "bsonType": "bool", + "title": "阅读加精", + "description": "阅读加精", + "permission": { + "write": false + } + }, + "comment_status": { + "bsonType": "int", + "title": "开放评论", + "description": "评论状态:0 关闭 1 开放", + "enum": [{ + "value": 0, + "text": "关闭" + }, { + "value": 1, + "text": "开放" + }] + }, + "comment_count": { + "bsonType": "int", + "description": "评论数量", + "permission": { + "write": false + } + }, + "last_comment_user_id": { + "bsonType": "string", + "description": "最后回复用户 id,参考`uni-id-users` 表", + "foreignKey": "uni-id-users._id" + }, + "avatar": { + "bsonType": "string", + "title": "封面大图", + "description": "缩略图地址", + "label": "封面大图", + "trim": "both" + }, + "publish_date": { + "bsonType": "timestamp", + "title": "发表时间", + "description": "发表时间", + "defaultValue": { + "$env": "now" + } + }, + "publish_ip": { + "bsonType": "string", + "title": "发布文章时IP地址", + "description": "发表时 IP 地址", + "forceDefaultValue": { + "$env": "clientIP" + } + }, + "last_modify_date": { + "bsonType": "timestamp", + "title": "最后修改时间", + "description": "最后修改时间", + "defaultValue": { + "$env": "now" + } + }, + "last_modify_ip": { + "bsonType": "string", + "description": "最后修改时 IP 地址", + "forceDefaultValue": { + "$env": "clientIP" + } + }, + "mode": { + "bsonType": "number", + "title": "排版显示模式", + "description": "排版显示模式,如左图右文、上图下文等" + } + } +} diff --git a/uniCloud-aliyun/database/opendb-news-categories.schema.json b/uniCloud-aliyun/database/opendb-news-categories.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..976c8a8f4fd0d89c87a28365b8cacafb641b1a95 --- /dev/null +++ b/uniCloud-aliyun/database/opendb-news-categories.schema.json @@ -0,0 +1,50 @@ +{ + "bsonType": "object", + "required": ["name"], + "permission": { + "read": true, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "存储文档 ID(文章 ID),系统自动生成" + }, + "name": { + "bsonType": "string", + "description": "类别名称", + "label": "名称", + "trim": "both" + }, + "description": { + "bsonType": "string", + "description": "类别描述", + "label": "描述", + "trim": "both" + }, + "icon": { + "bsonType": "string", + "description": "类别图标地址", + "label": "图标地址", + "pattern": "^(http:\/\/|https:\/\/|\/|.\/|@\/)\\S", + "trim": "both" + }, + "sort": { + "bsonType": "int", + "description": "类别显示顺序", + "label": "排序" + }, + "article_count": { + "bsonType": "int", + "description": "该类别下文章数量" + }, + "create_date": { + "bsonType": "timestamp", + "description": "创建时间", + "forceDefaultValue": { + "$env": "now" + } + } + } +} diff --git a/uniCloud-aliyun/database/opendb-news-comments.schema.json b/uniCloud-aliyun/database/opendb-news-comments.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..1c0b197f6ba56b59970e3cd4da0baa9a7a73c76a --- /dev/null +++ b/uniCloud-aliyun/database/opendb-news-comments.schema.json @@ -0,0 +1,68 @@ +{ + "bsonType": "object", + "required": ["article_id", "user_id", "comment_content", "like_count", "comment_type", "reply_user_id", + "reply_comment_id" + ], + "permission": { + "read": true, + "create": "auth.uid != null && get(`database.opendb-news-article.${doc.article_id}`).comment_status == 1", + "update": "doc.user_id == auth.uid", + "delete": "doc.user_id == auth.uid" + }, + "properties": { + "_id": { + "description": "存储文档 ID(文章 ID),系统自动生成" + }, + "article_id": { + "bsonType": "string", + "description": "文章ID,opendb-news-posts 表中的`_id`字段", + "foreignKey": "opendb-news-articles._id" + }, + "user_id": { + "bsonType": "string", + "description": "评论者ID,参考`uni-id-users` 表", + "forceDefaultValue": { + "$env": "uid" + }, + "foreignKey": "uni-id-users._id" + }, + "comment_content": { + "bsonType": "string", + "description": "评论内容", + "title": "评论内容", + "trim": "right" + }, + "like_count": { + "bsonType": "int", + "description": "评论喜欢数、点赞数" + }, + "comment_type": { + "bsonType": "int", + "description": "回复类型: 0 针对文章的回复 1 针对评论的回复" + }, + "reply_user_id": { + "bsonType": "string", + "description": "被回复的评论用户ID,comment_type为1时有效", + "foreignKey": "uni-id-users._id" + }, + "reply_comment_id": { + "bsonType": "string", + "description": "被回复的评论ID,comment_type为1时有效", + "foreignKey": "opendb-news-comments._id" + }, + "comment_date": { + "bsonType": "timestamp", + "description": "评论发表时间", + "forceDefaultValue": { + "$env": "now" + } + }, + "comment_ip": { + "bsonType": "string", + "description": "评论发表时 IP 地址", + "forceDefaultValue": { + "$env": "clientIP" + } + } + } +} diff --git a/uniCloud-aliyun/database/opendb-news-favorite.schema.json b/uniCloud-aliyun/database/opendb-news-favorite.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..a4034f17042a1d855c256dab5761e4b34f59f007 --- /dev/null +++ b/uniCloud-aliyun/database/opendb-news-favorite.schema.json @@ -0,0 +1,46 @@ +{ + "bsonType": "object", + "required": ["user_id", "article_id"], + "permission": { + "read": "doc.user_id == auth.uid", + "create": "auth.uid != null", + "update": "doc.user_id == auth.uid", + "delete": "doc.user_id == auth.uid" + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "article_id": { + "bsonType": "string", + "description": "文章id,参考opendb-news-articles表", + "foreignKey": "opendb-news-articles._id" + }, + "article_title": { + "bsonType": "string", + "description": "文章标题" + }, + "user_id": { + "bsonType": "string", + "description": "收藏者id,参考uni-id-users表", + "forceDefaultValue": { + "$env": "uid" + }, + "foreignKey": "uni-id-users._id" + }, + "create_date": { + "bsonType": "timestamp", + "description": "收藏时间", + "forceDefaultValue": { + "$env": "now" + } + }, + "update_date": { + "bsonType": "timestamp", + "description": "更新\/修改时间", + "forceDefaultValue": { + "$env": "now" + } + } + } +} diff --git a/uniCloud-aliyun/database/opendb-search-hot.schema.json b/uniCloud-aliyun/database/opendb-search-hot.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..1230be4c8bd443f26ab8136c69854dfe47d27e03 --- /dev/null +++ b/uniCloud-aliyun/database/opendb-search-hot.schema.json @@ -0,0 +1,27 @@ +{ + "bsonType": "object", + "permission": { + "create": false, + "delete": false, + "read": true, + "update": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "content": { + "bsonType": "string", + "description": "搜索内容" + }, + "count": { + "bsonType": "long", + "description": "搜索次数" + }, + "create_date": { + "bsonType": "timestamp", + "description": "统计时间" + } + }, + "required": ["content", "count"] +} diff --git a/uniCloud-aliyun/database/opendb-search-log.schema.json b/uniCloud-aliyun/database/opendb-search-log.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..421ee8c0aa01062fcd3cc86ccffe00bbb2b32cab --- /dev/null +++ b/uniCloud-aliyun/database/opendb-search-log.schema.json @@ -0,0 +1,31 @@ +{ + "bsonType": "object", + "permission": { + "create": true, + "delete": false, + "read": false, + "update": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "content": { + "bsonType": "string", + "description": "搜索内容" + }, + "create_date": { + "bsonType": "timestamp", + "description": "统计时间" + }, + "device_id": { + "bsonType": "string", + "description": "设备id" + }, + "user_id": { + "bsonType": "string", + "description": "收藏者id,参考uni-id-users表" + } + }, + "required": ["content"] +} diff --git a/uniCloud-aliyun/database/opendb-verify-codes.schema.json b/uniCloud-aliyun/database/opendb-verify-codes.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..50c27c6ae3cc4e90e90eede4d9ea1cd02385c8e9 --- /dev/null +++ b/uniCloud-aliyun/database/opendb-verify-codes.schema.json @@ -0,0 +1,45 @@ +{ + "bsonType": "object", + "required": [], + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "mobile": { + "bsonType": "string", + "description": "手机号码" + }, + "email": { + "bsonType": "string", + "description": "邮箱" + }, + "device_uuid": { + "bsonType": "string", + "description": "设备UUID,常用于图片验证码" + }, + "code": { + "bsonType": "string", + "description": "验证码" + }, + "scene": { + "bsonType": "string", + "description": "使用验证码的场景,如:login, bind, unbind, pay" + }, + "state": { + "bsonType": "int", + "description": "验证状态:0 未验证、1 已验证、2 已作废" + }, + "ip": { + "bsonType": "string", + "description": "请求时客户端IP地址" + }, + "create_date": { + "bsonType": "timestamp", + "description": "创建时间" + }, + "expired_date": { + "bsonType": "timestamp", + "description": "过期时间" + } + } +} \ No newline at end of file diff --git a/uniCloud-aliyun/database/read-news-log.schema.json b/uniCloud-aliyun/database/read-news-log.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..1cbd342af01ef11e4c2a4fec86e8da5e7e2385c6 --- /dev/null +++ b/uniCloud-aliyun/database/read-news-log.schema.json @@ -0,0 +1,35 @@ +{ + "bsonType": "object", + "required": ["user_id", "article_id"], + "permission": { + "read": "doc.user_id == auth.uid", + "create": "auth.uid != null", + "update": "doc.user_id == auth.uid" + //"delete": "doc.user_id == auth.uid" + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "article_id": { + "bsonType": "string", + "description": "文章id,参考opendb-news-articles表", + "foreignKey": "opendb-news-articles._id" + }, + "user_id": { + "bsonType": "string", + "description": "收藏者id,参考uni-id-users表", + "forceDefaultValue": { + "$env": "uid" + }, + "foreignKey": "uni-id-users._id" + }, + "last_time": { //设计策略是多次看同一个文章只做一次记录,重复看的文章时间更新为当前时间 + "bsonType": "timestamp", + "description": "最后一次看的时间", + "defaultValue": { + "$env": "now" + } + } + } +} diff --git a/uniCloud-aliyun/database/uni-id-device.schema.json b/uniCloud-aliyun/database/uni-id-device.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..483a7852ddd886366a2640e18e778044b677158b --- /dev/null +++ b/uniCloud-aliyun/database/uni-id-device.schema.json @@ -0,0 +1,64 @@ +{ + "bsonType": "object", + "required": ["user_id"], + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "user_id": { + "bsonType": "string", + "description": "用户id,参考uni-id-users表" + }, + "ua": { + "bsonType": "string", + "description": "userAgent" + }, + "uuid": { + "bsonType": "string", + "description": "设备唯一标识(需要加密存储)" + }, + "vendor": { + "bsonType": "string", + "description": "设备厂商" + }, + "push_clientid": { + "bsonType": "string", + "description": "推送设备客户端标识" + }, + "imei": { + "bsonType": "string", + "description": "国际移动设备识别码IMEI(International Mobile Equipment Identity)" + }, + "oaid": { + "bsonType": "string", + "description": "移动智能设备标识公共服务平台提供的匿名设备标识符(OAID)" + }, + "idfa": { + "bsonType": "string", + "description": "iOS平台配置应用使用广告标识(IDFA)" + }, + "model": { + "bsonType": "string", + "description": "设备型号" + }, + "platform": { + "bsonType": "string", + "description": "平台类型" + }, + "create_date": { + "bsonType": "timestamp", + "description": "创建时间", + "forceDefaultValue": { + "$env": "now" + } + }, + "last_active_date": { + "bsonType": "timestamp", + "description": "最后登录时间" + }, + "last_active_ip": { + "bsonType": "string", + "description": "最后登录IP" + } + } +} diff --git a/uniCloud-aliyun/database/uni-id-log.schema.json b/uniCloud-aliyun/database/uni-id-log.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..086432acee206583e54003c22eef62d377a510c5 --- /dev/null +++ b/uniCloud-aliyun/database/uni-id-log.schema.json @@ -0,0 +1,41 @@ +{ + "bsonType": "object", + "required": ["user_id"], + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "user_id": { + "bsonType": "string", + "description": "用户id,参考uni-id-users表" + }, + "ua": { + "bsonType": "string", + "description": "userAgent" + }, + "device_uuid": { + "bsonType": "string", + "description": "设备唯一标识(需要加密存储)" + }, + "type": { + "bsonType": "string", + "enum": ["login", "logout"], + "description": "登录类型" + }, + "state": { + "bsonType": "int", + "description": "结果:0 失败、1 成功" + }, + "ip": { + "bsonType": "string", + "description": "ip地址" + }, + "create_date": { + "bsonType": "timestamp", + "description": "创建时间", + "forceDefaultValue": { + "$env": "now" + } + } + } +} diff --git a/uniCloud-aliyun/database/uni-id-permissions.schema.json b/uniCloud-aliyun/database/uni-id-permissions.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..f0ddc99de8928df2446b125155406c4f86f043f1 --- /dev/null +++ b/uniCloud-aliyun/database/uni-id-permissions.schema.json @@ -0,0 +1,46 @@ +{ + "bsonType": "object", + "required": ["permission_id", "permission_name"], + "properties": { + "_id": { + "description": "存储文档 ID,系统自动生成" + }, + "permission_id": { + "bsonType": "string", + "description": "权限唯一标识,不可修改,不允许重复", + "label": "权限标识", + "trim": "both", + "title": "权限ID", + "component": { + "name": "input" + } + }, + "permission_name": { + "bsonType": "string", + "description": "权限名称", + "label": "权限名称", + "title": "权限名称", + "trim": "both", + "component": { + "name": "input" + } + }, + "comment": { + "bsonType": "string", + "description": "备注", + "label": "备注", + "title": "备注", + "trim": "both", + "component": { + "name": "textarea" + } + }, + "create_date": { + "bsonType": "timestamp", + "description": "创建时间", + "forceDefaultValue": { + "$env": "now" + } + } + } +} diff --git a/uniCloud-aliyun/database/uni-id-roles.schema.json b/uniCloud-aliyun/database/uni-id-roles.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..9802441517783dbd906fafa8e01c3c5a607184c5 --- /dev/null +++ b/uniCloud-aliyun/database/uni-id-roles.schema.json @@ -0,0 +1,46 @@ +{ + "bsonType": "object", + "required": ["role_id"], + "permission": { + "read": false, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "存储文档 ID,系统自动生成" + }, + "role_id": { + "title": "唯一ID", + "bsonType": "string", + "description": "角色唯一标识,不可修改,不允许重复", + "trim": "both" + }, + "role_name": { + "title": "名称", + "bsonType": "string", + "description": "角色名称", + "trim": "both" + }, + "permission": { + "title": "权限", + "bsonType": "array", + "foreignKey": "uni-id-permissions.permission_id", + "description": "角色拥有的权限列表" + }, + "comment": { + "title": "备注", + "bsonType": "string", + "description": "备注", + "trim": "both" + }, + "create_date": { + "bsonType": "timestamp", + "description": "创建时间", + "forceDefaultValue": { + "$env": "now" + } + } + } +} diff --git a/uniCloud-aliyun/database/uni-id-scores.schema.json b/uniCloud-aliyun/database/uni-id-scores.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..0e2a7f6587fe90bde952a7dc5565593d96661689 --- /dev/null +++ b/uniCloud-aliyun/database/uni-id-scores.schema.json @@ -0,0 +1,44 @@ +{ + "bsonType": "object", + "required": ["user_id", "score", "balance"], + "permission": { + "read": "doc.user_id == auth.uid", + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "user_id": { + "bsonType": "string", + "description": "用户id,参考uni-id-users表" + }, + "score": { + "bsonType": "int", + "description": "本次变化的积分" + }, + "type": { + "bsonType": "int", + "enum": [1, 2], + "description": "积分类型 1:收入 2:支出" + }, + "balance": { + "bsonType": "int", + "description": "变化后的积分余额" + }, + "comment": { + "bsonType": "string", + "description": "备注,说明积分新增、消费的缘由", + "trim": "both" + }, + "create_date": { + "bsonType": "timestamp", + "description": "创建时间", + "forceDefaultValue": { + "$env": "now" + } + } + } +} diff --git a/uniCloud-aliyun/database/uni-id-users.schema.json b/uniCloud-aliyun/database/uni-id-users.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..a75e9cb9eaf852d4bd5974a2566e0195216028ea --- /dev/null +++ b/uniCloud-aliyun/database/uni-id-users.schema.json @@ -0,0 +1,366 @@ +{ + "bsonType": "object", + "permission":{ + "read":true, + "update":"doc._id == auth.uid" + }, + "required": [], + "properties": { + "_id": { + "description": "存储文档 ID(用户 ID),系统自动生成" + }, + "username": { + "bsonType": "string", + "title": "用户名", + "description": "用户名,不允许重复", + "trim": "both", + "permission":{ + "write":false + } + }, + "password": { + "bsonType": "password", + "title": "密码", + "description": "密码,加密存储", + "trim": "both", + "permission":{ + "write":false + } + }, + "password_secret_version": { + "bsonType": "int", + "title": "passwordSecret", + "description": "密码使用的passwordSecret版本", + "permission":{ + "write":false + } + }, + "nickname": { + "bsonType": "string", + "title": "昵称", + "description": "用户昵称", + "trim": "both" + }, + "gender": { + "bsonType": "int", + "title": "性别", + "description": "用户性别:0 未知 1 男性 2 女性", + "defaultValue": 0, + "enum": [{ + "text": "未知", + "value": 0 + }, + { + "text": "男", + "value": 1 + }, + { + "text": "女", + "value": 2 + } + ] + }, + "status": { + "bsonType": "int", + "description": "用户状态:0 正常 1 禁用 2 审核中 3 审核拒绝", + "title": "用户状态", + "defaultValue": 0, + "permission":{ + "write":false + }, + "enum": [{ + "text": "正常", + "value": 0 + }, + { + "text": "禁用", + "value": 1 + }, + { + "text": "审核中", + "value": 2 + }, + { + "text": "审核拒绝", + "value": 3 + } + ] + }, + "mobile": { + "bsonType": "string", + "title": "手机号码", + "description": "手机号码", + "pattern": "^\\+?[0-9-]{3,20}$", + "trim": "both", + "permission":{ + "write":false + } + }, + "mobile_confirmed": { + "bsonType": "int", + "description": "手机号验证状态:0 未验证 1 已验证", + "title": "手机号验证状态", + "defaultValue": 0, + "enum": [{ + "text": "未验证", + "value": 0 + }, + { + "text": "已验证", + "value": 1 + } + ], + "permission":{ + "write":false + } + }, + "email": { + "bsonType": "string", + "format": "email", + "title": "邮箱", + "description": "邮箱地址", + "trim": "both", + "permission":{ + "write":false + } + }, + "email_confirmed": { + "bsonType": "int", + "description": "邮箱验证状态:0 未验证 1 已验证", + "title": "邮箱验证状态", + "defaultValue": 0, + "enum": [{ + "text": "未验证", + "value": 0 + }, + { + "text": "已验证", + "value": 1 + } + ], + "permission":{ + "write":false + } + }, + "avatar": { + "bsonType": "string", + "title": "头像地址", + "description": "头像地址", + "trim": "both" + }, + "avatar_file": { + "bsonType": "file", + "title": "头像文件", + "description": "用file类型方便使用uni-file-picker组件" + }, + "department_id": { + "bsonType": "array", + "description": "部门ID", + "permission":{ + "write":false + }, + "title": "部门", + "enumType": "tree", + "enum": { + "collection": "opendb-department", + "orderby": "name asc", + "field": "_id as value, name as text" + } + }, + "role": { + "bsonType": "array", + "title": "角色", + "description": "用户角色", + "enum": { + "collection": "uni-id-roles", + "field": "role_id as value, role_name as text" + }, + "foreignKey": "uni-id-roles.role_id", + "permission": { + "write": false + } + }, + "wx_unionid": { + "bsonType": "string", + "description": "微信unionid", + "permission":{ + "write":false + } + }, + "wx_openid": { + "bsonType": "object", + "description": "微信各个平台openid", + "properties": { + "app-plus": { + "bsonType": "string", + "description": "app平台微信openid" + }, + "mp-weixin": { + "bsonType": "string", + "description": "微信小程序平台openid" + } + }, + "permission":{ + "write":false + } + }, + "ali_openid": { + "bsonType": "string", + "description": "支付宝平台openid", + "permission":{ + "write":false + } + }, + "apple_openid": { + "bsonType": "string", + "description": "苹果登录openid", + "permission":{ + "write":false + } + }, + "comment": { + "bsonType": "string", + "title": "备注", + "description": "备注", + "trim": "both", + "permission":{ + "write":false + } + }, + "realname_auth": { + "bsonType": "object", + "description": "实名认证信息", + "required": [ + "type", + "auth_status" + ], + "permission":{ + "write":false + }, + "properties": { + "type": { + "bsonType": "int", + "minimum": 0, + "maximum": 1, + "description": "用户类型:0 个人用户 1 企业用户" + }, + "auth_status": { + "bsonType": "int", + "minimum": 0, + "maximum": 3, + "description": "认证状态:0 未认证 1 等待认证 2 认证通过 3 认证失败" + }, + "auth_date": { + "bsonType": "timestamp", + "description": "认证通过时间" + }, + "real_name": { + "bsonType": "string", + "description": "真实姓名/企业名称" + }, + "identity": { + "bsonType": "string", + "description": "身份证号码/营业执照号码" + }, + "id_card_front": { + "bsonType": "string", + "description": "身份证正面照 URL" + }, + "id_card_back": { + "bsonType": "string", + "description": "身份证反面照 URL" + }, + "in_hand": { + "bsonType": "string", + "description": "手持身份证照片 URL" + }, + "license": { + "bsonType": "string", + "description": "营业执照 URL" + }, + "contact_person": { + "bsonType": "string", + "description": "联系人姓名" + }, + "contact_mobile": { + "bsonType": "string", + "description": "联系人手机号码" + }, + "contact_email": { + "bsonType": "string", + "description": "联系人邮箱" + } + } + }, + "score": { + "bsonType": "int", + "description": "用户积分,积分变更记录可参考:uni-id-scores表定义", + "permission":{ + "write":false + } + }, + "register_date": { + "bsonType": "timestamp", + "description": "注册时间", + "forceDefaultValue": { + "$env": "now" + }, + "permission":{ + "write":false + } + }, + "register_ip": { + "bsonType": "string", + "description": "注册时 IP 地址", + "forceDefaultValue": { + "$env": "clientIP" + }, + "permission":{ + "write":false + } + }, + "last_login_date": { + "bsonType": "timestamp", + "description": "最后登录时间", + "permission":{ + "write":false + } + }, + "last_login_ip": { + "bsonType": "string", + "description": "最后登录时 IP 地址", + "permission":{ + "write":false + } + }, + "token": { + "bsonType": "array", + "description": "用户token", + "permission":{ + "write":false + } + }, + "inviter_uid": { + "bsonType": "array", + "description": "用户全部上级邀请者", + "trim": "both", + "permission":{ + "write":false + } + }, + "invite_time": { + "bsonType": "timestamp", + "description": "受邀时间", + "permission":{ + "write":false + } + }, + "my_invite_code": { + "bsonType": "string", + "description": "用户自身邀请码", + "permission":{ + "write":false + } + } + } +} diff --git a/uni_modules.config.json b/uni_modules.config.json new file mode 100644 index 0000000000000000000000000000000000000000..ebabb75f3953edb8ebb2b78a440765f627301409 --- /dev/null +++ b/uni_modules.config.json @@ -0,0 +1,6 @@ +{ + "scripts": { + "preupload": "node uni_modules_tools/main.js change", + "postupload": "node uni_modules_tools/main.js recovery" + } +} \ No newline at end of file diff --git a/uni_modules/json-gps/changelog.md b/uni_modules/json-gps/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..d9847e2e43455e00468d589f6414667bc097c4c4 --- /dev/null +++ b/uni_modules/json-gps/changelog.md @@ -0,0 +1,2 @@ +## 1.0.0(2021-07-12) +第一版发布 diff --git a/uni_modules/json-gps/js_sdk/gps.js b/uni_modules/json-gps/js_sdk/gps.js new file mode 100644 index 0000000000000000000000000000000000000000..34bd0e819f86465066099a7f1688fbc4ff0394d9 --- /dev/null +++ b/uni_modules/json-gps/js_sdk/gps.js @@ -0,0 +1,133 @@ +// #ifdef APP-PLUS +import permision from "./wa-permission/permission.js" +// #endif +class Gps { + constructor(arg) { + this.lock = false //锁防止重复请求 + } + async getLocation(param = { + type: 'wgs84' + }) { + return new Promise(async (callback) => { + if (this.lock) { + // console.log('已锁,已有另一个请求正在执行。无需重复请求'); + callback(false); + return false + } + this.lock = true //加锁防止重复的请求 + uni.getLocation({ + ...param, + success: res => { + this.lock = false //成功后解锁 + //console.log(res); + callback(res) + }, + fail: async (err) => { + uni.showToast({ + title: '定位获取失败', + icon: 'none' + }); + console.error(err) + callback(false) + + // #ifdef APP-PLUS + await this.checkGpsIsOpen() + // #endif + + + // #ifdef MP-WEIXIN + if (err.errMsg == 'getLocation:fail auth deny') { + uni.showModal({ + content: '应用无定位权限', + confirmText: '前往设置', + complete: (e) => { + if (e.confirm) { + uni.openSetting({ + success(res) { + console.log(res.authSetting) + } + }); + } + this.lock = false //解锁让回到界面重新获取 + } + }); + } + if (err.errMsg == 'getLocation:fail:ERROR_NOCELL&WIFI_LOCATIONSWITCHOFF') { + uni.showModal({ + content: '未开启定位权限,请前往手机系统设置中开启', + showCancel: false, + confirmText:"知道了" + }); + } + // #endif + } + }); + }) + } + // #ifdef APP-PLUS + async checkGpsIsOpen() { + this.lock = true //加锁防止重复的请求 + console.log('检查定位设置开启问题', permision.checkSystemEnableLocation()); + if (!permision.checkSystemEnableLocation()) { + plus.nativeUI.confirm("手机定位权限没有开启,是否去开启?", (e) => { + this.lock = false + if (e.index == 0) { + if (uni.getSystemInfoSync().platform == "ios") { + plus.runtime.openURL("app-settings://"); + } else { + var main = plus.android.runtimeMainActivity(); //获取activity + var Intent = plus.android.importClass('android.content.Intent'); + var Settings = plus.android.importClass('android.provider.Settings'); + var intent = new Intent(Settings + .ACTION_LOCATION_SOURCE_SETTINGS); //可设置表中所有Action字段 + main.startActivity(intent); + } + } else { + uni.showToast({ + title: '设备无定位权限', + icon: 'none' + }); + callback(false) + } + }, { + "buttons": ["去设置", "不开启"], + "verticalAlign": "center" + }); + return false + } + let checkAppGpsRes = await this.checkAppGps() + console.log(checkAppGpsRes, 'checkAppGpsRes'); + if (!checkAppGpsRes) { + plus.nativeUI.confirm("应用定位权限没有开启,是否去开启?", (e) => { + this.lock = false + if (e.index == 0) { + permision.gotoAppPermissionSetting() + callback(false) + } else { + uni.showToast({ + title: '应用无定位权限', + icon: 'none' + }); + return false + } + }, { + "buttons": ["去设置", "不开启"], + "verticalAlign": "center" + }); + } else { + this.lock = false + } + } + async checkAppGps() { + if (uni.getSystemInfoSync().platform == "ios" && !permision.judgeIosPermission("location")) { + return false + } + if (uni.getSystemInfoSync().platform != "ios" && await permision.requestAndroidPermission( + "android.permission.ACCESS_FINE_LOCATION") != 1) { + return false + } + return true + } + // #endif +} +export default Gps diff --git a/uni_modules/json-gps/js_sdk/wa-permission/permission.js b/uni_modules/json-gps/js_sdk/wa-permission/permission.js new file mode 100644 index 0000000000000000000000000000000000000000..501cab45618434cca3c4597682bc9c404d8e18bc --- /dev/null +++ b/uni_modules/json-gps/js_sdk/wa-permission/permission.js @@ -0,0 +1,272 @@ +/** + * 本模块封装了Android、iOS的应用权限判断、打开应用权限设置界面、以及位置系统服务是否开启 + */ + +var isIos +// #ifdef APP-PLUS +isIos = (plus.os.name == "iOS") +// #endif + +// 判断推送权限是否开启 +function judgeIosPermissionPush() { + var result = false; + var UIApplication = plus.ios.import("UIApplication"); + var app = UIApplication.sharedApplication(); + var enabledTypes = 0; + if (app.currentUserNotificationSettings) { + var settings = app.currentUserNotificationSettings(); + enabledTypes = settings.plusGetAttribute("types"); + console.log("enabledTypes1:" + enabledTypes); + if (enabledTypes == 0) { + console.log("推送权限没有开启"); + } else { + result = true; + console.log("已经开启推送功能!") + } + plus.ios.deleteObject(settings); + } else { + enabledTypes = app.enabledRemoteNotificationTypes(); + if (enabledTypes == 0) { + console.log("推送权限没有开启!"); + } else { + result = true; + console.log("已经开启推送功能!") + } + console.log("enabledTypes2:" + enabledTypes); + } + plus.ios.deleteObject(app); + plus.ios.deleteObject(UIApplication); + return result; +} + +// 判断定位权限是否开启 +function judgeIosPermissionLocation() { + var result = false; + var cllocationManger = plus.ios.import("CLLocationManager"); + var status = cllocationManger.authorizationStatus(); + result = (status != 2) + console.log("定位权限开启:" + result); + // 以下代码判断了手机设备的定位是否关闭,推荐另行使用方法 checkSystemEnableLocation + /* var enable = cllocationManger.locationServicesEnabled(); + var status = cllocationManger.authorizationStatus(); + console.log("enable:" + enable); + console.log("status:" + status); + if (enable && status != 2) { + result = true; + console.log("手机定位服务已开启且已授予定位权限"); + } else { + console.log("手机系统的定位没有打开或未给予定位权限"); + } */ + plus.ios.deleteObject(cllocationManger); + return result; +} + +// 判断麦克风权限是否开启 +function judgeIosPermissionRecord() { + var result = false; + var avaudiosession = plus.ios.import("AVAudioSession"); + var avaudio = avaudiosession.sharedInstance(); + var permissionStatus = avaudio.recordPermission(); + console.log("permissionStatus:" + permissionStatus); + if (permissionStatus == 1684369017 || permissionStatus == 1970168948) { + console.log("麦克风权限没有开启"); + } else { + result = true; + console.log("麦克风权限已经开启"); + } + plus.ios.deleteObject(avaudiosession); + return result; +} + +// 判断相机权限是否开启 +function judgeIosPermissionCamera() { + var result = false; + var AVCaptureDevice = plus.ios.import("AVCaptureDevice"); + var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide'); + console.log("authStatus:" + authStatus); + if (authStatus == 3) { + result = true; + console.log("相机权限已经开启"); + } else { + console.log("相机权限没有开启"); + } + plus.ios.deleteObject(AVCaptureDevice); + return result; +} + +// 判断相册权限是否开启 +function judgeIosPermissionPhotoLibrary() { + var result = false; + var PHPhotoLibrary = plus.ios.import("PHPhotoLibrary"); + var authStatus = PHPhotoLibrary.authorizationStatus(); + console.log("authStatus:" + authStatus); + if (authStatus == 3) { + result = true; + console.log("相册权限已经开启"); + } else { + console.log("相册权限没有开启"); + } + plus.ios.deleteObject(PHPhotoLibrary); + return result; +} + +// 判断通讯录权限是否开启 +function judgeIosPermissionContact() { + var result = false; + var CNContactStore = plus.ios.import("CNContactStore"); + var cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0); + if (cnAuthStatus == 3) { + result = true; + console.log("通讯录权限已经开启"); + } else { + console.log("通讯录权限没有开启"); + } + plus.ios.deleteObject(CNContactStore); + return result; +} + +// 判断日历权限是否开启 +function judgeIosPermissionCalendar() { + var result = false; + var EKEventStore = plus.ios.import("EKEventStore"); + var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0); + if (ekAuthStatus == 3) { + result = true; + console.log("日历权限已经开启"); + } else { + console.log("日历权限没有开启"); + } + plus.ios.deleteObject(EKEventStore); + return result; +} + +// 判断备忘录权限是否开启 +function judgeIosPermissionMemo() { + var result = false; + var EKEventStore = plus.ios.import("EKEventStore"); + var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1); + if (ekAuthStatus == 3) { + result = true; + console.log("备忘录权限已经开启"); + } else { + console.log("备忘录权限没有开启"); + } + plus.ios.deleteObject(EKEventStore); + return result; +} + +// Android权限查询 +function requestAndroidPermission(permissionID) { + return new Promise((resolve, reject) => { + plus.android.requestPermissions( + [permissionID], // 理论上支持多个权限同时查询,但实际上本函数封装只处理了一个权限的情况。有需要的可自行扩展封装 + function(resultObj) { + var result = 0; + for (var i = 0; i < resultObj.granted.length; i++) { + var grantedPermission = resultObj.granted[i]; + console.log('已获取的权限:' + grantedPermission); + result = 1 + } + for (var i = 0; i < resultObj.deniedPresent.length; i++) { + var deniedPresentPermission = resultObj.deniedPresent[i]; + console.log('拒绝本次申请的权限:' + deniedPresentPermission); + result = 0 + } + for (var i = 0; i < resultObj.deniedAlways.length; i++) { + var deniedAlwaysPermission = resultObj.deniedAlways[i]; + console.log('永久拒绝申请的权限:' + deniedAlwaysPermission); + result = -1 + } + resolve(result); + // 若所需权限被拒绝,则打开APP设置界面,可以在APP设置界面打开相应权限 + // if (result != 1) { + // gotoAppPermissionSetting() + // } + }, + function(error) { + console.log('申请权限错误:' + error.code + " = " + error.message); + resolve({ + code: error.code, + message: error.message + }); + } + ); + }); +} + +// 使用一个方法,根据参数判断权限 +function judgeIosPermission(permissionID) { + if (permissionID == "location") { + return judgeIosPermissionLocation() + } else if (permissionID == "camera") { + return judgeIosPermissionCamera() + } else if (permissionID == "photoLibrary") { + return judgeIosPermissionPhotoLibrary() + } else if (permissionID == "record") { + return judgeIosPermissionRecord() + } else if (permissionID == "push") { + return judgeIosPermissionPush() + } else if (permissionID == "contact") { + return judgeIosPermissionContact() + } else if (permissionID == "calendar") { + return judgeIosPermissionCalendar() + } else if (permissionID == "memo") { + return judgeIosPermissionMemo() + } + return false; +} + +// 跳转到**应用**的权限页面 +function gotoAppPermissionSetting() { + if (isIos) { + var UIApplication = plus.ios.import("UIApplication"); + var application2 = UIApplication.sharedApplication(); + var NSURL2 = plus.ios.import("NSURL"); + // var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES"); + var setting2 = NSURL2.URLWithString("app-settings:"); + application2.openURL(setting2); + + plus.ios.deleteObject(setting2); + plus.ios.deleteObject(NSURL2); + plus.ios.deleteObject(application2); + } else { + // console.log(plus.device.vendor); + 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); + } +} + +// 检查系统的设备服务是否开启 +// var checkSystemEnableLocation = async function () { +function checkSystemEnableLocation() { + if (isIos) { + var result = false; + var cllocationManger = plus.ios.import("CLLocationManager"); + var result = cllocationManger.locationServicesEnabled(); + console.log("系统定位开启:" + result); + plus.ios.deleteObject(cllocationManger); + return result; + } else { + var context = plus.android.importClass("android.content.Context"); + var locationManager = plus.android.importClass("android.location.LocationManager"); + var main = plus.android.runtimeMainActivity(); + var mainSvr = main.getSystemService(context.LOCATION_SERVICE); + var result = mainSvr.isProviderEnabled(locationManager.GPS_PROVIDER); + console.log("系统定位开启:" + result); + return result + } +} + +export default { + judgeIosPermission: judgeIosPermission, + requestAndroidPermission: requestAndroidPermission, + checkSystemEnableLocation: checkSystemEnableLocation, + gotoAppPermissionSetting: gotoAppPermissionSetting +} diff --git a/uni_modules/json-gps/package.json b/uni_modules/json-gps/package.json new file mode 100644 index 0000000000000000000000000000000000000000..3689121274d939b689de3d8e841da4b65fdb83fe --- /dev/null +++ b/uni_modules/json-gps/package.json @@ -0,0 +1,76 @@ +{ + "id": "json-gps", + "displayName": "地理位置获取方法,支持在onShow生命周期使用集成权限判断和引导开启(包括设备权限和应用权限)", + "version": "1.0.0", + "description": "支持在onShow生命周期使用集成权限判断和引导开启(包括设备权限和应用权限)的地理位置获取方法", + "keywords": [ + "权限引导" +], + "repository": "", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "JS SDK", + "通用 SDK" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "插件不采集任何数据", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "u", + "Android Browser": "u", + "微信浏览器(Android)": "u", + "QQ浏览器(Android)": "u" + }, + "H5-pc": { + "Chrome": "u", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "y", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/json-gps/readme.md b/uni_modules/json-gps/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..739335fc233a750753ffc013ab9a96688de68c42 --- /dev/null +++ b/uni_modules/json-gps/readme.md @@ -0,0 +1,30 @@ +### 插件简介: +支持在onShow生命周期,使用的集成权限判断和引导开启(包括设备权限和应用权限)的地理位置获取方法 + +### 插件背景: +实现获取用户地理位置,当手机未开启定位模块或应用无定位权限时,引导用户前往手机系统或应用权限设置页面。设置完回到应用界面自动重新获取。 +为了实现该效果,开发者把获取定位权限放在onShow生命周期,然而即使是原生开发,调用判断设备权限操作也会触发onShow生命周期,直接使用会导致死循环。因此本插件,二次封装用锁的方式控制该问题。 + +### 使用方式 +```js +import Gps from '@/uni_modules/json-gps/js_sdk/gps.js'; +const gps = new Gps() +export default { + async onShow() { + uni.showLoading({ + title:"获取定位中" + }); + let location = await gps.getLocation() + console.log(location); + if(location){ + uni.showToast({ + title: JSON.stringify(location), + icon: 'none' + }); + } + uni.hideLoading() + } +} +``` + +>本插件基于第三方插件:[App权限判断和提示](https://ext.dcloud.net.cn/plugin?id=594)二次封装而成 \ No newline at end of file diff --git a/uni_modules/json-interceptor-chooseImage/changelog.md b/uni_modules/json-interceptor-chooseImage/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..512bce81867403f5638af728a1d30982e9537e7f --- /dev/null +++ b/uni_modules/json-interceptor-chooseImage/changelog.md @@ -0,0 +1,6 @@ +## 1.0.2(2021-05-20) +修复IOS提示不准确,无摄像头权限提示了无法访问相册 +## 1.0.1(2021-05-20) +新增文档和示例代码 +## 1.0.0(2021-05-20) +第一版本发布 diff --git a/uni_modules/json-interceptor-chooseImage/js_sdk/main.js b/uni_modules/json-interceptor-chooseImage/js_sdk/main.js new file mode 100644 index 0000000000000000000000000000000000000000..7cf7bc605d189d68764b0b4251bed83e76496e1a --- /dev/null +++ b/uni_modules/json-interceptor-chooseImage/js_sdk/main.js @@ -0,0 +1,70 @@ +export default function(){ + //当应用无访问摄像头/相册权限,引导跳到设置界面 + uni.addInterceptor('chooseImage', { + fail(e) { // 失败回调拦截 更多拦截器用法 [详情](https://uniapp.dcloud.io/api/interceptor?id=addinterceptor) + console.log(e); + if (uni.getSystemInfoSync().platform == "android" && e.errMsg == 'chooseImage:fail No Permission') { + if (e.code === 11) { + uni.showModal({ + title: "无法访问摄像头", + content: "当前无摄像头访问权限,建议前往设置", + confirmText: "前往设置", + success(e) { + if (e.confirm) { + gotoAppPermissionSetting() + } + } + }); + } else { + uni.showModal({ + title: "无法访问相册", + content: "当前无系统相册访问权限,建议前往设置", + confirmText: "前往设置", + success(e) { + if (e.confirm) { + gotoAppPermissionSetting() + } + } + }); + } + } else if (e.errCode === 2&&e.errMsg == "chooseImage:fail No filming permission") { + console.log('e.errMsg === 2 ios无法拍照权限 '); + // 注:e.errCode === 8 ios无从相册选择图片的权限 api已内置无需自己用拦截器实现 + uni.showModal({ + title: "无法访问摄像头", + content: "当前无摄像头访问权限,建议前往设置", + confirmText: "前往设置", + success(e) { + if (e.confirm) { + gotoAppPermissionSetting() + } + } + }); + } + } + }) + + //跳转到**应用**的权限页面 参考来源:https://ext.dcloud.net.cn/plugin?id=594 + function gotoAppPermissionSetting() { + if (uni.getSystemInfoSync().platform == "ios") { + 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); + } + } +} \ No newline at end of file diff --git a/uni_modules/json-interceptor-chooseImage/package.json b/uni_modules/json-interceptor-chooseImage/package.json new file mode 100644 index 0000000000000000000000000000000000000000..9b81e6a1209db4fe0c6f32e101f0ee124cf83367 --- /dev/null +++ b/uni_modules/json-interceptor-chooseImage/package.json @@ -0,0 +1,76 @@ +{ + "id": "json-interceptor-chooseImage", + "displayName": "拦截器应用示例 — 图片选择", + "version": "1.0.2", + "description": "当选择图片遇到权限问题时引导用户快捷打开系统设置", + "keywords": [ + "interceptor,拦截器,相册权限" +], + "repository": "", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "JS SDK", + "通用 SDK" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "n", + "Android Browser": "n", + "微信浏览器(Android)": "n", + "QQ浏览器(Android)": "n" + }, + "H5-pc": { + "Chrome": "n", + "IE": "n", + "Edge": "n", + "Firefox": "n", + "Safari": "n" + }, + "小程序": { + "微信": "n", + "阿里": "n", + "百度": "n", + "字节跳动": "n", + "QQ": "n" + }, + "快应用": { + "华为": "n", + "联盟": "n" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/json-interceptor-chooseImage/readme.md b/uni_modules/json-interceptor-chooseImage/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..e9d98fb956397d004d17975bd05fd64d00624f85 --- /dev/null +++ b/uni_modules/json-interceptor-chooseImage/readme.md @@ -0,0 +1,30 @@ +拦截器顾名思义,是在框架方法执行的各个环节(包含:拦截前触发、成功回调拦截、失败回调拦截、完成回调拦截) +插入逻辑,篡改参数或终止运行。 +#### 优势 +- 这种方式相当于改写了api的内部逻辑,相比语法糖他更加直观,不影响ide的代码提示。 +- 将常规固定的逻辑封装到框架的api内,且支持个性化设计。如你可以在本插件路径:`/uni_modules/json-interceptor-chooseImage/js_sdk/main.js`修改弹出框元素,绘制更漂亮的样式和文字说明。 + +#### 使用示例,App.vue页代码如下: +``` + +``` + +> 跳转到**应用**的权限页面 参考来源:[https://ext.dcloud.net.cn/plugin?id=594](https://ext.dcloud.net.cn/plugin?id=594) 感谢作者@DCloud_heavensoft diff --git a/uni_modules/uni-badge/changelog.md b/uni_modules/uni-badge/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..4ca8c5b477a3f996a515460a986c96c6bf63d4ef --- /dev/null +++ b/uni_modules/uni-badge/changelog.md @@ -0,0 +1,22 @@ +## 1.1.6(2021-09-22) +- 修复 在字节小程序上样式不生效的 bug +## 1.1.5(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.4(2021-07-29) +- 修复 去掉 nvue 不支持css 的 align-self 属性,nvue 下不暂支持 absolute 属性 +## 1.1.3(2021-06-24) +- 优化 示例项目 +## 1.1.1(2021-05-12) +- 新增 组件示例地址 +## 1.1.0(2021-05-12) +- 新增 uni-badge 的 absolute 属性,支持定位 +- 新增 uni-badge 的 offset 属性,支持定位偏移 +- 新增 uni-badge 的 is-dot 属性,支持仅显示有一个小点 +- 新增 uni-badge 的 max-num 属性,支持自定义封顶的数字值,超过 99 显示99+ +- 优化 uni-badge 属性 custom-style, 支持以对象形式自定义样式 +## 1.0.7(2021-05-07) +- 修复 uni-badge 在 App 端,数字小于10时不是圆形的bug +- 修复 uni-badge 在父元素不是 flex 布局时,宽度缩小的bug +- 新增 uni-badge 属性 custom-style, 支持自定义样式 +## 1.0.6(2021-02-04) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-badge/components/uni-badge/uni-badge.vue b/uni_modules/uni-badge/components/uni-badge/uni-badge.vue new file mode 100644 index 0000000000000000000000000000000000000000..f3869c481ec5f1547e41d3cf3d2dc1c9ce5b0f22 --- /dev/null +++ b/uni_modules/uni-badge/components/uni-badge/uni-badge.vue @@ -0,0 +1,253 @@ + + + + + diff --git a/uni_modules/uni-badge/package.json b/uni_modules/uni-badge/package.json new file mode 100644 index 0000000000000000000000000000000000000000..4efafd509b3489e15a5081e838f51456acfbb9a4 --- /dev/null +++ b/uni_modules/uni-badge/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-badge", + "displayName": "uni-badge 数字角标", + "version": "1.1.6", + "description": "数字角标(徽章)组件,在元素周围展示消息提醒,一般用于列表、九宫格、按钮等地方。", + "keywords": [ + "", + "badge", + "uni-ui", + "uniui", + "数字角标", + "徽章" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "y", + "联盟": "y" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-badge/readme.md b/uni_modules/uni-badge/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..d29680be1c2da3d0e6ad943e41568f7d1161fc84 --- /dev/null +++ b/uni_modules/uni-badge/readme.md @@ -0,0 +1,58 @@ + + +## Badge 数字角标 +> **组件名:uni-badge** +> 代码块: `uBadge` + + +数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景, + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +在 ``template`` 中使用组件 + +```html + + + + + + + +``` + + +## API + +### Badge Props + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|text |String |- |角标内容 | +|type |String |default|颜色类型,可选值:default(灰色)、primary(蓝色)、success(绿色)、warning(黄色)、error(红色)| +|size |String |normal |Badge 大小,可取值:normal、small | +|is-dot |Boolean|false |不展示数字,只有一个小点 | +|max-num |String/Numbuer|99 |展示封顶的数字值,超过 99 显示99+ | +|custom-style |Object | {} |自定义 Badge 样式, 样式对象语法 | +|inverted |Boolean|false |是否无需背景颜色,为 true 时,背景颜色将变为文字的字体颜色 | +|absolute (不支持 nvue) |String| rightTop|开启绝对定位, 角标将定位到其包裹的标签的四个角上,可选值: rightTop(右上角)、rightBottom(右下角)、leftBottom(左下角) 、leftTop(左上角) | +|offset |Array[number]| [0, 0]|距定位角中心点的偏移量,[-10, -10] 表示向 absolute 指定的方向偏移 10px,[10, 10] 表示向 absolute 指定的反方向偏移 10px,只有存在 absolute 属性时有效,与absolute 的值一一对应(例如:值为rightTop, 对应 offset 为 [right, Top])| + +### Badge Events + +|事件名 |事件说明 |返回参数 | +|:-: |:-: |:-: | +|@click |点击 Badge 触发事件| - | + + + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/badge/badge](https://hellouniapp.dcloud.net.cn/pages/extUI/badge/badge) \ No newline at end of file diff --git a/uni_modules/uni-calendar/changelog.md b/uni_modules/uni-calendar/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..bd8974992c1e97a734e1c3ae9d251d9025f483e0 --- /dev/null +++ b/uni_modules/uni-calendar/changelog.md @@ -0,0 +1,10 @@ +## 1.4.2(2021-08-24) +- 新增 支持国际化 +## 1.4.1(2021-08-05) +- 修复 弹出层被 tabbar 遮盖 bug +## 1.4.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.3.16(2021-05-12) +- 新增 组件示例地址 +## 1.3.15(2021-02-04) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-calendar/components/uni-calendar/calendar.js b/uni_modules/uni-calendar/components/uni-calendar/calendar.js new file mode 100644 index 0000000000000000000000000000000000000000..b8d7d6fc44038901fe7a47def32b53764575c1a8 --- /dev/null +++ b/uni_modules/uni-calendar/components/uni-calendar/calendar.js @@ -0,0 +1,546 @@ +/** +* @1900-2100区间内的公历、农历互转 +* @charset UTF-8 +* @github https://github.com/jjonline/calendar.js +* @Author Jea杨(JJonline@JJonline.Cn) +* @Time 2014-7-21 +* @Time 2016-8-13 Fixed 2033hex、Attribution Annals +* @Time 2016-9-25 Fixed lunar LeapMonth Param Bug +* @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year +* @Version 1.0.3 +* @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0] +* @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0] +*/ +/* eslint-disable */ +var calendar = { + + /** + * 农历1900-2100的润大小信息表 + * @Array Of Property + * @return Hex + */ + lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909 + 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919 + 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929 + 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939 + 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949 + 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959 + 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969 + 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979 + 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989 + 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999 + 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009 + 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019 + 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029 + 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039 + 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049 + /** Add By JJonline@JJonline.Cn**/ + 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059 + 0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069 + 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079 + 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089 + 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099 + 0x0d520], // 2100 + + /** + * 公历每个月份的天数普通表 + * @Array Of Property + * @return Number + */ + solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + + /** + * 天干地支之天干速查表 + * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"] + * @return Cn string + */ + Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'], + + /** + * 天干地支之地支速查表 + * @Array Of Property + * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"] + * @return Cn string + */ + Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'], + + /** + * 天干地支之地支速查表<=>生肖 + * @Array Of Property + * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"] + * @return Cn string + */ + Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'], + + /** + * 24节气速查表 + * @Array Of Property + * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"] + * @return Cn string + */ + solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'], + + /** + * 1900-2100各年的24节气日期速查表 + * @Array Of Property + * @return 0x string For splice + */ + sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', + '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', + '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', + 'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f', + '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa', + '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', + '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f', + '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', + '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', + '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', + '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722', + '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f', + '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e', + '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', + '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722', + '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', + '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', + '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', + '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e', + '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', + '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa', + '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', + '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721', + '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2', + '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', + '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', + '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', + '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721', + '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5', + '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722', + '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', + '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721', + '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd', + '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35', + '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', + '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721', + '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5', + '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35', + '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35', + '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'], + + /** + * 数字转中文速查表 + * @Array Of Property + * @trans ['日','一','二','三','四','五','六','七','八','九','十'] + * @return Cn string + */ + nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'], + + /** + * 日期转农历称呼速查表 + * @Array Of Property + * @trans ['初','十','廿','卅'] + * @return Cn string + */ + nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'], + + /** + * 月份转农历称呼速查表 + * @Array Of Property + * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊'] + * @return Cn string + */ + nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'], + + /** + * 返回农历y年一整年的总天数 + * @param lunar Year + * @return Number + * @eg:var count = calendar.lYearDays(1987) ;//count=387 + */ + lYearDays: function (y) { + var i; var sum = 348 + for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 } + return (sum + this.leapDays(y)) + }, + + /** + * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0 + * @param lunar Year + * @return Number (0-12) + * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6 + */ + leapMonth: function (y) { // 闰字编码 \u95f0 + return (this.lunarInfo[y - 1900] & 0xf) + }, + + /** + * 返回农历y年闰月的天数 若该年没有闰月则返回0 + * @param lunar Year + * @return Number (0、29、30) + * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29 + */ + leapDays: function (y) { + if (this.leapMonth(y)) { + return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29) + } + return (0) + }, + + /** + * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法 + * @param lunar Year + * @return Number (-1、29、30) + * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29 + */ + monthDays: function (y, m) { + if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1 + return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29) + }, + + /** + * 返回公历(!)y年m月的天数 + * @param solar Year + * @return Number (-1、28、29、30、31) + * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30 + */ + solarDays: function (y, m) { + if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1 + var ms = m - 1 + if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29 + return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28) + } else { + return (this.solarMonth[ms]) + } + }, + + /** + * 农历年份转换为干支纪年 + * @param lYear 农历年的年份数 + * @return Cn string + */ + toGanZhiYear: function (lYear) { + var ganKey = (lYear - 3) % 10 + var zhiKey = (lYear - 3) % 12 + if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干 + if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支 + return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1] + }, + + /** + * 公历月、日判断所属星座 + * @param cMonth [description] + * @param cDay [description] + * @return Cn string + */ + toAstro: function (cMonth, cDay) { + var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf' + var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22] + return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座 + }, + + /** + * 传入offset偏移量返回干支 + * @param offset 相对甲子的偏移量 + * @return Cn string + */ + toGanZhi: function (offset) { + return this.Gan[offset % 10] + this.Zhi[offset % 12] + }, + + /** + * 传入公历(!)y年获得该年第n个节气的公历日期 + * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起 + * @return day Number + * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春 + */ + getTerm: function (y, n) { + if (y < 1900 || y > 2100) { return -1 } + if (n < 1 || n > 24) { return -1 } + var _table = this.sTermInfo[y - 1900] + var _info = [ + parseInt('0x' + _table.substr(0, 5)).toString(), + parseInt('0x' + _table.substr(5, 5)).toString(), + parseInt('0x' + _table.substr(10, 5)).toString(), + parseInt('0x' + _table.substr(15, 5)).toString(), + parseInt('0x' + _table.substr(20, 5)).toString(), + parseInt('0x' + _table.substr(25, 5)).toString() + ] + var _calday = [ + _info[0].substr(0, 1), + _info[0].substr(1, 2), + _info[0].substr(3, 1), + _info[0].substr(4, 2), + + _info[1].substr(0, 1), + _info[1].substr(1, 2), + _info[1].substr(3, 1), + _info[1].substr(4, 2), + + _info[2].substr(0, 1), + _info[2].substr(1, 2), + _info[2].substr(3, 1), + _info[2].substr(4, 2), + + _info[3].substr(0, 1), + _info[3].substr(1, 2), + _info[3].substr(3, 1), + _info[3].substr(4, 2), + + _info[4].substr(0, 1), + _info[4].substr(1, 2), + _info[4].substr(3, 1), + _info[4].substr(4, 2), + + _info[5].substr(0, 1), + _info[5].substr(1, 2), + _info[5].substr(3, 1), + _info[5].substr(4, 2) + ] + return parseInt(_calday[n - 1]) + }, + + /** + * 传入农历数字月份返回汉语通俗表示法 + * @param lunar month + * @return Cn string + * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月' + */ + toChinaMonth: function (m) { // 月 => \u6708 + if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1 + var s = this.nStr3[m - 1] + s += '\u6708'// 加上月字 + return s + }, + + /** + * 传入农历日期数字返回汉字表示法 + * @param lunar day + * @return Cn string + * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一' + */ + toChinaDay: function (d) { // 日 => \u65e5 + var s + switch (d) { + case 10: + s = '\u521d\u5341'; break + case 20: + s = '\u4e8c\u5341'; break + break + case 30: + s = '\u4e09\u5341'; break + break + default : + s = this.nStr2[Math.floor(d / 10)] + s += this.nStr1[d % 10] + } + return (s) + }, + + /** + * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春” + * @param y year + * @return Cn string + * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔' + */ + getAnimal: function (y) { + return this.Animals[(y - 4) % 12] + }, + + /** + * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON + * @param y solar year + * @param m solar month + * @param d solar day + * @return JSON object + * @eg:console.log(calendar.solar2lunar(1987,11,01)); + */ + solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31 + // 年份限定、上限 + if (y < 1900 || y > 2100) { + return -1// undefined转换为数字变为NaN + } + // 公历传参最下限 + if (y == 1900 && m == 1 && d < 31) { + return -1 + } + // 未传参 获得当天 + if (!y) { + var objDate = new Date() + } else { + var objDate = new Date(y, parseInt(m) - 1, d) + } + var i; var leap = 0; var temp = 0 + // 修正ymd参数 + var y = objDate.getFullYear() + var m = objDate.getMonth() + 1 + var d = objDate.getDate() + var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000 + for (i = 1900; i < 2101 && offset > 0; i++) { + temp = this.lYearDays(i) + offset -= temp + } + if (offset < 0) { + offset += temp; i-- + } + + // 是否今天 + var isTodayObj = new Date() + var isToday = false + if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) { + isToday = true + } + // 星期几 + var nWeek = objDate.getDay() + var cWeek = this.nStr1[nWeek] + // 数字表示周几顺应天朝周一开始的惯例 + if (nWeek == 0) { + nWeek = 7 + } + // 农历年 + var year = i + var leap = this.leapMonth(i) // 闰哪个月 + var isLeap = false + + // 效验闰月 + for (i = 1; i < 13 && offset > 0; i++) { + // 闰月 + if (leap > 0 && i == (leap + 1) && isLeap == false) { + --i + isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数 + } else { + temp = this.monthDays(year, i)// 计算农历普通月天数 + } + // 解除闰月 + if (isLeap == true && i == (leap + 1)) { isLeap = false } + offset -= temp + } + // 闰月导致数组下标重叠取反 + if (offset == 0 && leap > 0 && i == leap + 1) { + if (isLeap) { + isLeap = false + } else { + isLeap = true; --i + } + } + if (offset < 0) { + offset += temp; --i + } + // 农历月 + var month = i + // 农历日 + var day = offset + 1 + // 天干地支处理 + var sm = m - 1 + var gzY = this.toGanZhiYear(year) + + // 当月的两个节气 + // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year` + var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始 + var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始 + + // 依据12节气修正干支月 + var gzM = this.toGanZhi((y - 1900) * 12 + m + 11) + if (d >= firstNode) { + gzM = this.toGanZhi((y - 1900) * 12 + m + 12) + } + + // 传入的日期的节气与否 + var isTerm = false + var Term = null + if (firstNode == d) { + isTerm = true + Term = this.solarTerm[m * 2 - 2] + } + if (secondNode == d) { + isTerm = true + Term = this.solarTerm[m * 2 - 1] + } + // 日柱 当月一日与 1900/1/1 相差天数 + var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10 + var gzD = this.toGanZhi(dayCyclical + d - 1) + // 该日期所属的星座 + var astro = this.toAstro(m, d) + + return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro } + }, + + /** + * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON + * @param y lunar year + * @param m lunar month + * @param d lunar day + * @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可] + * @return JSON object + * @eg:console.log(calendar.lunar2solar(1987,9,10)); + */ + lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1 + var isLeapMonth = !!isLeapMonth + var leapOffset = 0 + var leapMonth = this.leapMonth(y) + var leapDay = this.leapDays(y) + if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同 + if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值 + var day = this.monthDays(y, m) + var _day = day + // bugFix 2016-9-25 + // if month is leap, _day use leapDays method + if (isLeapMonth) { + _day = this.leapDays(y, m) + } + if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验 + + // 计算农历的时间差 + var offset = 0 + for (var i = 1900; i < y; i++) { + offset += this.lYearDays(i) + } + var leap = 0; var isAdd = false + for (var i = 1; i < m; i++) { + leap = this.leapMonth(y) + if (!isAdd) { // 处理闰月 + if (leap <= i && leap > 0) { + offset += this.leapDays(y); isAdd = true + } + } + offset += this.monthDays(y, i) + } + // 转换闰月农历 需补充该年闰月的前一个月的时差 + if (isLeapMonth) { offset += day } + // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点) + var stmap = Date.UTC(1900, 1, 30, 0, 0, 0) + var calObj = new Date((offset + d - 31) * 86400000 + stmap) + var cY = calObj.getUTCFullYear() + var cM = calObj.getUTCMonth() + 1 + var cD = calObj.getUTCDate() + + return this.solar2lunar(cY, cM, cD) + } +} + +export default calendar diff --git a/uni_modules/uni-calendar/components/uni-calendar/i18n/en.json b/uni_modules/uni-calendar/components/uni-calendar/i18n/en.json new file mode 100644 index 0000000000000000000000000000000000000000..fcbd13cfc08064e801531332625f0fdacbb7c509 --- /dev/null +++ b/uni_modules/uni-calendar/components/uni-calendar/i18n/en.json @@ -0,0 +1,12 @@ +{ + "uni-calender.ok": "ok", + "uni-calender.cancel": "cancel", + "uni-calender.today": "today", + "uni-calender.MON": "MON", + "uni-calender.TUE": "TUE", + "uni-calender.WED": "WED", + "uni-calender.THU": "THU", + "uni-calender.FRI": "FRI", + "uni-calender.SAT": "SAT", + "uni-calender.SUN": "SUN" +} diff --git a/uni_modules/uni-calendar/components/uni-calendar/i18n/index.js b/uni_modules/uni-calendar/components/uni-calendar/i18n/index.js new file mode 100644 index 0000000000000000000000000000000000000000..de7509c87ba5197b9f5d20ca94a0558c7a8e08a9 --- /dev/null +++ b/uni_modules/uni-calendar/components/uni-calendar/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hans.json b/uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hans.json new file mode 100644 index 0000000000000000000000000000000000000000..1ca43de010911bfec3405d84de46530f6de1a03e --- /dev/null +++ b/uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hans.json @@ -0,0 +1,12 @@ +{ + "uni-calender.ok": "确定", + "uni-calender.cancel": "取消", + "uni-calender.today": "今日", + "uni-calender.SUN": "日", + "uni-calender.MON": "一", + "uni-calender.TUE": "二", + "uni-calender.WED": "三", + "uni-calender.THU": "四", + "uni-calender.FRI": "五", + "uni-calender.SAT": "六" +} diff --git a/uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hant.json b/uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hant.json new file mode 100644 index 0000000000000000000000000000000000000000..e0fe33b958f8ee991a4de2d1b370c8ca81e0dbb9 --- /dev/null +++ b/uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hant.json @@ -0,0 +1,12 @@ +{ + "uni-calender.ok": "確定", + "uni-calender.cancel": "取消", + "uni-calender.today": "今日", + "uni-calender.SUN": "日", + "uni-calender.MON": "一", + "uni-calender.TUE": "二", + "uni-calender.WED": "三", + "uni-calender.THU": "四", + "uni-calender.FRI": "五", + "uni-calender.SAT": "六" +} diff --git a/uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue b/uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..0353011e99ec6f362fba732ce5ca62c490288f23 --- /dev/null +++ b/uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue b/uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue new file mode 100644 index 0000000000000000000000000000000000000000..401a2de6d5d5d1ea583d562dd67d0711e94f2ce3 --- /dev/null +++ b/uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue @@ -0,0 +1,547 @@ + + + + + diff --git a/uni_modules/uni-calendar/components/uni-calendar/util.js b/uni_modules/uni-calendar/components/uni-calendar/util.js new file mode 100644 index 0000000000000000000000000000000000000000..37f44321ebc299212eea8646c9e7e63e9b2ed6f8 --- /dev/null +++ b/uni_modules/uni-calendar/components/uni-calendar/util.js @@ -0,0 +1,352 @@ +import CALENDAR from './calendar.js' + +class Calendar { + constructor({ + date, + selected, + startDate, + endDate, + range + } = {}) { + // 当前日期 + this.date = this.getDate(new Date()) // 当前初入日期 + // 打点信息 + this.selected = selected || []; + // 范围开始 + this.startDate = startDate + // 范围结束 + this.endDate = endDate + this.range = range + // 多选状态 + this.cleanMultipleStatus() + // 每周日期 + this.weeks = {} + // this._getWeek(this.date.fullDate) + } + /** + * 设置日期 + * @param {Object} date + */ + setDate(date) { + this.selectDate = this.getDate(date) + this._getWeek(this.selectDate.fullDate) + } + + /** + * 清理多选状态 + */ + cleanMultipleStatus() { + this.multipleStatus = { + before: '', + after: '', + data: [] + } + } + + /** + * 重置开始日期 + */ + resetSatrtDate(startDate) { + // 范围开始 + this.startDate = startDate + + } + + /** + * 重置结束日期 + */ + resetEndDate(endDate) { + // 范围结束 + this.endDate = endDate + } + + /** + * 获取任意时间 + */ + getDate(date, AddDayCount = 0, str = 'day') { + if (!date) { + date = new Date() + } + if (typeof date !== 'object') { + date = date.replace(/-/g, '/') + } + const dd = new Date(date) + switch (str) { + case 'day': + dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期 + break + case 'month': + if (dd.getDate() === 31) { + dd.setDate(dd.getDate() + AddDayCount) + } else { + dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期 + } + break + case 'year': + dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期 + break + } + const y = dd.getFullYear() + const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0 + const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0 + return { + fullDate: y + '-' + m + '-' + d, + year: y, + month: m, + date: d, + day: dd.getDay() + } + } + + + /** + * 获取上月剩余天数 + */ + _getLastMonthDays(firstDay, full) { + let dateArr = [] + for (let i = firstDay; i > 0; i--) { + const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate() + dateArr.push({ + date: beforeDate, + month: full.month - 1, + lunar: this.getlunar(full.year, full.month - 1, beforeDate), + disable: true + }) + } + return dateArr + } + /** + * 获取本月天数 + */ + _currentMonthDys(dateData, full) { + let dateArr = [] + let fullDate = this.date.fullDate + for (let i = 1; i <= dateData; i++) { + let isinfo = false + let nowDate = full.year + '-' + (full.month < 10 ? + full.month : full.month) + '-' + (i < 10 ? + '0' + i : i) + // 是否今天 + let isDay = fullDate === nowDate + // 获取打点信息 + let info = this.selected && this.selected.find((item) => { + if (this.dateEqual(nowDate, item.date)) { + return item + } + }) + + // 日期禁用 + let disableBefore = true + let disableAfter = true + if (this.startDate) { + let dateCompBefore = this.dateCompare(this.startDate, fullDate) + disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate) + } + + if (this.endDate) { + let dateCompAfter = this.dateCompare(fullDate, this.endDate) + disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate) + } + let multiples = this.multipleStatus.data + let checked = false + let multiplesStatus = -1 + if (this.range) { + if (multiples) { + multiplesStatus = multiples.findIndex((item) => { + return this.dateEqual(item, nowDate) + }) + } + if (multiplesStatus !== -1) { + checked = true + } + } + let data = { + fullDate: nowDate, + year: full.year, + date: i, + multiple: this.range ? checked : false, + beforeMultiple: this.dateEqual(this.multipleStatus.before, nowDate), + afterMultiple: this.dateEqual(this.multipleStatus.after, nowDate), + month: full.month, + lunar: this.getlunar(full.year, full.month, i), + disable: !disableBefore || !disableAfter, + isDay + } + if (info) { + data.extraInfo = info + } + + dateArr.push(data) + } + return dateArr + } + /** + * 获取下月天数 + */ + _getNextMonthDays(surplus, full) { + let dateArr = [] + for (let i = 1; i < surplus + 1; i++) { + dateArr.push({ + date: i, + month: Number(full.month) + 1, + lunar: this.getlunar(full.year, Number(full.month) + 1, i), + disable: true + }) + } + return dateArr + } + + /** + * 获取当前日期详情 + * @param {Object} date + */ + getInfo(date) { + if (!date) { + date = new Date() + } + const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate) + return dateInfo + } + + /** + * 比较时间大小 + */ + dateCompare(startDate, endDate) { + // 计算截止时间 + startDate = new Date(startDate.replace('-', '/').replace('-', '/')) + // 计算详细项的截止时间 + endDate = new Date(endDate.replace('-', '/').replace('-', '/')) + if (startDate <= endDate) { + return true + } else { + return false + } + } + + /** + * 比较时间是否相等 + */ + dateEqual(before, after) { + // 计算截止时间 + before = new Date(before.replace('-', '/').replace('-', '/')) + // 计算详细项的截止时间 + after = new Date(after.replace('-', '/').replace('-', '/')) + if (before.getTime() - after.getTime() === 0) { + return true + } else { + return false + } + } + + + /** + * 获取日期范围内所有日期 + * @param {Object} begin + * @param {Object} end + */ + geDateAll(begin, end) { + var arr = [] + var ab = begin.split('-') + var ae = end.split('-') + var db = new Date() + db.setFullYear(ab[0], ab[1] - 1, ab[2]) + var de = new Date() + de.setFullYear(ae[0], ae[1] - 1, ae[2]) + var unixDb = db.getTime() - 24 * 60 * 60 * 1000 + var unixDe = de.getTime() - 24 * 60 * 60 * 1000 + for (var k = unixDb; k <= unixDe;) { + k = k + 24 * 60 * 60 * 1000 + arr.push(this.getDate(new Date(parseInt(k))).fullDate) + } + return arr + } + /** + * 计算阴历日期显示 + */ + getlunar(year, month, date) { + return CALENDAR.solar2lunar(year, month, date) + } + /** + * 设置打点 + */ + setSelectInfo(data, value) { + this.selected = value + this._getWeek(data) + } + + /** + * 获取多选状态 + */ + setMultiple(fullDate) { + let { + before, + after + } = this.multipleStatus + + if (!this.range) return + if (before && after) { + this.multipleStatus.before = '' + this.multipleStatus.after = '' + this.multipleStatus.data = [] + } else { + if (!before) { + this.multipleStatus.before = fullDate + } else { + this.multipleStatus.after = fullDate + if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) { + this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after); + } else { + this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before); + } + } + } + this._getWeek(fullDate) + } + + /** + * 获取每周数据 + * @param {Object} dateData + */ + _getWeek(dateData) { + const { + fullDate, + year, + month, + date, + day + } = this.getDate(dateData) + let firstDay = new Date(year, month - 1, 1).getDay() + let currentDay = new Date(year, month, 0).getDate() + let dates = { + lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天 + currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数 + nextMonthDays: [], // 下个月开始几天 + weeks: [] + } + let canlender = [] + const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length) + dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData)) + canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays) + let weeks = {} + // 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天 + for (let i = 0; i < canlender.length; i++) { + if (i % 7 === 0) { + weeks[parseInt(i / 7)] = new Array(7) + } + weeks[parseInt(i / 7)][i % 7] = canlender[i] + } + this.canlender = canlender + this.weeks = weeks + } + + //静态方法 + // static init(date) { + // if (!this.instance) { + // this.instance = new Calendar(date); + // } + // return this.instance; + // } +} + + +export default Calendar diff --git a/uni_modules/uni-calendar/package.json b/uni_modules/uni-calendar/package.json new file mode 100644 index 0000000000000000000000000000000000000000..8a5023b2807c5935b209dd88602a448e5a6a95ca --- /dev/null +++ b/uni_modules/uni-calendar/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-calendar", + "displayName": "uni-calendar 日历", + "version": "1.4.2", + "description": "日历组件", + "keywords": [ + "uni-ui", + "uniui", + "日历", + "", + "打卡", + "日历选择" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-calendar/readme.md b/uni_modules/uni-calendar/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..4f3ca0e84117a03d4f3630b92880894b81bb45f0 --- /dev/null +++ b/uni_modules/uni-calendar/readme.md @@ -0,0 +1,103 @@ + + +## Calendar 日历 +> **组件名:uni-calendar** +> 代码块: `uCalendar` + + +日历组件 + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - 本组件农历转换使用的js是 [@1900-2100区间内的公历、农历互转](https://github.com/jjonline/calendar.js) +> - 仅支持自定义组件模式 +> - `date`属性传入的应该是一个 String ,如: 2019-06-27 ,而不是 new Date() +> - 通过 `insert` 属性来确定当前的事件是 @change 还是 @confirm 。理应合并为一个事件,但是为了区分模式,现使用两个事件,这里需要注意 +> - 弹窗模式下无法阻止后面的元素滚动,如有需要阻止,请在弹窗弹出后,手动设置滚动元素为不可滚动 + + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +在 ``template`` 中使用组件 + +```html + + + +``` + +### 通过方法打开日历 + +需要设置 `insert` 为 `false` + +```html + + + + +``` + +```javascript + +export default { + data() { + return {}; + }, + methods: { + open(){ + this.$refs.calendar.open(); + }, + confirm(e) { + console.log(e); + } + } +}; + +``` + + +## API + +### Calendar Props + +| 属性名 | 类型 | 默认值| 说明 | +| | | +| date | String |- | 自定义当前时间,默认为今天 | +| lunar | Boolean | false | 显示农历 | +| startDate | String |- | 日期选择范围-开始日期 | +| endDate | String |- | 日期选择范围-结束日期 | +| range | Boolean | false | 范围选择 | +| insert | Boolean | false | 插入模式,可选值,ture:插入模式;false:弹窗模式;默认为插入模式 | +|clearDate |Boolean |true |弹窗模式是否清空上次选择内容 | +| selected | Array |- | 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] | +|showMonth | Boolean | true | 是否显示月份为背景 | + +### Calendar Events + +| 事件名 | 说明 |返回值| +| | | | +| open | 弹出日历组件,`insert :false` 时生效|- | + + + + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar](https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar) \ No newline at end of file diff --git a/uni_modules/uni-captcha/changelog.md b/uni_modules/uni-captcha/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..a710020dbc70b575fc9b169f02a83fa61347edfd --- /dev/null +++ b/uni_modules/uni-captcha/changelog.md @@ -0,0 +1,4 @@ +## 0.1.1(2021-03-04) +- refresh不再读取上一条验证码状态 +## 0.1.0(2021-03-01) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-captcha/package.json b/uni_modules/uni-captcha/package.json new file mode 100644 index 0000000000000000000000000000000000000000..7245ea0fc88f8725eace8210063c393e6d373961 --- /dev/null +++ b/uni_modules/uni-captcha/package.json @@ -0,0 +1,80 @@ +{ + "id": "uni-captcha", + "displayName": "uni-captcha", + "version": "0.1.1", + "description": "简洁、高效、灵活可配置的云端验证码模块", + "keywords": [ + "uniCloud", + "captcha", + "验证码", + "图形验证码", + "人机验证" +], + "repository": "https://gitee.com/dcloud/uni-captcha", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "uniCloud", + "云函数模板" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "u", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "u", + "Android Browser": "u", + "微信浏览器(Android)": "u", + "QQ浏览器(Android)": "u" + }, + "H5-pc": { + "Chrome": "u", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "u", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-captcha/readme.md b/uni_modules/uni-captcha/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..b23de182a8d172171104510b5d09bdf280536730 --- /dev/null +++ b/uni_modules/uni-captcha/readme.md @@ -0,0 +1,92 @@ +## uni 验证码验证文档 + +> 用途:主要使用在登录、需要人机校验或其他限制调用的场景 + +> 验证码生成、校验都在服务端。页面使用返回的 base64 显示。[云端一体登录模板](https://ext.dcloud.net.cn/plugin?id=13)已集成,可下载体验。 + +> 数据表使用[opendb-verify-codes](https://gitee.com/dcloud/opendb/blob/master/collection/opendb-verify-codes/collection.json) + +### 获取验证码@create + +用法:`uniCaptcha.create(Object params);` + +**参数说明** + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +| --------------- | ------ | ---- | ------- | ----------------------------------------------- | +| scene | String | 是 | 4 | 使用场景值,用于防止不同功能的验证码混用 | +| deviceId | String | - | - | 设备 id,如果不传,将自动从 uniCloud 上下文获取 | +| width | Number | - | 100 | 图片宽度 | +| height | Number | - | 40 | 图片高度 | +| backgroundColor | String | - | #FFFAE8 | 验证码背景色 | +| size | Number | - | 4 | 验证码长度,最多 6 个字符 | +| noise | Number | - | 4 | 验证码干扰线条数 | +| expiresDate | Number | - | 180 | 验证码过期时间(s) | + +**响应参数** + +| 字段 | 类型 | 说明 | +| ------------- | ------ | ------------------- | +| code | Number | 错误码,0 表示成功 | +| message | String | 详细信息 | +| captchaBase64 | String | 验证码:base64 格式 | + +`注意:` + +- 重新生成后,上条验证码作废 + +### 校验验证码@verify + +用法:`uniCaptcha.verify(Object params);` + +**参数说明** + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +| -------- | ------ | ---- | ------ | ----------------------------------------------- | +| scene | String | 是 | - | 类型,用于防止不同功能的验证码混用 | +| captcha | String | 是 | - | 验证码 | +| deviceId | String | - | - | 设备 id,如果不传,将自动从 uniCloud 上下文获取 | + +**响应参数** + +| 字段 | 类型 | 说明 | +| ------- | ------ | ------------------ | +| code | Number | 错误码,0 表示成功 | +| message | String | 详细信息 | + +`注意:` + +- 若提示验证码失效,请重新获取 + +### 刷新验证码@refresh + +用法:`uniCaptcha.refresh(Object params);` + +**参数说明** + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +| -------- | ------ | ---- | ------ | ----------------------------------------------- | +| scene | String | 是 | - | 类型,用于防止不同功能的验证码混用 | +| deviceId | String | - | - | 设备 id,如果不传,将自动从 uniCloud 上下文获取 | + +**响应参数** + +| 字段 | 类型 | 说明 | +| ------------- | ------ | ------------------- | +| code | Number | 错误码,0 表示成功 | +| message | String | 详细信息 | +| captchaBase64 | String | 验证码:base64 格式 | + +`注意:` + +- 支持传入 create 方法的所有参数,如果不传,则自动按照 deviceId 匹配上次生成时的配置生成新的验证码 + +## 错误码 + +_详细信息请查看 message 中查看_ + +| 模块 | 模块码 | 错误代码 | 错误信息 | +| :----: | :----: | :------: | :---------------------: | +| 验证码 | 100 | 01 | (10001)验证码生成失败 | +| | | 02 | (10002)验证码校验失败 | +| | | 03 | (10003)验证码刷新失败 | diff --git a/uni_modules/uni-captcha/uniCloud/cloudfunctions/common/uni-captcha/LICENSE.md b/uni_modules/uni-captcha/uniCloud/cloudfunctions/common/uni-captcha/LICENSE.md new file mode 100644 index 0000000000000000000000000000000000000000..261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64 --- /dev/null +++ b/uni_modules/uni-captcha/uniCloud/cloudfunctions/common/uni-captcha/LICENSE.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/uni_modules/uni-captcha/uniCloud/cloudfunctions/common/uni-captcha/index.js b/uni_modules/uni-captcha/uniCloud/cloudfunctions/common/uni-captcha/index.js new file mode 100644 index 0000000000000000000000000000000000000000..3bc88f4e1c501200d1d9fc04417eb2a04d93e99d --- /dev/null +++ b/uni_modules/uni-captcha/uniCloud/cloudfunctions/common/uni-captcha/index.js @@ -0,0 +1 @@ +"use strict";var t,e=(t=require("fs"))&&"object"==typeof t&&"default"in t?t.default:t,n="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function r(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}function o(t,e){return t(e={exports:{}},e.exports),e.exports}var i=o((function(t,e){Object.defineProperty(e,"__esModule",{value:!0}),e.font16x32=e.font12x24=e.font8x16=void 0;var n="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";e.font8x16={w:8,h:16,fonts:n,data:[[0,0,0,0,0,0,0,60,66,30,34,66,66,63,0,0],[0,0,0,192,64,64,64,88,100,66,66,66,100,88,0,0],[0,0,0,0,0,0,0,28,34,64,64,64,34,28,0,0],[0,0,0,6,2,2,2,30,34,66,66,66,38,27,0,0],[0,0,0,0,0,0,0,60,66,126,64,64,66,60,0,0],[0,0,0,15,17,16,16,126,16,16,16,16,16,124,0,0],[0,0,0,0,0,0,0,62,68,68,56,64,60,66,66,60],[0,0,0,192,64,64,64,92,98,66,66,66,66,231,0,0],[0,0,0,48,48,0,0,112,16,16,16,16,16,124,0,0],[0,0,0,12,12,0,0,28,4,4,4,4,4,4,68,120],[0,0,0,192,64,64,64,78,72,80,104,72,68,238,0,0],[0,0,0,112,16,16,16,16,16,16,16,16,16,124,0,0],[0,0,0,0,0,0,0,254,73,73,73,73,73,237,0,0],[0,0,0,0,0,0,0,220,98,66,66,66,66,231,0,0],[0,0,0,0,0,0,0,60,66,66,66,66,66,60,0,0],[0,0,0,0,0,0,0,216,100,66,66,66,68,120,64,224],[0,0,0,0,0,0,0,30,34,66,66,66,34,30,2,7],[0,0,0,0,0,0,0,238,50,32,32,32,32,248,0,0],[0,0,0,0,0,0,0,62,66,64,60,2,66,124,0,0],[0,0,0,0,0,16,16,124,16,16,16,16,16,12,0,0],[0,0,0,0,0,0,0,198,66,66,66,66,70,59,0,0],[0,0,0,0,0,0,0,231,66,36,36,40,16,16,0,0],[0,0,0,0,0,0,0,215,146,146,170,170,68,68,0,0],[0,0,0,0,0,0,0,110,36,24,24,24,36,118,0,0],[0,0,0,0,0,0,0,231,66,36,36,40,24,16,16,224],[0,0,0,0,0,0,0,126,68,8,16,16,34,126,0,0],[0,0,0,16,16,24,40,40,36,60,68,66,66,231,0,0],[0,0,0,248,68,68,68,120,68,66,66,66,68,248,0,0],[0,0,0,62,66,66,128,128,128,128,128,66,68,56,0,0],[0,0,0,248,68,66,66,66,66,66,66,66,68,248,0,0],[0,0,0,252,66,72,72,120,72,72,64,66,66,252,0,0],[0,0,0,252,66,72,72,120,72,72,64,64,64,224,0,0],[0,0,0,60,68,68,128,128,128,142,132,68,68,56,0,0],[0,0,0,231,66,66,66,66,126,66,66,66,66,231,0,0],[0,0,0,124,16,16,16,16,16,16,16,16,16,124,0,0],[0,0,0,62,8,8,8,8,8,8,8,8,8,8,136,240],[0,0,0,238,68,72,80,112,80,72,72,68,68,238,0,0],[0,0,0,224,64,64,64,64,64,64,64,64,66,254,0,0],[0,0,0,238,108,108,108,108,84,84,84,84,84,214,0,0],[0,0,0,199,98,98,82,82,74,74,74,70,70,226,0,0],[0,0,0,56,68,130,130,130,130,130,130,130,68,56,0,0],[0,0,0,252,66,66,66,66,124,64,64,64,64,224,0,0],[0,0,0,56,68,130,130,130,130,130,178,202,76,56,6,0],[0,0,0,252,66,66,66,124,72,72,68,68,66,227,0,0],[0,0,0,62,66,66,64,32,24,4,2,66,66,124,0,0],[0,0,0,254,146,16,16,16,16,16,16,16,16,56,0,0],[0,0,0,231,66,66,66,66,66,66,66,66,66,60,0,0],[0,0,0,231,66,66,68,36,36,40,40,24,16,16,0,0],[0,0,0,214,146,146,146,146,170,170,108,68,68,68,0,0],[0,0,0,231,66,36,36,24,24,24,36,36,66,231,0,0],[0,0,0,238,68,68,40,40,16,16,16,16,16,56,0,0],[0,0,0,126,132,4,8,8,16,32,32,66,66,252,0,0],[0,0,0,24,36,66,66,66,66,66,66,66,36,24,0,0],[0,0,0,16,112,16,16,16,16,16,16,16,16,124,0,0],[0,0,0,60,66,66,66,4,4,8,16,32,66,126,0,0],[0,0,0,60,66,66,4,24,4,2,2,66,68,56,0,0],[0,0,0,4,12,20,36,36,68,68,126,4,4,30,0,0],[0,0,0,126,64,64,64,88,100,2,2,66,68,56,0,0],[0,0,0,28,36,64,64,88,100,66,66,66,36,24,0,0],[0,0,0,126,68,68,8,8,16,16,16,16,16,16,0,0],[0,0,0,60,66,66,66,36,24,36,66,66,66,60,0,0],[0,0,0,24,36,66,66,66,38,26,2,2,36,56,0,0]]},e.font12x24={w:12,h:24,fonts:n,data:[[0,0,0,0,0,0,0,0,0,0,15,48,48,7,28,48,96,96,96,113,62,0,0,0,0,0,0,0,0,0,0,0,0,0,128,192,192,192,192,192,192,192,192,208,240,0,0,0],[0,0,0,0,16,112,48,48,48,48,51,60,56,48,48,48,48,48,48,56,47,0,0,0,0,0,0,0,0,0,0,0,0,0,128,192,96,96,96,96,96,96,64,192,128,0,0,0],[0,0,0,0,0,0,0,0,0,0,15,49,49,97,96,96,96,96,48,48,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,128,128,0,0,0,64,64,128,0,0,0,0],[0,0,0,0,0,1,0,0,0,0,30,49,48,96,96,96,96,96,32,49,30,0,0,0,0,0,0,0,64,192,192,192,192,192,192,192,192,192,192,192,192,192,192,224,128,0,0,0],[0,0,0,0,0,0,0,0,0,0,7,24,16,48,63,48,48,48,24,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,128,192,96,96,224,0,0,0,32,64,128,0,0,0],[0,0,0,0,0,3,6,12,12,12,127,12,12,12,12,12,12,12,12,12,63,0,0,0,0,0,0,0,0,192,96,96,0,0,192,0,0,0,0,0,0,0,0,0,128,0,0,0],[0,0,0,0,0,0,0,0,0,0,15,25,48,48,48,25,31,48,62,31,96,96,112,31,0,0,0,0,0,0,0,0,0,0,112,144,192,192,192,128,0,0,0,192,96,96,224,128],[0,0,0,0,16,112,48,48,48,48,55,56,48,48,48,48,48,48,48,48,121,0,0,0,0,0,0,0,0,0,0,0,0,0,128,192,192,192,192,192,192,192,192,192,224,0,0,0],[0,0,0,0,0,6,6,0,0,0,62,6,6,6,6,6,6,6,6,6,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,0,0,0],[0,0,0,0,0,1,1,0,0,0,15,1,1,1,1,1,1,1,1,1,1,1,51,62,0,0,0,0,0,128,128,0,0,0,128,128,128,128,128,128,128,128,128,128,128,128,0,0],[0,0,0,0,16,112,48,48,48,48,51,49,51,50,54,62,59,51,49,49,121,0,0,0,0,0,0,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,128,128,224,0,0,0],[0,0,0,0,2,62,6,6,6,6,6,6,6,6,6,6,6,6,6,6,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,0,0,0],[0,0,0,0,0,0,0,0,0,0,238,119,102,102,102,102,102,102,102,102,247,0,0,0,0,0,0,0,0,0,0,0,0,0,224,96,96,96,96,96,96,96,96,96,112,0,0,0],[0,0,0,0,0,0,0,0,0,0,115,60,48,48,48,48,48,48,48,48,121,0,0,0,0,0,0,0,0,0,0,0,0,0,128,192,192,192,192,192,192,192,192,192,224,0,0,0],[0,0,0,0,0,0,0,0,0,0,15,25,48,96,96,96,96,96,48,48,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,192,96,96,96,96,96,192,192,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,119,56,48,48,48,48,48,48,48,56,55,48,48,124,0,0,0,0,0,0,0,0,0,0,128,192,96,96,96,96,96,96,192,192,128,0,0,0],[0,0,0,0,0,0,0,0,0,0,30,49,48,96,96,96,96,96,32,49,30,0,0,3,0,0,0,0,0,0,0,0,0,0,64,192,192,192,192,192,192,192,192,192,192,192,192,224],[0,0,0,0,0,0,0,0,0,0,249,26,28,24,24,24,24,24,24,24,255,0,0,0,0,0,0,0,0,0,0,0,0,0,224,96,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,15,24,48,48,28,15,1,32,32,48,63,0,0,0,0,0,0,0,0,0,0,0,0,0,224,96,32,0,0,128,192,96,96,192,128,0,0,0],[0,0,0,0,0,0,4,4,12,12,127,12,12,12,12,12,12,12,12,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,64,64,128,0,0,0],[0,0,0,0,0,0,0,0,0,16,113,48,48,48,48,48,48,48,48,57,30,0,0,0,0,0,0,0,0,0,0,0,0,64,192,192,192,192,192,192,192,192,192,224,128,0,0,0],[0,0,0,0,0,0,0,0,0,0,124,56,24,24,12,12,12,7,7,7,2,0,0,0,0,0,0,0,0,0,0,0,0,0,240,96,64,64,128,128,128,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,247,99,99,103,55,53,57,57,57,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,176,32,32,32,32,192,192,192,192,128,128,0,0,0],[0,0,0,0,0,0,0,0,0,0,125,24,25,13,14,6,7,11,25,17,123,0,0,0,0,0,0,0,0,0,0,0,0,0,224,128,128,0,0,0,0,0,128,192,224,0,0,0],[0,0,0,0,0,0,0,0,0,0,125,56,24,24,13,13,13,6,6,2,4,4,40,56,0,0,0,0,0,0,0,0,0,0,224,128,128,128,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,63,33,35,3,7,6,14,12,28,24,63,0,0,0,0,0,0,0,0,0,0,0,0,0,192,128,128,0,0,0,0,32,32,96,192,0,0,0],[0,0,0,0,0,6,6,14,11,11,19,17,17,17,31,32,32,32,32,96,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,128,128,128,192,192,192,96,96,240,0,0,0],[0,0,0,0,0,255,97,96,96,96,96,97,127,96,96,96,96,96,96,96,255,0,0,0,0,0,0,0,0,0,128,192,192,192,192,128,0,192,64,96,96,96,96,192,128,0,0,0],[0,0,0,0,0,7,24,48,48,32,96,96,96,96,96,96,96,48,48,24,15,0,0,0,0,0,0,0,0,224,96,32,32,0,0,0,0,0,0,0,32,32,64,128,0,0,0,0],[0,0,0,0,0,254,97,96,96,96,96,96,96,96,96,96,96,96,96,99,254,0,0,0,0,0,0,0,0,0,128,192,192,96,96,96,96,96,96,96,96,192,192,128,0,0,0,0],[0,0,0,0,0,255,96,96,96,96,97,97,127,97,97,96,96,96,96,96,255,0,0,0,0,0,0,0,0,192,64,32,0,0,0,0,0,0,0,0,0,32,32,64,192,0,0,0],[0,0,0,0,0,255,96,96,96,96,97,97,127,97,97,96,96,96,96,96,240,0,0,0,0,0,0,0,0,192,192,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,15,24,48,48,32,96,96,96,96,99,96,96,48,48,24,15,0,0,0,0,0,0,0,0,64,192,64,64,0,0,0,0,0,240,192,192,192,192,192,0,0,0,0],[0,0,0,0,0,240,96,96,96,96,96,96,127,96,96,96,96,96,96,96,240,0,0,0,0,0,0,0,0,240,96,96,96,96,96,96,224,96,96,96,96,96,96,96,240,0,0,0],[0,0,0,0,0,63,6,6,6,6,6,6,6,6,6,6,6,6,6,6,63,0,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,0,0,0],[0,0,0,0,0,15,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,97,99,62,0,0,0,0,0,240,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,0,0],[0,0,0,0,0,243,96,97,98,98,100,108,124,118,103,99,99,97,96,96,241,0,0,0,0,0,0,0,0,224,128,0,0,0,0,0,0,0,0,0,128,128,192,224,240,0,0,0],[0,0,0,0,0,240,96,96,96,96,96,96,96,96,96,96,96,96,96,96,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,32,64,192,0,0,0],[0,0,0,0,0,240,112,112,112,89,89,89,89,90,78,78,78,78,68,68,228,0,0,0,0,0,0,0,0,240,224,224,224,96,96,96,96,96,96,96,96,96,96,96,240,0,0,0],[0,0,0,0,0,224,112,112,88,88,76,70,70,67,67,65,64,64,64,64,224,0,0,0,0,0,0,0,0,112,32,32,32,32,32,32,32,32,32,160,224,224,96,96,32,0,0,0],[0,0,0,0,0,15,25,48,48,96,96,96,96,96,96,96,96,48,48,25,15,0,0,0,0,0,0,0,0,0,128,192,64,96,96,96,96,96,96,96,96,64,192,128,0,0,0,0],[0,0,0,0,0,255,96,96,96,96,96,96,96,127,96,96,96,96,96,96,240,0,0,0,0,0,0,0,0,128,192,96,96,96,96,96,192,128,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,15,25,48,48,96,96,96,96,96,96,96,110,50,49,17,15,1,0,0,0,0,0,0,0,0,128,192,64,96,96,96,96,96,96,96,96,64,192,128,128,224,192,0],[0,0,0,0,0,255,96,96,96,96,96,96,127,102,99,99,97,97,96,96,240,0,0,0,0,0,0,0,0,128,192,96,96,96,96,192,0,0,0,0,128,128,192,192,112,0,0,0],[0,0,0,0,0,31,48,96,96,96,112,60,15,3,0,0,64,64,96,112,79,0,0,0,0,0,0,0,0,32,224,32,32,0,0,0,0,192,192,96,96,96,96,192,128,0,0,0],[0,0,0,0,0,127,70,134,134,6,6,6,6,6,6,6,6,6,6,6,15,0,0,0,0,0,0,0,0,224,32,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,240,96,96,96,96,96,96,96,96,96,96,96,96,96,48,31,0,0,0,0,0,0,0,0,112,32,32,32,32,32,32,32,32,32,32,32,32,32,64,128,0,0,0],[0,0,0,0,0,248,112,48,48,48,48,24,24,24,24,13,13,13,15,6,6,0,0,0,0,0,0,0,0,240,96,64,64,64,128,128,128,128,128,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,247,102,102,102,102,103,55,55,59,59,59,59,57,17,17,17,0,0,0,0,0,0,0,0,112,32,32,64,64,64,64,64,128,128,128,128,128,0,0,0,0,0,0],[0,0,0,0,0,121,48,24,24,25,13,14,6,6,7,11,11,25,17,48,121,0,0,0,0,0,0,0,0,224,192,128,128,0,0,0,0,0,0,0,128,128,128,192,224,0,0,0],[0,0,0,0,0,248,112,48,48,24,24,13,13,14,6,6,6,6,6,6,31,0,0,0,0,0,0,0,0,240,96,64,128,128,128,0,0,0,0,0,0,0,0,0,128,0,0,0],[0,0,0,0,0,63,32,65,1,3,3,3,6,6,12,12,24,24,56,48,127,0,0,0,0,0,0,0,0,224,192,192,128,128,0,0,0,0,0,0,0,32,32,64,192,0,0,0],[0,0,0,0,0,15,25,48,48,96,96,96,96,96,96,96,96,48,48,25,15,0,0,0,0,0,0,0,0,0,128,192,192,96,96,96,96,96,96,96,96,192,192,128,0,0,0,0],[0,0,0,0,0,2,6,62,6,6,6,6,6,6,6,6,6,6,6,6,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,0,0,0],[0,0,0,0,0,31,33,64,96,96,0,1,1,3,4,8,16,32,64,127,127,0,0,0,0,0,0,0,0,0,128,192,192,192,192,128,128,0,0,0,64,64,64,192,192,0,0,0],[0,0,0,0,0,30,35,97,97,97,1,3,14,1,0,0,96,96,96,33,31,0,0,0,0,0,0,0,0,0,0,128,128,128,128,0,0,128,128,192,192,192,192,128,0,0,0,0],[0,0,0,0,0,1,3,3,5,9,9,17,33,33,65,127,1,1,1,1,7,0,0,0,0,0,0,0,128,128,128,128,128,128,128,128,128,128,128,224,128,128,128,128,224,0,0,0],[0,0,0,0,0,63,63,32,32,32,32,47,49,32,0,0,96,96,65,33,31,0,0,0,0,0,0,0,0,192,192,0,0,0,0,0,128,192,192,192,192,192,128,128,0,0,0,0],[0,0,0,0,0,7,24,48,48,32,96,103,104,112,96,96,96,32,48,24,15,0,0,0,0,0,0,0,0,128,192,192,0,0,0,128,192,96,96,96,96,96,64,192,0,0,0,0],[0,0,0,0,0,31,63,48,32,32,0,1,1,2,2,2,6,6,6,6,6,0,0,0,0,0,0,0,0,224,224,64,128,128,128,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,31,48,96,96,96,112,60,15,51,32,96,96,96,96,48,15,0,0,0,0,0,0,0,0,128,192,96,96,96,64,192,0,128,192,96,96,96,96,192,128,0,0,0],[0,0,0,0,0,15,48,48,96,96,96,96,96,49,30,0,0,0,48,49,30,0,0,0,0,0,0,0,0,0,128,192,64,96,96,96,224,96,96,96,192,192,128,128,0,0,0,0]]},e.font16x32={w:16,h:32,fonts:n,data:[[0,0,0,0,0,0,0,0,0,0,0,0,0,15,24,48,48,0,1,14,56,48,96,96,96,48,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,96,48,48,48,240,48,48,48,48,48,50,242,28,0,0,0,0,0],[0,0,0,0,0,8,120,24,24,24,24,24,24,24,27,28,28,24,24,24,24,24,24,24,28,30,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,24,12,6,6,6,6,6,6,6,4,12,24,224,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,3,14,24,24,48,48,48,48,48,48,24,24,12,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,48,24,24,24,0,0,0,0,4,4,8,16,224,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,24,24,48,48,48,48,48,48,16,24,12,7,0,0,0,0,0,0,0,0,0,0,8,120,24,24,24,24,24,24,216,56,24,24,24,24,24,24,24,24,24,56,94,144,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,3,12,24,16,48,48,63,48,48,48,24,24,14,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,48,24,8,12,12,252,0,0,0,4,8,24,224,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,3,3,3,3,63,3,3,3,3,3,3,3,3,3,3,3,3,31,0,0,0,0,0,0,0,0,0,0,0,124,195,3,3,0,0,0,248,0,0,0,0,0,0,0,0,0,0,0,0,240,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,3,12,8,24,24,24,8,12,15,24,24,15,15,16,48,48,48,28,7,0,0,0,0,0,0,0,0,0,0,0,0,0,238,54,24,24,24,24,24,48,224,0,0,240,252,14,6,6,6,28,240],[0,0,0,0,0,8,120,24,24,24,24,24,24,25,27,28,24,24,24,24,24,24,24,24,24,24,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,24,12,12,12,12,12,12,12,12,12,12,12,63,0,0,0,0,0],[0,0,0,0,0,0,1,1,1,0,0,0,0,31,1,1,1,1,1,1,1,1,1,1,1,1,31,0,0,0,0,0,0,0,0,0,0,0,192,192,192,0,0,0,128,128,128,128,128,128,128,128,128,128,128,128,128,128,248,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,24,15,0,0,0,0,0,0,28,28,28,0,0,0,8,248,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,48,96,192],[0,0,0,0,0,8,120,24,24,24,24,24,24,24,24,24,24,24,25,27,28,24,24,24,24,24,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,124,48,96,192,128,128,128,192,224,96,48,56,24,62,0,0,0,0,0],[0,0,0,0,0,0,31,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,31,0,0,0,0,0,0,0,0,0,0,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,248,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,16,119,57,49,49,49,49,49,49,49,49,49,49,49,123,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,204,140,140,140,140,140,140,140,140,140,140,140,222,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,8,120,27,28,24,24,24,24,24,24,24,24,24,24,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,24,12,12,12,12,12,12,12,12,12,12,12,63,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,3,14,8,24,48,48,48,48,48,48,24,24,12,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,56,12,12,6,6,6,6,6,6,12,12,24,224,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,8,121,26,28,24,24,24,24,24,24,24,24,28,30,25,24,24,24,24,126,0,0,0,0,0,0,0,0,0,0,0,0,0,240,24,12,4,6,6,6,6,6,6,12,12,24,224,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,3,12,24,24,48,48,48,48,48,48,16,24,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,60,28,12,12,12,12,12,12,12,12,28,60,204,12,12,12,12,63],[0,0,0,0,0,0,0,0,0,0,0,0,6,126,6,6,7,7,6,6,6,6,6,6,6,6,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,102,134,0,0,0,0,0,0,0,0,0,0,224,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,7,12,24,24,24,14,7,1,0,32,32,48,56,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,228,28,12,4,0,0,192,240,56,12,12,12,24,240,0,0,0,0,0],[0,0,0,0,0,0,0,0,1,1,1,3,7,63,3,3,3,3,3,3,3,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,0,0,0,0,0,0,0,0,0,4,4,136,240,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,8,120,24,24,24,24,24,24,24,24,24,24,24,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,60,12,12,12,12,12,12,12,12,12,12,28,47,200,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,126,24,24,28,12,12,14,6,6,7,3,3,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,24,16,16,32,32,64,64,64,128,128,128,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,251,113,48,49,49,25,25,26,26,14,14,14,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,239,198,196,196,196,200,200,104,104,112,112,112,32,32,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,63,14,14,7,3,3,1,1,2,6,4,8,24,124,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,124,16,32,32,64,128,192,192,224,96,48,48,24,126,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,126,24,24,12,12,12,6,6,3,3,3,1,1,1,1,1,1,50,60,0,0,0,0,0,0,0,0,0,0,0,0,0,62,24,16,16,16,32,32,32,64,64,64,128,128,128,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,63,48,32,32,0,1,1,3,7,14,12,28,56,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,48,112,96,192,192,128,0,0,4,4,12,24,248,0,0,0,0,0],[0,0,0,0,0,0,3,3,3,2,6,4,4,4,12,8,8,8,31,16,16,16,48,32,32,96,248,0,0,0,0,0,0,0,0,0,0,128,128,128,128,128,192,192,192,192,96,96,96,96,240,48,48,48,48,24,24,24,62,0,0,0,0,0],[0,0,0,0,0,0,127,24,24,24,24,24,24,24,24,31,24,24,24,24,24,24,24,24,24,24,127,0,0,0,0,0,0,0,0,0,0,0,224,56,28,12,12,12,12,24,48,224,24,12,4,6,6,6,6,6,12,24,240,0,0,0,0,0],[0,0,0,0,0,0,3,6,8,24,48,48,32,96,96,96,96,96,96,96,96,48,48,48,24,12,3,0,0,0,0,0,0,0,0,0,0,0,228,28,12,4,2,2,0,0,0,0,0,0,0,0,0,2,2,4,12,24,224,0,0,0,0,0],[0,0,0,0,0,0,127,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,127,0,0,0,0,0,0,0,0,0,0,0,192,112,24,12,12,12,6,6,6,6,6,6,6,6,6,12,12,8,24,112,192,0,0,0,0,0],[0,0,0,0,0,0,127,24,24,24,24,24,24,24,24,31,24,24,24,24,24,24,24,24,24,24,127,0,0,0,0,0,0,0,0,0,0,0,252,12,4,6,2,0,16,16,48,240,48,16,16,0,0,0,2,2,4,12,252,0,0,0,0,0],[0,0,0,0,0,0,127,24,24,24,24,24,24,24,24,31,24,24,24,24,24,24,24,24,24,24,126,0,0,0,0,0,0,0,0,0,0,0,254,14,2,3,1,0,8,8,24,248,24,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,3,14,8,24,48,48,32,96,96,96,96,96,96,96,96,48,48,24,24,12,3,0,0,0,0,0,0,0,0,0,0,0,200,56,8,8,4,4,0,0,0,0,0,0,63,12,12,12,12,12,12,16,224,0,0,0,0,0],[0,0,0,0,0,0,252,48,48,48,48,48,48,48,48,48,63,48,48,48,48,48,48,48,48,48,252,0,0,0,0,0,0,0,0,0,0,0,126,24,24,24,24,24,24,24,24,24,248,24,24,24,24,24,24,24,24,24,126,0,0,0,0,0],[0,0,0,0,0,0,31,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,31,0,0,0,0,0,0,0,0,0,0,0,248,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,248,0,0,0,0,0],[0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,112,113,63,0,0,0,0,0,0,254,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,192,128,0],[0,0,0,0,0,0,126,24,24,24,24,24,24,25,25,27,29,28,24,24,24,24,24,24,24,24,126,0,0,0,0,0,0,0,0,0,0,0,62,24,16,32,96,64,128,128,128,128,192,192,224,96,112,48,56,24,12,12,63,0,0,0,0,0],[0,0,0,0,0,0,126,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,4,12,252,0,0,0,0,0],[0,0,0,0,0,0,248,56,56,56,56,44,44,44,44,46,38,38,38,38,35,35,35,35,35,33,249,0,0,0,0,0,0,0,0,0,0,0,31,28,28,28,60,44,44,44,108,76,76,76,76,140,140,140,140,12,12,12,63,0,0,0,0,0],[0,0,0,0,0,0,248,56,60,44,44,46,38,39,35,35,33,33,32,32,32,32,32,32,32,32,248,0,0,0,0,0,0,0,0,0,0,0,62,8,8,8,8,8,8,8,8,136,136,200,200,232,104,120,56,56,56,24,24,0,0,0,0,0],[0,0,0,0,0,0,3,12,24,16,48,48,96,96,96,96,96,96,96,96,96,48,48,16,24,12,3,0,0,0,0,0,0,0,0,0,0,0,192,48,24,8,12,12,6,6,6,6,6,6,6,6,6,4,12,8,24,48,192,0,0,0,0,0],[0,0,0,0,0,0,127,24,24,24,24,24,24,24,24,24,31,24,24,24,24,24,24,24,24,24,126,0,0,0,0,0,0,0,0,0,0,0,240,24,12,6,6,6,6,6,12,24,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,3,12,24,48,48,48,96,96,96,96,96,96,96,96,96,39,52,56,24,12,3,0,0,0,0,0,0,0,0,0,0,0,192,48,24,8,12,4,6,6,6,6,6,6,6,6,6,134,204,76,104,112,224,50,62,28,0,0],[0,0,0,0,0,0,127,24,24,24,24,24,24,24,24,31,25,24,24,24,24,24,24,24,24,24,126,0,0,0,0,0,0,0,0,0,0,0,224,56,28,12,12,12,12,24,48,224,192,192,224,96,96,112,48,48,56,24,30,0,0,0,0,0],[0,0,0,0,0,0,7,12,24,48,48,48,48,24,30,7,1,0,0,0,0,32,32,16,24,28,19,0,0,0,0,0,0,0,0,0,0,0,228,28,12,4,4,0,0,0,0,192,240,120,28,14,6,6,6,6,12,24,240,0,0,0,0,0],[0,0,0,0,0,0,63,49,33,65,65,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,7,0,0,0,0,0,0,0,0,0,0,0,252,132,134,130,130,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,224,0,0,0,0,0],[0,0,0,0,0,0,252,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,16,28,7,0,0,0,0,0,0,0,0,0,0,0,62,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,16,32,192,0,0,0,0,0],[0,0,0,0,0,0,124,24,24,24,12,12,12,12,6,6,6,7,3,3,3,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,31,4,4,4,8,8,8,8,16,16,16,48,32,32,32,192,192,192,192,128,128,0,0,0,0,0],[0,0,0,0,0,0,251,97,97,97,49,48,49,49,49,49,50,26,26,26,28,28,28,12,8,8,8,0,0,0,0,0,0,0,0,0,0,0,207,134,132,132,132,132,196,200,200,200,200,72,104,112,112,112,112,48,32,32,32,0,0,0,0,0],[0,0,0,0,0,0,126,24,28,12,12,14,6,7,3,3,1,1,2,2,4,4,8,8,16,16,124,0,0,0,0,0,0,0,0,0,0,0,124,16,16,32,32,64,64,128,128,128,128,192,192,224,96,96,48,48,24,24,62,0,0,0,0,0],[0,0,0,0,0,0,126,56,24,24,12,12,14,6,6,3,3,3,1,1,1,1,1,1,1,1,7,0,0,0,0,0,0,0,0,0,0,0,62,8,8,16,16,48,32,32,64,64,64,128,128,128,128,128,128,128,128,128,224,0,0,0,0,0],[0,0,0,0,0,0,31,24,16,32,32,0,0,0,1,1,3,3,7,6,14,12,28,24,56,48,127,0,0,0,0,0,0,0,0,0,0,0,252,24,24,48,112,96,224,192,192,128,128,0,0,0,0,0,4,4,8,24,248,0,0,0,0,0],[0,0,0,0,0,0,3,6,12,24,24,24,48,48,48,48,48,48,48,48,48,24,24,24,12,6,3,0,0,0,0,0,0,0,0,0,0,0,224,48,24,12,12,4,6,6,6,6,6,6,6,6,6,4,12,12,24,48,224,0,0,0,0,0],[0,0,0,0,0,0,0,1,31,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,31,0,0,0,0,0,0,0,0,0,0,0,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,192,248,0,0,0,0,0],[0,0,0,0,0,0,7,8,16,32,32,48,48,0,0,0,0,0,0,1,2,4,8,16,32,63,63,0,0,0,0,0,0,0,0,0,0,0,224,56,24,12,12,12,12,12,24,16,32,64,128,0,0,4,4,4,12,248,248,0,0,0,0,0],[0,0,0,0,0,0,7,24,48,48,48,48,0,0,0,3,0,0,0,0,0,48,48,48,48,24,7,0,0,0,0,0,0,0,0,0,0,0,192,112,48,24,24,24,24,48,96,192,112,24,8,12,12,12,12,8,24,48,192,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,1,1,2,6,4,8,8,16,32,32,127,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,48,112,112,240,112,112,112,112,112,112,112,112,112,112,254,112,112,112,112,112,112,254,0,0,0,0],[0,0,0,0,0,0,15,15,8,8,8,16,16,19,20,24,16,0,0,0,0,48,48,32,32,16,15,0,0,0,0,0,0,0,0,0,0,0,252,252,0,0,0,0,0,224,48,24,8,12,12,12,12,12,12,24,24,48,192,0,0,0,0,0],[0,0,0,0,0,0,1,3,4,8,24,24,16,48,49,54,60,56,48,48,48,48,24,24,12,6,3,0,0,0,0,0,0,0,0,0,0,0,240,8,12,12,0,0,0,0,240,24,12,6,6,6,6,6,6,4,12,24,224,0,0,0,0,0],[0,0,0,0,0,0,31,31,56,48,32,32,0,0,0,0,0,0,1,1,1,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,252,252,8,16,16,32,32,64,64,128,128,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,7,12,24,48,48,48,56,28,14,7,13,24,48,96,96,96,96,96,48,24,7,0,0,0,0,0,0,0,0,0,0,0,224,48,24,12,12,12,12,24,16,224,224,112,56,28,12,12,12,12,24,48,192,0,0,0,0,0],[0,0,0,0,0,0,7,24,48,48,96,96,96,96,96,96,48,24,15,0,0,0,0,48,48,48,15,0,0,0,0,0,0,0,0,0,0,0,192,32,16,24,8,12,12,12,12,28,60,108,140,12,24,24,24,48,96,192,128,0,0,0,0,0]]}}));r(i);i.font16x32,i.font12x24,i.font8x16;var a=o((function(t,r){var o=n&&n.__awaiter||function(t,e,n,r){return new(n||(n=Promise))((function(o,i){function a(t){try{s(r.next(t))}catch(t){i(t)}}function c(t){try{s(r.throw(t))}catch(t){i(t)}}function s(t){var e;t.done?o(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e)}))).then(a,c)}s((r=r.apply(t,e||[])).next())}))},a=n&&n.__generator||function(t,e){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(i){return function(c){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(o=a.trys,(o=o.length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]>16&255,green:n>>8&255,blue:255&n})},t.prototype.drawPointRGB=function(t,e,n){if(!(t>=this.w||e>=this.h||t<0||e<0)){var r=this.h-e-1,o=54+3*t+this._lineByteNum*r;this._data.writeUInt8(n.blue,o),this._data.writeUInt8(n.green,o+1),this._data.writeUInt8(n.red,o+2)}},t.prototype.getPointRGB=function(t,e){if(t>=this.w||e>=this.h||t<0||e<0)throw new Error("out of range");var n=this.h-e-1,r=54+3*t+this._lineByteNum*n;return{blue:this._data.readUInt8(r),green:this._data.readUInt8(r+1),red:this._data.readUInt8(r+2)}},t.prototype.drawLineH=function(t,e,n,r){if(t>e){var o=e;e=t,t=o}for(;t<=e;t++)this.drawPoint(t,n,r)},t.prototype.drawLineV=function(t,e,n,r){if(t>e){var o=e;e=t,t=o}for(;t<=e;t++)this.drawPoint(n,t,r)},t.prototype.drawLine=function(t,e,n,r,o){var i=t,a=e,c=n>t?n-t:t-n,s=r>e?r-e:e-r,d=!1,f=n>t?1:-1,u=r>e?1:-1;if(s>c){var h=c;c=s,s=h,d=!0}for(var l=(s<<1)-c,w=0;w<=c;w++)this.drawPoint(i,a,o),l>=0&&(d?i+=f:a+=u,l-=c<<1),d?a+=u:i+=f,l+=s<<1},t.prototype.drawRect=function(t,e,n,r,o){var i=t+n-1,a=e+r-1;this.drawLineH(t,i,e,o),this.drawLineH(t,i,a,o),this.drawLineV(e,a,t,o),this.drawLineV(e,a,i,o)},t.prototype.fillRect=function(t,e,n,r,o){var i=t+n-1,a=e+r-1;if(t>i){var c=i;i=t,t=c}if(e>a){c=a;a=e,e=c}for(;e<=a;e++)for(var s=t;s<=i;s++)this.drawPoint(s,e,o)},t.prototype.drawCircle=function(t,e,n,r){for(var o=0,i=n,a=3-2*n;o0;f<<=1)128&f&&this.drawPoint(c,a,o),c++;++a-n>=r.h&&(a=n,e+=8)}},t.prototype.drawString=function(t,e,n,r,o){for(var i=0,a=t;i(t.includes(r)&&n.push(e[r]),n),[]);return n[Math.random()*n.length|0]}function u(t={}){const e=["small","medium","big"],n={backgroundColor:16775912,size:4,noise:4,width:100,height:40,fontSize:["medium","big"]};let{backgroundColor:r,size:o,noise:i,width:a,height:s,fontSize:f}=Object.assign({},n,t);"string"==typeof r&&(r=r.replace("#","0x")),f instanceof Array||(f=[]),f.filter(t=>e.includes(t)),f.length||(f=n.fontSize),o=o>6?6:o;const u=new c(a,s);u.fillRect(0,0,a,s,r),function(t,e){for(let n=1;n>16,r=e>>8&255,o=255&e,i=Math.max(n,r,o),a=Math.min(n,r,o);return(i+a)/510}(t):1;let o,i;r>=.5?(o=Math.round(100*r)-45,i=Math.round(100*r)-25):(o=Math.round(100*r)+25,i=Math.round(100*r)+45);const a=h(o,i)/100,c=a<.5?a*(a+n):a+n-a*n,s=2*a-c,d=Math.floor(255*w(s,c,e+1/3)),f=Math.floor(255*w(s,c,e));return"#"+(Math.floor(255*w(s,c,e-1/3))|f<<8|d<<16|1<<24).toString(16).slice(1)};function w(t,e,n){return 6*(n=(n+1)%1)<1?t+(e-t)*n*6:2*n<1?e:3*n<2?t+(e-t)*(2/3-n)*6:t}const p=Object.prototype.toString;function g(t){return"[object Object]"===p.call(t)}function y(){"development"===process.env.NODE_ENV&&console.log(...arguments)}const v=async function(){};function m(t){return v.constructor===t.constructor?async function(){const e=await t.apply(this,arguments);return g(e)&&e.msg&&(e.message=e.msg),e}:function(){const e=t.apply(this,arguments);return g(e)&&e.msg&&(e.message=e.msg),e}}const b=uniCloud.database().collection("opendb-verify-codes"),_={};var I=Object.freeze({__proto__:null,create:async function(t={}){let{scene:e,expiresDate:n,deviceId:r,...o}=t;if(r=r||__ctx__.DEVICEID,!r)throw new Error("deviceId不可为空");if(!e)throw new Error("scene验证码场景不可为空");try{const{text:i,base64:a}=u(o),c=await this.setVerifyCode({deviceId:r,code:i,expiresDate:n,scene:e});return c.code>0?{...c,code:10001}:(_[r]=t,{code:0,msg:"验证码获取成功",captchaBase64:a})}catch(t){return{code:10001,msg:"验证码生成失败:"+t.message}}},verify:async function({deviceId:t,captcha:e,scene:n}){if(!(t=t||__ctx__.DEVICEID))throw new Error("deviceId不可为空");if(!n)throw new Error("scene验证码场景不可为空");try{const r=await this.verifyCode({deviceId:t,code:e,scene:n});return r.code>0?{...r,code:10002}:{code:0,msg:"验证码通过"}}catch(t){return{code:10002,msg:"验证码校验失败:"+t.message}}},refresh:async function(t={}){let{scene:e,expiresDate:n,deviceId:r,...o}=t;if(r=r||__ctx__.DEVICEID,!r)throw new Error("deviceId不可为空");if(!e)throw new Error("scene验证码场景不可为空");const i=await b.where({deviceId:r,scene:e,state:0}).orderBy("created_date","desc").limit(1).get();if(i&&i.data&&i.data.length>0){const t=i.data[0];await b.doc(t._id).update({state:2});let a={};Object.keys(o).length>0&&(_[r]=Object.assign({},_[r],o)),a=_[r];let c={};try{c=await this.create(Object.assign({},a,{deviceId:r,scene:e,expiresDate:n}))}catch(t){return{code:50403,msg:t.message}}return c.code>0?{...c,code:10003}:{code:0,msg:"验证码刷新成功",captchaBase64:c.captchaBase64}}return{code:10003,msg:"验证码刷新失败:无此设备信息,请重新获取"}},setVerifyCode:async function({deviceId:t,code:e,expiresDate:n,scene:r}){if(!t)return{code:10101,msg:"deviceId不可为空"};if(!e)return{code:10102,msg:"验证码不可为空"};n||(n=180);const o=Date.now(),i={deviceId:t,scene:r,code:e.toLocaleLowerCase(),state:0,ip:__ctx__.CLIENTIP,created_date:o,expired_date:o+1e3*n};return y("addRes",await b.add(i)),{code:0,deviceId:t}},verifyCode:async function({deviceId:t,code:e,scene:n}){if(!t)return{code:10101,msg:"deviceId不可为空"};if(!e)return{code:10102,msg:"验证码不可为空"};const r=Date.now(),o={deviceId:t,scene:n,code:e.toLocaleLowerCase(),state:0},i=await b.where(o).orderBy("created_date","desc").limit(1).get();if(y("verifyRecord:",i),i&&i.data&&i.data.length>0){const t=i.data[0];if(t.expired_date + + + + + + + + + {{ title }} + {{ extra }} + + + + + + + + + + + + + {{ title }} + {{ subTitle }} + + + + {{ extra }} + + + + + + + + + + + + {{ title }} + + + + + {{ extra }} + + + + + + + {{ note }} + + + + + + + + diff --git a/uni_modules/uni-card/package.json b/uni_modules/uni-card/package.json new file mode 100644 index 0000000000000000000000000000000000000000..507273c1f11d146f34c4bf180ffa21d30a535b6c --- /dev/null +++ b/uni_modules/uni-card/package.json @@ -0,0 +1,85 @@ +{ + "id": "uni-card", + "displayName": "uni-card 卡片", + "version": "1.2.1", + "description": "Card 组件,提供常见的卡片样式。", + "keywords": [ + "uni-ui", + "uniui", + "card", + "", + "卡片" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} diff --git a/uni_modules/uni-card/readme.md b/uni_modules/uni-card/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..6f72c837d0df7a5022e84c11257a47e79992f12a --- /dev/null +++ b/uni_modules/uni-card/readme.md @@ -0,0 +1,104 @@ + + +## Card 卡片 +> **组件名:uni-card** +> 代码块: `uCard` + + +卡片视图组件。 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - 因为平台兼容问题 , 目前 APP-NVUE 安卓平台下不支持阴影 + + +### 基本用法 + +在 ``template`` 中使用组件 + +```html + + + 内容主体,可自定义内容及样式 + + + + + + + + + + uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可编译到iOS、Android、H5、以及各种小程序等多个平台。即使不跨端,uni-app同时也是更好的小程序开发框架。 + + + + + uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可编译到iOS、Android、H5、以及各种小程序等多个平台。即使不跨端,uni-app同时也是更好的小程序开发框架。 + + + + + 默认内容 + + +``` + +## API + +### Card Props + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|title |String |- |标题文字 | +|extra |String |- |标题额外信息 | +|note |String |- |底部信息 | +|thumbnail |String |- |标题左侧缩略图,支持网络图片,本地图片,本图片需要传入一个绝对路径,如:`/static/xxx.png` | +|mode |String |basic |卡片模式 ,可选值, basic:基础卡片 ;style :图文卡片 ; title :标题卡片 | +|isFull |Boolean|false |卡片内容是否通栏,为true时将去除padding值 | +|isShadow |Boolean|false |卡片内容是否开启阴影 | + + +### Card Events + +|事件称名 |事件说明 |返回参数 | +|:-: |:-: |:-: | +|@click |点击 Card 触发事件 |- | + + +### Card Slots + +|插槽称名 |说明 | +|:-: |:-: | +|header |卡片头部插槽( 图文卡片 mode="style" 时,不支持)| +|footer |卡片底部插槽 | + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/card/card](https://hellouniapp.dcloud.net.cn/pages/extUI/card/card) \ No newline at end of file diff --git a/uni_modules/uni-collapse/changelog.md b/uni_modules/uni-collapse/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..9fb4b5c1b1cc1c89ff1be7a1456f0121e3736fbd --- /dev/null +++ b/uni_modules/uni-collapse/changelog.md @@ -0,0 +1,27 @@ +## 1.3.3(2021-08-17) +- 优化 show-arrow 属性默认为true +## 1.3.2(2021-08-17) +- 新增 show-arrow 属性,控制是否显示右侧箭头 +## 1.3.1(2021-07-30) +- 优化 vue3下小程序事件警告的问题 +## 1.3.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.2.2(2021-07-21) +- 修复 由1.2.0版本引起的 change 事件返回 undefined 的Bug +## 1.2.1(2021-07-21) +- 优化 组件示例 +## 1.2.0(2021-07-21) +- 新增 组件折叠动画 +- 新增 value\v-model 属性 ,动态修改面板折叠状态 +- 新增 title 插槽 ,可定义面板标题 +- 新增 border 属性 ,显示隐藏面板内容分隔线 +- 新增 title-border 属性 ,显示隐藏面板标题分隔线 +- 修复 resize 方法失效的Bug +- 修复 change 事件返回参数不正确的Bug +- 优化 H5、App 平台自动更具内容更新高度,无需调用 reszie() 方法 +## 1.1.7(2021-05-12) +- 新增 组件示例地址 +## 1.1.6(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 1.1.5(2021-02-05) +- 调整为uni_modules目录规范 \ No newline at end of file diff --git a/uni_modules/uni-collapse/components/uni-collapse-item/uni-collapse-item.vue b/uni_modules/uni-collapse/components/uni-collapse-item/uni-collapse-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..e962a9f1a30f0c052c826c01128758c3195e67a5 --- /dev/null +++ b/uni_modules/uni-collapse/components/uni-collapse-item/uni-collapse-item.vue @@ -0,0 +1,402 @@ + + + + + diff --git a/uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue b/uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue new file mode 100644 index 0000000000000000000000000000000000000000..b7360d47f03d31f38d931c43620b060bd7b5846d --- /dev/null +++ b/uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue @@ -0,0 +1,146 @@ + + + diff --git a/uni_modules/uni-collapse/package.json b/uni_modules/uni-collapse/package.json new file mode 100644 index 0000000000000000000000000000000000000000..965814fb4c8447cdcbdd8223ca4403303bc84cdb --- /dev/null +++ b/uni_modules/uni-collapse/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-collapse", + "displayName": "uni-collapse 折叠面板", + "version": "1.3.3", + "description": "Collapse 组件,可以折叠 / 展开的内容区域。", + "keywords": [ + "uni-ui", + "折叠", + "折叠面板", + "手风琴" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-collapse/readme.md b/uni_modules/uni-collapse/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..12e872fb1d6f51bdb6fb8259043b1fa3c04b47a2 --- /dev/null +++ b/uni_modules/uni-collapse/readme.md @@ -0,0 +1,276 @@ + + +## Collapse 折叠面板 +> **组件名:uni-collapse** +> 代码块: `uCollapse` +> 关联组件:`uni-collapse-item`、`uni-icons`。 + + +折叠面板用来折叠/显示过长的内容或者是列表。通常是在多内容分类项使用,折叠不重要的内容,显示重要内容。点击可以展开折叠部分。 + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - 组件需要依赖 `sass` 插件 ,请自行手动安装 +> - `App` 端默认关闭组件动画 ,因为 `height` 动画开销比较大,会导致页面卡顿,请酌情使用动画 +> - 如在使用组件过程从发现卡顿严重,请尝试停用组件动画,问题原因如上 +> - 在小程序端组件内容发生变化,需要手动调用 resize() 方法,手动更新几点信息,避免出现内容错位 +> - 如需自定义组件默认边框颜色等,请使用插槽自定义内容并合理使用 `border ` 和 `title-border` 属性 +> - 折叠面板仅支持嵌套使用,请勿单独使用 +> - 组件支持 nvue ,需要在 `manifest.json > app-plus` 节点下配置 `"nvueStyleCompiler" : "uni-app"` +> - 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + + +### 基本用法 + +使用 `title` 属性指定面板显示内容 + +使用 `open` 属性默认打开当前面板 + +使用 `disabled` 属性禁用面板 + + +```html + + + 折叠内容 + + + 折叠内容 + + + 折叠内容 + + +``` + +### 手风琴效果 + +使用 `accordion` 属性,可以仅打开一个面板并关闭其他已经打开的面板,效果类似手风琴 + +设置 `accordion` 属性时,`open` 属性则生效在最后一个组件 + +```html + + + 折叠内容 + + + 折叠内容 + + + 折叠内容 + + +``` + +### 动态设置折叠面板打开状态 + +使用 `v-model` 属性,动态设置面板的显示状态 + +使用 `name` 属性设置每个面板的唯一标识,如不设置使用默认索引,从字符串 `"0"` 开始记数 + +**注意** + +- 如果 `accordion` 属性为 `true` 则 `v-model` 类型为 `String` +- 如果 `accordion` 属性为 `false` 则 `v-model` 类型为 `Array` +- 请注意 `v-model` 属性与 `open` 属性请勿一起使用 ,建议只使用 `v-model` + +```html + + + 折叠内容 + + + 折叠内容 + + + 折叠内容 + + +``` + +```javascript +export default { + data(){ + return { + value:['key1','key2'], + // 如果设置了 accordion 属性,则使用 string 类型 + // value:'key1' + } + } +} +``` + +### 使用动画 + +使用 `show-animation` 属性开启或关闭面板折叠动画,默认动画开启 + +**注意** + +- `App` 端默认关闭组件动画 ,因为 height 动画开销比较大,会导致页面卡顿,请酌情使用动画,如出现明显卡顿,尝试关闭动画 + + +```html + + + 折叠内容 + + + 折叠内容 + + + 折叠内容 + + +``` + +### 配置图片 + +使用 `thumb` 配置图片地址, 可在面板左侧显示一个图片 + +如需显示更多内容,如图标等,请见下方自定义插槽的说明 + +```html + + + + 折叠内容主体,可自定义内容及样式 + + + +``` + +### 自定义插槽 + +如果需要自定义面板显示,可以使用 `title` 插槽达成完全自定义。下面是一个使用 `uni-list` 的列表示例,需要引入 `uni-list` 组件 + +```html + + + + + + 折叠内容主体,可自定义内容及样式 + + + +``` + +**注意** + +- 在折叠面板组件中使用list时,在 App-Nvue 下请勿单独使用 uni-list-item,会导致组件无法正常显示,其他平台不做限制 +- 在默认插槽里使用 uni-list 组件与上方示例一样,直接写在默认插槽里即可 + +## API + +### Collapse Props + +|属性名|类型|默认值|说明| +|:-:|:-:|:-:|:-:| +|value/v-model|String/Array|-|当前激活面板改变时触发(如果是手风琴模式,参数类型为string,否则为array)| +|accordion|Boolean|false|是否开启手风琴效果 | + +### Collapse Event + +|事件称名|说明|返回值| +|:-:|:-:|:-:| +|@change|切换面板时触发 |切换面板时触发,如果是手风琴模式,返回类型为string,否则为array| + +### Collapse Methods + +|方法名称|说明| +|:-:|:-:| +|resize |更新当前列表高度| + +> **提示** +> - resize 方法解决动态添加数据,带动画的折叠面板高度不更新的问题 +> - 需要在数据渲染完毕之后使用 `resize` 方法。推荐在 `this.$nextTick()` 中使用 +> - 当前只有小程序端需要调用此方法,H5\App 端已经做了处理,不需要手动更新高度 +> ```html +> +> +> +> +> {{content}} +> +> +> +> +> 折叠内容主体,这是一段比较长内容。默认折叠主要内容,只显示当前项标题。点击标题展开,才能看到这段文字。再次点击标题,折叠内容。 +> +> +> +> +> +> ``` +> ```javascript +> export default { +> data() { +> return { +> value:['0'], +> content: '折叠内容主体,可自定义内容及样式,点击按钮修改内容使高度发生变化。', +> } +> }, +> methods: { +> add() { +> if (this.content.length > 35) { +> this.content = '折叠内容主体,可自定义内容及样式,点击按钮修改内容使高度发生变化。' +> } else { +> this.content = '折叠内容主体,这是一段比较长内容。通过点击按钮修改后内容后,使组件高度发生变化,在次点击按钮恢复之前的内容和高度。' +> } +> // TODO 小程序中不支持自动更新 ,需要手动resize 更新组件高度 +> // #ifdef MP +> this.$nextTick(() => { +> this.$refs.collapse.resize() +> }) +> // #endif +> } +> } +> } +> ``` + + +### CollapseItem Props + +|属性名|类型|默认值|说明| +|:-:|:-:|:-:|:-:| +|title|String|-|标题文字| +|thumb|String|-|标题左侧缩略图| +|disabled|Boolean|false|是否禁用| +|open|Boolean|false|是否展开面板| +|show-animation|Boolean|false|开启动画| +|border|Boolean|true|折叠面板内容分隔线| +|title-border|String|auto|折叠面板标题分隔线可选值见下方 **TitleBorder Params**| +|show-arrow|Boolean|true|是否显示右侧箭头| + +#### TitleBorder Params + +|参数名|说明| +|:-:|:-:| +|auto|分隔线自动显示| +|none|不显示分隔线| +|show|一直显示分隔线| + +### Collapse Slots + +|插槽名|说明| +|:-:| :-:| +|default|默认插槽| +|title|面板标题插槽,如使用此插槽禁用样式效果将失效| + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/collapse/collapse](https://hellouniapp.dcloud.net.cn/pages/extUI/collapse/collapse) \ No newline at end of file diff --git a/uni_modules/uni-combox/changelog.md b/uni_modules/uni-combox/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..39e8a055d5e6d6bb1e7beffa6db32196b224a230 --- /dev/null +++ b/uni_modules/uni-combox/changelog.md @@ -0,0 +1,10 @@ +## 0.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 0.0.6(2021-05-12) +- 新增 组件示例地址 +## 0.0.5(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 0.0.4(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 0.0.3(2021-02-04) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-combox/components/uni-combox/uni-combox.vue b/uni_modules/uni-combox/components/uni-combox/uni-combox.vue new file mode 100644 index 0000000000000000000000000000000000000000..fef61111447a01d928d623d144e6c2700873eb5e --- /dev/null +++ b/uni_modules/uni-combox/components/uni-combox/uni-combox.vue @@ -0,0 +1,239 @@ + + + + + diff --git a/uni_modules/uni-combox/package.json b/uni_modules/uni-combox/package.json new file mode 100644 index 0000000000000000000000000000000000000000..1254459e9936ca6a53fa08206b75f6e12e945708 --- /dev/null +++ b/uni_modules/uni-combox/package.json @@ -0,0 +1,85 @@ +{ + "id": "uni-combox", + "displayName": "uni-combox 组合框", + "version": "0.1.0", + "description": "可以选择也可以输入的表单项 ", + "keywords": [ + "uni-ui", + "uniui", + "combox", + "组合框", + "select" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "n" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-combox/readme.md b/uni_modules/uni-combox/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..edab4d409d273103f364792c5b891f9da57644bf --- /dev/null +++ b/uni_modules/uni-combox/readme.md @@ -0,0 +1,52 @@ + + +## Combox 组合框 +> **组件名:uni-combox** +> 代码块: `uCombox` + + +组合框组件。 + +### 平台兼容性说明 + +**暂不支持nvue** + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +在 ``template`` 中使用组件 +```html + +``` + +## API + +### Combox Props + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|label |String |- |标签文字 | +|value |String |- |combox的值 | +|labelWidth |String |auto |标签宽度,有单位字符串,如:'100px' | +|placeholder|String |- |输入框占位符 | +|candidates |Array/String |[] |候选字段 | +|emptyTips |String |无匹配项 |无匹配项时的提示语 | + +### Combox Events + +|事件称名 |说明 |返回值 | +|:-: |:-: |:-: | +|@input |combox输入事件 |返回combox值| + + + + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/combox/combox](https://hellouniapp.dcloud.net.cn/pages/extUI/combox/combox) \ No newline at end of file diff --git a/uni_modules/uni-config-center/changelog.md b/uni_modules/uni-config-center/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..4d2eb92f668fd9c35d56f525fde7244b7b38972d --- /dev/null +++ b/uni_modules/uni-config-center/changelog.md @@ -0,0 +1,4 @@ +## 0.0.2(2021-04-16) +- 修改插件package信息 +## 0.0.1(2021-03-15) +- 初始化项目 diff --git a/uni_modules/uni-config-center/package.json b/uni_modules/uni-config-center/package.json new file mode 100644 index 0000000000000000000000000000000000000000..c5dec93b1299d7881b5f80f95960b8024f41772b --- /dev/null +++ b/uni_modules/uni-config-center/package.json @@ -0,0 +1,80 @@ +{ + "id": "uni-config-center", + "displayName": "uni-config-center", + "version": "0.0.2", + "description": "uniCloud 配置中心", + "keywords": [ + "配置", + "配置中心" +], + "repository": "", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "uniCloud", + "云函数模板" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "" + }, + "directories": { + "example": "../../../scripts/dist" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "u", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "u", + "Android Browser": "u", + "微信浏览器(Android)": "u", + "QQ浏览器(Android)": "u" + }, + "H5-pc": { + "Chrome": "u", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "u", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} diff --git a/uni_modules/uni-config-center/readme.md b/uni_modules/uni-config-center/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..03f7fc27964d93a6c16cc2671d777faa52e079a4 --- /dev/null +++ b/uni_modules/uni-config-center/readme.md @@ -0,0 +1,93 @@ +# 为什么使用uni-config-center + +实际开发中很多插件需要配置文件才可以正常运行,如果每个插件都单独进行配置的话就会产生下面这样的目录结构 + +```bash +cloudfunctions +└─────common 公共模块 + ├─plugin-a // 插件A对应的目录 + │ ├─index.js + │ ├─config.json // plugin-a对应的配置文件 + │ └─other-file.cert // plugin-a依赖的其他文件 + └─plugin-b // plugin-b对应的目录 + ├─index.js + └─config.json // plugin-b对应的配置文件 +``` + +假设插件作者要发布一个项目模板,里面使用了很多需要配置的插件,无论是作者发布还是用户使用都是一个大麻烦。 + +uni-config-center就是用了统一管理这些配置文件的,使用uni-config-center后的目录结构如下 + +```bash +cloudfunctions +└─────common 公共模块 + ├─plugin-a // 插件A对应的目录 + │ └─index.js + ├─plugin-b // plugin-b对应的目录 + │ └─index.js + └─uni-config-center + ├─index.js // config-center入口文件 + ├─plugin-a + │ ├─config.json // plugin-a对应的配置文件 + │ └─other-file.cert // plugin-a依赖的其他文件 + └─plugin-b + └─config.json // plugin-b对应的配置文件 +``` + +使用uni-config-center后的优势 + +- 配置文件统一管理,分离插件主体和配置信息,更新插件更方便 +- 支持对config.json设置schema,插件使用者在HBuilderX内编写config.json文件时会有更好的提示(后续HBuilderX会提供支持) + +# 用法 + +在要使用uni-config-center的公共模块或云函数内引入uni-config-center依赖,请参考:[使用公共模块](https://uniapp.dcloud.net.cn/uniCloud/cf-common) + +```js +const createConfig = require('uni-config-center') + +const uniIdConfig = createConfig({ + pluginId: 'uni-id', // 插件id + defaultConfig: { // 默认配置 + tokenExpiresIn: 7200, + tokenExpiresThreshold: 600, + }, + customMerge: function(defaultConfig, userConfig) { // 自定义默认配置和用户配置的合并规则,不设置的情况侠会对默认配置和用户配置进行深度合并 + // defaudltConfig 默认配置 + // userConfig 用户配置 + return Object.assign(defaultConfig, userConfig) + } +}) + + +// 以如下配置为例 +// { +// "tokenExpiresIn": 7200, +// "passwordErrorLimit": 6, +// "bindTokenToDevice": false, +// "passwordErrorRetryTime": 3600, +// "app-plus": { +// "tokenExpiresIn": 2592000 +// }, +// "service": { +// "sms": { +// "codeExpiresIn": 300 +// } +// } +// } + +// 获取配置 +uniIdConfig.config() // 获取全部配置,注意:uni-config-center内不存在对应插件目录时会返回空对象 +uniIdConfig.config('tokenExpiresIn') // 指定键值获取配置,返回:7200 +uniIdConfig.config('service.sms.codeExpiresIn') // 指定键值获取配置,返回:300 +uniIdConfig.config('tokenExpiresThreshold', 600) // 指定键值获取配置,如果不存在则取传入的默认值,返回:600 + +// 获取文件绝对路径 +uniIdConfig.resolve('custom-token.js') // 获取uni-config-center/uni-id/custom-token.js文件的路径 + +// 引用文件(require) +uniIDConfig.requireFile('custom-token.js') // 使用require方式引用uni-config-center/uni-id/custom-token.js文件。文件不存在时返回undefined,文件内有其他错误导致require失败时会抛出错误。 + +// 判断是否包含某文件 +uniIDConfig.hasFile('custom-token.js') // 配置目录是否包含某文件,true: 文件存在,false: 文件不存在 +``` \ No newline at end of file diff --git a/uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/index.js b/uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e14fb3b0a213443d5c51cb3fcb763d322c21c97f --- /dev/null +++ b/uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/index.js @@ -0,0 +1 @@ +"use strict";var t=require("fs"),r=require("path");function e(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var n=e(t),o=e(r),i="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};var u=function(t){var r={exports:{}};return t(r,r.exports),r.exports}((function(t,r){var e="__lodash_hash_undefined__",n=9007199254740991,o="[object Arguments]",u="[object Function]",c="[object Object]",a=/^\[object .+?Constructor\]$/,f=/^(?:0|[1-9]\d*)$/,s={};s["[object Float32Array]"]=s["[object Float64Array]"]=s["[object Int8Array]"]=s["[object Int16Array]"]=s["[object Int32Array]"]=s["[object Uint8Array]"]=s["[object Uint8ClampedArray]"]=s["[object Uint16Array]"]=s["[object Uint32Array]"]=!0,s[o]=s["[object Array]"]=s["[object ArrayBuffer]"]=s["[object Boolean]"]=s["[object DataView]"]=s["[object Date]"]=s["[object Error]"]=s[u]=s["[object Map]"]=s["[object Number]"]=s[c]=s["[object RegExp]"]=s["[object Set]"]=s["[object String]"]=s["[object WeakMap]"]=!1;var l="object"==typeof i&&i&&i.Object===Object&&i,h="object"==typeof self&&self&&self.Object===Object&&self,p=l||h||Function("return this")(),_=r&&!r.nodeType&&r,v=_&&t&&!t.nodeType&&t,d=v&&v.exports===_,y=d&&l.process,g=function(){try{var t=v&&v.require&&v.require("util").types;return t||y&&y.binding&&y.binding("util")}catch(t){}}(),b=g&&g.isTypedArray;function j(t,r,e){switch(e.length){case 0:return t.call(r);case 1:return t.call(r,e[0]);case 2:return t.call(r,e[0],e[1]);case 3:return t.call(r,e[0],e[1],e[2])}return t.apply(r,e)}var w,O,m,A=Array.prototype,z=Function.prototype,M=Object.prototype,x=p["__core-js_shared__"],C=z.toString,F=M.hasOwnProperty,U=(w=/[^.]+$/.exec(x&&x.keys&&x.keys.IE_PROTO||""))?"Symbol(src)_1."+w:"",S=M.toString,I=C.call(Object),P=RegExp("^"+C.call(F).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),T=d?p.Buffer:void 0,q=p.Symbol,E=p.Uint8Array,$=T?T.allocUnsafe:void 0,D=(O=Object.getPrototypeOf,m=Object,function(t){return O(m(t))}),k=Object.create,B=M.propertyIsEnumerable,N=A.splice,L=q?q.toStringTag:void 0,R=function(){try{var t=_t(Object,"defineProperty");return t({},"",{}),t}catch(t){}}(),G=T?T.isBuffer:void 0,V=Math.max,W=Date.now,H=_t(p,"Map"),J=_t(Object,"create"),K=function(){function t(){}return function(r){if(!Mt(r))return{};if(k)return k(r);t.prototype=r;var e=new t;return t.prototype=void 0,e}}();function Q(t){var r=-1,e=null==t?0:t.length;for(this.clear();++r-1},X.prototype.set=function(t,r){var e=this.__data__,n=nt(e,t);return n<0?(++this.size,e.push([t,r])):e[n][1]=r,this},Y.prototype.clear=function(){this.size=0,this.__data__={hash:new Q,map:new(H||X),string:new Q}},Y.prototype.delete=function(t){var r=pt(this,t).delete(t);return this.size-=r?1:0,r},Y.prototype.get=function(t){return pt(this,t).get(t)},Y.prototype.has=function(t){return pt(this,t).has(t)},Y.prototype.set=function(t,r){var e=pt(this,t),n=e.size;return e.set(t,r),this.size+=e.size==n?0:1,this},Z.prototype.clear=function(){this.__data__=new X,this.size=0},Z.prototype.delete=function(t){var r=this.__data__,e=r.delete(t);return this.size=r.size,e},Z.prototype.get=function(t){return this.__data__.get(t)},Z.prototype.has=function(t){return this.__data__.has(t)},Z.prototype.set=function(t,r){var e=this.__data__;if(e instanceof X){var n=e.__data__;if(!H||n.length<199)return n.push([t,r]),this.size=++e.size,this;e=this.__data__=new Y(n)}return e.set(t,r),this.size=e.size,this};var it,ut=function(t,r,e){for(var n=-1,o=Object(t),i=e(t),u=i.length;u--;){var c=i[it?u:++n];if(!1===r(o[c],c,o))break}return t};function ct(t){return null==t?void 0===t?"[object Undefined]":"[object Null]":L&&L in Object(t)?function(t){var r=F.call(t,L),e=t[L];try{t[L]=void 0;var n=!0}catch(t){}var o=S.call(t);n&&(r?t[L]=e:delete t[L]);return o}(t):function(t){return S.call(t)}(t)}function at(t){return xt(t)&&ct(t)==o}function ft(t){return!(!Mt(t)||function(t){return!!U&&U in t}(t))&&(At(t)?P:a).test(function(t){if(null!=t){try{return C.call(t)}catch(t){}try{return t+""}catch(t){}}return""}(t))}function st(t){if(!Mt(t))return function(t){var r=[];if(null!=t)for(var e in Object(t))r.push(e);return r}(t);var r=dt(t),e=[];for(var n in t)("constructor"!=n||!r&&F.call(t,n))&&e.push(n);return e}function lt(t,r,e,n,o){t!==r&&ut(r,(function(i,u){if(o||(o=new Z),Mt(i))!function(t,r,e,n,o,i,u){var a=yt(t,e),f=yt(r,e),s=u.get(f);if(s)return void rt(t,e,s);var l=i?i(a,f,e+"",t,r,u):void 0,h=void 0===l;if(h){var p=wt(f),_=!p&&mt(f),v=!p&&!_&&Ct(f);l=f,p||_||v?wt(a)?l=a:xt(j=a)&&Ot(j)?l=function(t,r){var e=-1,n=t.length;r||(r=Array(n));for(;++e-1&&t%1==0&&t0){if(++r>=800)return arguments[0]}else r=0;return t.apply(void 0,arguments)}}(R?function(t,r){return R(t,"toString",{configurable:!0,enumerable:!1,value:(e=r,function(){return e}),writable:!0});var e}:It);function bt(t,r){return t===r||t!=t&&r!=r}var jt=at(function(){return arguments}())?at:function(t){return xt(t)&&F.call(t,"callee")&&!B.call(t,"callee")},wt=Array.isArray;function Ot(t){return null!=t&&zt(t.length)&&!At(t)}var mt=G||function(){return!1};function At(t){if(!Mt(t))return!1;var r=ct(t);return r==u||"[object GeneratorFunction]"==r||"[object AsyncFunction]"==r||"[object Proxy]"==r}function zt(t){return"number"==typeof t&&t>-1&&t%1==0&&t<=n}function Mt(t){var r=typeof t;return null!=t&&("object"==r||"function"==r)}function xt(t){return null!=t&&"object"==typeof t}var Ct=b?function(t){return function(r){return t(r)}}(b):function(t){return xt(t)&&zt(t.length)&&!!s[ct(t)]};function Ft(t){return Ot(t)?tt(t,!0):st(t)}var Ut,St=(Ut=function(t,r,e){lt(t,r,e)},ht((function(t,r){var e=-1,n=r.length,o=n>1?r[n-1]:void 0,i=n>2?r[2]:void 0;for(o=Ut.length>3&&"function"==typeof o?(n--,o):void 0,i&&function(t,r,e){if(!Mt(e))return!1;var n=typeof r;return!!("number"==n?Ot(e)&&vt(r,e.length):"string"==n&&r in e)&&bt(e[r],t)}(r[0],r[1],i)&&(o=n<3?void 0:o,n=1),t=Object(t);++ec.call(t,r);class f{constructor({pluginId:t,defaultConfig:r={},customMerge:e,root:n}){this.pluginId=t,this.defaultConfig=r,this.pluginConfigPath=o.default.resolve(n||__dirname,t),this.customMerge=e,this._config=void 0}resolve(t){return o.default.resolve(this.pluginConfigPath,t)}hasFile(t){return n.default.existsSync(this.resolve(t))}requireFile(t){try{return require(this.resolve(t))}catch(t){if("MODULE_NOT_FOUND"===t.code)return;throw t}}_getUserConfig(){return this.requireFile("config.json")}config(t,r){this._config||(this._config=(this.customMerge||u)(this.defaultConfig,this._getUserConfig()));let e=this._config;return t?function(t,r,e){if("number"==typeof r)return t[r];if("symbol"==typeof r)return a(t,r)?t[r]:e;const n="string"!=typeof(o=r)?o:o.split(".").reduce(((t,r)=>(r.split(/\[([^}]+)\]/g).forEach((r=>r&&t.push(r))),t)),[]);var o;let i=t;for(let t=0;t + + {{ d }} + {{dayText}} + {{ h }} + {{ showColon ? ':' : hourText }} + {{ i }} + {{ showColon ? ':' : minuteText }} + {{ s }} + {{secondText}} + + + + diff --git a/uni_modules/uni-countdown/package.json b/uni_modules/uni-countdown/package.json new file mode 100644 index 0000000000000000000000000000000000000000..7960b62e9ff41de8976a2f4e3ba1d2ea00f06668 --- /dev/null +++ b/uni_modules/uni-countdown/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-countdown", + "displayName": "uni-countdown 倒计时", + "version": "1.1.2", + "description": "CountDown 倒计时组件", + "keywords": [ + "uni-ui", + "uniui", + "countdown", + "倒计时" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-countdown/readme.md b/uni_modules/uni-countdown/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..25c2a3a2756009668fe23208a4aab6048f088d10 --- /dev/null +++ b/uni_modules/uni-countdown/readme.md @@ -0,0 +1,57 @@ + + +## CountDown 倒计时 +> **组件名:uni-countdown** +> 代码块: `uCountDown` + + +倒计时组件。 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +在 ``template`` 中使用组件 + +```html + + + + + + + + +``` + +## API + +### Countdown Props + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|backgroundColor |String |#FFFFFF|背景色 | +|color |String |#000000|文字颜色 | +|splitorColor |String |#000000|分割符号颜色 | +|day |Number |0 |天数 | +|hour |Number |0 |小时 | +|minute |Number |0 |分钟 | +|second |Number |0 |秒 | +|showDay |Boolean|true |是否显示天数 | +|showColon |Boolean|true |是否以冒号为分隔符 | +|start |Boolean|true |是否初始化组件后就开始倒计时| + +### Countdown Events + +|事件称名 |说明 |返回值 | +|:-: |:-: |:-: | +|@timeup|倒计时时间到触发事件 |- | + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/countdown/countdown](https://hellouniapp.dcloud.net.cn/pages/extUI/countdown/countdown) \ No newline at end of file diff --git a/uni_modules/uni-data-checkbox/changelog.md b/uni_modules/uni-data-checkbox/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..cda4c3bb86d5c1c6d9fcbef053aba73d1710c369 --- /dev/null +++ b/uni_modules/uni-data-checkbox/changelog.md @@ -0,0 +1,36 @@ +## 0.2.5(2021-08-23) +- 修复 在uni-forms中 modelValue 中不存在当前字段,当前字段必填写也不参与校验的问题 +## 0.2.4(2021-08-17) +- 修复 单选 list 模式下 ,icon 为 left 时,选中图标不显示的问题 +## 0.2.3(2021-08-11) +- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题 +## 0.2.2(2021-07-30) +- 优化 在uni-forms组件,与label不对齐的问题 +## 0.2.1(2021-07-27) +- 修复 单选默认值为0不能选中的Bug +## 0.2.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 0.1.11(2021-07-06) +- 优化 删除无用日志 +## 0.1.10(2021-07-05) +- 修复 由 0.1.9 引起的非 nvue 端图标不显示的问题 +## 0.1.9(2021-07-05) +- 修复 nvue 黑框样式问题 +## 0.1.8(2021-06-28) +- 修复 selectedTextColor 属性不生效的Bug +## 0.1.7(2021-06-02) +- 新增 map 属性,可以方便映射text/value属性 +## 0.1.6(2021-05-26) +- 修复 不关联服务空间的情况下组件报错的Bug +## 0.1.5(2021-05-12) +- 新增 组件示例地址 +## 0.1.4(2021-04-09) +- 修复 nvue 下无法选中的问题 +## 0.1.3(2021-03-22) +- 新增 disabled属性 +## 0.1.2(2021-02-24) +- 优化 默认颜色显示 +## 0.1.1(2021-02-24) +- 新增 支持nvue +## 0.1.0(2021-02-18) +- “暂无数据”显示居中 diff --git a/uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue b/uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue new file mode 100644 index 0000000000000000000000000000000000000000..71dd59b1565ef1af6f3e7f471b29d2a9b7a5e1b7 --- /dev/null +++ b/uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue @@ -0,0 +1,823 @@ + + + + + diff --git a/uni_modules/uni-data-checkbox/package.json b/uni_modules/uni-data-checkbox/package.json new file mode 100644 index 0000000000000000000000000000000000000000..e978c4ef01fb640948e16c975851c3ba92aa8ff2 --- /dev/null +++ b/uni_modules/uni-data-checkbox/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-data-checkbox", + "displayName": "uni-data-checkbox 数据选择器", + "version": "0.2.5", + "description": "通过数据驱动的单选框和复选框", + "keywords": [ + "uni-ui", + "checkbox", + "单选", + "多选", + "单选多选" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "^3.1.1" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-load-more"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} diff --git a/uni_modules/uni-data-checkbox/readme.md b/uni_modules/uni-data-checkbox/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..485008580fc12ce343f67025ba60fec0a60b36f3 --- /dev/null +++ b/uni_modules/uni-data-checkbox/readme.md @@ -0,0 +1,299 @@ + + +## DataCheckbox 数据驱动的单选复选框 +> **组件名:uni-data-checkbox** +> 代码块: `uDataCheckbox` + + +本组件是基于uni-app基础组件checkbox的封装。本组件要解决问题包括: + +1. 数据绑定型组件:给本组件绑定一个data,会自动渲染一组候选内容。再以往,开发者需要编写不少代码实现类似功能 +2. 自动的表单校验:组件绑定了data,且符合[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)组件的表单校验规范,搭配使用会自动实现表单校验 +3. 本组件合并了单选多选 +4. 本组件有若干风格选择,如普通的单选多选框、并列button风格、tag风格。开发者可以快速选择需要的风格。但作为一个封装组件,样式代码虽然不用自己写了,却会牺牲一定的样式自定义性 + +在uniCloud开发中,`DB Schema`中配置了enum枚举等类型后,在web控制台的[自动生成表单](https://uniapp.dcloud.io/uniCloud/schema?id=autocode)功能中,会自动生成``uni-data-checkbox``组件并绑定好data + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - 组件需要依赖 `sass` 插件 ,请自行手动安装 +> - 本组件为数据驱动,目的是快速投入使用,只可通过 style 覆盖有限样式,不支持自定义更多样式 +> - 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 +> - 组件支持 nvue ,需要在 `manifest.json > app-plus` 节点下配置 `"nvueStyleCompiler" : "uni-app"` +> - 如组件显示有问题 ,请升级 `HBuilderX` 为 `v3.1.0` 以上 + + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另行文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +设置 `localdata` 属性后,组件会通过数据渲染出对应的内容,默认显示的是单选框 + +需要注意 `:multiple="false"` 时(单选) , `value/v-model` 的值是 `String|Number` 类型 + +```html + + +``` + +```javascript + +export default { + data() { + return { + value: 0, + range: [{"value": 0,"text": "篮球" },{"value": 1,"text": "足球"},{"value": 2,"text": "游泳"}] + } + }, + methods: { + change(e){ + console.log('e:',e); + } + } +} +``` + +### 多选框 + +设置 `multiple` 属性,组件显示为多选框 + +需要注意 `:multiple="true"` 时(多选) , `value/v-model` 的值是 `Array` 类型 + +```html + + +``` + +```javascript + +export default { + data() { + return { + value: [0,2], + range: [{"value": 0,"text": "篮球" },{"value": 1,"text": "足球"},{"value": 2,"text": "游泳"}] + } + }, + methods: { + change(e){ + console.log('e:',e); + } + } +} +``` + +### 设置最大最小值 + +设置 `:multiple="true"` 时(多选) ,可以设置 `min`、`max` 属性 + +如果选中个数小于 `min` 属性设置的值,那么选中内容将不可取消,只有当选中个数大于等于 `min`且小于 `max` 时,才可取消选中 + +如果选中个数大于等于 `max` 属性设置的值,那么其他未选中内容将不可选 + +```html + + +``` + +```javascript + +export default { + data() { + return { + value: [0,2], + range: [{"value": 0,"text": "篮球" },{"value": 1,"text": "足球"},{"value": 2,"text": "游泳"}] + } + }, + methods: { + change(e){ + console.log('e:',e); + } + } +} +``` + +### 设置禁用 + +如果需要禁用某项,需要在 `localdata` 属性的数据源中添加 `disable` 属性,而不是在组件中添加 `disable` 属性 + +```html + + +``` + +```javascript + +export default { + data() { + return { + value: 0, + range: [{ + "value": 0, + "text": "篮球" + }, + { + "value": 1, + "text": "足球", + // 禁用当前项 + "disable":true + }, + { + "value": 2, + "text": "游泳" + } + ] + } + }, + methods: { + change(e){ + console.log('e:',e); + } + } +} +``` + + +### 自定义选中颜色 + +设置 `selectedColor` 属性,可以修改组件选中后的图标及边框颜色 + +设置 `selectedTextColor` 属性,可以修改组件选中后的文字颜色,如不填写默认同 `selectedColor` 属性 ,`mode` 属性为 `tag` 时,默认为白色 + +```html + + +``` + +```javascript + +export default { + data() { + return { + value: [0,2], + range: [{"value": 0,"text": "篮球" },{"value": 1,"text": "足球"},{"value": 2,"text": "游泳"}] + } + }, + methods: { + change(e){ + console.log('e:',e); + } + } +} +``` + +### 更多模式 + +设置 `mode` 属性,可以设置更多显示样式,目前内置样式有四种 `default/list/button/tag` + +如果需要禁用某项,需要在 `localdata` 属性的数据源中添加 `disable` 属性,而不是在组件中添加 `disable` 属性 + +```html + + +``` + +```javascript + +export default { + data() { + return { + value: 0, + range: [{"value": 0,"text": "篮球" },{"value": 1,"text": "足球"},{"value": 2,"text": "游泳"}] + } + }, + methods: { + change(e){ + console.log('e:',e); + } + } +} +``` + + +## API + +### DataCheckbox Props + +| 属性名 | 类型 |可选值 | 默认值| 说明 | +| :-: | :-: |:-: |:-: | :-: | +|value/v-model|Array/String/Number|- |- |默认值,multiple=true时为 Array类型,否则为 String或Number类型 | +|localdata |Array |- |- |本地渲染数据, | +|mode | String |default/list/button/tag|default|显示模式 | +|multiple |Boolean |- |false |是否多选 | +|min |String/Number |- |- |最小选择个数 ,multiple为true时生效 | +|max |String/Number |- |- |最大选择个数 ,multiple为true时生效 | +|wrap |Boolean |- |- |是否换行显示 | +|icon |String |left/right |left |list 列表模式下 icon 显示的位置 | +|selectedColor|String |- |#007aff|选中颜色| +|selectedTextColor|String |- |#333 |选中文本颜色,如不填写则自动显示| +|emptyText |String |- |暂无数据 |没有数据时显示的文字 ,本地数据无效| +|map |Object |- |{text:'text',value:'value'} |字段映射,将text/value映射到数据中的其他字段| + +#### Localdata Options + +`localdata` 属性的格式为数组,数组内每项是对象,需要严格遵循如下格式 + +|属性名 | 说明 | +|:-: | :-: | +|text |显示文本 | +|value |选中后的值 | +|disable |是否禁用 | + +#### Mode Options + +|属性名 | 说明 | +|:-: | :-: | +|default |默认值,横向显示 | +|list |列表 | +|button |按钮 | +|tag |标签 | + + +### DataCheckbox Events + +| 事件名 | 事件说明 | 返回参数| +| :-: | :-: | :-: | +| @change| 选中状态改变时触发事件 | - | + + + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/data-checkbox/data-checkbox](https://hellouniapp.dcloud.net.cn/pages/extUI/data-checkbox/data-checkbox) \ No newline at end of file diff --git a/uni_modules/uni-data-picker/changelog.md b/uni_modules/uni-data-picker/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..269cedcf404c41523691bb7cfeaba22730bcc736 --- /dev/null +++ b/uni_modules/uni-data-picker/changelog.md @@ -0,0 +1,25 @@ +## 0.4.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 0.3.5(2021-06-04) +- 修复 无法加载云端数据的问题 +## 0.3.4(2021-05-28) +- 修复 v-model无效问题 +- 修复 loaddata 为空数据组时加载时间过长问题 +- 修复 上个版本引出的本地数据无法选择带有children的2级节点 +## 0.3.3(2021-05-12) +- 新增 组件示例地址 +## 0.3.2(2021-04-22) +- 修复 非树形数据有 where 属性查询报错的问题 +## 0.3.1(2021-04-15) +- 修复 本地数据概率无法回显时问题 +## 0.3.0(2021-04-07) +- 新增 支持云端非树形表结构数据 +- 修复 根节点 parent_field 字段等于null时选择界面错乱问题 +## 0.2.0(2021-03-15) +- 修复 nodeclick、popupopened、popupclosed事件无法触发的问题 +## 0.1.9(2021-03-09) +- 修复 微信小程序某些情况下无法选择的问题 +## 0.1.8(2021-02-05) +- 优化 部分样式在nvue上的兼容表现 +## 0.1.7(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-data-picker/components/uni-data-picker/config.json b/uni_modules/uni-data-picker/components/uni-data-picker/config.json new file mode 100644 index 0000000000000000000000000000000000000000..dc6b403974e5b00c4bc44de7334de52448cff076 --- /dev/null +++ b/uni_modules/uni-data-picker/components/uni-data-picker/config.json @@ -0,0 +1,12 @@ +{ + "id": "3796", + "name": "DataPicker", + "desc": "数据驱动的picker选择器", + "url": "data-picker", + "type": "表单组件", + "edition": "0.0.8", + "suffix": "vue", + "module": ["uni-data-picker","uni-data-pickerview","uni-load-more"], + "path": "https://ext.dcloud.net.cn/plugin?id=3796", + "update_log": ["- 优化 增加下拉箭头"] +} diff --git a/uni_modules/uni-data-picker/components/uni-data-picker/keypress.js b/uni_modules/uni-data-picker/components/uni-data-picker/keypress.js new file mode 100644 index 0000000000000000000000000000000000000000..6ef26a26211ff28976e6fc835fbef5d2ea5b5b1e --- /dev/null +++ b/uni_modules/uni-data-picker/components/uni-data-picker/keypress.js @@ -0,0 +1,45 @@ +// #ifdef H5 +export default { + name: 'Keypress', + props: { + disable: { + type: Boolean, + default: false + } + }, + mounted () { + const keyNames = { + esc: ['Esc', 'Escape'], + tab: 'Tab', + enter: 'Enter', + space: [' ', 'Spacebar'], + up: ['Up', 'ArrowUp'], + left: ['Left', 'ArrowLeft'], + right: ['Right', 'ArrowRight'], + down: ['Down', 'ArrowDown'], + delete: ['Backspace', 'Delete', 'Del'] + } + const listener = ($event) => { + if (this.disable) { + return + } + const keyName = Object.keys(keyNames).find(key => { + const keyName = $event.key + const value = keyNames[key] + return value === keyName || (Array.isArray(value) && value.includes(keyName)) + }) + if (keyName) { + // 避免和其他按键事件冲突 + setTimeout(() => { + this.$emit(keyName, {}) + }, 0) + } + } + document.addEventListener('keyup', listener) + this.$once('hook:beforeDestroy', () => { + document.removeEventListener('keyup', listener) + }) + }, + render: () => {} +} +// #endif diff --git a/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue b/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue new file mode 100644 index 0000000000000000000000000000000000000000..fced5ecd14dd65cb4797444d337056f93514b60a --- /dev/null +++ b/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue @@ -0,0 +1,472 @@ + + + + + diff --git a/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js new file mode 100644 index 0000000000000000000000000000000000000000..bb285009fc728adbb77ee0efb4f8fe65cf0a50a4 --- /dev/null +++ b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js @@ -0,0 +1,545 @@ +export default { + props: { + localdata: { + type: [Array, Object], + default () { + return [] + } + }, + collection: { + type: String, + default: '' + }, + action: { + type: String, + default: '' + }, + field: { + type: String, + default: '' + }, + orderby: { + type: String, + default: '' + }, + where: { + type: [String, Object], + default: '' + }, + pageData: { + type: String, + default: 'add' + }, + pageCurrent: { + type: Number, + default: 1 + }, + pageSize: { + type: Number, + default: 20 + }, + getcount: { + type: [Boolean, String], + default: false + }, + getone: { + type: [Boolean, String], + default: false + }, + gettree: { + type: [Boolean, String], + default: false + }, + manual: { + type: Boolean, + default: false + }, + value: { + type: [Array, String, Number], + default () { + return [] + } + }, + modelValue: { + type: [Array, String, Number], + default () { + return [] + } + }, + preload: { + type: Boolean, + default: false + }, + stepSearh: { + type: Boolean, + default: true + }, + selfField: { + type: String, + default: '' + }, + parentField: { + type: String, + default: '' + }, + multiple: { + type: Boolean, + default: false + } + }, + data() { + return { + loading: false, + errorMessage: '', + loadMore: { + contentdown: '', + contentrefresh: '', + contentnomore: '' + }, + dataList: [], + selected: [], + selectedIndex: 0, + page: { + current: this.pageCurrent, + size: this.pageSize, + count: 0 + } + } + }, + computed: { + isLocaldata() { + return !this.collection.length + }, + postField() { + let fields = [this.field]; + if (this.parentField) { + fields.push(`${this.parentField} as parent_value`); + } + return fields.join(','); + }, + dataValue(){ + let isarr = Array.isArray(this.value) && this.value.length === 0 + let isstr = typeof this.value === 'string' && !this.value + let isnum = typeof this.value === 'number' && !this.value + + if(isarr || isstr || isnum){ + return this.modelValue + } + + return this.value + } + }, + created() { + this.$watch(() => { + var al = []; + ['pageCurrent', + 'pageSize', + 'value', + 'modelValue', + 'localdata', + 'collection', + 'action', + 'field', + 'orderby', + 'where', + 'getont', + 'getcount', + 'gettree' + ].forEach(key => { + al.push(this[key]) + }); + return al + }, (newValue, oldValue) => { + let needReset = false + for (let i = 2; i < newValue.length; i++) { + if (newValue[i] != oldValue[i]) { + needReset = true + break + } + } + if (newValue[0] != oldValue[0]) { + this.page.current = this.pageCurrent + } + this.page.size = this.pageSize + + this.onPropsChange() + }) + this._treeData = [] + }, + methods: { + onPropsChange() { + this._treeData = [] + }, + getCommand(options = {}) { + /* eslint-disable no-undef */ + let db = uniCloud.database() + + const action = options.action || this.action + if (action) { + db = db.action(action) + } + + const collection = options.collection || this.collection + db = db.collection(collection) + + const where = options.where || this.where + if (!(!where || !Object.keys(where).length)) { + db = db.where(where) + } + + const field = options.field || this.field + if (field) { + db = db.field(field) + } + + const orderby = options.orderby || this.orderby + if (orderby) { + db = db.orderBy(orderby) + } + + const current = options.pageCurrent !== undefined ? options.pageCurrent : this.page.current + const size = options.pageSize !== undefined ? options.pageSize : this.page.size + const getCount = options.getcount !== undefined ? options.getcount : this.getcount + const getTree = options.gettree !== undefined ? options.gettree : this.gettree + + const getOptions = { + getCount, + getTree + } + if (options.getTreePath) { + getOptions.getTreePath = options.getTreePath + } + + db = db.skip(size * (current - 1)).limit(size).get(getOptions) + + return db + }, + getNodeData(callback) { + if (this.loading) { + return + } + this.loading = true + this.getCommand({ + field: this.postField, + where: this._pathWhere() + }).then((res) => { + this.loading = false + this.selected = res.result.data + callback && callback() + }).catch((err) => { + this.loading = false + this.errorMessage = err + }) + }, + getTreePath(callback) { + if (this.loading) { + return + } + this.loading = true + + this.getCommand({ + field: this.postField, + getTreePath: { + startWith: `${this.selfField}=='${this.dataValue}'` + } + }).then((res) => { + this.loading = false + let treePath = [] + this._extractTreePath(res.result.data, treePath) + this.selected = treePath + callback && callback() + }).catch((err) => { + this.loading = false + this.errorMessage = err + }) + }, + loadData() { + if (this.isLocaldata) { + this._processLocalData() + return + } + + if (this.dataValue.length) { + this._loadNodeData((data) => { + this._treeData = data + this._updateBindData() + this._updateSelected() + }) + return + } + + if (this.stepSearh) { + this._loadNodeData((data) => { + this._treeData = data + this._updateBindData() + }) + } else { + this._loadAllData((data) => { + this._treeData = [] + this._extractTree(data, this._treeData, null) + this._updateBindData() + }) + } + }, + _loadAllData(callback) { + if (this.loading) { + return + } + this.loading = true + + this.getCommand({ + field: this.postField, + gettree: true, + startwith: `${this.selfField}=='${this.dataValue}'` + }).then((res) => { + this.loading = false + callback(res.result.data) + this.onDataChange() + }).catch((err) => { + this.loading = false + this.errorMessage = err + }) + }, + _loadNodeData(callback, pw) { + if (this.loading) { + return + } + this.loading = true + + this.getCommand({ + field: this.postField, + where: pw || this._postWhere(), + pageSize: 500 + }).then((res) => { + this.loading = false + callback(res.result.data) + this.onDataChange() + }).catch((err) => { + this.loading = false + this.errorMessage = err + }) + }, + _pathWhere() { + let result = [] + let where_field = this._getParentNameByField(); + if (where_field) { + result.push(`${where_field} == '${this.dataValue}'`) + } + + if (this.where) { + return `(${this.where}) && (${result.join(' || ')})` + } + + return result.join(' || ') + }, + _postWhere() { + let result = [] + let selected = this.selected + let parentField = this.parentField + if (parentField) { + result.push(`${parentField} == null || ${parentField} == ""`) + } + if (selected.length) { + for (var i = 0; i < selected.length - 1; i++) { + result.push(`${parentField} == '${selected[i].value}'`) + } + } + + let where = [] + if (this.where) { + where.push(`(${this.where})`) + } + if (result.length) { + where.push(`(${result.join(' || ')})`) + } + + return where.join(' && ') + }, + _nodeWhere() { + let result = [] + let selected = this.selected + if (selected.length) { + result.push(`${this.parentField} == '${selected[selected.length - 1].value}'`) + } + + if (this.where) { + return `(${this.where}) && (${result.join(' || ')})` + } + + return result.join(' || ') + }, + _getParentNameByField() { + const fields = this.field.split(','); + let where_field = null; + for (let i = 0; i < fields.length; i++) { + const items = fields[i].split('as'); + if (items.length < 2) { + continue; + } + if (items[1].trim() === 'value') { + where_field = items[0].trim(); + break; + } + } + return where_field + }, + _isTreeView() { + return (this.parentField && this.selfField) + }, + _updateSelected() { + var dl = this.dataList + var sl = this.selected + for (var i = 0; i < sl.length; i++) { + var value = sl[i].value + var dl2 = dl[i] + for (var j = 0; j < dl2.length; j++) { + var item2 = dl2[j] + if (item2.value === value) { + sl[i].text = item2.text + break + } + } + } + }, + _updateBindData(node) { + const { + dataList, + hasNodes + } = this._filterData(this._treeData, this.selected) + + let isleaf = this._stepSearh === false && !hasNodes + + if (node) { + node.isleaf = isleaf + } + + this.dataList = dataList + this.selectedIndex = dataList.length - 1 + + if (!isleaf && this.selected.length < dataList.length) { + this.selected.push({ + value: null, + text: "请选择" + }) + } + + return { + isleaf, + hasNodes + } + }, + _filterData(data, paths) { + let dataList = [] + + let hasNodes = true + + dataList.push(data.filter((item) => { + return item.parent_value === undefined + })) + for (let i = 0; i < paths.length; i++) { + var value = paths[i].value + var nodes = data.filter((item) => { + return item.parent_value === value + }) + + if (nodes.length) { + dataList.push(nodes) + } else { + hasNodes = false + } + } + + return { + dataList, + hasNodes + } + }, + _extractTree(nodes, result, parent_value) { + let list = result || [] + for (let i = 0; i < nodes.length; i++) { + let node = nodes[i] + + let child = {} + for (let key in node) { + if (key !== 'children') { + child[key] = node[key] + } + } + if (parent_value !== undefined) { + child.parent_value = parent_value + } + result.push(child) + + let children = node.children + if (children) { + this._extractTree(children, result, node.value) + } + } + }, + _extractTreePath(nodes, result) { + let list = result || [] + for (let i = 0; i < nodes.length; i++) { + let node = nodes[i] + + let child = {} + for (let key in node) { + if (key !== 'children') { + child[key] = node[key] + } + } + result.push(child) + + let children = node.children + if (children) { + this._extractTreePath(children, result) + } + } + }, + _findNodePath(key, nodes, path = []) { + for (let i = 0; i < nodes.length; i++) { + let { + value, + text, + children + } = nodes[i] + + path.push({ + value, + text + }) + + if (value === key) { + return path + } + + if (children) { + const p = this._findNodePath(key, children, path) + if (p.length) { + return p + } + } + + path.pop() + } + return [] + }, + _processLocalData() { + this._treeData = [] + this._extractTree(this.localdata, this._treeData) + + var inputValue = this.dataValue + if (inputValue === undefined) { + return + } + + if (Array.isArray(inputValue)) { + inputValue = inputValue[inputValue.length - 1] + if (typeof inputValue === 'object' && inputValue.value) { + inputValue = inputValue.value + } + } + + this.selected = this._findNodePath(inputValue, this.localdata) + } + } +} diff --git a/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue new file mode 100644 index 0000000000000000000000000000000000000000..7b5952965b31469d9c1f6b23166491d5135fa0ad --- /dev/null +++ b/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue @@ -0,0 +1,300 @@ + + + + + diff --git a/uni_modules/uni-data-picker/package.json b/uni_modules/uni-data-picker/package.json new file mode 100644 index 0000000000000000000000000000000000000000..469b06583e3c2f0e31c35689cd899f3b5b3e3eeb --- /dev/null +++ b/uni_modules/uni-data-picker/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-data-picker", + "displayName": "uni-data-picker 数据驱动的picker选择器", + "version": "0.4.0", + "description": "Picker选择器", + "keywords": [ + "uni-ui", + "uniui", + "picker", + "级联", + "省市区", + "" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-load-more" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-data-picker/readme.md b/uni_modules/uni-data-picker/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..70eed230caef370d15a0511884374d5b4b7ae988 --- /dev/null +++ b/uni_modules/uni-data-picker/readme.md @@ -0,0 +1,270 @@ +## DataPicker 级联选择 +> **组件名:uni-data-picker** +> 代码块: `uDataPicker` +> 关联组件:`uni-data-pickerview`、`uni-load-more`。 + + +`` 是一个选择类[datacom组件](https://uniapp.dcloud.net.cn/component/datacom)。 + +支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。 + +候选数据支持一次性加载完毕,也支持懒加载,比如示例图中,选择了“北京”后,动态加载北京的区县数据。 + +`` 组件尤其适用于地址选择、分类选择等选择类。 + +`` 支持本地数据、云端静态数据(json),uniCloud云数据库数据。 + +`` 可以通过JQL直连uniCloud云数据库,配套[DB Schema](https://uniapp.dcloud.net.cn/uniCloud/schema),可在schema2code中自动生成前端页面,还支持服务器端校验。 + +在uniCloud数据表中新建表“uni-id-address”和“opendb-city-china”,这2个表的schema自带foreignKey关联。在“uni-id-address”表的表结构页面使用schema2code生成前端页面,会自动生成地址管理的维护页面,自动从“opendb-city-china”表包含的中国所有省市区信息里选择地址。 + + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - 组件需要依赖 `sass` 插件 ,请自行手动安装 +> - 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 +> - `` 内部包含了弹出层组件 `` 外层的布局可能会影响弹出层,[详情](https://developer.mozilla.org/zh-Hans/docs/Web/CSS/Common_CSS_Questions) + + + +### 平台差异说明 + +暂不支持在nvue页面中使用 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`componets`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +## API + +### DataPicker Props + +|属性名 | 类型 | 可选值 | 默认值 | 说明| +|:-: | :-: |:-: | :-: | :-: | +|v-model |String/ Number | - | - |绑定数据| +|localdata |Array | | |数据,[详情](https://gitee.com/dcloud/datacom)| +|preload |Boolean | true/false | false |预加载数据| +|readonly |Boolean | true/false | false |是否禁用| +|step-searh |Boolean | true/false | true |分步查询时,点击节点请求数据| +|step-search-url |String | | |分步查询时,动态加载云端数据url格式,`https://xxx.com/{parentValue}`(当前版本暂不支持,下版支持)| +|self-field |String | | |分步查询时当前字段名称| +|parent-field |String | | |分步查询时父字段名称| +|collection |String | | |表名。支持输入多个表名,用 `,` 分割| +|field |String | | |查询字段,多个字段用 `,` 分割| +|where |String | | |查询条件,内容较多,另见jql文档:[详情](https://uniapp.dcloud.net.cn/uniCloud/uni-clientDB?id=jsquery)| +|orderby |String | | |排序字段及正序倒叙设置| +|popup-title |String | | |弹出层标题| + + +> **** +> `collection/where/orderby` 和 `` 的用法一致,[详情](https://uniapp.dcloud.net.cn/uniCloud/unicloud-db) + + + +### DataPicker Events + +|事件称名 | 类型 | 说明 | +|:-: | :-: | :-: | +|@change |EventHandle | 选择完成时触发 {detail: {value}} | +|@nodeclick |EventHandle | 节点被点击时触发 | +|@stepsearch |EventHandle | 动态加载节点数据前触发(当前版本暂不支持,下版支持) | +|@popupopened |EventHandle | 弹出层弹出时触发 | +|@popupclosed |EventHandle | 弹出层关闭时触发 | + + + +### 基本用法 + +#### 云端数据 + +> **注意事项** +> - 云端数据需要关联服务空间 +> - 下面示例中使用的表 `opendb-city-china`(中国城市省市区数据,含港澳台), 在[uniCloud控制台](https://unicloud.dcloud.net.cn/)使用opendb创建,[详情](https://gitee.com/dcloud/opendb) + + +```html + +``` + +```js + + +``` + + + + + +#### 本地数据 + +```html + +``` + +```js + + +``` + + +#### 自定义solt + +```html + + + {{error}} + + + + {{item.text}} + + + + 请选择 + + +``` + + +> **注意事项** +> `localdata` 和 `collection` 同时配置时,`localdata` 优先 + + + +#### 完整示例 + +```html + +``` + +```js + + +``` + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/data-picker/data-picker](https://hellouniapp.dcloud.net.cn/pages/extUI/data-picker/data-picker) \ No newline at end of file diff --git a/uni_modules/uni-dateformat/changelog.md b/uni_modules/uni-dateformat/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..7006d13a0854c4f636bef64670a4de41526789db --- /dev/null +++ b/uni_modules/uni-dateformat/changelog.md @@ -0,0 +1,7 @@ +## 0.0.5(2021-07-08) +- 调整 默认时间不再是当前时间,而是显示'-'字符 +## 0.0.4(2021-05-12) +- 新增 组件示例地址 +## 0.0.3(2021-02-04) +- 调整为uni_modules目录规范 +- 修复 iOS 平台日期格式化出错的问题 diff --git a/uni_modules/uni-dateformat/components/uni-dateformat/date-format.js b/uni_modules/uni-dateformat/components/uni-dateformat/date-format.js new file mode 100644 index 0000000000000000000000000000000000000000..e00d5597e7a0b8dcdea64ef0c1c3f4d69e9522f8 --- /dev/null +++ b/uni_modules/uni-dateformat/components/uni-dateformat/date-format.js @@ -0,0 +1,200 @@ +// yyyy-MM-dd hh:mm:ss.SSS 所有支持的类型 +function pad(str, length = 2) { + str += '' + while (str.length < length) { + str = '0' + str + } + return str.slice(-length) +} + +const parser = { + yyyy: (dateObj) => { + return pad(dateObj.year, 4) + }, + yy: (dateObj) => { + return pad(dateObj.year) + }, + MM: (dateObj) => { + return pad(dateObj.month) + }, + M: (dateObj) => { + return dateObj.month + }, + dd: (dateObj) => { + return pad(dateObj.day) + }, + d: (dateObj) => { + return dateObj.day + }, + hh: (dateObj) => { + return pad(dateObj.hour) + }, + h: (dateObj) => { + return dateObj.hour + }, + mm: (dateObj) => { + return pad(dateObj.minute) + }, + m: (dateObj) => { + return dateObj.minute + }, + ss: (dateObj) => { + return pad(dateObj.second) + }, + s: (dateObj) => { + return dateObj.second + }, + SSS: (dateObj) => { + return pad(dateObj.millisecond, 3) + }, + S: (dateObj) => { + return dateObj.millisecond + }, +} + +// 这都n年了iOS依然不认识2020-12-12,需要转换为2020/12/12 +function getDate(time) { + if (time instanceof Date) { + return time + } + switch (typeof time) { + case 'string': + { + // 2020-12-12T12:12:12.000Z、2020-12-12T12:12:12.000 + if (time.indexOf('T') > -1) { + return new Date(time) + } + return new Date(time.replace(/-/g, '/')) + } + default: + return new Date(time) + } +} + +export function formatDate(date, format = 'yyyy/MM/dd hh:mm:ss') { + if (!date && date !== 0) { + return '' + } + date = getDate(date) + const dateObj = { + year: date.getFullYear(), + month: date.getMonth() + 1, + day: date.getDate(), + hour: date.getHours(), + minute: date.getMinutes(), + second: date.getSeconds(), + millisecond: date.getMilliseconds() + } + const tokenRegExp = /yyyy|yy|MM|M|dd|d|hh|h|mm|m|ss|s|SSS|SS|S/ + let flag = true + let result = format + while (flag) { + flag = false + result = result.replace(tokenRegExp, function(matched) { + flag = true + return parser[matched](dateObj) + }) + } + return result +} + +export function friendlyDate(time, { + locale = 'zh', + threshold = [60000, 3600000], + format = 'yyyy/MM/dd hh:mm:ss' +}) { + if (time === '-') { + return time + } + if (!time && time !== 0) { + return '' + } + const localeText = { + zh: { + year: '年', + month: '月', + day: '天', + hour: '小时', + minute: '分钟', + second: '秒', + ago: '前', + later: '后', + justNow: '刚刚', + soon: '马上', + template: '{num}{unit}{suffix}' + }, + en: { + year: 'year', + month: 'month', + day: 'day', + hour: 'hour', + minute: 'minute', + second: 'second', + ago: 'ago', + later: 'later', + justNow: 'just now', + soon: 'soon', + template: '{num} {unit} {suffix}' + } + } + const text = localeText[locale] || localeText.zh + let date = getDate(time) + let ms = date.getTime() - Date.now() + let absMs = Math.abs(ms) + if (absMs < threshold[0]) { + return ms < 0 ? text.justNow : text.soon + } + if (absMs >= threshold[1]) { + return formatDate(date, format) + } + let num + let unit + let suffix = text.later + if (ms < 0) { + suffix = text.ago + ms = -ms + } + const seconds = Math.floor((ms) / 1000) + const minutes = Math.floor(seconds / 60) + const hours = Math.floor(minutes / 60) + const days = Math.floor(hours / 24) + const months = Math.floor(days / 30) + const years = Math.floor(months / 12) + switch (true) { + case years > 0: + num = years + unit = text.year + break + case months > 0: + num = months + unit = text.month + break + case days > 0: + num = days + unit = text.day + break + case hours > 0: + num = hours + unit = text.hour + break + case minutes > 0: + num = minutes + unit = text.minute + break + default: + num = seconds + unit = text.second + break + } + + if (locale === 'en') { + if (num === 1) { + num = 'a' + } else { + unit += 's' + } + } + + return text.template.replace(/{\s*num\s*}/g, num + '').replace(/{\s*unit\s*}/g, unit).replace(/{\s*suffix\s*}/g, + suffix) +} diff --git a/uni_modules/uni-dateformat/components/uni-dateformat/uni-dateformat.vue b/uni_modules/uni-dateformat/components/uni-dateformat/uni-dateformat.vue new file mode 100644 index 0000000000000000000000000000000000000000..c5ed03078b25ab149f531e86385dfef4c9d7f4cc --- /dev/null +++ b/uni_modules/uni-dateformat/components/uni-dateformat/uni-dateformat.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/uni_modules/uni-dateformat/package.json b/uni_modules/uni-dateformat/package.json new file mode 100644 index 0000000000000000000000000000000000000000..64ec8cf13521394f42e6d954957c2b19dfc6ad1a --- /dev/null +++ b/uni_modules/uni-dateformat/package.json @@ -0,0 +1,84 @@ +{ + "id": "uni-dateformat", + "displayName": "uni-dateformat 日期格式化", + "version": "0.0.5", + "description": "日期格式化组件,可以将日期格式化为1分钟前、刚刚等形式", + "keywords": [ + "uni-ui", + "uniui", + "日期格式化", + "时间格式化", + "格式化时间", + "" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "y", + "联盟": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-dateformat/readme.md b/uni_modules/uni-dateformat/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..7d2da4aa033e718269d8d3d662ce98b4e788c234 --- /dev/null +++ b/uni_modules/uni-dateformat/readme.md @@ -0,0 +1,77 @@ + + +### DateFormat 日期格式化 +> **组件名:uni-dateformat** +> 代码块: `uDateformat` + + +日期格式化组件。 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +在 ``template`` 中使用组件 + +```html + + + + + +``` + +## API + +### Dateformat Props + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|date |Object|String|Number |Date.now() |要格式化的日期对象/日期字符串/时间戳 | +|threshold |Array |[0, 0] |转化类型阈值 | +|format |String |'yyyy/MM/dd hh:mm:ss' |格式字符串 | +|locale |String |zh |格式化使用的语言,目前支持zh(中文)、en(英文) | + + +#### Threshold Options + +格式化组件会对时间进行用户友好转化,threshold就是用来控制转化的时间阈值的。 + +以`[60000, 3600000]`为例,将传入时间与当前时间差的绝对值记为delta(单位毫秒) + +- `delta < 60000`时,时间会被转化为“刚刚|马上” +- `delta >= 60000 && delta < 3600000`时,时间会被转化为“xx分钟前|xx分钟后”,如果超过1小时会显示成“xx小时前|xx小时后”,以此类推 +- `delta >= 3600000`时,会按照format参数传入的格式进行格式化 + +如果不想转化为“马上|刚刚”可以传入`:threshold = "[0,3600000]"`。默认值`[0,0]`既不会转换为“马上|刚刚”也不会转化为“xx分钟前|xx分钟后” + +#### Format Options + +format接收字符以及含义如下: + +|字符 |说明 | +|:-: |:-: | +|yyyy |四位年份 | +|yy |两位年份 | +|MM |两位月份(不足两位在前面补0) | +|M |月份,不自动补0 | +|dd |两位天(不足两位在前面补0) | +|d |天,不自动补0 | +|hh |两位小时(不足两位在前面补0) | +|h |小时,不自动补0 | +|mm |两位分钟(不足两位在前面补0) | +|m |分钟,不自动补0 | +|ss |两位秒(不足两位在前面补0) | +|s |秒,不自动补0 | +|SSS |三位毫秒(不足三位在前面补0) | +|S |毫秒,不自动补0 | + + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/dateformat/dateformat](https://hellouniapp.dcloud.net.cn/pages/extUI/dateformat/dateformat) \ No newline at end of file diff --git a/uni_modules/uni-datetime-picker/changelog.md b/uni_modules/uni-datetime-picker/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..1011ada56a1c5b1cbfe3d1ac0e47f4539a185c90 --- /dev/null +++ b/uni_modules/uni-datetime-picker/changelog.md @@ -0,0 +1,65 @@ +## 2.1.1(2021-08-24) +- 新增 支持国际化 +- 优化 范围选择器在 pc 端过宽的问题 +## 2.1.0(2021-08-09) +- 新增 适配 vue3 +## 2.0.19(2021-08-09) +- 新增 支持作为 uni-forms 子组件相关功能 +- 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的 bug +## 2.0.18(2021-08-05) +- 修复 type 属性动态赋值无效的 bug +- 修复 ‘确认’按钮被 tabbar 遮盖 bug +- 修复 组件未赋值时范围选左、右日历相同的 bug +## 2.0.17(2021-08-04) +- 修复 范围选未正确显示当前值的 bug +- 修复 h5 平台(移动端)报错 'cale' of undefined 的 bug +## 2.0.16(2021-07-21) +- 新增 return-type 属性支持返回 date 日期对象 +## 2.0.15(2021-07-14) +- 修复 单选日期类型,初始赋值后不在当前日历的 bug +- 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效) +- 优化 移动端移除显示框的清空按钮,无实际用途 +## 2.0.14(2021-07-14) +- 修复 组件赋值为空,界面未更新的 bug +- 修复 start 和 end 不能动态赋值的 bug +- 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的 bug +## 2.0.13(2021-07-08) +- 修复 范围选择不能动态赋值的 bug +## 2.0.12(2021-07-08) +- 修复 范围选择的初始时间在一个月内时,造成无法选择的bug +## 2.0.11(2021-07-08) +- 优化 弹出层在超出视窗边缘定位不准确的问题 +## 2.0.10(2021-07-08) +- 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的 bug +- 优化 弹出层在超出视窗边缘被遮盖的问题 +## 2.0.9(2021-07-07) +- 新增 maskClick 事件 +- 修复 特殊情况日历 rpx 布局错误的 bug,rpx -> px +- 修复 范围选择时清空返回值不合理的bug,['', ''] -> [] +## 2.0.8(2021-07-07) +- 新增 日期时间显示框支持插槽 +## 2.0.7(2021-07-01) +- 优化 添加 uni-icons 依赖 +## 2.0.6(2021-05-22) +- 修复 图标在小程序上不显示的 bug +- 优化 重命名引用组件,避免潜在组件命名冲突 +## 2.0.5(2021-05-20) +- 优化 代码目录扁平化 +## 2.0.4(2021-05-12) +- 新增 组件示例地址 +## 2.0.3(2021-05-10) +- 修复 ios 下不识别 '-' 日期格式的 bug +- 优化 pc 下弹出层添加边框和阴影 +## 2.0.2(2021-05-08) +- 修复 在 admin 中获取弹出层定位错误的bug +## 2.0.1(2021-05-08) +- 修复 type 属性向下兼容,默认值从 date 变更为 datetime +## 2.0.0(2021-04-30) +- 支持日历形式的日期+时间的范围选择 + > 注意:此版本不向后兼容,不再支持单独时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker) +## 1.0.6(2021-03-18) +- 新增 hide-second 属性,时间支持仅选择时、分 +- 修复 选择跟显示的日期不一样的 bug +- 修复 chang事件触发2次的 bug +- 修复 分、秒 end 范围错误的 bug +- 优化 更好的 nvue 适配 diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..608dd7853dc016d241b0890cb6f19ee923ee9031 --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue @@ -0,0 +1,183 @@ + + + + + diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.js b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.js new file mode 100644 index 0000000000000000000000000000000000000000..b8d7d6fc44038901fe7a47def32b53764575c1a8 --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/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/uni-datetime-picker/components/uni-datetime-picker/calendar.vue b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue new file mode 100644 index 0000000000000000000000000000000000000000..73e949336b1b87beff08e43e119c45e14ca88442 --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue @@ -0,0 +1,801 @@ + + + + + diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json new file mode 100644 index 0000000000000000000000000000000000000000..cc76311be5be4de856623efaa10e42e7f7fa0b8d --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json @@ -0,0 +1,19 @@ +{ + "uni-datetime-picker.selectDate": "select date", + "uni-datetime-picker.selectTime": "select time", + "uni-datetime-picker.selectDateTime": "select datetime", + "uni-datetime-picker.startDate": "start date", + "uni-datetime-picker.endDate": "end date", + "uni-datetime-picker.startTime": "start time", + "uni-datetime-picker.endTime": "end time", + "uni-datetime-picker.ok": "ok", + "uni-datetime-picker.clear": "clear", + "uni-datetime-picker.cancel": "cancel", + "uni-calender.MON": "MON", + "uni-calender.TUE": "TUE", + "uni-calender.WED": "WED", + "uni-calender.THU": "THU", + "uni-calender.FRI": "FRI", + "uni-calender.SAT": "SAT", + "uni-calender.SUN": "SUN" +} diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js new file mode 100644 index 0000000000000000000000000000000000000000..de7509c87ba5197b9f5d20ca94a0558c7a8e08a9 --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json new file mode 100644 index 0000000000000000000000000000000000000000..7bc7405f553cff1a0a7f1ec4855747e2011b331c --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json @@ -0,0 +1,19 @@ +{ + "uni-datetime-picker.selectDate": "选择日期", + "uni-datetime-picker.selectTime": "选择时间", + "uni-datetime-picker.selectDateTime": "选择日期时间", + "uni-datetime-picker.startDate": "开始日期", + "uni-datetime-picker.endDate": "结束日期", + "uni-datetime-picker.startTime": "开始时间", + "uni-datetime-picker.endTime": "结束时间", + "uni-datetime-picker.ok": "确定", + "uni-datetime-picker.clear": "清除", + "uni-datetime-picker.cancel": "取消", + "uni-calender.SUN": "日", + "uni-calender.MON": "一", + "uni-calender.TUE": "二", + "uni-calender.WED": "三", + "uni-calender.THU": "四", + "uni-calender.FRI": "五", + "uni-calender.SAT": "六" +} diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json new file mode 100644 index 0000000000000000000000000000000000000000..7d370432ebf316bdd7bd1442fbefcf94169e6d09 --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json @@ -0,0 +1,19 @@ +{ + "uni-datetime-picker.selectDate": "選擇日期", + "uni-datetime-picker.selectTime": "選擇時間", + "uni-datetime-picker.selectDateTime": "選擇日期時間", + "uni-datetime-picker.startDate": "開始日期", + "uni-datetime-picker.endDate": "結束日期", + "uni-datetime-picker.startTime": "開始时间", + "uni-datetime-picker.endTime": "結束时间", + "uni-datetime-picker.ok": "確定", + "uni-datetime-picker.clear": "清除", + "uni-datetime-picker.cancel": "取消", + "uni-calender.SUN": "日", + "uni-calender.MON": "一", + "uni-calender.TUE": "二", + "uni-calender.WED": "三", + "uni-calender.THU": "四", + "uni-calender.FRI": "五", + "uni-calender.SAT": "六" +} diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/keypress.js b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/keypress.js new file mode 100644 index 0000000000000000000000000000000000000000..9601abae31babfc7ce5892085b07dab20ae355b0 --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/keypress.js @@ -0,0 +1,45 @@ +// #ifdef H5 +export default { + name: 'Keypress', + props: { + disable: { + type: Boolean, + default: false + } + }, + mounted () { + const keyNames = { + esc: ['Esc', 'Escape'], + tab: 'Tab', + enter: 'Enter', + space: [' ', 'Spacebar'], + up: ['Up', 'ArrowUp'], + left: ['Left', 'ArrowLeft'], + right: ['Right', 'ArrowRight'], + down: ['Down', 'ArrowDown'], + delete: ['Backspace', 'Delete', 'Del'] + } + const listener = ($event) => { + if (this.disable) { + return + } + const keyName = Object.keys(keyNames).find(key => { + const keyName = $event.key + const value = keyNames[key] + return value === keyName || (Array.isArray(value) && value.includes(keyName)) + }) + if (keyName) { + // 避免和其他按键事件冲突 + setTimeout(() => { + this.$emit(keyName, {}) + }, 0) + } + } + document.addEventListener('keyup', listener) + this.$once('hook:beforeDestroy', () => { + document.removeEventListener('keyup', listener) + }) + }, + render: () => {} +} +// #endif \ No newline at end of file diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue new file mode 100644 index 0000000000000000000000000000000000000000..1748de582172d5c1af138e8b16304e6056ddece5 --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue @@ -0,0 +1,924 @@ + + + + + diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue new file mode 100644 index 0000000000000000000000000000000000000000..7d75ebf4160c40dfc0a3b6ea6d167a6e733e7b47 --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue @@ -0,0 +1,951 @@ + + + + diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js new file mode 100644 index 0000000000000000000000000000000000000000..1f9f97793282d8e534352bb4a36047bd02caad00 --- /dev/null +++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js @@ -0,0 +1,408 @@ +import CALENDAR from './calendar.js' + +class Calendar { + constructor({ + date, + selected, + startDate, + endDate, + range, + // multipleStatus + } = {}) { + // 当前日期 + this.date = this.getDate(new Date()) // 当前初入日期 + // 打点信息 + this.selected = selected || []; + // 范围开始 + this.startDate = startDate + // 范围结束 + this.endDate = endDate + this.range = range + // 多选状态 + this.cleanMultipleStatus() + // 每周日期 + this.weeks = {} + // this._getWeek(this.date.fullDate) + // this.multipleStatus = multipleStatus + this.lastHover = false + } + /** + * 设置日期 + * @param {Object} date + */ + setDate(date) { + this.selectDate = this.getDate(date) + this._getWeek(this.selectDate.fullDate) + } + + /** + * 清理多选状态 + */ + cleanMultipleStatus() { + this.multipleStatus = { + before: '', + after: '', + data: [] + } + } + + /** + * 重置开始日期 + */ + resetSatrtDate(startDate) { + // 范围开始 + this.startDate = startDate + + } + + /** + * 重置结束日期 + */ + resetEndDate(endDate) { + // 范围结束 + this.endDate = endDate + } + + /** + * 获取任意时间 + */ + getDate(date, AddDayCount = 0, str = 'day') { + if (!date) { + date = new Date() + } + if (typeof date !== 'object') { + date = date.replace(/-/g, '/') + } + const dd = new Date(date) + switch (str) { + case 'day': + dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期 + break + case 'month': + if (dd.getDate() === 31) { + dd.setDate(dd.getDate() + AddDayCount) + } else { + dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期 + } + break + case 'year': + dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期 + break + } + const y = dd.getFullYear() + const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0 + const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0 + return { + fullDate: y + '-' + m + '-' + d, + year: y, + month: m, + date: d, + day: dd.getDay() + } + } + + + /** + * 获取上月剩余天数 + */ + _getLastMonthDays(firstDay, full) { + let dateArr = [] + for (let i = firstDay; i > 0; i--) { + const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate() + dateArr.push({ + date: beforeDate, + month: full.month - 1, + lunar: this.getlunar(full.year, full.month - 1, beforeDate), + disable: true + }) + } + return dateArr + } + /** + * 获取本月天数 + */ + _currentMonthDys(dateData, full) { + let dateArr = [] + let fullDate = this.date.fullDate + for (let i = 1; i <= dateData; i++) { + let isinfo = false + let nowDate = full.year + '-' + (full.month < 10 ? + full.month : full.month) + '-' + (i < 10 ? + '0' + i : i) + // 是否今天 + let isDay = fullDate === nowDate + // 获取打点信息 + let info = this.selected && this.selected.find((item) => { + if (this.dateEqual(nowDate, item.date)) { + return item + } + }) + + // 日期禁用 + let disableBefore = true + let disableAfter = true + if (this.startDate) { + // let dateCompBefore = this.dateCompare(this.startDate, fullDate) + // disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate) + disableBefore = this.dateCompare(this.startDate, nowDate) + } + + if (this.endDate) { + // let dateCompAfter = this.dateCompare(fullDate, this.endDate) + // disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate) + disableAfter = this.dateCompare(nowDate, this.endDate) + } + let multiples = this.multipleStatus.data + let checked = false + let multiplesStatus = -1 + if (this.range) { + if (multiples) { + multiplesStatus = multiples.findIndex((item) => { + return this.dateEqual(item, nowDate) + }) + } + if (multiplesStatus !== -1) { + checked = true + } + } + let data = { + fullDate: nowDate, + year: full.year, + date: i, + multiple: this.range ? checked : false, + beforeMultiple: this.dateEqual(this.multipleStatus.before, nowDate), + afterMultiple: this.dateEqual(this.multipleStatus.after, nowDate), + month: full.month, + lunar: this.getlunar(full.year, full.month, i), + disable: !(disableBefore && disableAfter), + isDay + } + if (info) { + data.extraInfo = info + } + + dateArr.push(data) + } + return dateArr + } + /** + * 获取下月天数 + */ + _getNextMonthDays(surplus, full) { + let dateArr = [] + for (let i = 1; i < surplus + 1; i++) { + dateArr.push({ + date: i, + month: Number(full.month) + 1, + lunar: this.getlunar(full.year, Number(full.month) + 1, i), + disable: true + }) + } + return dateArr + } + + /** + * 获取当前日期详情 + * @param {Object} date + */ + getInfo(date) { + if (!date) { + date = new Date() + } + const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate) + return dateInfo + } + + /** + * 比较时间大小 + */ + dateCompare(startDate, endDate) { + // 计算截止时间 + startDate = new Date(startDate.replace('-', '/').replace('-', '/')) + // 计算详细项的截止时间 + endDate = new Date(endDate.replace('-', '/').replace('-', '/')) + if (startDate <= endDate) { + return true + } else { + return false + } + } + + /** + * 比较时间是否相等 + */ + dateEqual(before, after) { + // 计算截止时间 + before = new Date(before.replace('-', '/').replace('-', '/')) + // 计算详细项的截止时间 + after = new Date(after.replace('-', '/').replace('-', '/')) + if (before.getTime() - after.getTime() === 0) { + return true + } else { + return false + } + } + + + /** + * 获取日期范围内所有日期 + * @param {Object} begin + * @param {Object} end + */ + geDateAll(begin, end) { + var arr = [] + var ab = begin.split('-') + var ae = end.split('-') + var db = new Date() + db.setFullYear(ab[0], ab[1] - 1, ab[2]) + var de = new Date() + de.setFullYear(ae[0], ae[1] - 1, ae[2]) + var unixDb = db.getTime() - 24 * 60 * 60 * 1000 + var unixDe = de.getTime() - 24 * 60 * 60 * 1000 + for (var k = unixDb; k <= unixDe;) { + k = k + 24 * 60 * 60 * 1000 + arr.push(this.getDate(new Date(parseInt(k))).fullDate) + } + return arr + } + /** + * 计算阴历日期显示 + */ + getlunar(year, month, date) { + return CALENDAR.solar2lunar(year, month, date) + } + /** + * 设置打点 + */ + setSelectInfo(data, value) { + this.selected = value + this._getWeek(data) + } + + /** + * 获取多选状态 + */ + setMultiple(fullDate) { + let { + before, + after + } = this.multipleStatus + + if (!this.range) return + if (before && after) { + if (!this.lastHover) { + this.lastHover = true + return + } + this.multipleStatus.before = '' + this.multipleStatus.after = '' + this.multipleStatus.data = [] + this.multipleStatus.fulldate = '' + this.lastHover = false + } else { + this.lastHover = false + if (!before) { + this.multipleStatus.before = fullDate + } else { + this.multipleStatus.after = fullDate + if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) { + this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus + .after); + } else { + this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus + .before); + } + } + } + this._getWeek(fullDate) + } + + /** + * 鼠标 hover 更新多选状态 + */ + setHoverMultiple(fullDate) { + let { + before, + after + } = this.multipleStatus + + if (!this.range) return + if (this.lastHover) return + + if (!before) { + this.multipleStatus.before = fullDate + } else { + this.multipleStatus.after = fullDate + if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) { + this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after); + } else { + this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before); + } + } + this._getWeek(fullDate) + } + + /** + * 更新默认值多选状态 + */ + setDefaultMultiple(before, after) { + this.multipleStatus.before = before + this.multipleStatus.after = after + if (before && after) { + if (this.dateCompare(before, after)) { + this.multipleStatus.data = this.geDateAll(before, after); + this._getWeek(after) + } else { + this.multipleStatus.data = this.geDateAll(after, before); + this._getWeek(before) + } + } + } + + /** + * 获取每周数据 + * @param {Object} dateData + */ + _getWeek(dateData) { + const { + fullDate, + year, + month, + date, + day + } = this.getDate(dateData) + let firstDay = new Date(year, month - 1, 1).getDay() + let currentDay = new Date(year, month, 0).getDate() + let dates = { + lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天 + currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数 + nextMonthDays: [], // 下个月开始几天 + weeks: [] + } + let canlender = [] + const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length) + dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData)) + canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays) + let weeks = {} + // 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天 + for (let i = 0; i < canlender.length; i++) { + if (i % 7 === 0) { + weeks[parseInt(i / 7)] = new Array(7) + } + weeks[parseInt(i / 7)][i % 7] = canlender[i] + } + this.canlender = canlender + this.weeks = weeks + } + + //静态方法 + // static init(date) { + // if (!this.instance) { + // this.instance = new Calendar(date); + // } + // return this.instance; + // } +} + + +export default Calendar diff --git a/uni_modules/uni-datetime-picker/package.json b/uni_modules/uni-datetime-picker/package.json new file mode 100644 index 0000000000000000000000000000000000000000..ad0b2b00587e0e7784c0698a980c97e5064b78f9 --- /dev/null +++ b/uni_modules/uni-datetime-picker/package.json @@ -0,0 +1,89 @@ +{ + "id": "uni-datetime-picker", + "displayName": "uni-datetime-picker 日期选择器", + "version": "2.1.1", + "description": "uni-datetime-picker 日期时间选择器,支持日历,支持范围选择", + "keywords": [ + "uni-datetime-picker", + "uni-ui", + "uniui", + "日期时间选择器", + "日期时间" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "n" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} diff --git a/uni_modules/uni-datetime-picker/readme.md b/uni_modules/uni-datetime-picker/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..1902ae1a60789e1398e5033b423603b40a2db860 --- /dev/null +++ b/uni_modules/uni-datetime-picker/readme.md @@ -0,0 +1,155 @@ + +> `重要通知:组件升级更新 2.0.0 后,支持日期+时间范围选择,组件 ui 将使用日历选择日期,ui 变化较大,同时支持 PC 和 移动端。此版本不向后兼容,不再支持单独的时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)。若仍需使用旧版本,可在插件市场下载*非uni_modules版本*,旧版本将不再维护` +## DatetimePicker 时间选择器 +> **组件名:uni-datetime-picker** +> 代码块: `uDatetimePicker` + + +该组件的优势是,支持**时间戳**输入和输出(起始时间、终止时间也支持时间戳),可**同时选择**日期和时间。 + +若只是需要单独选择日期和时间,不需要时间戳输入和输出,可使用原生的 picker 组件。 + + +___点击 picker 默认值规则:___ + +- 若设置初始值 value, 会显示在 picker 显示框中 +- 若无初始值 value,则初始值 value 为当前本地时间 Date.now(), 但不会显示在 picker 显示框中 + + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +在 ``template`` 中使用组件 + +```html + + + + + +``` + +## API + +### DatetimePicker Props + +|属性名 |类型 |默认值 |值域 |说明 | +|:-: |:-: |:-: | |:-: | +|type |String |datetime |date/daterange/datetime/datetimerange|选择器类型 | +|value |String、Number、Array(范围选择)、Date|- |- |输入框当前值 | +|start |String、Number |- |- |最小值,可以使用日期的字符串(String)、时间戳(Number) | +|end |String、Number |- |- |最大值,可以使用日期的字符串(String)、时间戳(Number) | +|return-type |String |string |timestamp 、string、date |返回值格式 | +|border |Boolean、String |true | |是否有边框 | +|rangeSeparator |String |'-' |- |选择范围时的分隔符 | +|placeholder |String |- |- |非范围选择时的占位内容 | +|start-placeholder|String |- |- |范围选择时开始日期的占位内容 | +|end-placeholder |String |- |- |范围选择时结束日期的占位内容 | +|disabled |Boolean、String |false | |是否不可选择 | +|clearIcon |Boolean、String |true | |是否显示清除按钮(仅PC端适用) | + + + + +### DatetimePicker Events + +|事件名称 |说明 |返回值 | +|:-: |:-: |:-: | +|change |确定日期时间时触发的事件,参数为当前选择的日期对象 |单选返回日期字符串,如:'2010-02-3';范围选返回日期字符串数组,如:['2020-10-1', '2021-4-1'] | +|maskClick|点击遮罩层触发 |- | + +### Popup Methods + +|方法称名 |说明|参数| +|:-:|:-:|:-:| +|show|打开弹出层|-| +|close|关闭弹出层 |-| + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/datetime-picker/datetime-picker](https://hellouniapp.dcloud.net.cn/pages/extUI/datetime-picker/datetime-picker) \ No newline at end of file diff --git a/uni_modules/uni-drawer/changelog.md b/uni_modules/uni-drawer/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..c8eed01969f6b14644b452e0c3b28f7ad2ecd3ff --- /dev/null +++ b/uni_modules/uni-drawer/changelog.md @@ -0,0 +1,8 @@ +## 1.1.1(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-05-12) +- 新增 组件示例地址 +## 1.0.6(2021-02-04) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-drawer/components/uni-drawer/keypress.js b/uni_modules/uni-drawer/components/uni-drawer/keypress.js new file mode 100644 index 0000000000000000000000000000000000000000..62dda461bd5d5923a173e541f2cf049d816d612b --- /dev/null +++ b/uni_modules/uni-drawer/components/uni-drawer/keypress.js @@ -0,0 +1,45 @@ +// #ifdef H5 +export default { + name: 'Keypress', + props: { + disable: { + type: Boolean, + default: false + } + }, + mounted () { + const keyNames = { + esc: ['Esc', 'Escape'], + tab: 'Tab', + enter: 'Enter', + space: [' ', 'Spacebar'], + up: ['Up', 'ArrowUp'], + left: ['Left', 'ArrowLeft'], + right: ['Right', 'ArrowRight'], + down: ['Down', 'ArrowDown'], + delete: ['Backspace', 'Delete', 'Del'] + } + const listener = ($event) => { + if (this.disable) { + return + } + const keyName = Object.keys(keyNames).find(key => { + const keyName = $event.key + const value = keyNames[key] + return value === keyName || (Array.isArray(value) && value.includes(keyName)) + }) + if (keyName) { + // 避免和其他按键事件冲突 + setTimeout(() => { + this.$emit(keyName, {}) + }, 0) + } + } + document.addEventListener('keyup', listener) + // this.$once('hook:beforeDestroy', () => { + // document.removeEventListener('keyup', listener) + // }) + }, + render: () => {} +} +// #endif diff --git a/uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue b/uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue new file mode 100644 index 0000000000000000000000000000000000000000..c3cb00bb43e7a7025526b825bb3794b874a44653 --- /dev/null +++ b/uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue @@ -0,0 +1,182 @@ + + + + + diff --git a/uni_modules/uni-drawer/package.json b/uni_modules/uni-drawer/package.json new file mode 100644 index 0000000000000000000000000000000000000000..0fad43f6303f91845bcbad3d97e72c9018c1c79d --- /dev/null +++ b/uni_modules/uni-drawer/package.json @@ -0,0 +1,83 @@ +{ + "id": "uni-drawer", + "displayName": "uni-drawer 抽屉", + "version": "1.1.1", + "description": "抽屉式导航,用于展示侧滑菜单,侧滑导航。", + "keywords": [ + "uni-ui", + "uniui", + "drawer", + "抽屉", + "侧滑导航" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-drawer/readme.md b/uni_modules/uni-drawer/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..d4a8b15bbb2c60d89d18ac2a0cea09e87fc161c7 --- /dev/null +++ b/uni_modules/uni-drawer/readme.md @@ -0,0 +1,86 @@ + + +## Drawer 抽屉 +> **组件名:uni-drawer** +> 代码块: `uDrawer` + + +抽屉侧滑菜单。 + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - `width` 属性仅在 `vue` 页面生效,`nvue` 页面因性能问题,不支持动态设置宽度,如需修改,请下载组件修改源码 + + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + + +在 ``template`` 中使用组件 + +```html + + + { + timer = null; + }, wait); + if (callNow) func.apply(context, args); + } else { + timer = setTimeout(() => { + func.apply(context, args); + }, wait) + } + } +} +/** + * @desc 函数节流 + * @param func 函数 + * @param wait 延迟执行毫秒数 + * @param type 1 使用表时间戳,在时间段开始的时候触发 2 使用表定时器,在时间段结束的时候触发 + */ +export const throttle = (func, wait = 1000, type = 1) => { + let previous = 0; + let timeout; + return function() { + let context = this; + let args = arguments; + if (type === 1) { + let now = Date.now(); + + if (now - previous > wait) { + func.apply(context, args); + previous = now; + } + } else if (type === 2) { + if (!timeout) { + timeout = setTimeout(() => { + timeout = null; + func.apply(context, args) + }, wait) + } + } + } +} diff --git a/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue b/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue new file mode 100644 index 0000000000000000000000000000000000000000..16911f247bbe9c1437682587ffba6006ebe20653 --- /dev/null +++ b/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue @@ -0,0 +1,461 @@ + + + + + diff --git a/uni_modules/uni-easyinput/package.json b/uni_modules/uni-easyinput/package.json new file mode 100644 index 0000000000000000000000000000000000000000..cb436acb9391929557096a95741ecfba9f3d1956 --- /dev/null +++ b/uni_modules/uni-easyinput/package.json @@ -0,0 +1,89 @@ +{ + "id": "uni-easyinput", + "displayName": "uni-easyinput 增强输入框", + "version": "0.1.4", + "description": "Easyinput 组件是对原生input组件的增强", + "keywords": [ + "uni-ui", + "uniui", + "input", + "uni-easyinput", + "输入框" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-easyinput/readme.md b/uni_modules/uni-easyinput/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..ab6c50bf4107aff9cff2e01c44e1f89b949657e1 --- /dev/null +++ b/uni_modules/uni-easyinput/readme.md @@ -0,0 +1,198 @@ + + +### Easyinput 增强输入框 +> **组件名:uni-easyinput** +> 代码块: `uEasyinput` + + +easyinput 组件是对原生input组件的增强 ,是专门为配合表单组件[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)而设计的,easyinput 内置了边框,图标等,同时包含 input 所有功能 + + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - 组件需要依赖 `sass` 插件 ,请自行手动安装 +> - 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + +### 平台差异说明 + +暂不支持在nvue页面中使用 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +输入内容后,输入框尾部会显示清除按钮,点击可清除内容,如不需要展示图标,`clearable` 属性设置为 `false` 即可 + +`clearable` 属性设置为 `true` ,输入框聚焦且内容不为空时,才会显示内容 + +```html + +``` + + +### 输入框带左右图标 + +设置 `prefixIcon` 属性来显示输入框的头部图标 + +设置 `suffixIcon` 属性来显示输入框的尾部图标 + +注意图标当前只支持 `uni-icons` 内置的图标,当配置 `suffixIcon` 属性后,会覆盖 `:clearable="true"` 和 `type="password"` 时的原有图标 + +绑定 `@iconClick` 事件可以触发图标的点击 ,返回 `prefix` 表示点击左侧图标,返回 `suffix` 表示点击右侧图标 + +```html + + + + + +``` + +### 输入框禁用 + +设置 `disable` 属性可以禁用输入框,此时输入框不可编辑 + +```html + +``` + +### 密码框 + +设置 `type="password"` 时,输入框内容将会不可见,由实心点代替,同时输入框尾部会显示眼睛图标,点击可切换内容显示状态 + +```html + +``` + +### 输入框聚焦 + +设置 `focus` 属性可以使输入框聚焦 + +如果页面存在多个设置 `focus` 属性的输入框,只有最后一个输入框的 `focus` 属性会生效 + +```html + +``` + + +### 多行文本 + +设置 `type="textarea"` 时可输入多行文本 + +```html + +``` + +### 多行文本自动高度 + +设置 `type="textarea"` 时且设置 `autoHeight` 属性,可使用多行文本的自动高度,会跟随内容调整输入框的显示高度 + +```html + +``` + +### 取消边框 + +设置 `:inputBorder="false"` 时可取消输入框的边框显示,同时搭配 `uni-forms` 的 `:border="true"` 有较好的效果 + +```html + + + + + + + + +``` + + +## API + +### Easyinput Props + +|属性名| 类型| 可选值|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|value|String/ Number|-|-|输入内容| +|type|String|见 type Options|text|输入框的类型(默认text)| +|clearable|Boolean|-|true| 是否显示右侧清空内容的图标控件(输入框有内容且不禁用时显示),点击可清空输入框内容| +|autoHeight|Boolean|-|false| 是否自动增高输入区域,type为textarea时有效| +|placeholder|String |-| - | 输入框的提示文字| +|placeholderStyle|String| - | - | placeholder的样式(内联样式,字符串),如"color: #ddd"| +|focus|Boolean|-|false|是否自动获得焦点| +|disabled|Boolean|-|false|是否不可输入| +|maxlength|Number|-|140|最大输入长度,设置为 -1 的时候不限制最大长度| +|confirmType|String|-|done|设置键盘右下角按钮的文字,仅在type="text"时生效| +|clearSize|Number|-|15|清除图标的大小,单位px| +|prefixIcon|String|-|-|输入框头部图标 | +|suffixIcon|String|-|-|输入框尾部图标| +|trim|Boolean/String|见 trim Options | false | 是否自动去除空格,传入类型为 Boolean 时,自动去除前后空格| +|inputBorder|Boolean|-|true|是否显示input输入框的边框| +|styles|Object|-|-| 样式自定义| +|passwordIcon|Boolean|-| true | type=password 时,是否显示小眼睛图标| + + +#### Type Options + +|属性名| 说明| +|:-:| :-:| +|text|文本输入键盘| +|textarea |多行文本输入键盘| +|password |密码输入键盘| +|number|数字输入键盘,注意iOS上app-vue弹出的数字键盘并非9宫格方式 | +|idcard|身份证输入键盘,仅支持微信、支付宝、百度、QQ小程序| +|digit|带小数点的数字键盘,仅支持微信、支付宝、百度、头条、QQ小程序 | + +#### ConfirmType Options + +平台差异与 [input](https://uniapp.dcloud.io/component/input) 相同 + +|属性名 | 说明| +|:-:| :-:| +|send|右下角按钮为“发送” | +|search |右下角按钮为“搜索” | +|next|右下角按钮为“下一个”| +|go|右下角按钮为“前往” | +|done|右下角按钮为“完成” | + + +#### Styles Options + +|属性名| 默认值 |说明| +|:-:| :-:| :-:| +|color| #333| 输入文字颜色| +|disableColor |#eee| 输入框禁用背景色| +|borderColor |#e5e5e5 | 边框颜色| + +#### Trim Options + +传入类型为 `Boolean` 时,自动去除前后空格,传入类型为 `String` 时,可以单独控制,下面是可选值 + +|属性名|说明| +|:-:| :-:| +|both|去除两端空格| +|left|去除左侧空格| +|right|去除右侧空格| +|all|去除所有空格| +|none|不去除空格| + + +### Easyinput Events + +|事件称名| 说明|返回值| +|:-:| :-:|:-:| +|@input|输入框内容发生变化时触发| -| +|@focus|输入框获得焦点时触发| -| +|@blur|输入框失去焦点时触发| -| +|@confirm|点击完成按钮时触发| -| +|@iconClick |点击图标时触发| prefix/suffix | + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/easyinput/easyinput](https://hellouniapp.dcloud.net.cn/pages/extUI/easyinput/easyinput) \ No newline at end of file diff --git a/uni_modules/uni-fab/changelog.md b/uni_modules/uni-fab/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..cbf69d6a572103f2046e3c25adabe0b353b74fd0 --- /dev/null +++ b/uni_modules/uni-fab/changelog.md @@ -0,0 +1,8 @@ +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-05-12) +- 新增 组件示例地址 +## 1.0.6(2021-02-05) +- 调整为uni_modules目录规范 +- 优化 按钮背景色调整 +- 优化 兼容pc端 diff --git a/uni_modules/uni-fab/components/uni-fab/uni-fab.vue b/uni_modules/uni-fab/components/uni-fab/uni-fab.vue new file mode 100644 index 0000000000000000000000000000000000000000..0afab66ee8185475dc2618e8db85175b5a3a856c --- /dev/null +++ b/uni_modules/uni-fab/components/uni-fab/uni-fab.vue @@ -0,0 +1,448 @@ + + + + + diff --git a/uni_modules/uni-fab/components/uni-fab/uni-fab.vue.bak b/uni_modules/uni-fab/components/uni-fab/uni-fab.vue.bak new file mode 100644 index 0000000000000000000000000000000000000000..9df4dcd7f5958b17b962e9d37c52c298d549ad00 --- /dev/null +++ b/uni_modules/uni-fab/components/uni-fab/uni-fab.vue.bak @@ -0,0 +1,383 @@ + + + + + diff --git a/uni_modules/uni-fab/package.json b/uni_modules/uni-fab/package.json new file mode 100644 index 0000000000000000000000000000000000000000..84cb81408a8e9439ae57447dad27aa6bab8e4ad6 --- /dev/null +++ b/uni_modules/uni-fab/package.json @@ -0,0 +1,83 @@ +{ + "id": "uni-fab", + "displayName": "uni-fab 悬浮按钮", + "version": "1.1.0", + "description": "悬浮按钮 fab button ,点击可展开一个图标按钮菜单。", + "keywords": [ + "uni-ui", + "uniui", + "按钮", + "悬浮按钮", + "fab" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} diff --git a/uni_modules/uni-fab/readme.md b/uni_modules/uni-fab/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..0ed6ce815e18c9df82034c4318051ab34ce72902 --- /dev/null +++ b/uni_modules/uni-fab/readme.md @@ -0,0 +1,91 @@ + + +## Fab 悬浮按钮 +> **组件名:uni-fab** +> 代码块: `uFab` + + +点击可展开一个图形按钮菜单 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - 不建议动态修改属性,可能会耗损部分性能。 +> - 展开菜单暂不支持字体图标,使用图片路径时建议使用绝对路径,相对路径可能会有问题。 +> - 选中状态要通过自己控制,如果不希望有选中状态,不处理 `active` 即可。 +> - 展开菜单建议最多显示四个,如果过多对于小屏手机可能会超出屏幕。 + + +### 基本用法 + +在 `template` 中使用组件 + +```html + +``` + + +## API + +### Fab Props + +| 属性名 | 类型 | 默认值 | 说明 | +| :-: | :-: | :-: | :-: | +| pattern | Object | - | 可选样式配置项 | +| horizontal| String | 'left' | 水平对齐方式。`left`:左对齐,`right`:右对齐 | +| vertical | String | 'bottom' | 垂直对齐方式。`bottom`:下对齐,`top`:上对齐 | +| direction | String | 'horizontal' | 展开菜单显示方式。`horizontal`:水平显示,`vertical`:垂直显示 | +| popMenu | Boolean | true | 是否使用弹出菜单 | +| content | Array | - | 展开菜单内容配置项 | + + + +**pattern配置项:** + +| 参数 | 类型 | 默认值 | 说明 | +| :-: | :-: | :-: | :-: | +| color | String | #3c3e49 | 文字默认颜色 | +| selectedColor | String | #007AFF | 文字选中时的颜色 | +| backgroundColor | String | #ffffff | 背景色 | +| buttonColor | String | #3c3e49 | 按钮背景色 | + +**content配置项:** + +| 参数 | 类型 | 说明 | +| :-: | :-: | :-: | :-: | +| iconPath | String | 图片路径 | +| selectedIconPath | String | 选中后图片路径| +| text | String | 文字 | +| active | Boolean | 是否选中当前 | + +### Fab Events + +| 参数 | 类型 | 说明 | +| :-: | :-: | :-: | +| @trigger | Function | 展开菜单点击事件,返回点击信息| +| @fabClick | Function | 悬浮按钮点击事件 | + + + + + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/fab/fab](https://hellouniapp.dcloud.net.cn/pages/extUI/fab/fab) \ No newline at end of file diff --git a/uni_modules/uni-fav/changelog.md b/uni_modules/uni-fav/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..f35b81748e84866247f8d42fd67e10fbf59584da --- /dev/null +++ b/uni_modules/uni-fav/changelog.md @@ -0,0 +1,14 @@ +## 1.1.1(2021-08-24) +- 新增 支持国际化 +## 1.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.6(2021-05-12) +- 新增 组件示例地址 +## 1.0.5(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.4(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 1.0.3(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 1.0.2(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-fav/components/uni-fav/i18n/en.json b/uni_modules/uni-fav/components/uni-fav/i18n/en.json new file mode 100644 index 0000000000000000000000000000000000000000..9a0759e0214562df0a0e69aca7760e288b68366b --- /dev/null +++ b/uni_modules/uni-fav/components/uni-fav/i18n/en.json @@ -0,0 +1,4 @@ +{ + "uni-fav.collect": "collect", + "uni-fav.collected": "collected" +} diff --git a/uni_modules/uni-fav/components/uni-fav/i18n/index.js b/uni_modules/uni-fav/components/uni-fav/i18n/index.js new file mode 100644 index 0000000000000000000000000000000000000000..de7509c87ba5197b9f5d20ca94a0558c7a8e08a9 --- /dev/null +++ b/uni_modules/uni-fav/components/uni-fav/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json b/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json new file mode 100644 index 0000000000000000000000000000000000000000..67c89bfc7e5b57a51a04aa655d39c895325e8308 --- /dev/null +++ b/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json @@ -0,0 +1,4 @@ +{ + "uni-fav.collect": "收藏", + "uni-fav.collected": "已收藏" +} diff --git a/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json b/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json new file mode 100644 index 0000000000000000000000000000000000000000..67c89bfc7e5b57a51a04aa655d39c895325e8308 --- /dev/null +++ b/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json @@ -0,0 +1,4 @@ +{ + "uni-fav.collect": "收藏", + "uni-fav.collected": "已收藏" +} diff --git a/uni_modules/uni-fav/components/uni-fav/uni-fav.vue b/uni_modules/uni-fav/components/uni-fav/uni-fav.vue new file mode 100644 index 0000000000000000000000000000000000000000..d82864bb9249847d6fe68600dd5d96fb1512cc5a --- /dev/null +++ b/uni_modules/uni-fav/components/uni-fav/uni-fav.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/uni_modules/uni-fav/package.json b/uni_modules/uni-fav/package.json new file mode 100644 index 0000000000000000000000000000000000000000..0556086716f4f1f4035bdfdb781d0ed1d5712939 --- /dev/null +++ b/uni_modules/uni-fav/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-fav", + "displayName": "uni-fav 收藏按钮", + "version": "1.1.1", + "description": " Fav 收藏组件,可自定义颜色、大小。", + "keywords": [ + "fav", + "uni-ui", + "uniui", + "收藏" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} diff --git a/uni_modules/uni-fav/readme.md b/uni_modules/uni-fav/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..83a01fca35e333c3f942680915632fa0c99a5a8b --- /dev/null +++ b/uni_modules/uni-fav/readme.md @@ -0,0 +1,50 @@ + + +## Fav 收藏按钮 +> **组件名:uni-fav** +> 代码块: `uFav` + + +用于收藏功能,可点击切换选中、不选中的状态。 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +在 ``template`` 中使用组件 + +```html + + +``` + +## API + +### Fav Props + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|star |Boolean|true |按钮是否带星星 | +|bgColor |String |#eeeeee |未收藏时的背景色 | +|bgColorChecked |String |#007aff |已收藏时的背景色 | +|fgColor |String |#666666 |未收藏时的文字颜色 | +|fgColorChecked |String |#FFFFFF |已收藏时的文字颜色 | +|circle |Boolean|false |是否为圆角 | +|checked |Boolean|false |是否为已收藏 | +|contentText |Object |```{contentDefault: '收藏',contentFav: '已收藏'}```|收藏按钮文字 | + + +### Fav Events + +|事件称名 |说明 |返回值 | +|:-: |:-: |:-: | +|click |点击 fav按钮 触发事件 |- | + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/fav/fav](https://hellouniapp.dcloud.net.cn/pages/extUI/fav/fav) \ No newline at end of file diff --git a/uni_modules/uni-feedback/changelog.md b/uni_modules/uni-feedback/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..57c13b89d8bcfe95c907caaef24304a6624980bf --- /dev/null +++ b/uni_modules/uni-feedback/changelog.md @@ -0,0 +1,2 @@ +## 1.0.3(2021-08-26) +删除多余的云函数test2 diff --git a/uni_modules/uni-feedback/js_sdk/validator/opendb-feedback.js b/uni_modules/uni-feedback/js_sdk/validator/opendb-feedback.js new file mode 100644 index 0000000000000000000000000000000000000000..be3d330f45a52e0f78e632809f57e20f9c5c5b88 --- /dev/null +++ b/uni_modules/uni-feedback/js_sdk/validator/opendb-feedback.js @@ -0,0 +1,98 @@ +// 表单校验规则由 schema2code 生成,不建议直接修改校验规则,而建议通过 schema2code 生成, 详情: https://uniapp.dcloud.net.cn/uniCloud/schema + + +const validator = { + "content": { + "rules": [ + { + "required": true + }, + { + "format": "string" + } + ], + "label": "留言内容/回复内容" + }, + "imgs": { + "rules": [ + { + "format": "array" + }, + { + "arrayType": "file" + }, + { + "maxLength": 6 + } + ], + "label": "图片列表" + }, + "contact": { + "rules": [ + { + "format": "string" + } + ], + "label": "联系人" + }, + "mobile": { + "rules": [ + { + "format": "string" + }, + { + "pattern": "^\\+?[0-9-]{3,20}$" + } + ], + "label": "联系电话" + } +} + +const enumConverter = {} + +function filterToWhere(filter, command) { + let where = {} + for (let field in filter) { + let { type, value } = filter[field] + switch (type) { + case "search": + if (typeof value === 'string' && value.length) { + where[field] = new RegExp(value) + } + break; + case "select": + if (value.length) { + let selectValue = [] + for (let s of value) { + selectValue.push(command.eq(s)) + } + where[field] = command.or(selectValue) + } + break; + case "range": + if (value.length) { + let gt = value[0] + let lt = value[1] + where[field] = command.and([command.gte(gt), command.lte(lt)]) + } + break; + case "date": + if (value.length) { + let [s, e] = value + let startDate = new Date(s) + let endDate = new Date(e) + where[field] = command.and([command.gte(startDate), command.lte(endDate)]) + } + break; + case "timestamp": + if (value.length) { + let [startDate, endDate] = value + where[field] = command.and([command.gte(startDate), command.lte(endDate)]) + } + break; + } + } + return where +} + +export { validator, enumConverter, filterToWhere } diff --git a/uni_modules/uni-feedback/package.json b/uni_modules/uni-feedback/package.json new file mode 100644 index 0000000000000000000000000000000000000000..fb20e7a1bfc276d8de38e66bc21912e430295d7c --- /dev/null +++ b/uni_modules/uni-feedback/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-feedback", + "displayName": "问题反馈用户端页面模板", + "version": "1.0.3", + "description": "问题反馈用户端页面模板,方便开发者快速搭建问题反馈界面", + "keywords": [ + "问题反馈用户端页面模板" +], + "repository": "", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "uniCloud", + "云端一体页面模板" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [ + "uni-forms", + "uni-file-picker", + "uni-easyinput", + "uni-load-more", + "uni-list", + "uni-fab", + "uni-link" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "u", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-feedback/pages/opendb-feedback/detail.vue b/uni_modules/uni-feedback/pages/opendb-feedback/detail.vue new file mode 100644 index 0000000000000000000000000000000000000000..fa28fca65965679627691445b2bd86e43d7a62da --- /dev/null +++ b/uni_modules/uni-feedback/pages/opendb-feedback/detail.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/uni_modules/uni-feedback/pages/opendb-feedback/edit.vue b/uni_modules/uni-feedback/pages/opendb-feedback/edit.vue new file mode 100644 index 0000000000000000000000000000000000000000..bd65125da9dcde706bb90f4ccb80ca2e3ce472d7 --- /dev/null +++ b/uni_modules/uni-feedback/pages/opendb-feedback/edit.vue @@ -0,0 +1,167 @@ + + + + + diff --git a/uni_modules/uni-feedback/pages/opendb-feedback/list.vue b/uni_modules/uni-feedback/pages/opendb-feedback/list.vue new file mode 100644 index 0000000000000000000000000000000000000000..d075eb33cae8394539d0d8b61dd368d26fadec2f --- /dev/null +++ b/uni_modules/uni-feedback/pages/opendb-feedback/list.vue @@ -0,0 +1,70 @@ + + + + + diff --git a/uni_modules/uni-feedback/pages/opendb-feedback/opendb-feedback.vue b/uni_modules/uni-feedback/pages/opendb-feedback/opendb-feedback.vue new file mode 100644 index 0000000000000000000000000000000000000000..1978c7214009b4b7a6077e78134452ed279d8c14 --- /dev/null +++ b/uni_modules/uni-feedback/pages/opendb-feedback/opendb-feedback.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/uni_modules/uni-feedback/readme.md b/uni_modules/uni-feedback/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..aa0ede5e64cc25f1fab0f667d2e2bf5522571d01 --- /dev/null +++ b/uni_modules/uni-feedback/readme.md @@ -0,0 +1,2 @@ +这是一个问题反馈客户端插件,admin端插件:[https://ext.dcloud.net.cn/plugin?id=4992](https://ext.dcloud.net.cn/plugin?id=4992) +> 参考案例 [uni-starter](https://ext.dcloud.net.cn/plugin?id=5057) \ No newline at end of file diff --git a/uni_modules/uni-feedback/uniCloud/database/opendb-feedback.schema.json b/uni_modules/uni-feedback/uniCloud/database/opendb-feedback.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..e13d8742204b53ad3632925152228e28de4354e6 --- /dev/null +++ b/uni_modules/uni-feedback/uniCloud/database/opendb-feedback.schema.json @@ -0,0 +1,72 @@ +{ + "bsonType": "object", + "required": ["content"], + "permission": { + "create": "auth.uid != null", + "read": true, + "delete": false, + "update": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "user_id": { + "bsonType": "string", + "description": "留言反馈用户ID\/回复留言用户ID,参考uni-id-users表", + "foreignKey": "uni-id-users._id", + "forceDefaultValue": { + "$env": "uid" + } + }, + "create_date": { + "bsonType": "timestamp", + "title": "留言时间\/回复留言时间", + "forceDefaultValue": { + "$env": "now" + } + }, + "content": { + "bsonType": "string", + "title": "留言内容\/回复内容", + "componentForEdit": { + "name": "textarea" + }, + "trim": "right" + }, + "imgs": { + "bsonType": "array", + "arrayType": "file", + "maxLength": 6, + "fileMediaType": "image", + "title": "图片列表" + }, + "is_reply": { + "bsonType": "bool", + "title": "是否是回复类型" + }, + "feedback_id": { + "bsonType": "string", + "title": "被回复留言ID" + }, + "contact": { + "bsonType": "string", + "title": "联系人", + "trim": "both" + }, + "mobile": { + "bsonType": "string", + "title": "联系电话", + "pattern": "^\\+?[0-9-]{3,20}$", + "trim": "both" + }, + "reply_count": { + "permission": { + "write": false + }, + "bsonType": "int", + "title": "被回复条数", + "defaultValue": 0 + } + } +} diff --git a/uni_modules/uni-file-picker/changelog.md b/uni_modules/uni-file-picker/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..df26c52345e85eea3500e547715ac26b25ed9fc9 --- /dev/null +++ b/uni_modules/uni-file-picker/changelog.md @@ -0,0 +1,52 @@ +## 0.2.14(2021-08-23) +- 新增 参数中返回 fileID 字段 +## 0.2.13(2021-08-23) +- 修复 腾讯云传入fileID 不能回显的bug +- 修复 选择图片后,不能放大的问题 +## 0.2.12(2021-08-17) +- 修复 由于 0.2.11 版本引起的不能回显图片的Bug +## 0.2.11(2021-08-16) +- 新增 clearFiles(index) 方法,可以手动删除指定文件 +- 修复 v-model 值设为 null 报错的Bug +## 0.2.10(2021-08-13) +- 修复 return-type="object" 时,无法删除文件的Bug +## 0.2.9(2021-08-03) +- 修复 auto-upload 属性失效的Bug +## 0.2.8(2021-07-31) +- 修复 fileExtname属性不指定值报错的Bug +## 0.2.7(2021-07-31) +- 修复 在某种场景下图片不回显的Bug +## 0.2.6(2021-07-30) +- 修复 return-type为object下,返回值不正确的Bug +## 0.2.5(2021-07-30) +- 修复(重要) H5 平台下如果和uni-forms组件一同使用导致页面卡死的问题 +## 0.2.3(2021-07-28) +- 优化 调整示例代码 +## 0.2.2(2021-07-27) +- 修复 vue3 下赋值错误的Bug +- 优化 h5平台下上传文件导致页面卡死的问题 +## 0.2.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 0.1.1(2021-07-02) +- 修复 sourceType 缺少默认值导致 ios 无法选择文件 +## 0.1.0(2021-06-30) +- 优化 解耦与uniCloud的强绑定关系 ,如不绑定服务空间,默认autoUpload为false且不可更改 +## 0.0.11(2021-06-30) +- 修复 由 0.0.10 版本引发的 returnType 属性失效的问题 +## 0.0.10(2021-06-29) +- 优化 文件上传后进度条消失时机 +## 0.0.9(2021-06-29) +- 修复 在uni-forms 中,删除文件 ,获取的值不对的Bug +## 0.0.8(2021-06-15) +- 修复 删除文件时无法触发 v-model 的Bug +## 0.0.7(2021-05-12) +- 新增 组件示例地址 +## 0.0.6(2021-04-09) +- 修复 选择的文件非 file-extname 字段指定的扩展名报错的Bug +## 0.0.5(2021-04-09) +- 优化 更新组件示例 +## 0.0.4(2021-04-09) +- 优化 file-extname 字段支持字符串写法,多个扩展名需要用逗号分隔 +## 0.0.3(2021-02-05) +- 调整为uni_modules目录规范 +- 修复 微信小程序不指定 fileExtname 属性选择失败的Bug diff --git a/uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js b/uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js new file mode 100644 index 0000000000000000000000000000000000000000..24a07f5787f23688cb233546644c28ee0ca7ee29 --- /dev/null +++ b/uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js @@ -0,0 +1,224 @@ +'use strict'; + +const ERR_MSG_OK = 'chooseAndUploadFile:ok'; +const ERR_MSG_FAIL = 'chooseAndUploadFile:fail'; + +function chooseImage(opts) { + const { + count, + sizeType = ['original', 'compressed'], + sourceType = ['album', 'camera'], + extension + } = opts + return new Promise((resolve, reject) => { + uni.chooseImage({ + count, + sizeType, + sourceType, + extension, + success(res) { + resolve(normalizeChooseAndUploadFileRes(res, 'image')); + }, + fail(res) { + reject({ + errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL), + }); + }, + }); + }); +} + +function chooseVideo(opts) { + const { + camera, + compressed, + maxDuration, + sourceType = ['album', 'camera'], + extension + } = opts; + return new Promise((resolve, reject) => { + uni.chooseVideo({ + camera, + compressed, + maxDuration, + sourceType, + extension, + success(res) { + const { + tempFilePath, + duration, + size, + height, + width + } = res; + resolve(normalizeChooseAndUploadFileRes({ + errMsg: 'chooseVideo:ok', + tempFilePaths: [tempFilePath], + tempFiles: [ + { + name: (res.tempFile && res.tempFile.name) || '', + path: tempFilePath, + size, + type: (res.tempFile && res.tempFile.type) || '', + width, + height, + duration, + fileType: 'video', + cloudPath: '', + }, ], + }, 'video')); + }, + fail(res) { + reject({ + errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL), + }); + }, + }); + }); +} + +function chooseAll(opts) { + const { + count, + extension + } = opts; + return new Promise((resolve, reject) => { + let chooseFile = uni.chooseFile; + if (typeof wx !== 'undefined' && + typeof wx.chooseMessageFile === 'function') { + chooseFile = wx.chooseMessageFile; + } + if (typeof chooseFile !== 'function') { + return reject({ + errMsg: ERR_MSG_FAIL + ' 请指定 type 类型,该平台仅支持选择 image 或 video。', + }); + } + chooseFile({ + type: 'all', + count, + extension, + success(res) { + resolve(normalizeChooseAndUploadFileRes(res)); + }, + fail(res) { + reject({ + errMsg: res.errMsg.replace('chooseFile:fail', ERR_MSG_FAIL), + }); + }, + }); + }); +} + +function normalizeChooseAndUploadFileRes(res, fileType) { + res.tempFiles.forEach((item, index) => { + if (!item.name) { + item.name = item.path.substring(item.path.lastIndexOf('/') + 1); + } + if (fileType) { + item.fileType = fileType; + } + item.cloudPath = + Date.now() + '_' + index + item.name.substring(item.name.lastIndexOf('.')); + }); + if (!res.tempFilePaths) { + res.tempFilePaths = res.tempFiles.map((file) => file.path); + } + return res; +} + +function uploadCloudFiles(files, max = 5, onUploadProgress) { + files = JSON.parse(JSON.stringify(files)) + const len = files.length + let count = 0 + let self = this + return new Promise(resolve => { + while (count < max) { + next() + } + + function next() { + let cur = count++ + if (cur >= len) { + !files.find(item => !item.url && !item.errMsg) && resolve(files) + return + } + const fileItem = files[cur] + const index = self.files.findIndex(v => v.uuid === fileItem.uuid) + fileItem.url = '' + delete fileItem.errMsg + + uniCloud + .uploadFile({ + filePath: fileItem.path, + cloudPath: fileItem.cloudPath, + fileType: fileItem.fileType, + onUploadProgress: res => { + res.index = index + onUploadProgress && onUploadProgress(res) + } + }) + .then(res => { + fileItem.url = res.fileID + fileItem.index = index + if (cur < len) { + next() + } + }) + .catch(res => { + fileItem.errMsg = res.errMsg || res.message + fileItem.index = index + if (cur < len) { + next() + } + }) + } + }) +} + + + + + +function uploadFiles(choosePromise, { + onChooseFile, + onUploadProgress +}) { + return choosePromise + .then((res) => { + if (onChooseFile) { + const customChooseRes = onChooseFile(res); + if (typeof customChooseRes !== 'undefined') { + return Promise.resolve(customChooseRes).then((chooseRes) => typeof chooseRes === 'undefined' ? + res : chooseRes); + } + } + return res; + }) + .then((res) => { + if (res === false) { + return { + errMsg: ERR_MSG_OK, + tempFilePaths: [], + tempFiles: [], + }; + } + return res + }) +} + +function chooseAndUploadFile(opts = { + type: 'all' +}) { + if (opts.type === 'image') { + return uploadFiles(chooseImage(opts), opts); + } + else if (opts.type === 'video') { + return uploadFiles(chooseVideo(opts), opts); + } + return uploadFiles(chooseAll(opts), opts); +} + +export { + chooseAndUploadFile, + uploadCloudFiles +}; diff --git a/uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue b/uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue new file mode 100644 index 0000000000000000000000000000000000000000..ddf1c93b5fb43c1ab38cc8b5412ef5e73de09a34 --- /dev/null +++ b/uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue @@ -0,0 +1,652 @@ + + + + + diff --git a/uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue b/uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue new file mode 100644 index 0000000000000000000000000000000000000000..625d92ec72822c33e50deb5cda45304ff76a59fb --- /dev/null +++ b/uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue @@ -0,0 +1,325 @@ + + + + + diff --git a/uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue b/uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue new file mode 100644 index 0000000000000000000000000000000000000000..6d6cdc0785164e0d9043280df5fdda6e163d4fa8 --- /dev/null +++ b/uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue @@ -0,0 +1,290 @@ + + + + + diff --git a/uni_modules/uni-file-picker/components/uni-file-picker/utils.js b/uni_modules/uni-file-picker/components/uni-file-picker/utils.js new file mode 100644 index 0000000000000000000000000000000000000000..60aaa3e4ebccc9cf9f456feb3cd6fa5a87a1f5ca --- /dev/null +++ b/uni_modules/uni-file-picker/components/uni-file-picker/utils.js @@ -0,0 +1,109 @@ +/** + * 获取文件名和后缀 + * @param {String} name + */ +export const get_file_ext = (name) => { + const last_len = name.lastIndexOf('.') + const len = name.length + return { + name: name.substring(0, last_len), + ext: name.substring(last_len + 1, len) + } +} + +/** + * 获取扩展名 + * @param {Array} fileExtname + */ +export const get_extname = (fileExtname) => { + if (!Array.isArray(fileExtname)) { + let extname = fileExtname.replace(/(\[|\])/g, '') + return extname.split(',') + } else { + return fileExtname + } + return [] +} + +/** + * 获取文件和检测是否可选 + */ +export const get_files_and_is_max = (res, _extname) => { + let filePaths = [] + let files = [] + if(!_extname || _extname.length === 0){ + return { + filePaths, + files + } + } + res.tempFiles.forEach(v => { + let fileFullName = get_file_ext(v.name) + const extname = fileFullName.ext.toLowerCase() + if (_extname.indexOf(extname) !== -1) { + files.push(v) + filePaths.push(v.path) + } + }) + if (files.length !== res.tempFiles.length) { + uni.showToast({ + title: `当前选择了${res.tempFiles.length}个文件 ,${res.tempFiles.length - files.length} 个文件格式不正确`, + icon: 'none', + duration: 5000 + }) + } + + return { + filePaths, + files + } +} + + +/** + * 获取图片信息 + * @param {Object} filepath + */ +export const get_file_info = (filepath) => { + return new Promise((resolve, reject) => { + uni.getImageInfo({ + src: filepath, + success(res) { + resolve(res) + }, + fail(err) { + reject(err) + } + }) + }) +} +/** + * 获取封装数据 + */ +export const get_file_data = async (files, type = 'image') => { + // 最终需要上传数据库的数据 + let fileFullName = get_file_ext(files.name) + const extname = fileFullName.ext.toLowerCase() + let filedata = { + name: files.name, + uuid: files.uuid, + extname: extname || '', + cloudPath: files.cloudPath, + fileType: files.fileType, + url: files.path || files.path, + size: files.size, //单位是字节 + image: {}, + path: files.path, + video: {} + } + if (type === 'image') { + const imageinfo = await get_file_info(files.path) + delete filedata.video + filedata.image.width = imageinfo.width + filedata.image.height = imageinfo.height + filedata.image.location = imageinfo.path + } else { + delete filedata.image + } + return filedata +} diff --git a/uni_modules/uni-file-picker/package.json b/uni_modules/uni-file-picker/package.json new file mode 100644 index 0000000000000000000000000000000000000000..0a02dda64ff59b26b0a1eb51a31ebeeed9802fa3 --- /dev/null +++ b/uni_modules/uni-file-picker/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-file-picker", + "displayName": "uni-file-picker 文件选择上传", + "version": "0.2.14", + "description": "文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间", + "keywords": [ + "uni-ui", + "uniui", + "图片上传", + "文件上传" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "n" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} diff --git a/uni_modules/uni-file-picker/readme.md b/uni_modules/uni-file-picker/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..496327b7f2e9961d2333a315ca7af8a4bf743c75 --- /dev/null +++ b/uni_modules/uni-file-picker/readme.md @@ -0,0 +1,305 @@ + +## FilePicker 文件选择上传 + +> **组件名:uni-file-picker** +> 代码块: `uFilePicker` + + +文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间 + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - 组件需要依赖 `sass` 插件 ,请自行手动安装 +> - 如不绑定服务空间,`autoUpload`默认为`false`且不可更改 +> - 选择文件目前只支持 `H5` 和 `微信小程序平台` ,且 `微信小程序平台` 使用 `wx.chooseMessageFile()` +> - v-model 值需要自动上传成功后才会绑定值,一般只用来回显数据 +> - 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + + +## API + +### FilePicker Props + +| 属性名 | 类型 | 默认值 | 可选值 | 说明 | +| :-: | :-: | :-: | :-: | :-: | +| v-model/value | Array\Object | - | - | 组件数据,通常用来回显 ,类型由`return-type`属性决定 ,**格式见下文** | +| disabled | Boolean | false | - | 组件禁用 | +| readonly | Boolean | false | - | 组件只读,不可选择,不显示进度,不显示删除按钮 | +| return-type | String | array | array/object | 限制 `value` 格式,当为 `object` 时 ,组件只能单选,且会覆盖 | +| disable-preview| Boolean | false | - | 禁用图片预览,仅 `mode:grid`生效 | +| del-icon | Boolean | true | - | 是否显示删除按钮 | +| auto-upload | Boolean | true | - | 是否自动上传,值为`true`则只触发@select,可自行上传| +| limit | Number\String | 9 | - | 最大选择个数 ,h5 会自动忽略多选的部分 | +| title | String | - | - | 组件标题,右侧显示上传计数 | +| mode | String | list | list/grid | 选择文件后的文件列表样式 | +| file-mediatype| String | image | image/video/all | 选择文件类型,all 只支持 H5 和微信小程序平台 | +| file-extname | Array\String | - | - | 选择文件后缀,字符串的情况下需要用逗号分隔(推荐使用字符串),根据 `file-mediatype` 属性而不同| +| list-styles | Object | - | - | `mode:list` 时的样式 | +| image-styles | Object | - | - | `mode:grid` 时的样式 | + + +### value 格式 + +三个属性必填,否则影响组件显示 + +```json +[ + { + "name":"file.txt", + "extname":"txt", + "url":"https://xxxx", + // ... + } +] + +``` + +### list-styles 格式 + +```json +{ + "borderStyle":{ + "color":"#eee", // 边框颜色 + "width":"1px", // 边框宽度 + "style":"solid", // 边框样式 + "radius":"5px" // 边框圆角,不支持百分比 + }, + "border":false, // 是否显示边框 + "dividline":true // 是否显示分隔线 +} +``` + +### image-styles 格式 + +```json +{ + "height": 60, // 边框高度 + "width": 60, // 边框宽度 + "border":{ // 如果为 Boolean 值,可以控制边框显示与否 + "color":"#eee", // 边框颜色 + "width":"1px", // 边框宽度 + "style":"solid", // 边框样式 + "radius":"50%" // 边框圆角,支持百分比 + } +} +``` + +### FilePicker Events + +|事件称名 |说明 | 返回值 | +|:-: |:-: | :-: | +|@select | 选择文件后触发 | 见下文| +|@progress|文件上传时触发 | 见下文| +|@success |上传成功触发 | 见下文| +|@fail |上传失败触发 | 见下文| +|@delete |文件从列表移除时触发| 见下文| + + +#### Callback Params + +`**注意**:如果绑定的是腾讯云的服务空间 ,tempFilePaths 将返回 fileID` + +```json +{ + "progress" : Number, // 上传进度 ,仅 @progress 事件包含此字段 + "index" : Number, // 上传文件索引 ,仅 @progress 事件包含此字段 + "tempFile" : file, // 当前文件对象 ,包含文件流,文件大小,文件名称等,仅 @progress 事件包含此字段 + "tempFiles" : files, // 文件列表,包含文件流,文件大小,文件名称等 + "tempFilePaths" : filePaths, // 文件地址列表,@sucess 事件为上传后的线上文件地址 +} + +``` + + +### FilePicker Methods + +通过 `$ref` 调用 + +| 方法称名 | 说明 | 参数 | +| :-: | :-: | :-: | +| upload() | 手动上传 ,如`autoUpload`为`false` ,必须调用此方法| - | +| clearFiles(index:Number) | 清除选择结果| 传如 Number 为删除指定下标的文件 ,不传为删除所有| + +### FilePicker Slots + +插槽可定义上传按钮显示样式 + +| 插槽名 | 说明 | +| :-: | :-: | +| default |默认插槽| + +## 组件用法 + +### 基础用法 + +```html + +``` + +```javascript +export default { + data() { + return { + imageValue:[] + } + }, + methods:{ + // 获取上传状态 + select(e){ + console.log('选择文件:',e) + }, + // 获取上传进度 + progress(e){ + console.log('上传进度:',e) + }, + + // 上传成功 + success(e){ + console.log('上传成功') + }, + + // 上传失败 + fail(e){ + console.log('上传失败:',e) + } + } +} + +``` + +### 选择指定后缀图片,且限制选择个数 + +配置 `file-mediatype` 属性为 `image`,限定只选择图片 + +配置 `file-extname` 属性为 `'png,jpg'`,限定只选择 `png`和`jpg`后缀的图片 + +配置 `limit` 属性为 1 ,则最多选择一张图片 + +配置 `mode` 属性为 `grid` ,可以使用九宫格样式选择图片 + + +```html + +``` + +### 手动上传 + +配置 `auto-upload` 属性为 `false` ,可以停止自动上传,通过`ref`调用`upload`方法自行选择上传时机 + +```html + + + + +``` + +```javascript +export default { + data() {}, + methods:{ + upload(){ + this.$refs.files.upload() + } + } +} + +``` + +### 单选图片且点击再次选择 + +配置 `disable-preview` 属性为 `true`,禁用点击预览图片功能 + +配置 `del-icon` 属性为 `false`,隐藏删除按钮 + +配置 `return-type` 属性为 `object`,设置 `value` 类型 ,如需绑定 `array`类型 ,则设置`limit:1`,可达到一样的效果 + + + +```html +选择头像 +``` + +### 自定义样式 + +配置 `image-styles` 属性,可以自定义`mode:image`时的回显样式 + +配置 `list-styles` 属性,可以自定义`mode:video|| mode:all`时的回显样式 + +```html + + + + +``` + +```javascript +export default { + data() { + imageStyles:{ + width:64, + height:64, + border:{ + color:"#ff5a5f", + width:2, + style:'dashed', + radius:'2px' + } + }, + listStyles:{ + // 是否显示边框 + border: true, + // 是否显示分隔线 + dividline: true, + // 线条样式 + borderStyle: { + width:1, + color:'blue', + radius:2 + } + } + } +} + +``` + + + +### 使用插槽 + +使用默认插槽可以自定义选择文件按钮样式 + +```html + + + +``` + + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/file-picker/file-picker](https://hellouniapp.dcloud.net.cn/pages/extUI/file-picker/file-picker) \ No newline at end of file diff --git a/uni_modules/uni-forms/changelog.md b/uni_modules/uni-forms/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..b5b12f0b47b16bb62aab302b6812283f3536e757 --- /dev/null +++ b/uni_modules/uni-forms/changelog.md @@ -0,0 +1,53 @@ +## 1.2.7(2021-08-13) +- 修复 没有添加校验规则的字段依然报错的Bug +## 1.2.6(2021-08-11) +- 修复 重置表单错误信息无法清除的问题 +## 1.2.5(2021-08-11) +- 优化 组件文档 +## 1.2.4(2021-08-11) +- 修复 表单验证只生效一次的问题 +## 1.2.3(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.2.2(2021-07-26) +- 修复 vue2 下条件编译导致destroyed生命周期失效的Bug +- 修复 1.2.1 引起的示例在小程序平台报错的Bug +## 1.2.1(2021-07-22) +- 修复 动态校验表单,默认值为空的情况下校验失效的Bug +- 修复 不指定name属性时,运行报错的Bug +- 优化 label默认宽度从65调整至70,使required为true且四字时不换行 +- 优化 组件示例,新增动态校验示例代码 +- 优化 组件文档,使用方式更清晰 +## 1.2.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.2(2021-06-25) +- 修复 pattern 属性在微信小程序平台无效的问题 +## 1.1.1(2021-06-22) +- 修复 validate-trigger属性为submit且err-show-type属性为toast时不能弹出的Bug +## 1.1.0(2021-06-22) +- 修复 只写setRules方法而导致校验不生效的Bug +- 修复 由上个办法引发的错误提示文字错位的Bug +## 1.0.48(2021-06-21) +- 修复 不设置 label 属性 ,无法设置label插槽的问题 +## 1.0.47(2021-06-21) +- 修复 不设置label属性,label-width属性不生效的bug +- 修复 setRules 方法与rules属性冲突的问题 +## 1.0.46(2021-06-04) +- 修复 动态删减数据导致报错的问题 +## 1.0.45(2021-06-04) +- 新增 modelValue 属性 ,value 即将废弃 +## 1.0.44(2021-06-02) +- 新增 uni-forms-item 可以设置单独的 rules +- 新增 validate 事件增加 keepitem 参数,可以选择那些字段不过滤 +- 优化 submit 事件重命名为 validate +## 1.0.43(2021-05-12) +- 新增 组件示例地址 +## 1.0.42(2021-04-30) +- 修复 自定义检验器失效的问题 +## 1.0.41(2021-03-05) +- 更新 校验器 +- 修复 表单规则设置类型为 number 的情况下,值为0校验失败的Bug +## 1.0.40(2021-03-04) +- 修复 动态显示uni-forms-item的情况下,submit 方法获取值错误的Bug +## 1.0.39(2021-02-05) +- 调整为uni_modules目录规范 +- 修复 校验器传入 int 等类型 ,返回String类型的Bug diff --git a/uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue b/uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..5837320edd038d76ee904f3f12314e974656cf06 --- /dev/null +++ b/uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue @@ -0,0 +1,509 @@ + + + + + diff --git a/uni_modules/uni-forms/components/uni-forms/uni-forms.vue b/uni_modules/uni-forms/components/uni-forms/uni-forms.vue new file mode 100644 index 0000000000000000000000000000000000000000..057afc8fedaf34f397fb4afdf1afff6d8955b430 --- /dev/null +++ b/uni_modules/uni-forms/components/uni-forms/uni-forms.vue @@ -0,0 +1,472 @@ + + + + + diff --git a/uni_modules/uni-forms/components/uni-forms/validate.js b/uni_modules/uni-forms/components/uni-forms/validate.js new file mode 100644 index 0000000000000000000000000000000000000000..1834c6cf61559bd1a9b8f2a984ef9b8e13350e98 --- /dev/null +++ b/uni_modules/uni-forms/components/uni-forms/validate.js @@ -0,0 +1,486 @@ +var pattern = { + email: /^\S+?@\S+?\.\S+?$/, + idcard: /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/, + url: new RegExp( + "^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$", + 'i') +}; + +const FORMAT_MAPPING = { + "int": 'integer', + "bool": 'boolean', + "double": 'number', + "long": 'number', + "password": 'string' + // "fileurls": 'array' +} + +function formatMessage(args, resources = '') { + var defaultMessage = ['label'] + defaultMessage.forEach((item) => { + if (args[item] === undefined) { + args[item] = '' + } + }) + + let str = resources + for (let key in args) { + let reg = new RegExp('{' + key + '}') + str = str.replace(reg, args[key]) + } + return str +} + +function isEmptyValue(value, type) { + if (value === undefined || value === null) { + return true; + } + + if (typeof value === 'string' && !value) { + return true; + } + + if (Array.isArray(value) && !value.length) { + return true; + } + + if (type === 'object' && !Object.keys(value).length) { + return true; + } + + return false; +} + +const types = { + integer(value) { + return types.number(value) && parseInt(value, 10) === value; + }, + string(value) { + return typeof value === 'string'; + }, + number(value) { + if (isNaN(value)) { + return false; + } + return typeof value === 'number'; + }, + "boolean": function(value) { + return typeof value === 'boolean'; + }, + "float": function(value) { + return types.number(value) && !types.integer(value); + }, + array(value) { + return Array.isArray(value); + }, + object(value) { + return typeof value === 'object' && !types.array(value); + }, + date(value) { + return value instanceof Date; + }, + timestamp(value) { + if (!this.integer(value) || Math.abs(value).toString().length > 16) { + return false + } + return true; + }, + file(value) { + return typeof value.url === 'string'; + }, + email(value) { + return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255; + }, + url(value) { + return typeof value === 'string' && !!value.match(pattern.url); + }, + pattern(reg, value) { + try { + return new RegExp(reg).test(value); + } catch (e) { + return false; + } + }, + method(value) { + return typeof value === 'function'; + }, + idcard(value) { + return typeof value === 'string' && !!value.match(pattern.idcard); + }, + 'url-https'(value) { + return this.url(value) && value.startsWith('https://'); + }, + 'url-scheme'(value) { + return value.startsWith('://'); + }, + 'url-web'(value) { + return false; + } +} + +class RuleValidator { + + constructor(message) { + this._message = message + } + + async validateRule(fieldKey, fieldValue, value, data, allData) { + var result = null + + let rules = fieldValue.rules + + let hasRequired = rules.findIndex((item) => { + return item.required + }) + if (hasRequired < 0) { + if (value === null || value === undefined) { + return result + } + if (typeof value === 'string' && !value.length) { + return result + } + } + + var message = this._message + + if (rules === undefined) { + return message['default'] + } + + for (var i = 0; i < rules.length; i++) { + let rule = rules[i] + let vt = this._getValidateType(rule) + + Object.assign(rule, { + label: fieldValue.label || `["${fieldKey}"]` + }) + + if (RuleValidatorHelper[vt]) { + result = RuleValidatorHelper[vt](rule, value, message) + if (result != null) { + break + } + } + + if (rule.validateExpr) { + let now = Date.now() + let resultExpr = rule.validateExpr(value, allData, now) + if (resultExpr === false) { + result = this._getMessage(rule, rule.errorMessage || this._message['default']) + break + } + } + + if (rule.validateFunction) { + result = await this.validateFunction(rule, value, data, allData, vt) + if (result !== null) { + break + } + } + } + + if (result !== null) { + result = message.TAG + result + } + + return result + } + + async validateFunction(rule, value, data, allData, vt) { + let result = null + try { + let callbackMessage = null + const res = await rule.validateFunction(rule, value, allData || data, (message) => { + callbackMessage = message + }) + if (callbackMessage || (typeof res === 'string' && res) || res === false) { + result = this._getMessage(rule, callbackMessage || res, vt) + } + } catch (e) { + result = this._getMessage(rule, e.message, vt) + } + return result + } + + _getMessage(rule, message, vt) { + return formatMessage(rule, message || rule.errorMessage || this._message[vt] || message['default']) + } + + _getValidateType(rule) { + var result = '' + if (rule.required) { + result = 'required' + } else if (rule.format) { + result = 'format' + } else if (rule.arrayType) { + result = 'arrayTypeFormat' + } else if (rule.range) { + result = 'range' + } else if (rule.maximum !== undefined || rule.minimum !== undefined) { + result = 'rangeNumber' + } else if (rule.maxLength !== undefined || rule.minLength !== undefined) { + result = 'rangeLength' + } else if (rule.pattern) { + result = 'pattern' + } else if (rule.validateFunction) { + result = 'validateFunction' + } + return result + } +} + +const RuleValidatorHelper = { + required(rule, value, message) { + if (rule.required && isEmptyValue(value, rule.format || typeof value)) { + return formatMessage(rule, rule.errorMessage || message.required); + } + + return null + }, + + range(rule, value, message) { + const { + range, + errorMessage + } = rule; + + let list = new Array(range.length); + for (let i = 0; i < range.length; i++) { + const item = range[i]; + if (types.object(item) && item.value !== undefined) { + list[i] = item.value; + } else { + list[i] = item; + } + } + + let result = false + if (Array.isArray(value)) { + result = (new Set(value.concat(list)).size === list.length); + } else { + if (list.indexOf(value) > -1) { + result = true; + } + } + + if (!result) { + return formatMessage(rule, errorMessage || message['enum']); + } + + return null + }, + + rangeNumber(rule, value, message) { + if (!types.number(value)) { + return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); + } + + let { + minimum, + maximum, + exclusiveMinimum, + exclusiveMaximum + } = rule; + let min = exclusiveMinimum ? value <= minimum : value < minimum; + let max = exclusiveMaximum ? value >= maximum : value > maximum; + + if (minimum !== undefined && min) { + return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMinimum ? + 'exclusiveMinimum' : 'minimum' + ]) + } else if (maximum !== undefined && max) { + return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMaximum ? + 'exclusiveMaximum' : 'maximum' + ]) + } else if (minimum !== undefined && maximum !== undefined && (min || max)) { + return formatMessage(rule, rule.errorMessage || message['number'].range) + } + + return null + }, + + rangeLength(rule, value, message) { + if (!types.string(value) && !types.array(value)) { + return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); + } + + let min = rule.minLength; + let max = rule.maxLength; + let val = value.length; + + if (min !== undefined && val < min) { + return formatMessage(rule, rule.errorMessage || message['length'].minLength) + } else if (max !== undefined && val > max) { + return formatMessage(rule, rule.errorMessage || message['length'].maxLength) + } else if (min !== undefined && max !== undefined && (val < min || val > max)) { + return formatMessage(rule, rule.errorMessage || message['length'].range) + } + + return null + }, + + pattern(rule, value, message) { + if (!types['pattern'](rule.pattern, value)) { + return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); + } + + return null + }, + + format(rule, value, message) { + var customTypes = Object.keys(types); + var format = FORMAT_MAPPING[rule.format] ? FORMAT_MAPPING[rule.format] : (rule.format || rule.arrayType); + + if (customTypes.indexOf(format) > -1) { + if (!types[format](value)) { + return formatMessage(rule, rule.errorMessage || message.typeError); + } + } + + return null + }, + + arrayTypeFormat(rule, value, message) { + if (!Array.isArray(value)) { + return formatMessage(rule, rule.errorMessage || message.typeError); + } + + for (let i = 0; i < value.length; i++) { + const element = value[i]; + let formatResult = this.format(rule, element, message) + if (formatResult !== null) { + return formatResult + } + } + + return null + } +} + +class SchemaValidator extends RuleValidator { + + constructor(schema, options) { + super(SchemaValidator.message); + + this._schema = schema + this._options = options || null + } + + updateSchema(schema) { + this._schema = schema + } + + async validate(data, allData) { + let result = this._checkFieldInSchema(data) + if (!result) { + result = await this.invokeValidate(data, false, allData) + } + return result.length ? result[0] : null + } + + async validateAll(data, allData) { + let result = this._checkFieldInSchema(data) + if (!result) { + result = await this.invokeValidate(data, true, allData) + } + return result + } + + async validateUpdate(data, allData) { + let result = this._checkFieldInSchema(data) + if (!result) { + result = await this.invokeValidateUpdate(data, false, allData) + } + return result.length ? result[0] : null + } + + async invokeValidate(data, all, allData) { + let result = [] + let schema = this._schema + for (let key in schema) { + let value = schema[key] + let errorMessage = await this.validateRule(key, value, data[key], data, allData) + if (errorMessage != null) { + result.push({ + key, + errorMessage + }) + if (!all) break + } + } + return result + } + + async invokeValidateUpdate(data, all, allData) { + let result = [] + for (let key in data) { + let errorMessage = await this.validateRule(key, this._schema[key], data[key], data, allData) + if (errorMessage != null) { + result.push({ + key, + errorMessage + }) + if (!all) break + } + } + return result + } + + _checkFieldInSchema(data) { + var keys = Object.keys(data) + var keys2 = Object.keys(this._schema) + if (new Set(keys.concat(keys2)).size === keys2.length) { + return '' + } + + var noExistFields = keys.filter((key) => { + return keys2.indexOf(key) < 0; + }) + var errorMessage = formatMessage({ + field: JSON.stringify(noExistFields) + }, SchemaValidator.message.TAG + SchemaValidator.message['defaultInvalid']) + return [{ + key: 'invalid', + errorMessage + }] + } +} + +function Message() { + return { + TAG: "", + default: '验证错误', + defaultInvalid: '提交的字段{field}在数据库中并不存在', + validateFunction: '验证无效', + required: '{label}必填', + 'enum': '{label}超出范围', + timestamp: '{label}格式无效', + whitespace: '{label}不能为空', + typeError: '{label}类型无效', + date: { + format: '{label}日期{value}格式无效', + parse: '{label}日期无法解析,{value}无效', + invalid: '{label}日期{value}无效' + }, + length: { + minLength: '{label}长度不能少于{minLength}', + maxLength: '{label}长度不能超过{maxLength}', + range: '{label}必须介于{minLength}和{maxLength}之间' + }, + number: { + minimum: '{label}不能小于{minimum}', + maximum: '{label}不能大于{maximum}', + exclusiveMinimum: '{label}不能小于等于{minimum}', + exclusiveMaximum: '{label}不能大于等于{maximum}', + range: '{label}必须介于{minimum}and{maximum}之间' + }, + pattern: { + mismatch: '{label}格式不匹配' + } + }; +} + + +SchemaValidator.message = new Message(); + +export default SchemaValidator diff --git a/uni_modules/uni-forms/package.json b/uni_modules/uni-forms/package.json new file mode 100644 index 0000000000000000000000000000000000000000..9d949b2be4a7f85c9cbba6fc3ecab4cb25f86382 --- /dev/null +++ b/uni_modules/uni-forms/package.json @@ -0,0 +1,89 @@ +{ + "id": "uni-forms", + "displayName": "uni-forms 表单", + "version": "1.2.7", + "description": "由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据", + "keywords": [ + "uni-ui", + "表单", + "校验", + "表单校验", + "表单验证" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} diff --git a/uni_modules/uni-forms/readme.md b/uni_modules/uni-forms/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..e8352620b6f85056593f571568f6cdfb8dc11406 --- /dev/null +++ b/uni_modules/uni-forms/readme.md @@ -0,0 +1,830 @@ + + +## Forms 表单 + +> **组件名:uni-forms** +> 代码块: `uForms`、`uni-forms-item` +> 关联组件:`uni-forms-item`、`uni-easyinput`、`uni-data-checkbox`、`uni-group`。 + + +uni-app的内置组件已经有了 `
`组件,用于提交表单内容。 + +然而几乎每个表单都需要做表单验证,为了方便做表单验证,减少重复开发,`uni ui` 又基于 ``组件封装了 ``组件,内置了表单验证功能。 + +`` 提供了 `rules`属性来描述校验规则、``子组件来包裹具体的表单项,以及给原生或三方组件提供了 `binddata()` 来设置表单值。 + +每个要校验的表单项,不管input还是checkbox,都必须放在``组件中,且一个``组件只能放置一个表单项。 + +``组件内部预留了显示error message的区域,默认是在表单项的底部。 + +另外,``组件下面的各个表单项,可以通过``包裹为不同的分组。同一``下的不同表单项目将聚拢在一起,同其他group保持垂直间距。``仅影响视觉效果。 + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - 组件需要依赖 `sass` 插件 ,请自行手动安装 +> - `resetFields` 方法不会重置原生组件和三方组件的值 +> - 如果配置 `validateTrigger` 属性为 `bind` 且表单域组件使用 `input` 事件触发会耗损部分性能,请谨慎使用 +> - 组件支持 nvue ,需要在 `manifest.json > app-plus` 节点下配置 `"nvueStyleCompiler" : "uni-app"` +> - uni-forms 中不包含其他表单组件,如需使用 uni-easyinput、uni-data-checkbox 等组件,需要自行引入 +> - 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +`uni-forms` 组件通常用来做表单校验和提交。每一个 `uni-forms-item` 是它的一个表单域组件,用来承载表单具体内容,`uni-forms-item` 中可以嵌套 `uni-easyinput`、`uni-data-checkbox` 和 uni-app内置的表单组件 ,不过 uni-app 的内置表单组件需要通过 `binddata` 或者 `uni-forms` 提供的 `setValue` 方法,将内容与 `uni-forms` 关联,才可完成表单的校验与提交(详见后文`表单校验` 部分) + +```html + +``` + +### 对齐方式 + +使用 `label-position` 属性可以设置所有表单域的位置,默认在左侧 + +```html + +``` + +## 表单校验 + +表单校验还可以直接通过 `uniCloud web 控制台` 快速根据 `schema` 自动生成表单维护界面,比如新建页面和编辑页面,自动处理校验规则,更多参考[DB Schema](https://uniapp.dcloud.io/uniCloud/schema) + +### 如何使用 + +1. `uni-forms` 需要通过 `rules` 属性传入约定的校验规则,详细描述下文`校验规则说明`。 +```html + + + ... + +``` + +2. `uni-forms` 需要绑定`modelValue`属性,值为表单的key\value 组成的对象。 +```html + + + ... + +``` + +3. `uni-forms-item` 需要设置 `name` 属性为当前字段名,字段为 `String` 类型而非变量。 +```html + + + + + + + + + +``` + +4. 如果使用`uni-easyinput` 和 `uni-data-checkbox` 等关联组件,只需绑定 v-model,无需其他操作。 +5. 如果使用原生 input、checkbox 或三方组件等,只需要给组件绑定 `binddata` 方法即可触发表单校验,无需绑定事件到 `methods` 中,见下方完整示例。 +6. `binddata('name',$event.detail.value,'form')"` 方法接受三个值, + - 第一个参数传入当前表单组件所在的 name,同当前父组件 `uni-forms-item` 绑定属性 `name` 的值 + - 第二个参数传入需要校验的值,内置组件可以通过 `$event.detail.value` 获取到组件的返回值,自定义组件传入需要校验的值即可 + - 第三个参数传入 `uni-forms` 组件绑定属性 `ref` 的值,通常在多表单的时候需要传入,用来区分表单,如页面中仅有一个 `uni-forms` 可忽略 +7. 如果内置 `binddata` 方法无法满足需求,在当前页面的 `methods` 中复写此方法即可,复写此方法需要调用 `uni-forms` 的 `setValue` 来触发表单校验,见下方 `setValue`方法说明 + +**完整示例** + +```html + + +``` + +```javascript +export default { + data() { + return { + // 表单数据 + formData: { + name: 'LiMing', + email: 'dcloud@email.com' + }, + rules: { + // 对name字段进行必填验证 + name: { + rules: [{ + required: true, + errorMessage: '请输入姓名', + }, + { + minLength: 3, + maxLength: 5, + errorMessage: '姓名长度在 {minLength} 到 {maxLength} 个字符', + } + ] + }, + // 对email字段进行必填验证 + email: { + rules: [{ + format: 'email', + errorMessage: '请输入正确的邮箱地址', + }] + } + } + } + }, + methods: { + /** + * 复写 binddata 方法,如果只是为了校验,无复杂自定义操作,可忽略此方法 + * @param {String} name 字段名称 + * @param {String} value 表单域的值 + */ + // binddata(name,value){ + // 通过 input 事件设置表单指定 name 的值 + // this.$refs.form.setValue(name, value) + // }, + // 触发提交表单 + submit() { + this.$refs.form.validate().then(res=>{ + console.log('表单数据信息:', res); + }).catch(err =>{ + console.log('表单错误信息:', err); + }) + } + } +} +``` + +> **注意** +> `modelValue` 对象目前有比较严格的格式要求: +> - 尽量不要使用嵌套的数据结构,因为表单域指定的`name`值与 modeValue 的 key 是一一对应的,只有一种情况例外,那就是动态校验表单,见下方`动态校验表单`章节 + + +### 校验规则说明 + +校验规则接受一个 `Object` 类型的值,通过传入不同的规则来表示每个表单域的值该如何校验 + +对象的 `key` 表示当前表单域的字段名,`value` 为具体的校验规则 + +以下为 `value` 所包含的内容: + +|属性名|类型|说明| +|:-:|:-:|:-:| +|rules|Array|校验规则,见下方 `rules 属性说明`| +|validateTrigger| String| 表单校验时机| +|label|String|当前表单域的字段中文名,多用于 `errorMessage` 的显示,可不填| + + +```javascript +rules: { + // 对name字段进行必填验证 + name: { + // name 字段的校验规则 + rules:[ + // 校验 name 不能为空 + { + required: true, + message: '请填写姓名', + }, + // 对name字段进行长度验证 + { + minLength: 3, + maxLength: 5, + message: '{label}长度在 {minLength} 到 {maxLength} 个字符', + } + ], + // 当前表单域的字段中文名,可不填写 + label:'姓名', + validateTrigger:'submit' + } +} + +``` + + +### rules 属性说明 +每一个验证规则中,可以配置多个属性,下面是一些常见规则属性。实际上这里的规范,与uniCloud的[DB Schema](https://uniapp.dcloud.io/uniCloud/schema?id=validator)规范相同。 + +|属性名|类型|默认值|可选值|说明 | +|:-:|:-:|:-:|:-:|:-:| +|required|Boolean|-|-|是否必填,配置此参数不会显示输入框左边的必填星号,如需要,请配置`uni-forms-item`组件的的required为true| +|range|Array|-|-|数组至少要有一个元素,且数组内的每一个元素都是唯一的。 | +|format|String|-|-|内置校验规则,如这些规则无法满足需求,可以使用正则匹配或者自定义规则 | +|pattern|RegExp|-|-|正则表达式,注意事项见下方说明| +|maximum|Number|-|-| 校验最大值(大于)| +|minimum|Number|-|-| 校验最小值(小于) | +|maxLength|Number|-|-| 校验数据最大长度 | +|errorMessage|String|-|-|校验失败提示信息语,可添加属性占位符,当前表格内属性都可用作占位符| +|trigger|String|bind| bind/submit|校验触发时机| +|validateFunction|Function|-|-|自定义校验规则 | + + +**format属性值说明** + +|属性名|说明| +|:-:|:-:| +|string|必须是 string 类型,默认类型| +|number|必须是 number 类型| +|boolean|必须是 boolean 类型| +|array|必须是 array 类型| +|object|必须是 object 类型| +|url|必须是 url 类型| +|email|必须是 email 类型| + + +> **pattern属性说明** +> 在小程序中,json 中不能使用正则对象,如:`/^\S+?@\S+?\.\S+?$/`,使用正则对象会被微信序列化,导致正则失效。 +> 所以建议统一使用字符串的方式来使用正则 ,如`'^\\S+?@\\S+?\\.\\S+?$'` ,需要注意 `\` 需要使用 `\\` 来转译。 +> 如验证邮箱:/^\S+?@\S+?\.\S+?$/ (注意不带引号),或使用 "^\\S+?@\\S+?\\.\\S+?$"(注意带引号需要使用 `\` 转义) + + + +### validateFunction 自定义校验规则使用说明 +`uni-forms` 的 `rules` 基础规则有时候不能满足项目的所有使用场景,这时候可以使用 `validateFunction` 来自定义校验规则 + +`validateFunction` 方法返回四个参数 `validateFunction:function(rule,value,data,callback){}` ,当然返回参数名开发者可以自定义: + - rule : 当前校验字段在 rules 中所对应的校验规则 + - value : 当前校验字段的值 + - data : 所有校验字段的字段和值的对象 + - callback : 校验完成时的回调,一般无需执行callback,返回true(校验通过)或者false(校验失败)即可 ,如果需要显示不同的 `errMessage`,如果校验不通过需要执行 callback('提示错误信息'),如果校验通过,执行callback()即可 + + +> **注意** +> 需要注意,如果需要使用 `validateFunction` 自定义校验规则,则不能采用 `uni-forms` 的 `rules` 属性来配置校验规则,这时候需要通过`ref`,在`onReady`生命周期调用组件的`setRules`方法绑定验证规则 +> 无法通过props传递变量,是因为微信小程序会过滤掉对象中的方法,导致自定义验证规则无效。 +> + + + + + +```html + + +``` + +```javascript +export default { + data() { + return { + formData:{ + + }, + rules: { + hobby: { + rules: [{ + required: true, + errorMessage: '请选择兴趣', + },{ + validateFunction:function(rule,value,data,callback){ + if (value.length < 2) { + callback('请至少勾选两个兴趣爱好') + } + return true + } + }] + } + } + } + }, + onReady() { + // 需要在onReady中设置规则 + this.$refs.form.setRules(this.rules) + }, + methods: { + submit(form) { + this.$refs.form.validate().then(res=>{ + console.log('表单数据信息:', res); + }).catch(err =>{ + console.log('表单错误信息:', err); + }) + } + } +} + +``` + + +### validateFunction 异步校验 + +上面的自定义校验方式为同步校验 ,如果需要异步校验,`validateFunction` 需要返回一个 `Promise` ,校验不通过 执行 `reject(new Error('错误信息'))` 返回对应的错误信息,如果校验通过则直接执行 `resolve()` 即可,在异步校验方法中,不需要使用 `callback` 。 + +```html + + +``` + +```javascript +export default { + data() { + return { + formData:{ + age:'' + }, + rules: { + age: { + rules: [{ + required: true, + errorMessage: '请输入年龄', + },{ + validateFunction: (rule, value, data, callback) => { + // 异步需要返回 Promise 对象 + return new Promise((resolve, reject) => { + setTimeout(() => { + if (value > 10 ) { + // 通过返回 resolve + resolve() + } else { + // 不通过返回 reject(new Error('错误信息')) + reject(new Error('年龄必须大于十岁')) + } + }, 2000) + }) + } + }] + } + } + } + }, + onReady() { + // 需要在onReady中设置规则 + this.$refs.form.setRules(this.rules) + }, + methods: { + /** + * 表单提交 + * @param {Object} event + */ + submit() { + uni.showLoading() + this.$refs.form.validate().then(res => { + uni.hideLoading() + console.log('表单数据信息:', res); + }).catch(err => { + uni.hideLoading() + console.log('表单错误信息:', err); + }) + } + } +} + +``` + + +### 动态表单校验 + +`uni-forms v1.0.44` 开始增加了动态校验表单的相关内容。 + +多用于同一个字段需要添加多次的场景,如需要动态创建多个域名参与检验。 + +1. 在 `formData` 中定义个变量用来接受同一个字段的多个结果。 +```javascript +dynamicFormData: { + email: '', + // domains 字段下会有多个结果 + domains: {} +} +``` + + +2. 使用 `uni-forms-item` 的 `rules` 属性定义单个表单域的校验规则。 +```html + + ... + +``` + +3. `name` 需要动态指定,格式为: `字段[唯一值]` +```html + + ... + +``` + +4. 需要绑定值的组件的 v-model 也需要动态指定,格式为:`数据源.字段[唯一值]` +```html + + + +``` + +**完整示例** + +```html + + + + + + + + + + + + + +``` + +```javascript +export default { + data() { + return { + // 数据源 + dynamicFormData: { + email: '', + domains: {} + }, + // 动态表单数据 + dynamicLists: [], + // 规则 + dynamicRules: { + email: { + rules: [{ + required: true, + errorMessage: '域名不能为空' + }, { + format: 'email', + errorMessage: '域名格式错误' + }] + } + } + } + }, + methods: { + // 新增表单域 + add() { + this.dynamicLists.push({ + label: '域名', + id: Date.now() + }) + }, + // 删除表单域 + del(id) { + let index = this.dynamicLists.findIndex(v => v.id === id) + this.dynamicLists.splice(index, 1) + }, + // 提交 + submit(ref) { + this.$refs[ref].validate((err,value)=>{ + console.log(err,value); + }) + }, + } +} + +// 返回值格式 ,根据自有业务,自行处理数据 +{ + emial:'', + domains:{ + id1:'', + id2:'', + ... + } +} +``` + + +### 表单校验时机说明 + +不管是在规则里还是`uni-forms`、`uni-forms-item`里,都有 `validateTrigger` 属性, `validateTrigger` 属性规定了表单校验时机,当前只有 `bind`、`submit` 两个值域 + +- `bind` : 数据绑定时触发校验,`uni-esayinput` 、`uni-data-checkbox` 组件表现为数据发生变化时。其他内置或三方组件为 `binddata` 事件执行时机 + +```html + + + +``` + +- `submit`: 只有提交表单才会触发表单校验 + + +对于表单校验时机,同时只会有一个 `validateTrigger` 发生作用,它的作用权重为 + +**`规则 > uni-forms-item > uni-forms `** + +- 如果规则里配置 `validateTrigger` ,则优先使用规则里的 `validateTrigger` 属性来决定表单校验时机 +- 如果规则里没有配置 `validateTrigger` ,则优先使用 `uni-forms-item` 的 `validateTrigger` 属性来决定表单校验时机 +- 如果 `uni-forms-item` 组件里没有配置 `validateTrigger` ,则优先使用 `uni-forms` 的 `validateTrigger` 属性来决定表单校验时机 +- 以此类推,如果都没有使用 `validateTrigger` 属性,则会使用 `uni-forms` 的 `validateTrigger` 属性默认值来决定表单校验时机 + + +## API + +### Forms Props + +|属性名|类型|默认值|可选值|说明| +|:-:|:-:|:-:|:-:|:-:| +|value [即将废弃]|Object|-|-| 表单数据| +|modelValue|Object|-|-| 表单数据| +|rules|Object|-|-|表单校验规则| +|validate-trigger|String|submit|bind/submit| 表单校验时机| +|label-position|String|left|top/left|label 位置| +|label-width|String/Number|75|-|label 宽度,单位 px| +|label-align|String|left| left/center/right|label 居中方式| +|err-show-type|String|undertext| undertext/toast/modal|表单错误信息提示方式| +|border|Boolean|false|-|是否显示分格线| + +### Forms Events + +|事件称名|说明| +|:-:|:-:| +|validate|任意表单项被校验后触发,返回表单校验信息| + +### Forms Methods + +|方法称名|说明| +|:-:| :-:| +|submit[即将废弃]| 对整个表单进行校验的方法,会返回一个 promise| +|validate|对整个表单进行校验的方法,会返回一个 promise| +|setValue|设置表单某一项 name 的对应值,通常在 uni-forms-item 和自定表单组件中使用| +|validateField|部分表单进行校验| +|clearValidate|移除表单的校验结果| +|resetFields|重置表单, 需要把 `uni-forms` 的`modelValue`属性改为 `v-model` ,且对内置组件可能不生效| + + + +### validate(keepItem:Array,callback:Function) 方法说明 +`validate` 方法是对整个表单进行校验,方法接受两个参数 + +|参数称名|类型|说明| +|:-:| :-:|:-:| +|keepItem|Array|保留不参与校验的字段| +|callback|Function|校验完成返回函数| + +校验成功后,校验对象只保留指定了`name`的字段(只要 ``uni-forms-item` 绑定了 `name`,哪怕不校验,也会返回),如果需要保留其他字段,则需要 `keepItem` 属性 + +```html + + + +``` + +```javascript +export default { + data() { + return { + formData:{ + age:'' + }, + rules: { + // ... + } + } + }, + onLoad(){ + this.formData.id = 'testId' + }, + methods: { + submit() { + // 在 onLoad 生命周期中,formData添加了一个 id 字段 ,此时这个字段是不参数校验的,所以结果中不返回 + // 在 validate(['id']) 方法中,指定第一个参数 ,即可返回id字段 + this.$refs.form.validate(['id'],(err,formData)=>{ + if(!err){ + console.log('success',formData) + } + }) + } + } +} + +``` + + +`validate` 方法还可以返回一个 `Promise` 对象,如果使用了 `callback` 则`Promise` 返回 `null`,`validate` 方法会优先使用 `callback`。 + +`callback` 方法会返回两个返回值 : +- 第一个返回值为检验结果,如果校验失败,则返回失败信息,如果成功,返回 `null` +- 第二个返回值校验数据 + + +```javascript + +// 使用 callback +// 如果不需要 keepItem 参数 ,则可以省略 +this.$refs.form.validate((err,formData)=>{ + // 如果校验成功 ,err 返回 null + if(!err){ + console.log('success',formData) + return + } + console.log('error',err) +}).then(res=>{ + // res 返回 null +}) + +// 使用 Promise +// 对整个表单进行校验,返回一个 Promise +this.$refs.form.validate().then((res)=>{ + // 成功返回,res 为表单数据 + // 其他逻辑处理 + // ... +}).catch((err)=>{ + // 表单校验验失败,err 为具体错误信息 + // 其他逻辑处理 + // ... +}) + +``` + +### setValue(name:String,value:any) 方法说明 + +`setValue` 方法通常用于内置组件或三方组件返回值的校验,因为`uni-esayinput` 等 uni 开头的组件内置了对 `uni-forms`的支持,所以这些组件返回的值可以直接使用,但是比如像`input` 这些内置组件值的变化,无法及时通知 `uni-forms` ,从而无法正常的校验,这时就需要我们手动将这些值加入到`uni-forms`的校验。 + +`setValue` 方法接受两个参数: +- name: 表单域对应的name +- value: 表单域对应的值 + +```html + + + +``` + +```javascript +export default { + data() { + return { + formData:{ + age:'' + }, + rules: { + // ... + } + } + }, + methods: { + setValue(name,value){ + // 设置表单某项对应得值来触发表单校验 + // 接受两个参数,第一个参数为表单域的 name ,第二个参数为表单域的值 + this.$refs.form.setValue(name,value) + }, + submit() { + this.$refs.form.validate(['id'],(err,formData)=>{ + if(!err){ + console.log('success',formData) + } + }) + } + } +} + +``` + +### 其他方法说明 + +```javascript + +// 部分表单进行校验,接受一个参数,类型为 String 或 Array ,只校验传入 name 表单域的值 +this.$refs.form.validateField(['name', 'email']).then((res)=>{ + // 成功返回,res 为对应表单数据 + // 其他逻辑处理 + // ... +}).catch((err)=>{ + // 表单校验验失败,err 为具体错误信息 + // 其他逻辑处理 + // ... +}) + +// 移除表单校验,接受一个参数,类型为 String 或 Array ,只移除传入 name 表单域的值,如果不传入参数,则移除所有 +this.$refs.form.clearValidate(['name', 'email']) + +``` + + +### FormsItem Props + +|属性名|类型|默认值|可选值 |说明| +|:-:|:-:|:-:|:-:|:-:| +|name|String|-|-|表单域的属性名,在使用校验规则时必填| +|required|Boolean|false|-|label 右边显示红色"*"号,样式显示不会对校验规则产生效果| +|validate-trigger|String|submit|bind/submit|表单校验时机| +|left-icon|String|-|-| label左边的图标,限uni-ui的图标名称| +|icon-color|String|#606266|-| 左边通过icon配置的图标的颜色| +|label|String|-|-| 输入框左边的文字提示| +|label-width|Number|70|-| label的宽度,单位px| +|label-align|String|left|left/center/right|label的文字对齐方式| +|label-position|String|left|top/left|label的文字的位置| +|error-message|String|-|-|显示的错误提示内容,如果为空字符串或者false,则不显示错误信息| + + +### FormsItem Slots +|插槽名|说明| +|:-:| :-:| +|default|默认插槽| +|left(已经失效,请使用label代替)|label插槽,自定义label显示内容| +|label|label插槽,自定义label显示内容| + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/forms/forms](https://hellouniapp.dcloud.net.cn/pages/extUI/forms/forms) \ No newline at end of file diff --git a/uni_modules/uni-goods-nav/changelog.md b/uni_modules/uni-goods-nav/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..b0212bbb6032d390ac36afd730c469cff087af91 --- /dev/null +++ b/uni_modules/uni-goods-nav/changelog.md @@ -0,0 +1,13 @@ +## 1.1.1(2021-08-24) +- 新增 支持国际化 +## 1.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-05-12) +- 新增 组件示例地址 +## 1.0.6(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.5(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.4(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/en.json b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/en.json new file mode 100644 index 0000000000000000000000000000000000000000..dcdba41cee4c2e0b6f4b5bc7d2bdc2df56982f15 --- /dev/null +++ b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/en.json @@ -0,0 +1,6 @@ +{ + "uni-goods-nav.options.shop": "shop", + "uni-goods-nav.options.cart": "cart", + "uni-goods-nav.buttonGroup.addToCart": "add to cart", + "uni-goods-nav.buttonGroup.buyNow": "buy now" +} diff --git a/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/index.js b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/index.js new file mode 100644 index 0000000000000000000000000000000000000000..de7509c87ba5197b9f5d20ca94a0558c7a8e08a9 --- /dev/null +++ b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hans.json b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hans.json new file mode 100644 index 0000000000000000000000000000000000000000..48ee344c39f8f2903b09ca02822e4f56305d3ca4 --- /dev/null +++ b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hans.json @@ -0,0 +1,6 @@ +{ + "uni-goods-nav.options.shop": "店铺", + "uni-goods-nav.options.cart": "购物车", + "uni-goods-nav.buttonGroup.addToCart": "加入购物车", + "uni-goods-nav.buttonGroup.buyNow": "立即购买" +} diff --git a/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hant.json b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hant.json new file mode 100644 index 0000000000000000000000000000000000000000..d0a0255c77edd18216bb6a37bb8d45f9c25bc41d --- /dev/null +++ b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hant.json @@ -0,0 +1,6 @@ +{ + "uni-goods-nav.options.shop": "店鋪", + "uni-goods-nav.options.cart": "購物車", + "uni-goods-nav.buttonGroup.addToCart": "加入購物車", + "uni-goods-nav.buttonGroup.buyNow": "立即購買" +} diff --git a/uni_modules/uni-goods-nav/components/uni-goods-nav/uni-goods-nav.vue b/uni_modules/uni-goods-nav/components/uni-goods-nav/uni-goods-nav.vue new file mode 100644 index 0000000000000000000000000000000000000000..32ab1b94052fb45ef55844db31c6b03bc930055f --- /dev/null +++ b/uni_modules/uni-goods-nav/components/uni-goods-nav/uni-goods-nav.vue @@ -0,0 +1,232 @@ + + + + + diff --git a/uni_modules/uni-goods-nav/package.json b/uni_modules/uni-goods-nav/package.json new file mode 100644 index 0000000000000000000000000000000000000000..d7d56ee0c151a95d8144272b20037f0bd996132c --- /dev/null +++ b/uni_modules/uni-goods-nav/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-goods-nav", + "displayName": "uni-goods-nav 商品导航", + "version": "1.1.1", + "description": "商品导航组件主要用于电商类应用底部导航,可自定义加入购物车,购买等操作", + "keywords": [ + "uni-ui", + "uniui", + "商品导航" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-goods-nav/readme.md b/uni_modules/uni-goods-nav/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..4e47bfda746815456ecb75a35f0ebd6b7f31f127 --- /dev/null +++ b/uni_modules/uni-goods-nav/readme.md @@ -0,0 +1,111 @@ + + +### GoodsNav 商品导航 +*已经支持在nvue页面中使用* + +商品加入购物车,立即购买,组件名:`uni-goods-nav`,代码块: uGoodsNav。 + +### 使用方式 + +引用组件 + +```javascript +import uniGoodsNav from '@/components/uni-goods-nav/uni-goods-nav.vue' +export default { + components: {uniGoodsNav} +} +``` + +使用组件 + +```html + +``` + +```javascript +export default { + data () { + return { + options: [{ + icon: 'headphones', + text: '客服' + }, { + icon: 'shop', + text: '店铺', + info: 2, + infoBackgroundColor:'#007aff', + infoColor:"red" + }, { + icon: 'cart', + text: '购物车', + info: 2 + }], + buttonGroup: [{ + text: '加入购物车', + backgroundColor: '#ff0000', + color: '#fff' + }, + { + text: '立即购买', + backgroundColor: '#ffa200', + color: '#fff' + } + ] + } + }, + methods: { + onClick (e) { + uni.showToast({ + title: `点击${e.content.text}`, + icon: 'none' + }) + }, + buttonClick (e) { + console.log(e) + this.options[2].info++ + } + } +} +``` + +### 属性说明 + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|options |Array |- |组件参数 | +|buttonGroup|Array |- |组件按钮组参数 | +|fill |Boolean|false |按钮是否平铺 | + + +**options 参数说明:** + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|text |String |- |显示文字 | +|icon |String | |图标,[参考](https://ext.dcloud.net.cn/plugin?id=28) | +|info |Number |0 |右上角数字角标 | +|infoBackgroundColor|String |#ff0000|角标背景色 | +|infoColor |String |#fff |角标前景色 | + +**buttonGroup 参数说明:** + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|text |String |- |按钮文字 | +|backgroundColor |String |- |按钮背景色 | +|color |String |- |字体颜色 | + +### 事件说明 + +|事件名 |说明 |返回值 | +|:-: |:-: |:-: | +|@click |左侧点击事件 |e = {index,content}| +|@buttonClick |右侧按钮组点击事件 |e = {index,content}| + +### 插件预览地址 + +[https://uniapp.dcloud.io/h5/pages/extUI/goods-nav/goods-nav](https://uniapp.dcloud.io/h5/pages/extUI/goods-nav/goods-nav) + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/goods-nav/goods-nav](https://hellouniapp.dcloud.net.cn/pages/extUI/goods-nav/goods-nav) \ No newline at end of file diff --git a/uni_modules/uni-grid/changelog.md b/uni_modules/uni-grid/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..49fe00850647eef74413854e47cf1cda6566cb57 --- /dev/null +++ b/uni_modules/uni-grid/changelog.md @@ -0,0 +1,8 @@ +## 1.3.1(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.3.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.2.4(2021-05-12) +- 新增 组件示例地址 +## 1.2.3(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-grid/components/uni-grid-item/uni-grid-item.vue b/uni_modules/uni-grid/components/uni-grid-item/uni-grid-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..1f2d79abaacb990ad870b4e7fa400a708de2874c --- /dev/null +++ b/uni_modules/uni-grid/components/uni-grid-item/uni-grid-item.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/uni_modules/uni-grid/components/uni-grid/uni-grid.vue b/uni_modules/uni-grid/components/uni-grid/uni-grid.vue new file mode 100644 index 0000000000000000000000000000000000000000..62bbc32eb1177ecea1610926e592570eefa79b33 --- /dev/null +++ b/uni_modules/uni-grid/components/uni-grid/uni-grid.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/uni_modules/uni-grid/package.json b/uni_modules/uni-grid/package.json new file mode 100644 index 0000000000000000000000000000000000000000..8b6da6a18069957243edfd8aaf83c3f15219722c --- /dev/null +++ b/uni_modules/uni-grid/package.json @@ -0,0 +1,82 @@ +{ + "id": "uni-grid", + "displayName": "uni-grid 宫格", + "version": "1.3.1", + "description": "Grid 宫格组件,提供移动端常见的宫格布局,如九宫格。", + "keywords": [ + "uni-ui", + "uniui", + "九宫格", + "表格" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-grid/readme.md b/uni_modules/uni-grid/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..a748954791c56f14bc6108bffc9f62c99d282c82 --- /dev/null +++ b/uni_modules/uni-grid/readme.md @@ -0,0 +1,95 @@ + + +## Grid 宫格 +> **组件名:uni-grid** +> 代码块: `uGrid` + + +宫格组件。 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - 删除组件自带圆点角标效果,完全交给用户实现,示例有简单角标效果实现 +> - Grid 组件仅在自定义组件模式下支持 +> - column 属性最大值最好不要超过 5 个,如果超过,需要注意内容显示 +> - 支付宝小程序平台需要在支付宝小程序开发者工具里开启 component2 编译模式,开启方式: `详情 --> 项目配置 --> 启用 component2 编译` +> - 为了避免高度显示错误组件内必须要有内容 + + +### 基本用法 + +在 ``template`` 中使用组件 + +```html + + + + 文本 + + + 文本 + + + 文本 + + + + + + + 文本 + + + 文本 + + + 文本 + + + 文本 + + + 文本 + + + 文本 + + +``` + +## API + +### Grid Props + +**uni-grid 属性说明:** + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|column |Number |3 |每列显示个数 | +|borderColor|String |#d0dee5|边框颜色 | +|showBorder |Boolean|true |是否显示边框 | +|square |Boolean|true |是否方形显示 | +|highlight |Boolean|true |点击背景是否高亮 | + +### Grid Events +|事件名 |说明 |返回值 | +|:-: |:-: |:-: | +|@change|点击 grid 触发 |e={detail:{index:0}},index 为当前点击 gird 下标| + + +### GridItem Props + +|属性名|类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|index|Number |- |子组件的唯一标识 ,点击gird会返回当前的标识| + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/grid/grid](https://hellouniapp.dcloud.net.cn/pages/extUI/grid/grid) \ No newline at end of file diff --git a/uni_modules/uni-group/changelog.md b/uni_modules/uni-group/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..7348312006aecf7ca08d8eb60d978559da434cfc --- /dev/null +++ b/uni_modules/uni-group/changelog.md @@ -0,0 +1,8 @@ +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +- 优化 组件文档 +## 1.0.3(2021-05-12) +- 新增 组件示例地址 +## 1.0.2(2021-02-05) +- 调整为uni_modules目录规范 +- 优化 兼容 nvue 页面 diff --git a/uni_modules/uni-group/components/uni-group/uni-group.vue b/uni_modules/uni-group/components/uni-group/uni-group.vue new file mode 100644 index 0000000000000000000000000000000000000000..25ddb294a4b9e5bd921457d32ac4334f2f94e2f1 --- /dev/null +++ b/uni_modules/uni-group/components/uni-group/uni-group.vue @@ -0,0 +1,130 @@ + + + + diff --git a/uni_modules/uni-group/package.json b/uni_modules/uni-group/package.json new file mode 100644 index 0000000000000000000000000000000000000000..bdbaf95368b0511f89c1facf5195486814adfc66 --- /dev/null +++ b/uni_modules/uni-group/package.json @@ -0,0 +1,83 @@ +{ + "id": "uni-group", + "displayName": "uni-group 分组", + "version": "1.1.0", + "description": "分组组件可用于将组件用于分组,添加间隔,以产生明显的区块", + "keywords": [ + "uni-ui", + "uniui", + "group", + "分组", + "" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-group/readme.md b/uni_modules/uni-group/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..deefb22cd115448fcf7f41e50f01bc8f44c62154 --- /dev/null +++ b/uni_modules/uni-group/readme.md @@ -0,0 +1,54 @@ + + +### Group 分组 + +分组组件可用于将组件分组,添加间隔,以产生明显的区块,组件名:``uni-group``,代码块: uGroup。 + +### 平台差异说明 + +如无特殊说明,则全平台可用 + +### 组件使用注意事项 + +为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 + +- 组件需要依赖 `sass` 插件 ,请自行手动安装 +- 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + + +### 使用方式 + +在 ``template`` 中使用组件 + +```html + + 分组1 的内容 + 分组1 的内容 + + + + 分组2 的内容 + 分组2 的内容 + +``` + +### 属性说明 + +|属性名|类型|默认值|说明| +|:-:|:-:|:-:|:-:| +|title|String|-|主标题| +|top|Number|-|分组间隔| +|mode|String|''|模式 ,card 为卡片模式| + + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/group/group](https://hellouniapp.dcloud.net.cn/pages/extUI/group/group) \ No newline at end of file diff --git a/uni_modules/uni-icons/changelog.md b/uni_modules/uni-icons/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..4cde217bb78c757e8520ad23ae736a5c6432a431 --- /dev/null +++ b/uni_modules/uni-icons/changelog.md @@ -0,0 +1,8 @@ +## 1.2.1(2021-09-17) +- 新增 支持使用 css 图标库扩展组件(仅 vue 支持) +## 1.2.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.5(2021-05-12) +- 新增 组件示例地址 +## 1.1.4(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-icons/components/uni-icons/icons.js b/uni_modules/uni-icons/components/uni-icons/icons.js new file mode 100644 index 0000000000000000000000000000000000000000..60b733214dc67b78c1523ae31f878d937e5a830b --- /dev/null +++ b/uni_modules/uni-icons/components/uni-icons/icons.js @@ -0,0 +1,132 @@ +export default { + "pulldown": "\ue588", + "refreshempty": "\ue461", + "back": "\ue471", + "forward": "\ue470", + "more": "\ue507", + "more-filled": "\ue537", + "scan": "\ue612", + "qq": "\ue264", + "weibo": "\ue260", + "weixin": "\ue261", + "pengyouquan": "\ue262", + "loop": "\ue565", + "refresh": "\ue407", + "refresh-filled": "\ue437", + "arrowthindown": "\ue585", + "arrowthinleft": "\ue586", + "arrowthinright": "\ue587", + "arrowthinup": "\ue584", + "undo-filled": "\ue7d6", + "undo": "\ue406", + "redo": "\ue405", + "redo-filled": "\ue7d9", + "bars": "\ue563", + "chatboxes": "\ue203", + "camera": "\ue301", + "chatboxes-filled": "\ue233", + "camera-filled": "\ue7ef", + "cart-filled": "\ue7f4", + "cart": "\ue7f5", + "checkbox-filled": "\ue442", + "checkbox": "\ue7fa", + "arrowleft": "\ue582", + "arrowdown": "\ue581", + "arrowright": "\ue583", + "smallcircle-filled": "\ue801", + "arrowup": "\ue580", + "circle": "\ue411", + "eye-filled": "\ue568", + "eye-slash-filled": "\ue822", + "eye-slash": "\ue823", + "eye": "\ue824", + "flag-filled": "\ue825", + "flag": "\ue508", + "gear-filled": "\ue532", + "reload": "\ue462", + "gear": "\ue502", + "hand-thumbsdown-filled": "\ue83b", + "hand-thumbsdown": "\ue83c", + "hand-thumbsup-filled": "\ue83d", + "heart-filled": "\ue83e", + "hand-thumbsup": "\ue83f", + "heart": "\ue840", + "home": "\ue500", + "info": "\ue504", + "home-filled": "\ue530", + "info-filled": "\ue534", + "circle-filled": "\ue441", + "chat-filled": "\ue847", + "chat": "\ue263", + "mail-open-filled": "\ue84d", + "email-filled": "\ue231", + "mail-open": "\ue84e", + "email": "\ue201", + "checkmarkempty": "\ue472", + "list": "\ue562", + "locked-filled": "\ue856", + "locked": "\ue506", + "map-filled": "\ue85c", + "map-pin": "\ue85e", + "map-pin-ellipse": "\ue864", + "map": "\ue364", + "minus-filled": "\ue440", + "mic-filled": "\ue332", + "minus": "\ue410", + "micoff": "\ue360", + "mic": "\ue302", + "clear": "\ue434", + "smallcircle": "\ue868", + "close": "\ue404", + "closeempty": "\ue460", + "paperclip": "\ue567", + "paperplane": "\ue503", + "paperplane-filled": "\ue86e", + "person-filled": "\ue131", + "contact-filled": "\ue130", + "person": "\ue101", + "contact": "\ue100", + "images-filled": "\ue87a", + "phone": "\ue200", + "images": "\ue87b", + "image": "\ue363", + "image-filled": "\ue877", + "location-filled": "\ue333", + "location": "\ue303", + "plus-filled": "\ue439", + "plus": "\ue409", + "plusempty": "\ue468", + "help-filled": "\ue535", + "help": "\ue505", + "navigate-filled": "\ue884", + "navigate": "\ue501", + "mic-slash-filled": "\ue892", + "search": "\ue466", + "settings": "\ue560", + "sound": "\ue590", + "sound-filled": "\ue8a1", + "spinner-cycle": "\ue465", + "download-filled": "\ue8a4", + "personadd-filled": "\ue132", + "videocam-filled": "\ue8af", + "personadd": "\ue102", + "upload": "\ue402", + "upload-filled": "\ue8b1", + "starhalf": "\ue463", + "star-filled": "\ue438", + "star": "\ue408", + "trash": "\ue401", + "phone-filled": "\ue230", + "compose": "\ue400", + "videocam": "\ue300", + "trash-filled": "\ue8dc", + "download": "\ue403", + "chatbubble-filled": "\ue232", + "chatbubble": "\ue202", + "cloud-download": "\ue8e4", + "cloud-upload-filled": "\ue8e5", + "cloud-upload": "\ue8e6", + "cloud-download-filled": "\ue8e9", + "headphones":"\ue8bf", + "shop":"\ue609" +} diff --git a/uni_modules/uni-icons/components/uni-icons/uni-icons.vue b/uni_modules/uni-icons/components/uni-icons/uni-icons.vue new file mode 100644 index 0000000000000000000000000000000000000000..6bcc0a2fa186f5350b6d2a747c69f14f0c2df74f --- /dev/null +++ b/uni_modules/uni-icons/components/uni-icons/uni-icons.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/uni_modules/uni-icons/components/uni-icons/uni.ttf b/uni_modules/uni-icons/components/uni-icons/uni.ttf new file mode 100644 index 0000000000000000000000000000000000000000..60a1968d08cc6056c70b5402b2effac43c6f96a3 Binary files /dev/null and b/uni_modules/uni-icons/components/uni-icons/uni.ttf differ diff --git a/uni_modules/uni-icons/package.json b/uni_modules/uni-icons/package.json new file mode 100644 index 0000000000000000000000000000000000000000..2dd573c33660384ec45e4348daba8bae9b3ac87e --- /dev/null +++ b/uni_modules/uni-icons/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-icons", + "displayName": "uni-icons 图标", + "version": "1.2.1", + "description": "图标组件,用于展示移动端常见的图标,可自定义颜色、大小。", + "keywords": [ + "uni-ui", + "uniui", + "icon", + "图标" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-icons/readme.md b/uni_modules/uni-icons/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..efc0a970a4ed3feabd818a67d3f249704079504b --- /dev/null +++ b/uni_modules/uni-icons/readme.md @@ -0,0 +1,64 @@ + + +## Icons 图标 +> **组件名:uni-icons** +> 代码块: `uIcons` + + +用于展示 icons 图标 。 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +在 ``template`` 中使用组件 + +```html + +``` + +### 扩展图标用法 + +1. 需要自行在项目 App.vue 中引入 css 图标扩展库(注意: css 图标库引用的 .ttf 文件路径是否正确) + + ```html + + ``` +2. 在 ``template`` 中使用组件 + + ```html + + ``` + + + + +## API + +### Icons Props + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|size |Number |24 |图标大小 | +|type |String |- |图标图案,参考示例 | +|color |String |- |图标颜色 | +|font-family(仅 vue 支持) |String |uniicons |图标库字体家族 | + + +### Icons Events +|事件名 |说明 |返回值| +|:-: |:-: |:-: | +|@click|点击 Icon 触发事件|- | + + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/icons/icons](https://hellouniapp.dcloud.net.cn/pages/extUI/icons/icons) \ No newline at end of file diff --git a/uni_modules/uni-id-cf/changelog.md b/uni_modules/uni-id-cf/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..0b22d646c88f35766ca2f3866d1df266227545d5 --- /dev/null +++ b/uni_modules/uni-id-cf/changelog.md @@ -0,0 +1,19 @@ +## 1.0.7(2021-10-20) +新增bindMobileByMpWeixin,一键获取微信绑定的手机号 +## 1.0.6(2021-09-23) +修复微信登录成功后没有添加日志的问题 +## 1.0.5(2021-08-10) +- 修复登录成功后响应体包含userInfo.password的问题 +- 新增微信登录成功后,自动获取用户的微信昵称和头像完善用户个人资料 +## 1.0.4(2021-07-31) +- 修复 登录日志在登录失败时不写入记录的 bug +- 修复 写入记录登录是未传递 type 参数的 bug +## 1.0.3(2021-07-02) +- 框架设定非 admin 不能创建用户, 用户可自定义 +## 1.0.2(2021-07-01) +1. 发送短信验证码api,默认注释掉:虚拟发送短信验证码的代码块。 +2. 统一action名称为驼峰法 +## 1.0.1(2021-06-28) +修复resetPwdBySmsCode接口,未注册过的用户也能调用的问题 +## 1.0.0(2021-06-21) +1.0.0版发布 \ No newline at end of file diff --git a/uni_modules/uni-id-cf/package.json b/uni_modules/uni-id-cf/package.json new file mode 100644 index 0000000000000000000000000000000000000000..f376744efb3ac3afb86e461ce175a7793b487cda --- /dev/null +++ b/uni_modules/uni-id-cf/package.json @@ -0,0 +1,81 @@ +{ + "id": "uni-id-cf", + "displayName": "uni-id-cf", + "version": "1.0.7", + "description": "uni-id-cf", + "keywords": [ + "uni-id-cf", + "uni-id的云函数" +], + "repository": "", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "uniCloud", + "云函数模板" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": ["uni-config-center","uni-captcha","uni-id"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "u", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "u", + "Android Browser": "u", + "微信浏览器(Android)": "u", + "QQ浏览器(Android)": "u" + }, + "H5-pc": { + "Chrome": "u", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "u", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-id-cf/readme.md b/uni_modules/uni-id-cf/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..75be0bbf87be33a4b886206d91c66f3424c033f0 --- /dev/null +++ b/uni_modules/uni-id-cf/readme.md @@ -0,0 +1,7 @@ +#### uni-id-cf是uni-id-uniCloudFunction的缩写。 + +#### 直接调用他内置的云函数,即可直接使用uni-id的各类api。 + +含:登录注册(含用户名密码登录、手机号验证码登录、app一键登录、微信登录、Apple登录、微信小程序登录)、修改密码、忘记密码、退出登录等 + +> 详细的使用方式见[uni-starter](https://ext.dcloud.net.cn/plugin?id=5057) \ No newline at end of file diff --git a/uni_modules/uni-id-cf/uniCloud/cloudfunctions/uni-id-cf/index.js b/uni_modules/uni-id-cf/uniCloud/cloudfunctions/uni-id-cf/index.js new file mode 100644 index 0000000000000000000000000000000000000000..be4876186b99029282bc9196932c042672b0fa5e --- /dev/null +++ b/uni_modules/uni-id-cf/uniCloud/cloudfunctions/uni-id-cf/index.js @@ -0,0 +1,580 @@ +'use strict'; +let uniID = require('uni-id') +const uniCaptcha = require('uni-captcha') +const createConfig = require('uni-config-center') +const uniIdConfig = createConfig({ + pluginId: 'uni-id' +}).config() +const db = uniCloud.database() +const dbCmd = db.command +const usersDB = db.collection('uni-id-users') +exports.main = async (event, context) => { + //UNI_WYQ:这里的uniID换成新的,保证多人访问不会冲突 + uniID = uniID.createInstance({ + context + }) + console.log('event : ' + JSON.stringify(event)) + /* + 1.event为客户端 uniCloud.callFunction填写的data的值,这里介绍一下其中的属性 + action:表示要执行的任务名称、比如:登录login、退出登录 logout等 + params:业务数据内容 + uniIdToken:系统自动传递的token,数据来源客户端的 uni.getStorageSync('uni_id_token') + */ + const { + action, + uniIdToken, + inviteCode + } = event; + const deviceInfo = event.deviceInfo || {}; + let params = event.params || {}; + /* + 2.在某些操作之前我们要对用户对身份进行校验(也就是要检查用户的token)再将得到的uid写入params.uid + 校验用到的方法是uniID.checkToken 详情:https://uniapp.dcloud.io/uniCloud/uni-id?id=checktoken + + 讨论,我们假设一个这样的场景,代码如下。 + 如: + uniCloud.callFunction({ + name:"xxx", + data:{ + "params":{ + uid:"通过某种方式获取来的别人的uid" + } + } + }) + 用户就这样轻易地伪造了他人的uid传递给服务端,有一句话叫:前端传来的数据都是不可信任的 + 所以这里我们需要将uniID.checkToken返回的uid写入到params.uid + */ + let noCheckAction = ['register', 'checkToken', 'login', 'logout', 'sendSmsCode', 'createCaptcha', + 'verifyCaptcha', 'refreshCaptcha', 'inviteLogin', 'loginByWeixin', 'loginByUniverify', + 'loginByApple', 'loginBySms', 'resetPwdBySmsCode', 'registerAdmin' + ] + if (!noCheckAction.includes(action)) { + if (!uniIdToken) { + return { + code: 403, + msg: '缺少token' + } + } + let payload = await uniID.checkToken(uniIdToken) + if (payload.code && payload.code > 0) { + return payload + } + params.uid = payload.uid + } + + //禁止前台用户传递角色 + if (action.slice(0, 7) == "loginBy") { + if (params.role) { + return { + code: 403, + msg: '禁止前台用户传递角色' + } + } + } + + // 3.注册成功后触发。 + async function registerSuccess(uid) { + //用户接受邀请 + if (inviteCode) { + await uniID.acceptInvite({ + inviteCode, + uid + }); + } + //添加当前用户设备信息 + await db.collection('uni-id-device').add({ + ...deviceInfo, + user_id: uid + }) + } + //4.记录成功登录的日志方法 + const loginLog = async (res = {}) => { + const now = Date.now() + const uniIdLogCollection = db.collection('uni-id-log') + let logData = { + deviceId: params.deviceId || context.DEVICEID, + ip: params.ip || context.CLIENTIP, + type: res.type, + ua: context.CLIENTUA, + create_date: now + }; + + if(res.code === 0){ + logData.user_id = res.uid + logData.state = 1 + if(res.userInfo&&res.userInfo.password){ + delete res.userInfo.password + } + if (res.type == 'register') { + await registerSuccess(res.uid) + } else { + if (Object.keys(deviceInfo).length) { + // console.log(979797, { + // deviceInfo, + // user_id: res + // }); + //更新当前用户设备信息 + await db.collection('uni-id-device').where({ + user_id: res.uid + }).update(deviceInfo) + } + } + }else{ + logData.state = 0 + } + return await uniIdLogCollection.add(logData) + } + + let res = {} + switch (action) { //根据action的值执行对应的操作 + case 'refreshSessionKey': + let getSessionKey = await uniID.code2SessionWeixin({code:params.code}); + if(getSessionKey.code){ + return getSessionKey + } + res = await uniID.updateUser({ + uid: params.uid, + sessionKey:getSessionKey.sessionKey + }) + console.log(res); + break; + case 'bindMobileByMpWeixin': + console.log(params); + let getSessionKeyRes = await uniID.getUserInfo({ + uid: params.uid, + field: ['sessionKey'] + }) + if(getSessionKeyRes.code){ + return getSessionKeyRes + } + let sessionKey = getSessionKeyRes.userInfo.sessionKey + console.log(getSessionKeyRes); + res = await uniID.wxBizDataCrypt({ + ...params, + sessionKey + }) + console.log(res); + if(res.code){ + return res + } + res = await uniID.bindMobile({ + uid: params.uid, + mobile: res.purePhoneNumber + }) + console.log(res); + break; + case 'bindMobileByUniverify': + let { + appid, apiKey, apiSecret + } = uniIdConfig.service.univerify + let univerifyRes = await uniCloud.getPhoneNumber({ + provider: 'univerify', + appid, + apiKey, + apiSecret, + access_token: params.access_token, + openid: params.openid + }) + if (univerifyRes.code === 0) { + res = await uniID.bindMobile({ + uid: params.uid, + mobile: univerifyRes.phoneNumber + }) + res.mobile = univerifyRes.phoneNumber + } + break; + case 'bindMobileBySms': + // console.log({ + // uid: params.uid, + // mobile: params.mobile, + // code: params.code + // }); + res = await uniID.bindMobile({ + uid: params.uid, + mobile: params.mobile, + code: params.code + }) + // console.log(res); + break; + case 'register': + var { + username, password, nickname + } = params + if (/^1\d{10}$/.test(username)) { + return { + code: 401, + msg: '用户名不能是手机号' + } + }; + if (/^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/.test(username)) { + return { + code: 401, + msg: '用户名不能是邮箱' + } + } + res = await uniID.register({ + username, + password, + nickname, + inviteCode + }); + if (res.code === 0) { + await registerSuccess(res.uid) + } + break; + case 'login': + //防止黑客恶意破解登录,连续登录失败一定次数后,需要用户提供验证码 + const getNeedCaptcha = async () => { + //当用户最近“2小时内(recordDate)”登录失败达到2次(recordSize)时。要求用户提交验证码 + const now = Date.now(), + recordDate = 120 * 60 * 1000, + recordSize = 2; + const uniIdLogCollection = db.collection('uni-id-log') + let recentRecord = await uniIdLogCollection.where({ + deviceId: params.deviceId || context.DEVICEID, + create_date: dbCmd.gt(now - recordDate), + type: 'login' + }) + .orderBy('create_date', 'desc') + .limit(recordSize) + .get(); + return recentRecord.data.filter(item => item.state === 0).length === recordSize; + } + + let passed = false; + let needCaptcha = await getNeedCaptcha(); + console.log('needCaptcha', needCaptcha); + if (needCaptcha) { + res = await uniCaptcha.verify({ + ...params, + scene: 'login' + }) + if (res.code === 0) passed = true; + } + + if (!needCaptcha || passed) { + res = await uniID.login({ + ...params, + queryField: ['username', 'email', 'mobile'] + }); + res.type = 'login' + await loginLog(res); + needCaptcha = await getNeedCaptcha(); + } + + res.needCaptcha = needCaptcha; + break; + case 'loginByWeixin': + let loginRes = await uniID.loginByWeixin(params); + if(loginRes.code===0){ + //用户完善资料(昵称、头像) + if(context.PLATFORM == "app-plus" && !loginRes.userInfo.nickname){ + let {accessToken:access_token,openid} = loginRes, + {appid,appsecret:secret} = uniIdConfig['app-plus'].oauth.weixin; + let wxRes = await uniCloud.httpclient.request( + `https://api.weixin.qq.com/sns/userinfo?access_token=${access_token}&openid=${openid}&scope=snsapi_userinfo&appid=${appid}&secret=${secret}`, { + method: 'POST', + contentType: 'json', // 指定以application/json发送data内的数据 + dataType: 'json' // 指定返回值为json格式,自动进行parse + }) + if(wxRes.status == 200){ + let {nickname,headimgurl} = wxRes.data; + let headimgurlFile = {},cloudPath = loginRes.uid+'/'+Date.now()+"headimgurl.jpg"; + let getImgBuffer = await uniCloud.httpclient.request(headimgurl) + if(getImgBuffer.status == 200){ + let {fileID} = await uniCloud.uploadFile({ + cloudPath, + fileContent: getImgBuffer.data + }); + headimgurlFile = { + name:cloudPath, + extname:"jpg", + url:fileID + } + }else{ + return getImgBuffer + } + await uniID.updateUser({ + uid: loginRes.uid, + nickname, + avatar_file:headimgurlFile + }) + loginRes.userInfo.nickname = nickname; + loginRes.userInfo.avatar_file = headimgurlFile; + }else{ + return wxRes + } + } + if(context.PLATFORM == "mp-weixin"){ + let resUpdateUser = await uniID.updateUser({ + uid: loginRes.uid, + sessionKey:loginRes.sessionKey + }) + console.log(resUpdateUser); + } + delete loginRes.openid + delete loginRes.sessionKey + delete loginRes.accessToken + delete loginRes.refreshToken + } + await loginLog(loginRes) + return loginRes + break; + case 'loginByUniverify': + res = await uniID.loginByUniverify(params) + await loginLog(res) + break; + case 'loginByApple': + res = await uniID.loginByApple(params) + await loginLog(res) + break; + case 'checkToken': + res = await uniID.checkToken(uniIdToken); + break; + case 'logout': + res = await uniID.logout(uniIdToken) + break; + case 'sendSmsCode': + /* -开始- 测试期间,为节约资源。统一虚拟短信验证码为: 123456;开启以下代码块即可 */ + return uniID.setVerifyCode({ + mobile: params.mobile, + code: '123456', + type: params.type + }) + /* -结束- */ + + // 简单限制一下客户端调用频率 + const ipLimit = await db.collection('opendb-verify-codes').where({ + ip: context.CLIENTIP, + created_at: dbCmd.gt(Date.now() - 60000) + }).get() + if (ipLimit.data.length > 0) { + return { + code: 429, + msg: '请求过于频繁' + } + } + const templateId = '11753' // 替换为自己申请的模板id + if (!templateId) { + return { + code: 500, + msg: 'sendSmsCode需要传入自己的templateId,参考https://uniapp.dcloud.net.cn/uniCloud/uni-id?id=sendsmscode' + } + } + const randomStr = '00000' + Math.floor(Math.random() * 1000000) + const code = randomStr.substring(randomStr.length - 6) + res = await uniID.sendSmsCode({ + mobile: params.mobile, + code, + type: params.type, + templateId + }) + break; + case 'loginBySms': + if (!params.code) { + return { + code: 500, + msg: '请填写验证码' + } + } + if (!/^1\d{10}$/.test(params.mobile)) { + return { + code: 500, + msg: '手机号码填写错误' + } + } + res = await uniID.loginBySms(params) + await loginLog(res) + break; + case 'resetPwdBySmsCode': + if (!params.code) { + return { + code: 500, + msg: '请填写验证码' + } + } + if (!/^1\d{10}$/.test(params.mobile)) { + return { + code: 500, + msg: '手机号码填写错误' + } + } + params.type = 'login' + let loginBySmsRes = await uniID.loginBySms(params) + // console.log(loginBySmsRes); + if (loginBySmsRes.code === 0) { + res = await uniID.resetPwd({ + password: params.password, + "uid": loginBySmsRes.uid + }) + } else { + return loginBySmsRes + } + break; + case 'getInviteCode': + res = await uniID.getUserInfo({ + uid: params.uid, + field: ['my_invite_code'] + }) + if (res.code === 0) { + res.myInviteCode = res.userInfo.my_invite_code + delete res.userInfo + } + break; + case 'getInvitedUser': + res = await uniID.getInvitedUser(params) + break; + case 'updatePwd': + res = await uniID.updatePwd(params) + break; + case 'createCaptcha': + res = await uniCaptcha.create(params) + break; + case 'refreshCaptcha': + res = await uniCaptcha.refresh(params) + break; + case 'getUserInviteCode': + res = await uniID.getUserInfo({ + uid: params.uid, + field: ['my_invite_code'] + }) + if (!res.userInfo.my_invite_code) { + res = await uniID.setUserInviteCode({ + uid: params.uid + }) + } + break; + + // =========================== admin api start ========================= + case 'registerAdmin': { + var { + username, + password + } = params + let { + total + } = await db.collection('uni-id-users').where({ + role: 'admin' + }).count() + if (total) { + return { + code: 10001, + message: '超级管理员已存在,请登录...' + } + } + const appid = params.appid + const appName = params.appName + delete params.appid + delete params.appName + res = await uniID.register({ + username, + password, + role: ["admin"] + }) + if (res.code === 0) { + const app = await db.collection('opendb-app-list').where({ + appid + }).count() + if (!app.total) { + await db.collection('opendb-app-list').add({ + appid, + name: appName, + description: "admin 管理后台", + create_date: Date.now() + }) + } + + } + } + break; + case 'registerUser': + const { + userInfo + } = await uniID.getUserInfo({ + uid: params.uid + }) + if (userInfo.role.indexOf('admin') === -1) { + res = { + code: 403, + message: '非法访问, 无权限注册超级管理员', + } + } else { + // 过滤 dcloud_appid,注册用户成功后再提交 + const dcloudAppidList = params.dcloud_appid + delete params.dcloud_appid + res = await uniID.register({ + autoSetDcloudAppid: false, + ...params + }) + if (res.code === 0) { + delete res.token + delete res.tokenExpired + await uniID.setAuthorizedAppLogin({ + uid: res.uid, + dcloudAppidList + }) + } + } + break; + case 'updateUser': { + const { + userInfo + } = await uniID.getUserInfo({ + uid: params.uid + }) + if (userInfo.role.indexOf('admin') === -1) { + res = { + code: 403, + message: '非法访问, 无权限注册超级管理员', + } + } else { + // 过滤 dcloud_appid,注册用户成功后再提交 + const dcloudAppidList = params.dcloud_appid + delete params.dcloud_appid + + // 过滤 password,注册用户成功后再提交 + const password = params.password + delete params.password + + // 过滤 uid、id + const id = params.id + delete params.id + delete params.uid + + + res = await uniID.updateUser({ + uid: id, + ...params + }) + if (res.code === 0) { + if (password) { + await uniID.resetPwd({ + uid: id, + password + }) + } + await uniID.setAuthorizedAppLogin({ + uid: id, + dcloudAppidList + }) + } + } + break; + } + case 'getCurrentUserInfo': + res = await uniID.getUserInfo({ + uid: params.uid, + ...params + }) + break; + // =========================== admin api end ========================= + default: + res = { + code: 403, + msg: '非法访问' + } + break; + } + //返回数据给客户端 + return res +} diff --git a/uni_modules/uni-id-cf/uniCloud/cloudfunctions/uni-id-cf/package.json b/uni_modules/uni-id-cf/uniCloud/cloudfunctions/uni-id-cf/package.json new file mode 100644 index 0000000000000000000000000000000000000000..e6ae55cae62890eb32cd9925b596e662e95c233a --- /dev/null +++ b/uni_modules/uni-id-cf/uniCloud/cloudfunctions/uni-id-cf/package.json @@ -0,0 +1,16 @@ +{ + "name": "uni-id-cf", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "uni-captcha": "file:../../../../uni-captcha/uniCloud/cloudfunctions/common/uni-captcha", + "uni-config-center": "file:../../../../uni-config-center/uniCloud/cloudfunctions/common/uni-config-center", + "uni-id": "file:../../../../uni-id/uniCloud/cloudfunctions/common/uni-id" + } +} diff --git a/uni_modules/uni-id/changelog.md b/uni_modules/uni-id/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..9615838f736e2d792350121eb9e3fb26d08b9034 --- /dev/null +++ b/uni_modules/uni-id/changelog.md @@ -0,0 +1,54 @@ +## 3.3.6(2021-09-08) +- 修复 邀请码可能重复的Bug +## 3.3.5(2021-08-10) +- 修复版本号错误 +## 3.3.4(2021-08-10) +- 微信、QQ、支付宝登录新增type参数用于指定当前是登录还是注册 +## 3.3.3(2021-08-04) +- 修复使用数组形式的配置文件报错的Bug +## 3.3.2(2021-08-03) +- 修复上3.3.0版本引出的createInstance接口传入配置不生效的Bug 感谢[hmh](https://gitee.com/hmh) +## 3.3.1(2021-07-30) +- 修复 将设置用户允许登录的应用列表时传入空数组报错的Bug +## 3.3.0(2021-07-30) +- 新增 不同端应用配置隔离 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id?id=isolate-config) +- 新增 不同端用户隔离 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id?id=isolate-user) + + 此版本升级需要开发者处理一下用户数据,请参考 [补齐用户dcloud_appid字段](https://uniapp.dcloud.net.cn/uniCloud/uni-id?id=makeup-dcloud-appid) +- 新增 QQ登录、注册相关功能 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id?id=qq) +- 调整 不再支持绑定手机、邮箱时不填验证码直接绑定 +## 3.2.1(2021-07-09) +- 撤销3.2.0版本所做的调整 +## 3.2.0(2021-07-09) +- 【重要】支持不同端(管理端、用户端等)用户隔离 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id?id=isolate-user) +- 支持不同端(管理端、用户端等)配置文件隔离 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id?id=isolate-config) +## 3.1.3(2021-07-08) +- 移除插件内误传的node_modules +## 3.1.2(2021-07-08) +- 修复 微信小程序绑定微信账号时报错的Bug +## 3.1.1(2021-07-01) +- 使用新的错误码规范,兼容旧版 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id?id=errcode) +- 修复微信登录、绑定时未返回用户accessToken的Bug +## 3.1.0(2021-04-19) +- 增加对用户名、邮箱、密码字段的两端去空格 +- 默认忽略用户名、邮箱的大小写 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id?id=case-sensitive) +- 修复 customToken导出async方法报错的Bug +## 3.0.12(2021-04-13) +- 调整bindTokenToDevice默认值为false +## 3.0.11(2021-04-12) +- 修复3.0.7版本引出的多个用户访问时可能出现30201报错的Bug +## 3.0.10(2021-04-08) +- 优化错误提示 +## 3.0.9(2021-04-08) +- bindMobile接口支持通过一键登录的方式绑定 +- 优化错误提示 +## 3.0.8(2021-03-19) +- 修复 3.0.7版本某些情况下生成token报错的Bug +## 3.0.7(2021-03-19) +- 新增 支持uni-config-center,更新uni-id无须再担心配置被覆盖 [详情](https://uniapp.dcloud.io/uniCloud/uni-id?id=uni-config-center) +- 新增 自定义token内容,可以缓存角色权限之外的更多信息到客户端 [详情](https://uniapp.dcloud.io/uniCloud/uni-id?id=custom-token) +- 新增 支持传入context获取uni-id实例,防止单实例多并发时全局context混乱 [详情](https://uniapp.dcloud.io/uniCloud/uni-id?id=create-instance) +## 3.0.6(2021-03-05) +- 新增[uniID.wxBizDataCrypt](https://uniapp.dcloud.io/uniCloud/uni-id?id=%e5%be%ae%e4%bf%a1%e6%95%b0%e6%8d%ae%e8%a7%a3%e5%af%86)方法 +- 优化loginByApple方法,提高接口响应速度 +## 3.0.5(2021-02-03) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-id/package.json b/uni_modules/uni-id/package.json new file mode 100644 index 0000000000000000000000000000000000000000..f94bee59b94056085e9fa323d28c426a43e44bca --- /dev/null +++ b/uni_modules/uni-id/package.json @@ -0,0 +1,84 @@ +{ + "id": "uni-id", + "displayName": "uni-id", + "version": "3.3.6", + "description": "简单、统一、可扩展的用户中心", + "keywords": [ + "uniid", + "uni-id", + "用户管理", + "用户中心", + "短信验证码" +], + "repository": "https://gitee.com/dcloud/uni-id.git", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "uniCloud", + "云函数模板" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": ["uni-config-center"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "u", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "u", + "Android Browser": "u", + "微信浏览器(Android)": "u", + "QQ浏览器(Android)": "u" + }, + "H5-pc": { + "Chrome": "u", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "u", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-id/readme.md b/uni_modules/uni-id/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..ea7751ccf07d57da8ea31887d3ca0562d20efba6 --- /dev/null +++ b/uni_modules/uni-id/readme.md @@ -0,0 +1,33 @@ +**文档已移至[uni-id文档](https://uniapp.dcloud.net.cn/uniCloud/uni-id)** + +> 一般uni-id升级大版本时为不兼容更新,从低版本迁移到高版本请参考:[uni-id迁移指南](https://uniapp.dcloud.net.cn/uniCloud/uni-id?id=migration) + +## 重要升级说明 + +**uni-id 3.x版本,搭配的uniCloud admin版本需大于1.2.10。** + +### 缓存角色权限 + +自`uni-id 3.0.0`起,支持在token内缓存用户的角色权限,默认开启此功能,各登录接口的needPermission参数不再生效。如需关闭请在config内配置`"removePermissionAndRoleFromToken": true`。 + +为什么要缓存角色权限?要知道云数据库是按照读写次数来收取费用的,并且读写数据库会拖慢接口响应速度。未配置`"removePermissionAndRoleFromToken": true`的情况下,可以在调用checkToken接口时不查询数据库获取用户角色权限。 + +详细checkToken流程如下: + +![](https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/ed45d350-5a4d-11eb-b997-9918a5dda011.jpg) + +可以看出,旧版token(removePermissionAndRoleFromToken为true时生成的)在checkToken时如需返回权限需要进行两次数据库查询。新版token不需要查库即可返回权限信息。 + +**注意** + +- 由于角色权限缓存在token内,可能会存在权限已经更新但是用户token未过期之前依然是旧版角色权限的情况。可以调短一些token过期时间来减少这种情况的影响。 +- admin角色token内不包含permission,如需自行判断用户是否有某个权限,要注意admin角色需要额外判断一下,写法如下 + ```js + const { + role, + permission + } = await uniID.checkToken(event.uniIdToken) + if(role.includes('admin') || permission.includes('your permission id')) { + // 当前角色拥有'your permission id'对应的权限 + } + ``` \ No newline at end of file diff --git a/uni_modules/uni-id/uniCloud/cloudfunctions/common/uni-id/LICENSE.md b/uni_modules/uni-id/uniCloud/cloudfunctions/common/uni-id/LICENSE.md new file mode 100644 index 0000000000000000000000000000000000000000..261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64 --- /dev/null +++ b/uni_modules/uni-id/uniCloud/cloudfunctions/common/uni-id/LICENSE.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/uni_modules/uni-id/uniCloud/cloudfunctions/common/uni-id/index.js b/uni_modules/uni-id/uniCloud/cloudfunctions/common/uni-id/index.js new file mode 100644 index 0000000000000000000000000000000000000000..2888a9124f35ab223a81156e1cc85309ef7ff8a1 --- /dev/null +++ b/uni_modules/uni-id/uniCloud/cloudfunctions/common/uni-id/index.js @@ -0,0 +1 @@ +"use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e.default:e}var t=e(require("fs")),r=e(require("path")),n=e(require("crypto")),i=e(require("querystring")),o=e(require("buffer")),s=e(require("stream")),a=e(require("util"));const c=Object.prototype.toString,u=Object.prototype.hasOwnProperty;var d=/[\\^$.*+?()[\]{}|]/g,l=RegExp(d.source);function p(e,t,r){return e.replace(new RegExp((n=t)&&l.test(n)?n.replace(d,"\\$&"):n,"g"),r);var n}function f(e,t){return u.call(e,t)}function m(e){return"[object Object]"===c.call(e)}function h(e){return"function"==typeof e}function g(e){return!!e&&("object"==typeof e||"function"==typeof e)&&"function"==typeof e.then}function y(e){return Object.prototype.toString.call(e).slice(8,-1).toLowerCase()}const w=/_(\w)/g,_=/[A-Z]/g;function v(e){return e.replace(w,(e,t)=>t?t.toUpperCase():"")}function b(e){return e.replace(_,e=>"_"+e.toLowerCase())}function E(e,t){let r,n;switch(t){case"snake2camel":n=v,r=w;break;case"camel2snake":n=b,r=_}for(const i in e)if(f(e,i)&&r.test(i)){const r=n(i);e[r]=e[i],delete e[i],m(e[r])?e[r]=E(e[r],t):Array.isArray(e[r])&&(e[r]=e[r].map(e=>E(e,t)))}return e}function C(e){return E(e,"snake2camel")}function T(e){return E(e,"camel2snake")}function A(e){return function(e,t="-"){e=e||new Date;const r=[];return r.push(e.getFullYear()),r.push(("00"+(e.getMonth()+1)).substr(-2)),r.push(("00"+e.getDate()).substr(-2)),r.join(t)}(e=e||new Date)+" "+function(e,t=":"){e=e||new Date;const r=[];return r.push(("00"+e.getHours()).substr(-2)),r.push(("00"+e.getMinutes()).substr(-2)),r.push(("00"+e.getSeconds()).substr(-2)),r.join(t)}(e)}function S(){"development"===process.env.NODE_ENV&&console.log(...arguments)}function I(e=6){let t="";for(let r=0;r0:t=d,r="年";break;case u>0:t=u,r="月";break;case c>0:t=c,r="天";break;case a>0:t=a,r="小时";break;case s>0:t=s,r="分钟";break;default:t=o,r="秒"}return`${t}${r}${i}`}function U(e){if(m(e))if(0===e.code)e.errCode=e.code,e.message=e.errMsg=e.msg,delete e.messageValues;else if(f(R,e.code)){const t=R[e.code];e.errCode="uni-id-"+t.errCode,e.errDetail=`${e.code}, ${e.msg}`,e.errMsg=function(e,t={}){const r=R[e];if(!r)throw new Error("unsupported error code: "+e);let n=r.errMsg;return Object.keys(t).forEach(e=>{n=p(n,`{${e}}`,t[e])}),n}(e.code,e.messageValues||{})||e.msg,e.message=e.msg=e.errMsg,delete e.messageValues}else e.code&&console.warn(`error code not found, error code: ${e.code}, please contact us`)}const V=uniCloud.database(),M=V.collection("uni-id-users"),L=V.collection("opendb-verify-codes"),B=V.collection("uni-id-roles"),q=V.collection("uni-id-permissions"),$={username:"用户名",mobile:"手机号",email:"邮箱",wx_unionid:"微信账号","wx_openid.app-plus":"微信账号","wx_openid.mp-weixin":"微信账号",qq_unionid:"QQ账号","qq_openid.app-plus":"QQ账号","qq_openid.mp-weixin":"QQ账号",ali_openid:"支付宝账号",apple_openid:"苹果账号"},F=90002,K=90003,H=90004,G=90005;async function Q({name:e,url:t,data:r,options:n,defaultOptions:i}){let o={};const s=T(Object.assign({},r));s&&s.access_token&&delete s.access_token;try{n=Object.assign({},i,n,{data:s}),o=await uniCloud.httpclient.request(t,n)}catch(t){return function(e,t){throw new P({code:t.code||-2,message:t.message||e+" fail"})}(e,t)}let a=o.data;const c=o.headers["content-type"];if(!Buffer.isBuffer(a)||0!==c.indexOf("text/plain")&&0!==c.indexOf("application/json"))Buffer.isBuffer(a)&&(a={buffer:a,contentType:c});else try{a=JSON.parse(a.toString())}catch(e){a=a.toString()}return C(function(e,t){if(t.errcode)throw new P({code:t.errcode||-2,message:t.errmsg||e+" fail"});return delete t.errcode,delete t.errmsg,{...t,errMsg:e+" ok",errCode:0}}(e,a||{errCode:-2,errMsg:"Request failed"}))}function Y(e,t){let r="";if(t&&t.accessToken){r=`${e.indexOf("?")>-1?"&":"?"}access_token=${t.accessToken}`}return`${e}${r}`}class J{constructor(e){this.options=Object.assign({baseUrl:"https://api.weixin.qq.com",timeout:5e3},e)}async _requestWxOpenapi({name:e,url:t,data:r,options:n}){const i={method:"GET",dataType:"json",dataAsQueryString:!0,timeout:this.options.timeout};return await Q({name:"auth."+e,url:`${this.options.baseUrl}${Y(t,r)}`,data:r,options:n,defaultOptions:i})}async code2Session(e){return await this._requestWxOpenapi({name:"code2Session",url:"/sns/jscode2session",data:{grant_type:"authorization_code",appid:this.options.appId,secret:this.options.secret,js_code:e}})}async getOauthAccessToken(e){const t=await this._requestWxOpenapi({name:"getOauthAccessToken",url:"/sns/oauth2/access_token",data:{grant_type:"authorization_code",appid:this.options.appId,secret:this.options.secret,code:e}});return t.expiresIn&&(t.expired=Date.now()+t.expiresIn),t}}async function z({name:e,url:t,data:r,options:n,defaultOptions:i}){let o;n=Object.assign({},i,n,{data:T(Object.assign({},r))});try{o=await uniCloud.httpclient.request(t,n)}catch(t){return function(e,t){throw new P({code:t.code||-2,message:t.message||e+" fail"})}(e,t)}let s=o.data;const a=o.headers["content-type"];if(!Buffer.isBuffer(s)||0!==a.indexOf("text/plain")&&0!==a.indexOf("application/json"))Buffer.isBuffer(s)&&(s={buffer:s,contentType:a});else try{s=JSON.parse(s.toString())}catch(e){s=s.toString()}return C(function(e,t){if(t.ret||t.error){const r=t.ret||t.error||t.errcode||-2,n=t.msg||t.error_description||t.errmsg||e+" fail";throw new P({code:r,message:n})}return delete t.ret,delete t.msg,delete t.error,delete t.error_description,delete t.errcode,delete t.errmsg,{...t,errMsg:e+" ok",errCode:0}}(e,s||{errCode:-2,errMsg:"Request failed"}))}class W{constructor(e){this.options=Object.assign({baseUrl:"https://graph.qq.com",timeout:5e3},e)}async _requestQQOpenapi({name:e,url:t,data:r,options:n}){const i={method:"GET",dataType:"json",dataAsQueryString:!0,timeout:this.options.timeout};return await z({name:"auth."+e,url:k(this.options.baseUrl,t),data:r,options:n,defaultOptions:i})}async getOpenidByToken({accessToken:e}={}){const t=await this._requestQQOpenapi({name:"getOpenidByToken",url:"/oauth2.0/me",data:{accessToken:e,unionid:1,fmt:"json"}});if(t.clientId!==this.options.appId)throw new P({code:"APPID_NOT_MATCH",message:"获取openid失败,appid不匹配"});return{openid:t.openid,unionid:t.unionid}}async code2Session({code:e}={}){return await this._requestQQOpenapi({name:"getOpenidByToken",url:"https://api.q.qq.com/sns/jscode2session",data:{grant_type:"authorization_code",appid:this.options.appId,secret:this.options.secret,js_code:e}})}}const X={RSA:"RSA-SHA1",RSA2:"RSA-SHA256"};var Z={code2Session:{returnValue:{openid:"userId"}}};class ee extends class{constructor(e={}){if(!e.appId)throw new Error("appId required");if(!e.privateKey)throw new Error("privateKey required");const t={gateway:"https://openapi.alipay.com/gateway.do",timeout:5e3,charset:"utf-8",version:"1.0",signType:"RSA2",timeOffset:-(new Date).getTimezoneOffset()/60,keyType:"PKCS8"};e.sandbox&&(e.gateway="https://openapi.alipaydev.com/gateway.do"),this.options=Object.assign({},t,e);const r="PKCS8"===this.options.keyType?"PRIVATE KEY":"RSA PRIVATE KEY";this.options.privateKey=this._formatKey(this.options.privateKey,r),this.options.alipayPublicKey&&(this.options.alipayPublicKey=this._formatKey(this.options.alipayPublicKey,"PUBLIC KEY"))}_formatKey(e,t){return`-----BEGIN ${t}-----\n${e}\n-----END ${t}-----`}_formatUrl(e,t){let r=e;const n=["app_id","method","format","charset","sign_type","sign","timestamp","version","notify_url","return_url","auth_token","app_auth_token"];for(const e in t)if(n.indexOf(e)>-1){const n=encodeURIComponent(t[e]);r=`${r}${r.includes("?")?"&":"?"}${e}=${n}`,delete t[e]}return{execParams:t,url:r}}_getSign(e,t){const r=t.bizContent||null;delete t.bizContent;const i=Object.assign({method:e,appId:this.options.appId,charset:this.options.charset,version:this.options.version,signType:this.options.signType,timestamp:A((o=this.options.timeOffset,new Date(Date.now()+6e4*((new Date).getTimezoneOffset()+60*(o||0)))))},t);var o;r&&(i.bizContent=JSON.stringify(T(r)));const s=T(i),a=Object.keys(s).sort().map(e=>{let t=s[e];return"[object String]"!==Array.prototype.toString.call(t)&&(t=JSON.stringify(t)),`${e}=${t}`}).join("&"),c=n.createSign(X[this.options.signType]).update(a,"utf8").sign(this.options.privateKey,"base64");return Object.assign(s,{sign:c})}async _exec(e,t={},r={}){const n=this._getSign(e,t),{url:i,execParams:o}=this._formatUrl(this.options.gateway,n),{status:s,data:a}=await uniCloud.httpclient.request(i,{method:"POST",data:o,dataType:"text",timeout:this.options.timeout});if(200!==s)throw new Error("request fail");const c=JSON.parse(a),u=e.replace(/\./g,"_")+"_response",d=c[u],l=c.error_response;if(d){if(!r.validateSign||this._checkResponseSign(a,u)){if(!d.code||"10000"===d.code){return{errCode:0,errMsg:d.msg||"",...C(d)}}const e=d.sub_code?`${d.sub_code} ${d.sub_msg}`:""+(d.msg||"unkonwn error");throw new Error(e)}throw new Error("返回结果签名错误")}if(l)throw new Error(l.sub_msg||l.msg||"接口返回错误");throw new Error("request fail")}_checkResponseSign(e,t){if(!this.options.alipayPublicKey||""===this.options.alipayPublicKey)return console.warn("options.alipayPublicKey is empty"),!0;if(!e)return!1;const r=this._getSignStr(e,t),i=JSON.parse(e).sign,o=n.createVerify(X[this.options.signType]);return o.update(r,"utf8"),o.verify(this.options.alipayPublicKey,i,"base64")}_getSignStr(e,t){let r=e.trim();const n=e.indexOf(t+'"'),i=e.lastIndexOf('"sign"');return r=r.substr(n+t.length+1),r=r.substr(0,i),r=r.replace(/^[^{]*{/g,"{"),r=r.replace(/\}([^}]*)$/g,"}"),r}_notifyRSACheck(e,t,r){const i=Object.keys(e).sort().filter(e=>e).map(t=>{let r=e[t];return"[object String]"!==Array.prototype.toString.call(r)&&(r=JSON.stringify(r)),`${t}=${decodeURIComponent(r)}`}).join("&");return n.createVerify(X[r]).update(i,"utf8").verify(this.options.alipayPublicKey,t,"base64")}_checkNotifySign(e){const t=e.sign;if(!this.options.alipayPublicKey||!t)return!1;const r=e.sign_type||this.options.signType||"RSA2",n={...e};delete n.sign,n.sign_type=r;return!!this._notifyRSACheck(n,t,r)||(delete n.sign_type,this._notifyRSACheck(n,t,r))}_verifyNotify(e){if(!e.headers)throw new Error("通知格式不正确");let t;for(const r in e.headers)"content-type"===r.toLowerCase()&&(t=e.headers[r]);if(!1!==e.isBase64Encoded&&-1===t.indexOf("application/x-www-form-urlencoded"))throw new Error("通知格式不正确");const r=i.parse(e.body);if(this._checkNotifySign(r))return C(r);throw new Error("通知验签未通过")}}{constructor(e){super(e),this._protocols=Z}async code2Session(e){return await this._exec("alipay.system.oauth.token",{grantType:"authorization_code",code:e})}}function te(e){var t=e[0];return t<"0"||t>"7"?"00"+e:e}function re(e){var t=e.toString(16);return t.length%2?"0"+t:t}function ne(e){if(e<=127)return re(e);var t=re(e);return re(128+t.length/2)+t}function ie(e,t){return e(t={exports:{}},t.exports),t.exports}var oe=ie((function(e,t){var r=o.Buffer;function n(e,t){for(var r in e)t[r]=e[r]}function i(e,t,n){return r(e,t,n)}r.from&&r.alloc&&r.allocUnsafe&&r.allocUnsafeSlow?e.exports=o:(n(o,t),t.Buffer=i),i.prototype=Object.create(r.prototype),n(r,i),i.from=function(e,t,n){if("number"==typeof e)throw new TypeError("Argument must not be a number");return r(e,t,n)},i.alloc=function(e,t,n){if("number"!=typeof e)throw new TypeError("Argument must be a number");var i=r(e);return void 0!==t?"string"==typeof n?i.fill(t,n):i.fill(t):i.fill(0),i},i.allocUnsafe=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return r(e)},i.allocUnsafeSlow=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return o.SlowBuffer(e)}})),se=(oe.Buffer,oe.Buffer);function ae(e){if(this.buffer=null,this.writable=!0,this.readable=!0,!e)return this.buffer=se.alloc(0),this;if("function"==typeof e.pipe)return this.buffer=se.alloc(0),e.pipe(this),this;if(e.length||"object"==typeof e)return this.buffer=e,this.writable=!1,process.nextTick(function(){this.emit("end",e),this.readable=!1,this.emit("close")}.bind(this)),this;throw new TypeError("Unexpected data type ("+typeof e+")")}a.inherits(ae,s),ae.prototype.write=function(e){this.buffer=se.concat([this.buffer,se.from(e)]),this.emit("data",e)},ae.prototype.end=function(e){e&&this.write(e),this.emit("end",e),this.emit("close"),this.writable=!1,this.readable=!1};var ce=ae,ue=o.Buffer,de=o.SlowBuffer,le=pe;function pe(e,t){if(!ue.isBuffer(e)||!ue.isBuffer(t))return!1;if(e.length!==t.length)return!1;for(var r=0,n=0;n=128&&--n,n}var be={derToJose:function(e,t){e=_e(e);var r=ye(t),n=r+1,i=e.length,o=0;if(48!==e[o++])throw new Error('Could not find expected "seq"');var s=e[o++];if(129===s&&(s=e[o++]),i-o0)return function(e){if((e=String(e)).length>100)return;var t=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!t)return;var r=parseFloat(t[1]);switch((t[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return 315576e5*r;case"weeks":case"week":case"w":return 6048e5*r;case"days":case"day":case"d":return r*yt;case"hours":case"hour":case"hrs":case"hr":case"h":return r*gt;case"minutes":case"minute":case"mins":case"min":case"m":return r*ht;case"seconds":case"second":case"secs":case"sec":case"s":return r*mt;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return r;default:return}}(e);if("number"===r&&isFinite(e))return t.long?function(e){var t=Math.abs(e);if(t>=yt)return _t(e,t,yt,"day");if(t>=gt)return _t(e,t,gt,"hour");if(t>=ht)return _t(e,t,ht,"minute");if(t>=mt)return _t(e,t,mt,"second");return e+" ms"}(e):function(e){var t=Math.abs(e);if(t>=yt)return Math.round(e/yt)+"d";if(t>=gt)return Math.round(e/gt)+"h";if(t>=ht)return Math.round(e/ht)+"m";if(t>=mt)return Math.round(e/mt)+"s";return e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))};function _t(e,t,r,n){var i=t>=1.5*r;return Math.round(e/r)+" "+n+(i?"s":"")}var vt=function(e,t){var r=t||Math.floor(Date.now()/1e3);if("string"==typeof e){var n=wt(e);if(void 0===n)return;return Math.floor(r+n/1e3)}return"number"==typeof e?r+e:void 0},bt=ie((function(e,t){var r;t=e.exports=G,r="object"==typeof process&&process.env&&process.env.NODE_DEBUG&&/\bsemver\b/i.test(process.env.NODE_DEBUG)?function(){var e=Array.prototype.slice.call(arguments,0);e.unshift("SEMVER"),console.log.apply(console,e)}:function(){},t.SEMVER_SPEC_VERSION="2.0.0";var n=Number.MAX_SAFE_INTEGER||9007199254740991,i=t.re=[],o=t.src=[],s=0,a=s++;o[a]="0|[1-9]\\d*";var c=s++;o[c]="[0-9]+";var u=s++;o[u]="\\d*[a-zA-Z-][a-zA-Z0-9-]*";var d=s++;o[d]="("+o[a]+")\\.("+o[a]+")\\.("+o[a]+")";var l=s++;o[l]="("+o[c]+")\\.("+o[c]+")\\.("+o[c]+")";var p=s++;o[p]="(?:"+o[a]+"|"+o[u]+")";var f=s++;o[f]="(?:"+o[c]+"|"+o[u]+")";var m=s++;o[m]="(?:-("+o[p]+"(?:\\."+o[p]+")*))";var h=s++;o[h]="(?:-?("+o[f]+"(?:\\."+o[f]+")*))";var g=s++;o[g]="[0-9A-Za-z-]+";var y=s++;o[y]="(?:\\+("+o[g]+"(?:\\."+o[g]+")*))";var w=s++,_="v?"+o[d]+o[m]+"?"+o[y]+"?";o[w]="^"+_+"$";var v="[v=\\s]*"+o[l]+o[h]+"?"+o[y]+"?",b=s++;o[b]="^"+v+"$";var E=s++;o[E]="((?:<|>)?=?)";var C=s++;o[C]=o[c]+"|x|X|\\*";var T=s++;o[T]=o[a]+"|x|X|\\*";var A=s++;o[A]="[v=\\s]*("+o[T]+")(?:\\.("+o[T]+")(?:\\.("+o[T]+")(?:"+o[m]+")?"+o[y]+"?)?)?";var S=s++;o[S]="[v=\\s]*("+o[C]+")(?:\\.("+o[C]+")(?:\\.("+o[C]+")(?:"+o[h]+")?"+o[y]+"?)?)?";var I=s++;o[I]="^"+o[E]+"\\s*"+o[A]+"$";var x=s++;o[x]="^"+o[E]+"\\s*"+o[S]+"$";var k=s++;o[k]="(?:^|[^\\d])(\\d{1,16})(?:\\.(\\d{1,16}))?(?:\\.(\\d{1,16}))?(?:$|[^\\d])";var O=s++;o[O]="(?:~>?)";var R=s++;o[R]="(\\s*)"+o[O]+"\\s+",i[R]=new RegExp(o[R],"g");var P=s++;o[P]="^"+o[O]+o[A]+"$";var D=s++;o[D]="^"+o[O]+o[S]+"$";var j=s++;o[j]="(?:\\^)";var N=s++;o[N]="(\\s*)"+o[j]+"\\s+",i[N]=new RegExp(o[N],"g");var U=s++;o[U]="^"+o[j]+o[A]+"$";var V=s++;o[V]="^"+o[j]+o[S]+"$";var M=s++;o[M]="^"+o[E]+"\\s*("+v+")$|^$";var L=s++;o[L]="^"+o[E]+"\\s*("+_+")$|^$";var B=s++;o[B]="(\\s*)"+o[E]+"\\s*("+v+"|"+o[A]+")",i[B]=new RegExp(o[B],"g");var q=s++;o[q]="^\\s*("+o[A]+")\\s+-\\s+("+o[A]+")\\s*$";var $=s++;o[$]="^\\s*("+o[S]+")\\s+-\\s+("+o[S]+")\\s*$";var F=s++;o[F]="(<|>)?=?\\s*\\*";for(var K=0;K<35;K++)r(K,o[K]),i[K]||(i[K]=new RegExp(o[K]));function H(e,t){if(t&&"object"==typeof t||(t={loose:!!t,includePrerelease:!1}),e instanceof G)return e;if("string"!=typeof e)return null;if(e.length>256)return null;if(!(t.loose?i[b]:i[w]).test(e))return null;try{return new G(e,t)}catch(e){return null}}function G(e,t){if(t&&"object"==typeof t||(t={loose:!!t,includePrerelease:!1}),e instanceof G){if(e.loose===t.loose)return e;e=e.version}else if("string"!=typeof e)throw new TypeError("Invalid Version: "+e);if(e.length>256)throw new TypeError("version is longer than 256 characters");if(!(this instanceof G))return new G(e,t);r("SemVer",e,t),this.options=t,this.loose=!!t.loose;var o=e.trim().match(t.loose?i[b]:i[w]);if(!o)throw new TypeError("Invalid Version: "+e);if(this.raw=e,this.major=+o[1],this.minor=+o[2],this.patch=+o[3],this.major>n||this.major<0)throw new TypeError("Invalid major version");if(this.minor>n||this.minor<0)throw new TypeError("Invalid minor version");if(this.patch>n||this.patch<0)throw new TypeError("Invalid patch version");o[4]?this.prerelease=o[4].split(".").map((function(e){if(/^[0-9]+$/.test(e)){var t=+e;if(t>=0&&t=0;)"number"==typeof this.prerelease[r]&&(this.prerelease[r]++,r=-2);-1===r&&this.prerelease.push(0)}t&&(this.prerelease[0]===t?isNaN(this.prerelease[1])&&(this.prerelease=[t,0]):this.prerelease=[t,0]);break;default:throw new Error("invalid increment argument: "+e)}return this.format(),this.raw=this.version,this},t.inc=function(e,t,r,n){"string"==typeof r&&(n=r,r=void 0);try{return new G(e,r).inc(t,n).version}catch(e){return null}},t.diff=function(e,t){if(X(e,t))return null;var r=H(e),n=H(t),i="";if(r.prerelease.length||n.prerelease.length){i="pre";var o="prerelease"}for(var s in r)if(("major"===s||"minor"===s||"patch"===s)&&r[s]!==n[s])return i+s;return o},t.compareIdentifiers=Y;var Q=/^[0-9]+$/;function Y(e,t){var r=Q.test(e),n=Q.test(t);return r&&n&&(e=+e,t=+t),e===t?0:r&&!n?-1:n&&!r?1:e0}function W(e,t,r){return J(e,t,r)<0}function X(e,t,r){return 0===J(e,t,r)}function Z(e,t,r){return 0!==J(e,t,r)}function ee(e,t,r){return J(e,t,r)>=0}function te(e,t,r){return J(e,t,r)<=0}function re(e,t,r,n){switch(t){case"===":return"object"==typeof e&&(e=e.version),"object"==typeof r&&(r=r.version),e===r;case"!==":return"object"==typeof e&&(e=e.version),"object"==typeof r&&(r=r.version),e!==r;case"":case"=":case"==":return X(e,r,n);case"!=":return Z(e,r,n);case">":return z(e,r,n);case">=":return ee(e,r,n);case"<":return W(e,r,n);case"<=":return te(e,r,n);default:throw new TypeError("Invalid operator: "+t)}}function ne(e,t){if(t&&"object"==typeof t||(t={loose:!!t,includePrerelease:!1}),e instanceof ne){if(e.loose===!!t.loose)return e;e=e.value}if(!(this instanceof ne))return new ne(e,t);r("comparator",e,t),this.options=t,this.loose=!!t.loose,this.parse(e),this.semver===ie?this.value="":this.value=this.operator+this.semver.version,r("comp",this)}t.rcompareIdentifiers=function(e,t){return Y(t,e)},t.major=function(e,t){return new G(e,t).major},t.minor=function(e,t){return new G(e,t).minor},t.patch=function(e,t){return new G(e,t).patch},t.compare=J,t.compareLoose=function(e,t){return J(e,t,!0)},t.rcompare=function(e,t,r){return J(t,e,r)},t.sort=function(e,r){return e.sort((function(e,n){return t.compare(e,n,r)}))},t.rsort=function(e,r){return e.sort((function(e,n){return t.rcompare(e,n,r)}))},t.gt=z,t.lt=W,t.eq=X,t.neq=Z,t.gte=ee,t.lte=te,t.cmp=re,t.Comparator=ne;var ie={};function oe(e,t){if(t&&"object"==typeof t||(t={loose:!!t,includePrerelease:!1}),e instanceof oe)return e.loose===!!t.loose&&e.includePrerelease===!!t.includePrerelease?e:new oe(e.raw,t);if(e instanceof ne)return new oe(e.value,t);if(!(this instanceof oe))return new oe(e,t);if(this.options=t,this.loose=!!t.loose,this.includePrerelease=!!t.includePrerelease,this.raw=e,this.set=e.split(/\s*\|\|\s*/).map((function(e){return this.parseRange(e.trim())}),this).filter((function(e){return e.length})),!this.set.length)throw new TypeError("Invalid SemVer Range: "+e);this.format()}function se(e){return!e||"x"===e.toLowerCase()||"*"===e}function ae(e,t,r,n,i,o,s,a,c,u,d,l,p){return((t=se(r)?"":se(n)?">="+r+".0.0":se(i)?">="+r+"."+n+".0":">="+t)+" "+(a=se(c)?"":se(u)?"<"+(+c+1)+".0.0":se(d)?"<"+c+"."+(+u+1)+".0":l?"<="+c+"."+u+"."+d+"-"+l:"<="+a)).trim()}function ce(e,t,n){for(var i=0;i0){var o=e[i].semver;if(o.major===t.major&&o.minor===t.minor&&o.patch===t.patch)return!0}return!1}return!0}function ue(e,t,r){try{t=new oe(t,r)}catch(e){return!1}return t.test(e)}function de(e,t,r,n){var i,o,s,a,c;switch(e=new G(e,n),t=new oe(t,n),r){case">":i=z,o=te,s=W,a=">",c=">=";break;case"<":i=W,o=ee,s=z,a="<",c="<=";break;default:throw new TypeError('Must provide a hilo val of "<" or ">"')}if(ue(e,t,n))return!1;for(var u=0;u=0.0.0")),l=l||e,p=p||e,i(e.semver,l.semver,n)?l=e:s(e.semver,p.semver,n)&&(p=e)})),l.operator===a||l.operator===c)return!1;if((!p.operator||p.operator===a)&&o(e,p.semver))return!1;if(p.operator===c&&s(e,p.semver))return!1}return!0}ne.prototype.parse=function(e){var t=this.options.loose?i[M]:i[L],r=e.match(t);if(!r)throw new TypeError("Invalid comparator: "+e);this.operator=r[1],"="===this.operator&&(this.operator=""),r[2]?this.semver=new G(r[2],this.options.loose):this.semver=ie},ne.prototype.toString=function(){return this.value},ne.prototype.test=function(e){return r("Comparator.test",e,this.options.loose),this.semver===ie||("string"==typeof e&&(e=new G(e,this.options)),re(e,this.operator,this.semver,this.options))},ne.prototype.intersects=function(e,t){if(!(e instanceof ne))throw new TypeError("a Comparator is required");var r;if(t&&"object"==typeof t||(t={loose:!!t,includePrerelease:!1}),""===this.operator)return r=new oe(e.value,t),ue(this.value,r,t);if(""===e.operator)return r=new oe(this.value,t),ue(e.semver,r,t);var n=!(">="!==this.operator&&">"!==this.operator||">="!==e.operator&&">"!==e.operator),i=!("<="!==this.operator&&"<"!==this.operator||"<="!==e.operator&&"<"!==e.operator),o=this.semver.version===e.semver.version,s=!(">="!==this.operator&&"<="!==this.operator||">="!==e.operator&&"<="!==e.operator),a=re(this.semver,"<",e.semver,t)&&(">="===this.operator||">"===this.operator)&&("<="===e.operator||"<"===e.operator),c=re(this.semver,">",e.semver,t)&&("<="===this.operator||"<"===this.operator)&&(">="===e.operator||">"===e.operator);return n||i||o&&s||a||c},t.Range=oe,oe.prototype.format=function(){return this.range=this.set.map((function(e){return e.join(" ").trim()})).join("||").trim(),this.range},oe.prototype.toString=function(){return this.range},oe.prototype.parseRange=function(e){var t=this.options.loose;e=e.trim();var n=t?i[$]:i[q];e=e.replace(n,ae),r("hyphen replace",e),e=e.replace(i[B],"$1$2$3"),r("comparator trim",e,i[B]),e=(e=(e=e.replace(i[R],"$1~")).replace(i[N],"$1^")).split(/\s+/).join(" ");var o=t?i[M]:i[L],s=e.split(" ").map((function(e){return function(e,t){return r("comp",e,t),e=function(e,t){return e.trim().split(/\s+/).map((function(e){return function(e,t){r("caret",e,t);var n=t.loose?i[V]:i[U];return e.replace(n,(function(t,n,i,o,s){var a;return r("caret",e,t,n,i,o,s),se(n)?a="":se(i)?a=">="+n+".0.0 <"+(+n+1)+".0.0":se(o)?a="0"===n?">="+n+"."+i+".0 <"+n+"."+(+i+1)+".0":">="+n+"."+i+".0 <"+(+n+1)+".0.0":s?(r("replaceCaret pr",s),a="0"===n?"0"===i?">="+n+"."+i+"."+o+"-"+s+" <"+n+"."+i+"."+(+o+1):">="+n+"."+i+"."+o+"-"+s+" <"+n+"."+(+i+1)+".0":">="+n+"."+i+"."+o+"-"+s+" <"+(+n+1)+".0.0"):(r("no pr"),a="0"===n?"0"===i?">="+n+"."+i+"."+o+" <"+n+"."+i+"."+(+o+1):">="+n+"."+i+"."+o+" <"+n+"."+(+i+1)+".0":">="+n+"."+i+"."+o+" <"+(+n+1)+".0.0"),r("caret return",a),a}))}(e,t)})).join(" ")}(e,t),r("caret",e),e=function(e,t){return e.trim().split(/\s+/).map((function(e){return function(e,t){var n=t.loose?i[D]:i[P];return e.replace(n,(function(t,n,i,o,s){var a;return r("tilde",e,t,n,i,o,s),se(n)?a="":se(i)?a=">="+n+".0.0 <"+(+n+1)+".0.0":se(o)?a=">="+n+"."+i+".0 <"+n+"."+(+i+1)+".0":s?(r("replaceTilde pr",s),a=">="+n+"."+i+"."+o+"-"+s+" <"+n+"."+(+i+1)+".0"):a=">="+n+"."+i+"."+o+" <"+n+"."+(+i+1)+".0",r("tilde return",a),a}))}(e,t)})).join(" ")}(e,t),r("tildes",e),e=function(e,t){return r("replaceXRanges",e,t),e.split(/\s+/).map((function(e){return function(e,t){e=e.trim();var n=t.loose?i[x]:i[I];return e.replace(n,(function(t,n,i,o,s,a){r("xRange",e,t,n,i,o,s,a);var c=se(i),u=c||se(o),d=u||se(s);return"="===n&&d&&(n=""),c?t=">"===n||"<"===n?"<0.0.0":"*":n&&d?(u&&(o=0),s=0,">"===n?(n=">=",u?(i=+i+1,o=0,s=0):(o=+o+1,s=0)):"<="===n&&(n="<",u?i=+i+1:o=+o+1),t=n+i+"."+o+"."+s):u?t=">="+i+".0.0 <"+(+i+1)+".0.0":d&&(t=">="+i+"."+o+".0 <"+i+"."+(+o+1)+".0"),r("xRange return",t),t}))}(e,t)})).join(" ")}(e,t),r("xrange",e),e=function(e,t){return r("replaceStars",e,t),e.trim().replace(i[F],"")}(e,t),r("stars",e),e}(e,this.options)}),this).join(" ").split(/\s+/);return this.options.loose&&(s=s.filter((function(e){return!!e.match(o)}))),s=s.map((function(e){return new ne(e,this.options)}),this)},oe.prototype.intersects=function(e,t){if(!(e instanceof oe))throw new TypeError("a Range is required");return this.set.some((function(r){return r.every((function(r){return e.set.some((function(e){return e.every((function(e){return r.intersects(e,t)}))}))}))}))},t.toComparators=function(e,t){return new oe(e,t).set.map((function(e){return e.map((function(e){return e.value})).join(" ").trim().split(" ")}))},oe.prototype.test=function(e){if(!e)return!1;"string"==typeof e&&(e=new G(e,this.options));for(var t=0;t":0===t.prerelease.length?t.patch++:t.prerelease.push(0),t.raw=t.format();case"":case">=":r&&!z(r,t)||(r=t);break;case"<":case"<=":break;default:throw new Error("Unexpected operation: "+e.operator)}}))}if(r&&e.test(r))return r;return null},t.validRange=function(e,t){try{return new oe(e,t).range||"*"}catch(e){return null}},t.ltr=function(e,t,r){return de(e,t,"<",r)},t.gtr=function(e,t,r){return de(e,t,">",r)},t.outside=de,t.prerelease=function(e,t){var r=H(e,t);return r&&r.prerelease.length?r.prerelease:null},t.intersects=function(e,t,r){return e=new oe(e,r),t=new oe(t,r),e.intersects(t)},t.coerce=function(e){if(e instanceof G)return e;if("string"!=typeof e)return null;var t=e.match(i[k]);if(null==t)return null;return H(t[1]+"."+(t[2]||"0")+"."+(t[3]||"0"))}})),Et=(bt.SEMVER_SPEC_VERSION,bt.re,bt.src,bt.parse,bt.valid,bt.clean,bt.SemVer,bt.inc,bt.diff,bt.compareIdentifiers,bt.rcompareIdentifiers,bt.major,bt.minor,bt.patch,bt.compare,bt.compareLoose,bt.rcompare,bt.sort,bt.rsort,bt.gt,bt.lt,bt.eq,bt.neq,bt.gte,bt.lte,bt.cmp,bt.Comparator,bt.Range,bt.toComparators,bt.satisfies,bt.maxSatisfying,bt.minSatisfying,bt.minVersion,bt.validRange,bt.ltr,bt.gtr,bt.outside,bt.prerelease,bt.intersects,bt.coerce,bt.satisfies(process.version,"^6.12.0 || >=8.0.0")),Ct=["RS256","RS384","RS512","ES256","ES384","ES512"],Tt=["RS256","RS384","RS512"],At=["HS256","HS384","HS512"];Et&&(Ct.splice(3,0,"PS256","PS384","PS512"),Tt.splice(3,0,"PS256","PS384","PS512"));var St=/^\s+|\s+$/g,It=/^[-+]0x[0-9a-f]+$/i,xt=/^0b[01]+$/i,kt=/^0o[0-7]+$/i,Ot=/^(?:0|[1-9]\d*)$/,Rt=parseInt;function Pt(e){return e!=e}function Dt(e,t){return function(e,t){for(var r=-1,n=e?e.length:0,i=Array(n);++r-1&&e%1==0&&e-1&&e%1==0&&e<=9007199254740991}(e.length)&&!function(e){var t=Qt(e)?Mt.call(e):"";return"[object Function]"==t||"[object GeneratorFunction]"==t}(e)}function Qt(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}function Yt(e){return!!e&&"object"==typeof e}var Jt=function(e,t,r,n){var i;e=Gt(e)?e:(i=e)?Dt(i,function(e){return Gt(e)?$t(e):Ft(e)}(i)):[],r=r&&!n?function(e){var t=function(e){if(!e)return 0===e?e:0;if((e=function(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||Yt(e)&&"[object Symbol]"==Mt.call(e)}(e))return NaN;if(Qt(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=Qt(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(St,"");var r=xt.test(e);return r||kt.test(e)?Rt(e.slice(2),r?2:8):It.test(e)?NaN:+e}(e))===1/0||e===-1/0){return 17976931348623157e292*(e<0?-1:1)}return e==e?e:0}(e),r=t%1;return t==t?r?t-r:t:0}(r):0;var o=e.length;return r<0&&(r=qt(o+r,0)),function(e){return"string"==typeof e||!Ht(e)&&Yt(e)&&"[object String]"==Mt.call(e)}(e)?r<=o&&e.indexOf(t,r)>-1:!!o&&function(e,t,r){if(t!=t)return function(e,t,r,n){for(var i=e.length,o=r+(n?1:-1);n?o--:++o-1},zt=Object.prototype.toString;var Wt=function(e){return!0===e||!1===e||function(e){return!!e&&"object"==typeof e}(e)&&"[object Boolean]"==zt.call(e)},Xt=/^\s+|\s+$/g,Zt=/^[-+]0x[0-9a-f]+$/i,er=/^0b[01]+$/i,tr=/^0o[0-7]+$/i,rr=parseInt,nr=Object.prototype.toString;function ir(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}var or=function(e){return"number"==typeof e&&e==function(e){var t=function(e){if(!e)return 0===e?e:0;if((e=function(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&"[object Symbol]"==nr.call(e)}(e))return NaN;if(ir(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=ir(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(Xt,"");var r=er.test(e);return r||tr.test(e)?rr(e.slice(2),r?2:8):Zt.test(e)?NaN:+e}(e))===1/0||e===-1/0){return 17976931348623157e292*(e<0?-1:1)}return e==e?e:0}(e),r=t%1;return t==t?r?t-r:t:0}(e)},sr=Object.prototype.toString;var ar=function(e){return"number"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&"[object Number]"==sr.call(e)};var cr=Function.prototype,ur=Object.prototype,dr=cr.toString,lr=ur.hasOwnProperty,pr=dr.call(Object),fr=ur.toString,mr=function(e,t){return function(r){return e(t(r))}}(Object.getPrototypeOf,Object);var hr=function(e){if(!function(e){return!!e&&"object"==typeof e}(e)||"[object Object]"!=fr.call(e)||function(e){var t=!1;if(null!=e&&"function"!=typeof e.toString)try{t=!!(e+"")}catch(e){}return t}(e))return!1;var t=mr(e);if(null===t)return!0;var r=lr.call(t,"constructor")&&t.constructor;return"function"==typeof r&&r instanceof r&&dr.call(r)==pr},gr=Object.prototype.toString,yr=Array.isArray;var wr=function(e){return"string"==typeof e||!yr(e)&&function(e){return!!e&&"object"==typeof e}(e)&&"[object String]"==gr.call(e)},_r=/^\s+|\s+$/g,vr=/^[-+]0x[0-9a-f]+$/i,br=/^0b[01]+$/i,Er=/^0o[0-7]+$/i,Cr=parseInt,Tr=Object.prototype.toString;function Ar(e,t){var r;if("function"!=typeof t)throw new TypeError("Expected a function");return e=function(e){var t=function(e){if(!e)return 0===e?e:0;if((e=function(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&"[object Symbol]"==Tr.call(e)}(e))return NaN;if(Sr(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=Sr(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(_r,"");var r=br.test(e);return r||Er.test(e)?Cr(e.slice(2),r?2:8):vr.test(e)?NaN:+e}(e))===1/0||e===-1/0){return 17976931348623157e292*(e<0?-1:1)}return e==e?e:0}(e),r=t%1;return t==t?r?t-r:t:0}(e),function(){return--e>0&&(r=t.apply(this,arguments)),e<=1&&(t=void 0),r}}function Sr(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}var Ir=function(e){return Ar(2,e)},xr=["RS256","RS384","RS512","ES256","ES384","ES512","HS256","HS384","HS512","none"];Et&&xr.splice(3,0,"PS256","PS384","PS512");var kr={expiresIn:{isValid:function(e){return or(e)||wr(e)&&e},message:'"expiresIn" should be a number of seconds or string representing a timespan'},notBefore:{isValid:function(e){return or(e)||wr(e)&&e},message:'"notBefore" should be a number of seconds or string representing a timespan'},audience:{isValid:function(e){return wr(e)||Array.isArray(e)},message:'"audience" must be a string or array'},algorithm:{isValid:Jt.bind(null,xr),message:'"algorithm" must be a valid string enum value'},header:{isValid:hr,message:'"header" must be an object'},encoding:{isValid:wr,message:'"encoding" must be a string'},issuer:{isValid:wr,message:'"issuer" must be a string'},subject:{isValid:wr,message:'"subject" must be a string'},jwtid:{isValid:wr,message:'"jwtid" must be a string'},noTimestamp:{isValid:Wt,message:'"noTimestamp" must be a boolean'},keyid:{isValid:wr,message:'"keyid" must be a string'},mutatePayload:{isValid:Wt,message:'"mutatePayload" must be a boolean'}},Or={iat:{isValid:ar,message:'"iat" should be a number of seconds'},exp:{isValid:ar,message:'"exp" should be a number of seconds'},nbf:{isValid:ar,message:'"nbf" should be a number of seconds'}};function Rr(e,t,r,n){if(!hr(r))throw new Error('Expected "'+n+'" to be a plain object.');Object.keys(r).forEach((function(i){var o=e[i];if(o){if(!o.isValid(r[i]))throw new Error(o.message)}else if(!t)throw new Error('"'+i+'" is not allowed in "'+n+'"')}))}var Pr={audience:"aud",issuer:"iss",subject:"sub",jwtid:"jti"},Dr=["expiresIn","notBefore","noTimestamp","audience","issuer","subject","jwtid"],jr=function(e,t,r,n){var i;if("function"!=typeof r||n||(n=r,r={}),r||(r={}),r=Object.assign({},r),i=n||function(e,t){if(e)throw e;return t},r.clockTimestamp&&"number"!=typeof r.clockTimestamp)return i(new ut("clockTimestamp must be a number"));if(void 0!==r.nonce&&("string"!=typeof r.nonce||""===r.nonce.trim()))return i(new ut("nonce must be a non-empty string"));var o=r.clockTimestamp||Math.floor(Date.now()/1e3);if(!e)return i(new ut("jwt must be provided"));if("string"!=typeof e)return i(new ut("jwt must be a string"));var s,a=e.split(".");if(3!==a.length)return i(new ut("jwt malformed"));try{s=at(e,{complete:!0})}catch(e){return i(e)}if(!s)return i(new ut("invalid token"));var c,u=s.header;if("function"==typeof t){if(!n)return i(new ut("verify must be called asynchronous if secret or public key is provided as a callback"));c=t}else c=function(e,r){return r(null,t)};return c(u,(function(t,n){if(t)return i(new ut("error in secret or public key callback: "+t.message));var c,d=""!==a[2].trim();if(!d&&n)return i(new ut("jwt signature is required"));if(d&&!n)return i(new ut("secret or public key must be provided"));if(d||r.algorithms||(r.algorithms=["none"]),r.algorithms||(r.algorithms=~n.toString().indexOf("BEGIN CERTIFICATE")||~n.toString().indexOf("BEGIN PUBLIC KEY")?Ct:~n.toString().indexOf("BEGIN RSA PUBLIC KEY")?Tt:At),!~r.algorithms.indexOf(s.header.alg))return i(new ut("invalid algorithm"));try{c=st.verify(e,s.header.alg,n)}catch(e){return i(e)}if(!c)return i(new ut("invalid signature"));var l=s.payload;if(void 0!==l.nbf&&!r.ignoreNotBefore){if("number"!=typeof l.nbf)return i(new ut("invalid nbf value"));if(l.nbf>o+(r.clockTolerance||0))return i(new lt("jwt not active",new Date(1e3*l.nbf)))}if(void 0!==l.exp&&!r.ignoreExpiration){if("number"!=typeof l.exp)return i(new ut("invalid exp value"));if(o>=l.exp+(r.clockTolerance||0))return i(new ft("jwt expired",new Date(1e3*l.exp)))}if(r.audience){var p=Array.isArray(r.audience)?r.audience:[r.audience];if(!(Array.isArray(l.aud)?l.aud:[l.aud]).some((function(e){return p.some((function(t){return t instanceof RegExp?t.test(e):t===e}))})))return i(new ut("jwt audience invalid. expected: "+p.join(" or ")))}if(r.issuer&&("string"==typeof r.issuer&&l.iss!==r.issuer||Array.isArray(r.issuer)&&-1===r.issuer.indexOf(l.iss)))return i(new ut("jwt issuer invalid. expected: "+r.issuer));if(r.subject&&l.sub!==r.subject)return i(new ut("jwt subject invalid. expected: "+r.subject));if(r.jwtid&&l.jti!==r.jwtid)return i(new ut("jwt jwtid invalid. expected: "+r.jwtid));if(r.nonce&&l.nonce!==r.nonce)return i(new ut("jwt nonce invalid. expected: "+r.nonce));if(r.maxAge){if("number"!=typeof l.iat)return i(new ut("iat required when maxAge is specified"));var f=vt(r.maxAge,l.iat);if(void 0===f)return i(new ut('"maxAge" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60'));if(o>=f+(r.clockTolerance||0))return i(new ft("maxAge exceeded",new Date(1e3*f)))}if(!0===r.complete){var m=s.signature;return i(null,{header:u,payload:l,signature:m})}return i(null,l)}))},Nr=function(e,t,r,n){"function"==typeof r?(n=r,r={}):r=r||{};var i="object"==typeof e&&!Buffer.isBuffer(e),o=Object.assign({alg:r.algorithm||"HS256",typ:i?"JWT":void 0,kid:r.keyid},r.header);function s(e){if(n)return n(e);throw e}if(!t&&"none"!==r.algorithm)return s(new Error("secretOrPrivateKey must have a value"));if(void 0===e)return s(new Error("payload is required"));if(i){try{!function(e){Rr(Or,!0,e,"payload")}(e)}catch(e){return s(e)}r.mutatePayload||(e=Object.assign({},e))}else{var a=Dr.filter((function(e){return void 0!==r[e]}));if(a.length>0)return s(new Error("invalid "+a.join(",")+" option for "+typeof e+" payload"))}if(void 0!==e.exp&&void 0!==r.expiresIn)return s(new Error('Bad "options.expiresIn" option the payload already has an "exp" property.'));if(void 0!==e.nbf&&void 0!==r.notBefore)return s(new Error('Bad "options.notBefore" option the payload already has an "nbf" property.'));try{!function(e){Rr(kr,!1,e,"options")}(r)}catch(e){return s(e)}var c=e.iat||Math.floor(Date.now()/1e3);if(r.noTimestamp?delete e.iat:i&&(e.iat=c),void 0!==r.notBefore){try{e.nbf=vt(r.notBefore,c)}catch(e){return s(e)}if(void 0===e.nbf)return s(new Error('"notBefore" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60'))}if(void 0!==r.expiresIn&&"object"==typeof e){try{e.exp=vt(r.expiresIn,c)}catch(e){return s(e)}if(void 0===e.exp)return s(new Error('"expiresIn" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60'))}Object.keys(Pr).forEach((function(t){var n=Pr[t];if(void 0!==r[t]){if(void 0!==e[n])return s(new Error('Bad "options.'+t+'" option. The payload already has an "'+n+'" property.'));e[n]=r[t]}}));var u=r.encoding||"utf8";if("function"!=typeof n)return st.sign({header:o,payload:e,secret:t,encoding:u});n=n&&Ir(n),st.createSign({header:o,privateKey:t,payload:e,encoding:u}).once("error",n).once("done",(function(e){n(null,e)}))};let Ur=[];class Vr{constructor(e){this.options=Object.assign({baseUrl:"https://appleid.apple.com",timeout:1e4},e)}async _fetch(e,t){const{baseUrl:r}=this.options;return uniCloud.httpclient.request(r+e,t)}async verifyIdentityToken(e){const t=e.split(".")[0],{kid:r}=JSON.parse(Buffer.from(t,"base64").toString());if(!Ur.length)try{Ur=await this.getAuthKeys()}catch(e){return{code:10705,msg:"Apple 公钥获取失败,请重新登录:"+e.message,messageValues:{account:"苹果账号"}}}const n=this.getUsedKey(Ur,r);if(!Object.keys(n).length)return Ur.length=0,{code:10705,msg:"Apple 公钥获取失败,请重新登录:usedKey",messageValues:{account:"苹果账号"}};let i=null;try{i=jr(e,function(e,t){var r=Buffer.from(e,"base64"),n=Buffer.from(t,"base64"),i=r.toString("hex"),o=n.toString("hex");i=te(i),o=te(o);var s=i.length/2,a=o.length/2,c=ne(s),u=ne(a),d="30"+ne(s+a+c.length/2+u.length/2+2)+"02"+c+i+"02"+u+o;return"-----BEGIN RSA PUBLIC KEY-----\n"+Buffer.from(d,"hex").toString("base64").match(/.{1,64}/g).join("\n")+"\n-----END RSA PUBLIC KEY-----\n"}(n.n,n.e),{algorithms:n.alg})}catch(e){return{code:10705,msg:"JWT 校验失败,请重新登录:"+e.message,messageValues:{account:"苹果账号"}}}return{code:0,msg:i}}async getAuthKeys(){const{status:e,data:t}=await this._fetch("/auth/keys",{method:"GET",dataType:"json",timeout:this.options.timeout});if(200!==e)throw new Error("request https://appleid.apple.com/auth/keys fail");return t.keys}getUsedKey(e,t){let r={};for(let n=0;nvoid 0===e))return{code:F,messageValues:{param:"应用Appid"}};const r=await M.doc(e).get(),n=r&&r.data&&r.data[0];if(!n)return{code:10002};const i=Object.keys($),o=i.reduce((e,t)=>{const r=t,i=function(e,t){return t.split(".").reduce((e,t)=>e&&e[t],e)}(n,t);return i&&e.push({[r]:i}),e},[]);let s;const a={dcloud_appid:Fr.in(t),_id:Fr.neq(n._id)},c={dcloud_appid:Fr.exists(!1),_id:Fr.neq(n._id)};switch(o.length){case 0:return{code:10004,message:"用户缺少唯一标识(包括但不限于用户名、手机号、邮箱)"};case 1:s=Fr.or([Fr.and([o[0],a]),Fr.and([o[0],c])]);break;default:s=Fr.or([Fr.and([Fr.or(o),a]),Fr.and([Fr.or(o),c])])}const u=await M.where(s).limit(1).get(),d=u&&u.data&&u.data[0];if(!d)return{code:0};const l=i.find(e=>n[e]===d[e]);return{code:10005,msg:`此用户的${$[l]}已被授权登录,不可再次授权`}}const Hr=uniCloud.database().command;const Gr=uniCloud.database();const Qr=uniCloud.database();const Yr=uniCloud.database();async function Jr(e){const t=["apiKey","apiSecret"];for(let r=0,n=t.length;r upRes",await M.doc(e.uid).update({avatar:e.avatar})),{code:0,msg:"头像设置成功"}},updatePwd:async function(e){const t=await M.doc(e.uid).get();if(t&&t.data&&t.data.length>0){if(0===this._checkPwd(t.data[0],e.oldPassword).code){const{passwordHash:r,version:n}=this.encryptPwd(e.newPassword),i={password:r,token:[]};n&&(i.password_secret_version=n);return S("upRes",await M.doc(t.data[0]._id).update(i)),{code:0,msg:"修改成功"}}return{code:40202,msg:"旧密码错误"}}return{code:40201}},updateUser:async function(e){const t=e.uid;if(!t)return{code:F,messageValues:{param:"用户Id"}};delete e.uid;const{username:r,email:n}=e,{usernameToLowerCase:i,emailToLowerCase:o}=this._getConfig();let s=r&&r.trim(),a=n&&n.trim();return s&&(i&&(s=s.toLowerCase()),e.username=s),a&&(o&&(a=a.toLowerCase()),e.email=a),S("update -> upRes",await M.doc(t).update(e)),{code:0,msg:"修改成功"}},_getAlipayApi:function({platform:e}={}){const t=e||this.context.PLATFORM;if(!t)throw new Error("未能获取客户端平台信息,请主动传入platform");const r=this._getConfig(t);if(!r.oauth||!r.oauth.alipay)throw new Error(`请在公用模块uni-id的config.json中添加${t}平台支付宝登录配置项`);return["appid","privateKey"].forEach(e=>{if(!r.oauth.alipay[e])throw new Error(`请在公用模块uni-id的config.json中添加配置项:${t}.oauth.alipay.${e}`)}),Br({...r.oauth.alipay,clientType:t})},_getValidInviteCode:async function({inviteCode:e}){let t,r=10;e?(r=1,t=e):t=$r();let n=!1;try{for(;r>0&&!n;){r--;if(0===(await M.where({my_invite_code:t}).get()).data.length){n=!0;break}t=$r()}return n?{code:0,inviteCode:t}:e?{code:80401,msg:"邀请码重复,设置失败"}:{code:80402,msg:"邀请码设置失败稍后再试"}}catch(e){return{code:90001,msg:"数据库读写异常"}}},_addUser:async function(e,{needPermission:t,autoSetDcloudAppid:r=!0}={}){const n=this._getConfig(),i={...e,dcloud_appid:r?[this.context.APPID]:[],register_date:Date.now(),register_ip:this.context.CLIENTIP},o=(await M.add(i)).id;let s;if(n.removePermissionAndRoleFromToken)s=await this.createToken({uid:o,needPermission:t});else{const t=e.role||[];let r;r=0===t.length||t.includes("admin")?[]:await this._getPermissionListByRoleList(t),s=await this.createToken({uid:o,role:t,permission:r})}const{token:a,tokenExpired:c}=s;return await M.doc(o).update({token:[a]}),{token:a,tokenExpired:c,uid:o,type:"register",userInfo:Object.assign({},i,{token:a})}},_loginExec:async function(e,t={}){if(1===e.status)return{code:10001,msg:"账号已禁用"};const r=this._getConfig();let n=e.token||[];"string"==typeof n&&(n=[n]);const i=this._getExpiredToken(n);let o;if(n=n.filter(e=>-1===i.indexOf(e)),r.removePermissionAndRoleFromToken){const r=t.needPermission;o=await this.createToken({uid:e._id,needPermission:r})}else{const t=e.role||[];let r;r=0===t.length||t.includes("admin")?[]:await this._getPermissionListByRoleList(t),o=await this.createToken({uid:e._id,role:t,permission:r})}const{token:s,tokenExpired:a}=o;n.push(s),e.token=n;const c={last_login_date:Date.now(),last_login_ip:this.context.CLIENTIP,token:n,...t.extraData};await M.doc(e._id).update(c);const u=Object.assign({},e,c);return{code:0,msg:"登录成功",token:s,uid:u._id,username:u.username,type:"login",userInfo:u,tokenExpired:a}},_registerExec:async function(e,{needPermission:t,autoSetDcloudAppid:r=!0}={}){const{my_invite_code:n}=e;if(this._getConfig().autoSetInviteCode||n){const t=await this._getValidInviteCode({inviteCode:n});if(t.code)return t;e.my_invite_code=t.inviteCode}return{code:0,msg:"注册成功",...await this._addUser(e,{needPermission:t,autoSetDcloudAppid:r})}},_getWeixinApi:function({platform:e}={}){const t=e||this.context.PLATFORM;if(!t)throw new Error("未能获取客户端平台信息,请主动传入platform");const r=this._getConfig(t);if(!r.oauth||!r.oauth.weixin)throw new Error(`请在公用模块uni-id的config.json中添加${t}平台微信登录配置项`);return["appid","appsecret"].forEach(e=>{if(!r.oauth.weixin[e])throw new Error(`请在公用模块uni-id的config.json中添加配置项:${t}.oauth.weixin.${e}`)}),Mr({...r.oauth.weixin,clientType:t})},_getQQApi:function(){const e=this.context.PLATFORM;if(!e)throw new Error("未能获取客户端平台信息,请主动传入platform");const t=this._getConfig(e);if(!t.oauth||!t.oauth.qq)throw new Error(`请在公用模块uni-id的config.json中添加${e}平台QQ登录配置项`);return["appid","appsecret"].forEach(r=>{if(!t.oauth.qq[r])throw new Error(`请在公用模块uni-id的config.json中添加配置项:${e}.oauth.qq.${r}`)}),Lr({...t.oauth.qq,clientType:e})},_getMatchedUser:function(e,t){if(0===e.length)return{code:10002};let r;const n={},i={};for(let r=e.length-1;r>=0;r--){const o=e[r];for(let s=0;s0?{code:10003,messageValues:{target:"用户"}}:{code:0,msg:"",userMatched:r,fieldMatched:s,isFallbackValueMatched:!!s&&i[s]}},_getCurrentAppUser:function(e){const t=this.context.APPID;return e.filter(e=>void 0===e.dcloud_appid||null===e.dcloud_appid||e.dcloud_appid.indexOf(t)>-1)},setAuthorizedAppLogin:async function({uid:e,dcloudAppidList:t}={}){if("array"!==y(t))return{code:K,messageValues:{param:"应用列表",reason:"应用列表必须是一个数组"}};if(t&&0!==t.length){const r=await Kr({uid:e,dcloudAppidList:t});if(r.code)return r}return await M.doc(e).update({dcloud_appid:Fr.set(t)}),{code:0}},authorizeAppLogin:async function({uid:e,dcloudAppid:t}={}){const r=await Kr({uid:e,dcloudAppidList:[t]});return r.code?r:(await M.doc(e).update({dcloud_appid:Fr.push(t)}),{code:0})},forbidAppLogin:async function({uid:e,dcloudAppid:t}={}){return e?(await M.doc(e).update({dcloud_appid:Fr.pull(t)}),{code:0}):{code:F,messageValues:{param:"用户ID"}}},acceptInvite:async function({uid:e,inviteCode:t}){const r=await M.where({_id:Hr.neq(e),inviter_uid:Hr.not(Hr.all([e])),my_invite_code:t}).get();if(1!==r.data.length)return{code:80501,msg:"邀请码无效"};const n=[r.data[0]._id].concat(r.data[0].inviter_uid||[]),i=await M.doc(e).field({my_invite_code:!0,inviter_uid:!0}).get();if(0===i.data.length)return{code:80502};if(i.data[0].inviter_uid&&i.data[0].inviter_uid.length>0)return{code:80503,msg:"邀请码不可修改"};const o=Date.now();return await M.doc(e).update({inviter_uid:n,invite_time:o}),await M.where({inviter_uid:e}).update({inviter_uid:Hr.push(n)}),{code:0,msg:"邀请码填写成功"}},getInvitedUser:async function({uid:e,level:t=1,limit:r=20,offset:n=0,needTotal:i=!1}){const o={code:0,msg:"获取邀请列表成功",invitedUser:(await M.where({["inviter_uid."+(t-1)]:e}).field({_id:!0,username:!0,mobile:!0,invite_time:!0}).orderBy("invite_time","desc").skip(n).limit(r).get()).data};if(i){const r=await M.where({["inviter_uid."+(t-1)]:e}).count();o.total=r.total}return o},setUserInviteCode:async function({uid:e,myInviteCode:t}){const r=await this._getValidInviteCode({inviteCode:t});return r.code?r:(await M.doc(e).update({my_invite_code:r.inviteCode}),{code:0,msg:"邀请码设置成功",myInviteCode:r.inviteCode})},loginByAlipay:async function(e){"string"==typeof e&&(e={code:e});const{needPermission:t,platform:r,code:n,myInviteCode:i,role:o,type:s}=e,a=r||this.context.PLATFORM,{openid:c}=await this._getAlipayApi({platform:a}).code2Session(n);if(!c)return{code:10501,messageValues:{account:"支付宝账号"}};let u=await M.where({ali_openid:c}).get();if(u=this._getCurrentAppUser(u.data),u&&u.length>0){if("register"===s)return{code:10502,messageValues:{type:"支付宝账号"}};const e=u[0],r=await this._loginExec(e,{needPermission:t});if(0!==r.code)return r;const{userInfo:n}=r;return{...r,openid:c,mobileConfirmed:1===n.mobile_confirmed,emailConfirmed:1===n.email_confirmed}}{if("login"===s)return{code:10503,messageValues:{type:"QQ账号"}};const e={ali_openid:c};e.my_invite_code=i,e.role=o;const r=await this._registerExec(e,{needPermission:t});return 0!==r.code?r:{...r,openid:c,mobileConfirmed:!1,emailConfirmed:!1}}},loginByEmail:async function({email:e,code:t,password:r,myInviteCode:n,type:i,needPermission:o,role:s}){if(!(e=e&&e.trim()))return{code:F,messageValues:{param:"邮箱"}};const{emailToLowerCase:a}=this._getConfig();let c=e;a&&(c=e.toLowerCase());const u=await this.verifyCode({email:c,code:t,type:i||"login"});if(0!==u.code)return u;let d={email:e,email_confirmed:1};const l={field:"email",value:e},p=Gr.command;c!==e&&(d=p.or(d,{email:c,email_confirmed:1}),l.fallbackValue=c);let f=await M.where(d).get();if(f=this._getCurrentAppUser(f.data),f&&f.length>0){if("register"===i)return{code:10301,messageValues:{type:"邮箱"}};const e=this._getMatchedUser(f,[l]);if(e.code)return e;const{userMatched:t}=e,r=await this._loginExec(t,{needPermission:o});return 0!==r.code?r:{...r,email:c}}{if("login"===i)return{code:10302,messageValues:{type:"邮箱"}};const e={email:c,email_confirmed:1},t=r&&r.trim();if(t){const{passwordHash:r,version:n}=this.encryptPwd(t);e.password=r,n&&(e.password_secret_version=n)}e.my_invite_code=n,e.role=s;const a=await this._registerExec(e,{needPermission:o});return 0!==a.code?a:{...a,email:c}}},loginBySms:async function({mobile:e,code:t,password:r,inviteCode:n,myInviteCode:i,type:o,needPermission:s,role:a}){if(!(e=e&&e.trim()))return{code:F,messageValues:{param:"手机号码"}};const c=this._getConfig();if(c.forceInviteCode&&!o)throw new Error("[loginBySms]强制使用邀请码注册时,需指明type为register还是login");const u=await this.verifyCode({mobile:e,code:t,type:o||"login"});if(0!==u.code)return u;const d={mobile:e,mobile_confirmed:1};let l=await M.where(d).get();if(l=this._getCurrentAppUser(l.data),l&&l.length>0){if("register"===o)return{code:10201,messageValues:{type:"手机号"}};const t=l[0],r=await this._loginExec(t,{needPermission:s});return 0!==r.code?r:{...r,mobile:e}}{const t=Date.now();if("login"===o)return{code:10202,messageValues:{type:"手机号"}};const u={mobile:e,mobile_confirmed:1,register_ip:this.context.CLIENTIP,register_date:t},d=r&&r.trim();if(d){const{passwordHash:e,version:t}=this.encryptPwd(d);u.password=e,t&&(u.password_secret_version=t)}if(n){const e=await M.where({my_invite_code:n}).get();if(1!==e.data.length)return{code:10203,msg:"邀请码无效"};u.inviter_uid=[e.data[0]._id].concat(e.data[0].inviter_uid||[]),u.invite_time=t}else if(c.forceInviteCode)return{code:10203,msg:"邀请码无效"};u.my_invite_code=i,u.role=a;const l=await this._registerExec(u,{needPermission:s});return 0!==l.code?l:{...l,mobile:e}}},loginByWeixin:async function(e){"string"==typeof e&&(e={code:e});const{needPermission:t,platform:r,code:n,myInviteCode:i,role:o,type:s}=e,a=r||this.context.PLATFORM,c="mp-weixin"===a,{openid:u,unionid:d,sessionKey:l,accessToken:p,refreshToken:f,expired:m}=await this._getWeixinApi({platform:a})[c?"code2Session":"getOauthAccessToken"](n);if(!u)return{code:10401,messageValues:{account:"微信openid"}};let h;h=c?{sessionKey:l}:{accessToken:p,refreshToken:f,accessTokenExpired:m};const g=Qr.command,y=[{wx_openid:{[a]:u}}];d&&y.push({wx_unionid:d});let w=await M.where(g.or(...y)).get();if(w=this._getCurrentAppUser(w.data),w&&w.length>0){if("register"===s)return{code:10402,messageValues:{type:"微信账号"}};const e=w[0],r={wx_openid:{[a]:u}};d&&(r.wx_unionid=d);const n=await this._loginExec(e,{needPermission:t,extraData:r});if(0!==n.code)return n;const{userInfo:i}=n;return{...n,openid:u,unionid:d,...h,mobileConfirmed:1===i.mobile_confirmed,emailConfirmed:1===i.email_confirmed}}{if("login"===s)return{code:10403,messageValues:{type:"微信账号"}};const e={wx_openid:{[a]:u},wx_unionid:d};e.my_invite_code=i,e.role=o;const r=await this._registerExec(e,{needPermission:t});return 0!==r.code?r:{...r,openid:u,unionid:d,...h,mobileConfirmed:!1,emailConfirmed:!1}}},loginByQQ:async function({code:e,accessToken:t,myInviteCode:r,needPermission:n,role:i,type:o}={}){const s=this.context.PLATFORM,a="mp-qq"===s,{openid:c,unionid:u,sessionKey:d}=await this._getQQApi()[a?"code2Session":"getOpenidByToken"]({code:e,accessToken:t});if(!c)return{code:10801,messageValues:{account:"qq openid"}};const l={accessToken:t,sessionKey:d},p=Yr.command,f=[{qq_openid:{[s]:c}}];u&&f.push({qq_unionid:u});let m=await M.where(p.or(...f)).get();if(m=this._getCurrentAppUser(m.data),m&&m.length>0){if("register"===o)return{code:10802,messageValues:{type:"QQ账号"}};const e=m[0],t={qq_openid:{[s]:c}};u&&(t.qq_unionid=u);const r=await this._loginExec(e,{needPermission:n,extraData:t});if(0!==r.code)return r;const{userInfo:i}=r;return{...r,openid:c,unionid:u,...l,mobileConfirmed:1===i.mobile_confirmed,emailConfirmed:1===i.email_confirmed}}{if("login"===o)return{code:10803,messageValues:{type:"QQ账号"}};const e={qq_openid:{[s]:c},qq_unionid:u};e.my_invite_code=r,e.role=i;const t=await this._registerExec(e);return 0!==t.code?t:{...t,openid:c,unionid:u,...l,mobileConfirmed:!1,emailConfirmed:!1}}},loginByUniverify:async function({openid:e,access_token:t,password:r,inviteCode:n,myInviteCode:i,type:o,needPermission:s,role:a}){const c=this._getConfig(),u=c&&c.service&&c.service.univerify;if(!u)throw new Error("请在config.json中配置service.univerify下一键登录相关参数");if(c.forceInviteCode&&!o)throw new Error("[loginByUniverify] 强制使用邀请码注册时,需指明type为register还是login");const d=await Jr({...u,openid:e,access_token:t});if(0!==d.code)return d;const l=String(d.phoneNumber);let p=await M.where({mobile:l}).get();if(p=this._getCurrentAppUser(p.data),p&&p.length>0){if("register"===o)return{code:10601,messageValues:{type:"手机号"}};const e=p[0],t=await this._loginExec(e,{needPermission:s});return 0!==t.code?t:{...t,mobile:l}}if("login"===o)return{code:10602,messageValues:{type:"手机号"}};const f=Date.now(),m={mobile:l,my_invite_code:i,mobile_confirmed:1,role:a},h=r&&r.trim();if(h){const{passwordHash:e,version:t}=this.encryptPwd(h);m.password=e,t&&(m.password_secret_version=t)}if(n){let e=await M.where({my_invite_code:n}).get();if(1!==e.data.length)return{code:10203,msg:"邀请码无效"};e=e.data[0],m.inviter_uid=[e._id].concat(e.inviter_uid||[]),m.invite_time=f}else if(c.forceInviteCode)return{code:10203,msg:"邀请码无效"};m.my_invite_code=i;const g=await this._registerExec(m,{needPermission:s});return 0!==g.code?g:{...g,mobile:l}},loginByApple:async function({nickName:e,fullName:t,identityToken:r,myInviteCode:n,type:i,needPermission:o,platform:s,role:a}){const c=this._getConfig(),u=c&&c["app-plus"]&&c["app-plus"].oauth&&c["app-plus"].oauth.apple;if(!u)throw new Error("请在config.json或init方法中,app-plus.oauth.apple 下配置相关参数");const{bundleId:d}=u;if(!d)throw new Error("请在config.json或init方法中 app-plus.oauth.apple 下配置bundleId");if(!r)throw new Error("[loginByApple] 苹果登录需要传递identityToken");const l=s||this.context.PLATFORM;t=e||(t&&Object.keys(t).length>0?t.familyName+t.givenName:"");const{code:p,msg:f}=await qr({clientType:l}).verifyIdentityToken(r);if(0!==p)return{code:p,msg:f};const{iss:m,sub:h,aud:g,email:y}=f;if("https://appleid.apple.com"!==m)return{code:10706,msg:"签发机构检验失败",messageValues:{account:"苹果账号"}};if(!h)return{code:10701,msg:"获取用户唯一标识符失败",messageValues:{account:"苹果账号"}};if(d!==g)return{code:10702,msg:"bundleId校验失败,请确认配置后重试",messageValues:{account:"苹果账号"}};const w=t||"新用户"+y.split("@")[0];let _=await M.where({apple_openid:h}).get();if(_=this._getCurrentAppUser(_.data),_&&_.length>0){if("register"===i)return{code:10703,messageValues:{type:"苹果账号"}};const e=_[0],t=await this._loginExec(e,{needPermission:o});return 0!==t.code?t:{...t,openid:h}}if("login"===i)return{code:10704,messageValues:{type:"苹果账号"}};const v={nickname:w,apple_openid:h,my_invite_code:n,role:a},b=await this._registerExec(v,{needPermission:o});return 0!==b.code?b:{...b,openid:h}},login:async function({username:e,password:t,queryField:r=[],needPermission:n}){const i=zr.command,o=[];r&&r.length||(r=["username"]),r.length>1&&console.warn("检测到当前使用queryField匹配多字段进行登录操作,需要注意:uni-id并未限制用户名不能是手机号或邮箱,需要开发者自行限制。否则可能出现用户输入abc@xx.com会同时匹配到邮箱为此值的用户和用户名为此值的用户,导致登录失败");const{usernameToLowerCase:s,emailToLowerCase:a,passwordErrorLimit:c,passwordErrorRetryTime:u}=this._getConfig(),d={email:{email_confirmed:1},mobile:{mobile_confirmed:1}},l={},p=e.trim();if(!p)return{code:F,messageValues:{param:"用户名"}};s&&(l.username=p.toLowerCase()),a&&(l.email=p.toLowerCase());const f=[];r.forEach(t=>{o.push({[t]:e,...d[t]});const r={field:t,value:e};"username"===t&&l.username!==e?(o.push({[t]:l.username,...d[t]}),r.fallbackValue=l.username):"email"===t&&l.email!==e&&(o.push({[t]:l.email,...d[t]}),r.fallbackValue=l.email),f.push(r)});let m=await M.where(i.or(...o)).limit(1).get();m=this._getCurrentAppUser(m.data);const h=this.context.CLIENTIP,g=this._getMatchedUser(m,f);if(g.code)return g;const{userMatched:y}=g;let w=y.login_ip_limit||[];w=w.filter(e=>e.last_error_time>Date.now()-1e3*u);let _=w.find(e=>e.ip===h);if(_&&_.error_times>=c)return{code:10103,msg:`密码错误次数过多,请${N(_.last_error_time+1e3*u)}再试。`};const v=t&&t.trim();if(!v)return{code:F,messageValues:{param:"密码"}};const b=this._checkPwd(y,v);if(0===b.code){const e=w.indexOf(_);e>-1&&w.splice(e,1);const t={login_ip_limit:w},{passwordHash:r,passwordVersion:i}=b;r&&i&&(t.password=r,t.password_secret_version=i);const o=await this._loginExec(y,{needPermission:n,extraData:t});return o.code,o}return _?(_.error_times++,_.last_error_time=Date.now()):(_={ip:h,error_times:1,last_error_time:Date.now()},w.push(_)),await M.doc(y._id).update({login_ip_limit:w}),{code:10102,msg:"密码错误"}},register:async function(e){const t=[],r=[{name:"username",desc:"用户名"},{name:"email",desc:"邮箱",extraCond:{email_confirmed:1}},{name:"mobile",desc:"手机号",extraCond:{mobile_confirmed:1}}],{usernameToLowerCase:n,emailToLowerCase:i}=this._getConfig();r.forEach(r=>{const o=r.name;let s=e[o]&&e[o].trim();s?(("username"===r.name&&n||"email"===r.name&&i)&&(s=s.toLowerCase()),e[o]=s,t.push({[o]:s,...r.extraCond})):delete e[o]});const{username:o,email:s,mobile:a,myInviteCode:c,needPermission:u,autoSetDcloudAppid:d=!0}=e;if("needPermission"in e&&delete e.needPermission,"autoSetDcloudAppid"in e&&delete e.autoSetDcloudAppid,0===t.length)return{code:20101,messageValues:{param:"用户名、邮箱或手机号"}};const l=Wr.command;let p=await M.where(l.or(...t)).get();if(p=this._getCurrentAppUser(p.data),p&&p.length>0){const t=p[0];for(let n=0;nt[e]===i.extraCond[e])),t[i.name]===e[i.name]&&o)return{code:20102,messageValues:{type:i.desc}}}}const f=e.password&&e.password.trim();if(!f)return{code:F,messageValues:{param:"密码"}};const{passwordHash:m,version:h}=this.encryptPwd(f);e.password=m,h&&(e.password_secret_version=h),e.my_invite_code=c,delete e.myInviteCode;const g=await this._registerExec(e,{needPermission:u,autoSetDcloudAppid:d});return 0!==g.code?g:{...g,username:o,email:s,mobile:a}},logout:async function(e){const t=await this.checkToken(e);if(t.code)return t;const r=Xr.command;return await M.doc(t.uid).update({token:r.pull(e)}),{code:0,msg:"退出成功"}},getRoleByUid:async function({uid:e}){if(!e)return{code:F,messageValues:{param:"用户Id"}};const t=await M.doc(e).get();return 0===t.data.length?{code:H}:{code:0,msg:"获取角色成功",role:t.data[0].role||[]}},getPermissionByRole:async function({roleID:e}){if(!e)return{code:F,messageValues:{param:"角色ID"}};if("admin"===e){return{code:0,msg:"获取权限成功",permission:(await q.limit(1e3).get()).data.map(e=>e.permission_id)}}const t=await B.where({role_id:e}).get();return 0===t.data.length?{code:G}:{code:0,msg:"获取权限成功",permission:t.data[0].permission||[]}},getPermissionByUid:async function({uid:e}){const t=await M.aggregate().match({_id:e}).project({role:!0}).unwind("$role").lookup({from:"uni-id-roles",localField:"role",foreignField:"role_id",as:"roleDetail"}).unwind("$roleDetail").replaceRoot({newRoot:"$roleDetail"}).end(),r=[];return t.data.forEach(e=>{Array.prototype.push.apply(r,e.permission)}),{code:0,msg:"获取权限成功",permission:x(r)}},bindRole:async function({uid:e,roleList:t,reset:r=!1}){const n={};return"string"==typeof t&&(t=[t]),n.role=r?t:Zr.push(t),await M.doc(e).update(n),{code:0,msg:"角色绑定成功"}},bindPermission:async function({roleID:e,permissionList:t,reset:r=!1}){const n={};return"string"==typeof t&&(t=[t]),n.permission=r?t:Zr.push(t),await B.where({role_id:e}).update(n),{code:0,msg:"权限绑定成功"}},unbindRole:async function({uid:e,roleList:t}){return"string"==typeof t&&(t=[t]),await M.doc(e).update({role:Zr.pull(Zr.in(t))}),{code:0,msg:"角色解绑成功"}},unbindPermission:async function({roleID:e,permissionList:t}){return"string"==typeof t&&(t=[t]),await B.where({role_id:e}).update({permission:Zr.pull(Zr.in(t))}),{code:0,msg:"权限解绑成功"}},addRole:async function({roleID:e,roleName:t,comment:r,permission:n=[]}){return e?"admin"===e?{code:K,messageValues:{param:"roleID",reason:"不可新增roleID为admin的角色"}}:(await B.add({role_id:e,role_name:t,comment:r,permission:n,create_date:Date.now()}),{code:0,msg:"角色新增成功"}):{code:F,messageValues:{param:"角色Id"}}},addPermission:async function({permissionID:e,permissionName:t,comment:r}){return e?(await q.add({permission_id:e,permission_name:t,comment:r,create_date:Date.now()}),{code:0,msg:"权限新增成功"}):{code:F,messageValues:{param:"权限ID"}}},getRoleList:async function({limit:e=20,offset:t=0,needTotal:r=!0}){const n={code:0,msg:"获取角色列表成功",roleList:(await B.skip(t).limit(e).get()).data};if(r){const{total:e}=await B.where({_id:Zr.exists(!0)}).count();n.total=e}return n},getRoleInfo:async function(e){const t=await B.where({role_id:e}).get();return 0===t.data.length?{code:G}:{code:0,...t.data[0]}},updateRole:async function({roleID:e,roleName:t,comment:r,permission:n}){return e?(await B.where({role_id:e}).update({role_name:t,comment:r,permission:n}),{code:0,msg:"角色更新成功"}):{code:F,messageValues:{param:"角色ID"}}},deleteRole:async function({roleID:e}){const t=y(e);if("string"===t)e=[e];else if("array"!==t)throw new Error("roleID只能为字符串或者数组");return await B.where({role_id:Zr.in(e)}).remove(),await M.where({role:Zr.elemMatch(Zr.in(e))}).update({role:Zr.pullAll(e)}),{code:0,msg:"角色删除成功"}},getPermissionList:async function({limit:e=20,offset:t=0,needTotal:r=!0}){const n={code:0,msg:"获取权限列表成功",permissionList:(await q.skip(t).limit(e).get()).data};if(r){const{total:e}=await q.where({_id:Zr.exists(!0)}).count();n.total=e}return n},getPermissionInfo:async function(e){const t=await q.where({permission_id:e}).get();return 0===t.data.length?{code:F,messageValues:{param:"权限ID"}}:{code:0,...t.data[0]}},updatePermission:async function({permissionID:e,permissionName:t,comment:r}){return e?(await q.where({permission_id:e}).update({permission_name:t,comment:r}),{code:0,msg:"权限更新成功"}):{code:F,messageValues:{param:"权限ID"}}},deletePermission:async function({permissionID:e}){const t=y(e);if("string"===t)e=[e];else if("array"!==t)throw new Error("permissionID只能为字符串或者数组");return await q.where({permission_id:Zr.in(e)}).remove(),await B.where({permission:Zr.elemMatch(Zr.in(e))}).update({permission:Zr.pullAll(e)}),{code:0,msg:"权限删除成功"}},bindAlipay:async function({uid:e,code:t,platform:r}){const n=r||this.context.PLATFORM,{openid:i}=await this._getAlipayApi({platform:n}).code2Session(t);if(!i)return{code:60401,messageValues:{account:"支付宝账号"}};let o=await M.where({ali_openid:i}).get();return o=this._getCurrentAppUser(o.data),o&&o.length>0?{code:60402,messageValues:{type:"支付宝账号"}}:(await M.doc(e).update({ali_openid:i}),{code:0,openid:i,msg:"绑定成功"})},bindEmail:async function({uid:e,email:t,code:r}){if(!(t=t&&t.trim()))return{code:F,messageValues:{param:"邮箱"}};if(!r)return{code:F,messageValues:{param:"验证码"}};const{emailToLowerCase:n}=this._getConfig();n&&(t=t.toLowerCase());let i=await M.where({email:t,email_confirmed:1}).get();if(i=this._getCurrentAppUser(i.data),i&&i.length>0)return{code:60201,messageValues:{type:"邮箱"}};if(r){const e=await this.verifyCode({email:t,code:r,type:"bind"});if(0!==e.code)return e}return await M.doc(e).update({email:t,email_confirmed:1}),{code:0,msg:"邮箱绑定成功",email:t}},bindMobile:async function({uid:e,mobile:t,code:r,openid:n,access_token:i,type:o="sms"}){if("univerify"===o){const e=this._getConfig(),r=e&&e.service&&e.service.univerify;if(!r)throw new Error("请在config.json中配置service.univerify下一键登录相关参数");const o=await Jr({...r,openid:n,access_token:i});if(0!==o.code)return o;t=""+o.phoneNumber}let s=await M.where({mobile:t,mobile_confirmed:1}).get();if(s=this._getCurrentAppUser(s.data),s&&s.length>0)return{code:60101,messageValues:{type:"手机号"}};if("sms"===o&&r){if(!t)return{code:F,messageValues:{param:"手机号码"}};if(!r)return{code:F,messageValues:{param:"验证码"}};const e=await this.verifyCode({mobile:t,code:r,type:"bind"});if(0!==e.code)return e}return await M.doc(e).update({mobile:t,mobile_confirmed:1}),{code:0,msg:"手机号码绑定成功",mobile:t}},bindWeixin:async function({uid:e,code:t,platform:r}){const n=r||this.context.PLATFORM,i="mp-weixin"===n,{openid:o,unionid:s,sessionKey:a,accessToken:c,refreshToken:u,expired:d}=await this._getWeixinApi({platform:n})[i?"code2Session":"getOauthAccessToken"](t);if(!o)return{code:60301,messageValues:{account:"微信openid"}};const l=en.command,p=[{wx_openid:{[n]:o}}];s&&p.push({wx_unionid:s});let f=await M.where(l.or(...p)).get();if(f=this._getCurrentAppUser(f.data),f&&f.length>0)return{code:60302,messageValues:{type:"微信账号"}};const m={wx_openid:{[n]:o}};let h;return s&&(m.wx_unionid=s),await M.doc(e).update(m),h=i?{sessionKey:a}:{accessToken:c,refreshToken:u,accessTokenExpired:d},{code:0,openid:o,unionid:s,...h,msg:"绑定成功"}},bindQQ:async function({uid:e,code:t,accessToken:r,platform:n}={}){const i=n||this.context.PLATFORM,o="mp-qq"===i,{openid:s,unionid:a,sessionKey:c}=await this._getQQApi()[o?"code2Session":"getOpenidByToken"]({code:t,accessToken:r});if(!s)return{code:60501,messageValues:{account:"qq openid"}};const u=tn.command,d=[{qq_openid:{[i]:s}}];a&&d.push({qq_unionid:a});let l=await M.where(u.or(...d)).get();if(l=this._getCurrentAppUser(l.data),l&&l.length>0)return{code:60502,messageValues:{type:"QQ账号"}};const p={qq_openid:{[i]:s}};return a&&(p.qq_unionid=a),await M.doc(e).update(p),{code:0,openid:s,unionid:a,...{accessToken:r,sessionKey:c},msg:"绑定成功"}},unbindAlipay:async function(e){const t=rn.command,r=await M.doc(e).update({ali_openid:t.remove()});return S("upRes:",r),1===r.updated?{code:0,msg:"支付宝解绑成功"}:{code:70401,msg:"支付宝解绑失败,请稍后再试"}},unbindEmail:async function({uid:e,email:t,code:r}){if(t=t&&t.trim(),!e||!t)return{code:F,messageValues:{param:e?"邮箱":"用户Id"}};const{emailToLowerCase:n}=this._getConfig();if(r){const e=await this.verifyCode({email:t,code:r,type:"unbind"});if(0!==e.code)return e}const i=nn.command;let o={_id:e,email:t};if(n){const r=t.toLowerCase();r!==t&&(o=i.or(o,{_id:e,email:r}))}return 1===(await M.where(o).update({email:i.remove(),email_confirmed:i.remove()})).updated?{code:0,msg:"邮箱解绑成功"}:{code:70201,msg:"邮箱解绑失败,请稍后再试"}},unbindMobile:async function({uid:e,mobile:t,code:r}){if(r){const e=await this.verifyCode({mobile:t,code:r,type:"unbind"});if(0!==e.code)return e}const n=on.command;return 1===(await M.where({_id:e,mobile:t}).update({mobile:n.remove(),mobile_confirmed:n.remove()})).updated?{code:0,msg:"手机号解绑成功"}:{code:70101,msg:"手机号解绑失败,请稍后再试"}},unbindWeixin:async function(e){const t=sn.command,r=await M.doc(e).update({wx_openid:t.remove(),wx_unionid:t.remove()});return S("upRes:",r),1===r.updated?{code:0,msg:"微信解绑成功"}:{code:70301,msg:"微信解绑失败,请稍后再试"}},unbindQQ:async function(e){const t=an.command,r=await M.doc(e).update({qq_openid:t.remove(),qq_unionid:t.remove()});return S("upRes:",r),1===r.updated?{code:0,msg:"QQ解绑成功"}:{code:70501,msg:"QQ解绑失败,请稍后再试"}},code2SessionAlipay:async function(e){let t=e;"string"==typeof e&&(t={code:e});try{const e=t.platform||this.context.PLATFORM,r=await this._getAlipayApi({platform:e}).code2Session(t.code);return r.openid?{code:0,msg:"",...r}:{code:80701,messageValues:{account:"支付宝账号"}}}catch(e){return{code:80702,messageValues:{account:"支付宝账号"}}}},code2SessionWeixin:async function(e){let t=e;"string"==typeof e&&(t={code:e});try{const e=t.platform||this.context.PLATFORM,r=await this._getWeixinApi({platform:e})["mp-weixin"===e?"code2Session":"getOauthAccessToken"](t.code);return r.openid?{code:0,msg:"",...r}:{code:80601,messageValues:{account:"微信openid"}}}catch(e){return{code:80602,messageValues:{account:"微信openid"}}}},verifyAppleIdentityToken:async function({identityToken:e,platform:t}){const r=t||this.context.PLATFORM,{code:n,msg:i}=await qr({clientType:r}).verifyIdentityToken(e);return 0!==n?{code:n,msg:i}:{code:n,msg:"验证通过",...i}},wxBizDataCrypt:async function({code:e,sessionKey:t,encryptedData:r,iv:i}){if(!r)return{code:80805,messageValues:{param:"encryptedData"}};if(!i)return{code:80806,messageValues:{param:"iv"}};if(!e&&!t)return{code:80804,messageValues:{param:"code或sessionKey"}};const o=this._getWeixinApi();if(!t){const r=await o.code2Session(e);if(!r.sessionKey)return{code:80801,msg:"sessionKey获取失败"};t=r.sessionKey}t=Buffer.from(t,"base64"),r=Buffer.from(r,"base64"),i=Buffer.from(i,"base64");try{var s=n.createDecipheriv("aes-128-cbc",t,i);s.setAutoPadding(!0);var a=s.update(r,"binary","utf8");a+=s.final("utf8"),a=JSON.parse(a)}catch(e){return{code:80802,msg:"解密失败:"+e.message}}return a.watermark.appid!==o.options.appId?{code:80803,msg:"appid不匹配"}:{code:0,msg:"解密成功",...a}},encryptPwd:function(e,{value:t,version:r}={}){if(!(e=e&&e.trim()))throw new Error("密码不可为空");if(!t){const e=this._getConfig(),{passwordSecret:n}=e;if("array"===y(n)){const e=n.sort((e,t)=>e.version-t.version);t=e[e.length-1].value,r=e[e.length-1].version}else t=n}if(!t)throw new Error("passwordSecret不正确");const i=n.createHmac("sha1",t.toString("ascii"));return i.update(e),{passwordHash:i.digest("hex"),version:r}},checkToken:async function(e,{needPermission:t,needUserInfo:r=!0}={}){const n=this._getConfig();try{const i=this._verifyToken(e);if(i.code)return i;const{uid:o,needPermission:s,role:a,permission:c,exp:u}=i,d=a&&c;t=void 0===t?s:t;const l=n.removePermissionAndRoleFromToken||!d||r,p=!n.removePermissionAndRoleFromToken&&!d||n.removePermissionAndRoleFromToken&&d||n.tokenExpiresThreshold&&u-Date.now()/1e3-1===r.indexOf(e)),t.push(e.token),await M.doc(o).update({token:t,last_login_date:Date.now(),last_login_ip:this.context.CLIENTIP}),{...m,...e}}return m}catch(e){return{code:90001,msg:"数据库读写异常:"+e.message,err:e}}},createToken:function({uid:e,needPermission:t,role:r,permission:n}){if(!e)return{code:30101,messageValues:{param:"用户ID"}};const i={uid:e,needPermission:t,role:r,permission:n},o=this._getConfig();if(!this.interceptorMap.has("customToken")){const e={...i};return this._createTokenInternal({signContent:e,config:o})}const s=this.interceptorMap.get("customToken");if("function"!=typeof s)throw new Error("custom-token.js应导出一个function");const a=s(i);return a instanceof Promise?a.then(e=>this._createTokenInternal({signContent:e,config:o})):this._createTokenInternal({signContent:a,config:o})},_checkPwd:function(e,t){if(!t)return{code:1,message:"密码不能为空"};const{password:r,password_secret_version:n}=e,i=this._getConfig(),{passwordSecret:o}=i,s=y(o);if("string"===s){const{passwordHash:e}=this.encryptPwd(t,{value:o});return e===r?{code:0,message:"密码校验通过"}:{code:2,message:"密码不正确"}}if("array"!==s)throw new Error("config内passwordSecret类型错误,只可设置string类型和array类型");const a=o.sort((e,t)=>e.version-t.version);let c;if(c=n?a.find(e=>e.version===n):a[0],!c)return{code:3,message:"secretVersion不正确"};const u=a[a.length-1],{passwordHash:d}=this.encryptPwd(t,c);if(d===r){const e={code:0,message:"密码校验通过"};if(c!==u){const{passwordHash:r,version:n}=this.encryptPwd(t,u);e.passwordHash=r,e.passwordVersion=n}return e}return{code:4,message:""}},_verifyToken:function(e){const t=this._getConfig();let r;try{r=jr(e,t.tokenSecret)}catch(e){return"TokenExpiredError"===e.name?{code:30203,msg:"token已过期,请重新登录",err:e}:{code:30204,msg:"非法token",err:e}}return t.bindTokenToDevice&&r.clientId&&r.clientId!==this._getClientUaHash()?{code:30201,msg:"token不合法,请重新登录"}:{code:0,message:"",...r}},_getExpiredToken:function(e){const t=this._getConfig(),r=[];return e.forEach(e=>{try{jr(e,t.tokenSecret)}catch(t){r.push(e)}}),r},_getPermissionListByRoleList:async function(e){if(!Array.isArray(e))return[];if(0===e.length)return[];if(e.includes("admin")){return(await q.limit(500).get()).data.map(e=>e.permission_id)}const t=await B.where({role_id:cn.in(e)}).get(),r=[];return t.data.forEach(e=>{Array.prototype.push.apply(r,e.permission)}),x(r)},_getClientUaHash:function(){const e=n.createHash("md5"),t=/MicroMessenger/i.test(this.context.CLIENTUA)?this.context.CLIENTUA.replace(/(MicroMessenger\S+).*/i,"$1"):this.context.CLIENTUA;return e.update(t),e.digest("hex")},_createTokenInternal:function({signContent:e,config:t}){if(t.tokenExpiresIn&&t.tokenExpiresThreshold&&t.tokenExpiresIn<=t.tokenExpiresThreshold)throw new Error(`tokenExpiresIn(${t.tokenExpiresIn})不可小于或等于tokenExpiresThreshold(${t.tokenExpiresThreshold})`);return"object"===y(e)&&e.uid?(t.bindTokenToDevice&&(e.clientId=this._getClientUaHash()),{token:Nr(e,t.tokenSecret,{expiresIn:t.tokenExpiresIn}),tokenExpired:Date.now()+1e3*t.tokenExpiresIn}):{code:30101,messageValues:{param:"用户ID"}}},setVerifyCode:async function({mobile:e,email:t,code:r,expiresIn:n,type:i}){if(t=t&&t.trim(),e=e&&e.trim(),t){const{emailToLowerCase:e}=this._getConfig();e&&(t=t.toLowerCase())}if(!e&&!t)return{code:50101,messageValues:{param:"手机号或邮箱"}};if(e&&t)return{code:50102,messageValues:{param:"参数",reason:"手机号和邮箱不可同时存在"}};r||(r=I()),n||(n=180);const o=Date.now(),s={mobile:e,email:t,type:i,code:r,state:0,ip:this.context.CLIENTIP,created_at:o,expired_at:o+1e3*n};return S("addRes",await L.add(s)),{code:0,mobile:e,email:t}},verifyCode:async function({mobile:e,email:t,code:r,type:n}){if(t=t&&t.trim(),e=e&&e.trim(),t){const{emailToLowerCase:e}=this._getConfig();e&&(t=t.toLowerCase())}if(!e&&!t)return{code:50201,messageValues:{param:"手机号或邮箱"}};if(e&&t)return{code:50203,messageValues:{param:"参数",reason:"手机号和邮箱不可同时存在"}};const i=un.command,o=Date.now(),s={mobile:e,email:t,type:n,code:r,state:0,expired_at:i.gt(o)},a=await L.where(s).orderBy("created_at","desc").limit(1).get();if(S("verifyRecord:",a),a&&a.data&&a.data.length>0){const e=a.data[0];return S("upRes",await L.doc(e._id).update({state:1})),{code:0,msg:"验证通过"}}return{code:50202,msg:"验证码错误或已失效"}},sendSmsCode:async function({mobile:e,code:t,type:r,templateId:n}){if(!e)throw new Error("手机号码不可为空");if(t||(t=I()),!r)throw new Error("验证码类型不可为空");const i=this._getConfig();let o=i&&i.service&&i.service.sms;if(!o)throw new Error("请在config.json或init方法中配置service.sms下短信相关参数");o=Object.assign({codeExpiresIn:300},o);const s=["smsKey","smsSecret"];if(!n&&!o.name)throw new Error("不传入templateId时应在config.json或init方法内service.sms下配置name字段以正确使用uniID_code模板");for(let e=0,t=s.length;e=0?o:{code:0,msg:"验证码发送成功"}}catch(e){return{code:50301,msg:"验证码发送失败, "+e.message}}}});let ln;try{ln=require("uni-config-center")}catch(e){}const pn="\n传入配置的方式有以下几种:\n- 在uni-config-center公共模块的uni-id目录下放置config.json文件(推荐)\n- 在uni-id公共模块的目录下放置config.json文件\n- 使用init方法传入配置\n- 如果使用uni-config-center且HBuilderX版本低于3.1.8,批量上传云函数及公共模块后需要再单独上传一次uni-id";class fn{constructor({context:e,config:t}={}){const r=ln&&ln({pluginId:"uni-id"});this.pluginConfig=r,this.config=t||this._getConfigContent(),Object.defineProperty(this,"context",{get:()=>e||global.__ctx__}),this.interceptorMap=new Map,r&&r.hasFile("custom-token.js")&&this.setInterceptor("customToken",require(r.resolve("custom-token.js")))}get dev(){return console.warn("当前正在使用uniID.dev属性,注意此属性仅可用于开发调试"),{getConfig:this._getConfig.bind(this)}}_parseConfig(e){return Array.isArray(e)?e:e[0]?Object.values(e):e}_getCurrentAppConfig(e){if(!Array.isArray(e))return e;if(!this.context.APPID)throw new Error("uni-id初始化时未传入DCloud AppId,如果使用云函数url化访问需要使用uniID.createInstance方法创建uni-id实例,并在context内传入APPID参数");return e.find(e=>e.dcloudAppid===this.context.APPID)||e.find(e=>e.isDefaultConfig)}_getConfigContent(){if(this.pluginConfig&&this.pluginConfig.hasFile("config.json")){this._hasConfigFile=!0;try{return this._parseConfig(this.pluginConfig.config())}catch(e){return}}const e=r.resolve(__dirname,"config.json");this._hasConfigFile=t.existsSync(e);try{return this._parseConfig(require(e))}catch(e){}}init(e){console.warn("uniID.init接口已废弃,如需自行传入配置请使用uniID.createInstance接口创建uniID实例来使用"),this.config=e}setInterceptor(e,t){this.interceptorMap.set(e,t)}_getConfig(e){const t=this.config&&0!==Object.keys(this.config).length;if(this._hasConfigFile&&!t)throw new Error("请确保公用模块uni-id对应的配置文件格式正确(不可包含注释)"+pn);if(!t)throw new Error("公用模块uni-id缺少配置信息"+pn);const r=this._getCurrentAppConfig(this.config),n=Object.assign(r,r[e||this.context.PLATFORM])||{},i=Object.assign({bindTokenToDevice:!1,tokenExpiresIn:7200,tokenExpiresThreshold:1200,passwordErrorLimit:6,passwordErrorRetryTime:3600,usernameToLowerCase:!0,emailToLowerCase:!0},n);return["passwordSecret","tokenSecret","tokenExpiresIn","passwordErrorLimit","passwordErrorRetryTime"].forEach(e=>{if(!i||!i[e])throw new Error("请在公用模块uni-id的配置信息中内添加配置项:"+e)}),i}}for(const e in dn)fn.prototype[e]=dn[e];const mn=["wxBizDataCrypt","verifyAppleIdentityToken","code2SessionWeixin","code2SessionAlipay"];function hn({context:e,config:t}={}){const r=new fn({context:e,config:t});return new Proxy(r,{get(e,t){if(t in e&&0!==t.indexOf("_"))return"function"==typeof e[t]?(mn.indexOf(t)>-1&&console.warn(`uniID.${t}方法即将废弃,后续版本将不再暴露此方法`),(r=e[t],function(){const e=r.apply(this,arguments);return g(e)?e.then(e=>(U(e),e)):(U(e),e)}).bind(e)):e[t];var r}})}fn.prototype.createInstance=hn;var gn=hn();module.exports=gn; diff --git a/uni_modules/uni-id/uniCloud/cloudfunctions/common/uni-id/package.json b/uni_modules/uni-id/uniCloud/cloudfunctions/common/uni-id/package.json new file mode 100644 index 0000000000000000000000000000000000000000..00a527f6e13062e2bbcaeb54d0d4dbde96fae232 --- /dev/null +++ b/uni_modules/uni-id/uniCloud/cloudfunctions/common/uni-id/package.json @@ -0,0 +1,16 @@ +{ + "name": "uni-id", + "version": "3.3.6", + "description": "uni-id for uniCloud", + "main": "index.js", + "homepage": "https://uniapp.dcloud.io/uniCloud/uni-id", + "repository": { + "type": "git", + "url": "git+https://gitee.com/dcloud/uni-id.git" + }, + "author": "", + "license": "Apache-2.0", + "dependencies": { + "uni-config-center": "file:../../../../../uni-config-center/uniCloud/cloudfunctions/common/uni-config-center" + } +} \ No newline at end of file diff --git a/uni_modules/uni-image-menu/changelog.md b/uni_modules/uni-image-menu/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/uni_modules/uni-image-menu/js_sdk/uni-image-menu.js b/uni_modules/uni-image-menu/js_sdk/uni-image-menu.js new file mode 100644 index 0000000000000000000000000000000000000000..b482b833dc47ce9406b701ca467fafbb6cc7c415 --- /dev/null +++ b/uni_modules/uni-image-menu/js_sdk/uni-image-menu.js @@ -0,0 +1,169 @@ +var nvMask,nvImageMenu; +export default { + show({list,cancelText},callback){ + console.log(789789879); + if(!list){ + list = [{ + "img":"/static/sharemenu/wechatfriend.png", + "text":"图标文字" + }] + } + //以下为计算菜单的nview绘制布局,为固定算法,使用者无关关心 + var screenWidth = plus.screen.resolutionWidth + //以360px宽度屏幕为例,上下左右边距及2排按钮边距留25像素,图标宽度55像素,同行图标间的间距在360宽的屏幕是30px,但需要动态计算,以此原则计算4列图标分别的left位置 + //图标下的按钮文字距离图标5像素,文字大小12像素 + //底部取消按钮高度固定为44px + //TODO 未处理横屏和pad,这些情况6个图标应该一排即可 + var margin = 20, + iconWidth = 60, + icontextSpace = 5, + textHeight = 12 + var left1 = margin / 360 * screenWidth + var iconSpace = (screenWidth - (left1 * 2) - (iconWidth * 4)) / 3 //屏幕宽度减去左右留白间距,再减去4个图标的宽度,就是3个同行图标的间距 + if (iconSpace <= 5) { //屏幕过窄时,缩小边距和图标大小,再算一次 + margin = 15 + iconWidth = 40 + left1 = margin / 360 * screenWidth + iconSpace = (screenWidth - (left1 * 2) - (iconWidth * 4)) / 3 //屏幕宽度减去左右留白间距,再减去4个图标的宽度,就是3个同行图标的间距 + } + var left2 = left1 + iconWidth + iconSpace + var left3 = left1 + (iconWidth + iconSpace) * 2 + var left4 = left1 + (iconWidth + iconSpace) * 3 + var top1 = left1 + var top2 = top1 + iconWidth + icontextSpace + textHeight + left1 + + const TOP = {top1,top2}, LEFT = {left1,left2,left3,left4}; + + nvMask = new plus.nativeObj.View("nvMask", { //先创建遮罩层 + top: '0px', + left: '0px', + height: '100%', + width: '100%', + backgroundColor: 'rgba(0,0,0,0.2)' + }); + nvMask.addEventListener("click", function() { //处理遮罩层点击 + nvMask.hide(); + nvImageMenu.hide(); + }) + nvImageMenu = new plus.nativeObj.View("nvImageMenu", { //创建底部图标菜单 + bottom: '0px', + left: '0px', + height: (iconWidth + textHeight + 2 * margin)*Math.ceil(list.length/4) +44+'px',//'264px', + width: '100%', + backgroundColor: 'rgb(255,255,255)' + }); + + let myList = [] + list.forEach((item,i)=>{ + myList.push({ + tag: 'img', + src: item.img, + position: { + top: TOP['top'+( parseInt(i/4) +1)], + left: LEFT['left'+(1+i%4)], + width: iconWidth, + height: iconWidth + } + }) + myList.push({ + tag: 'font', + text: item.text, + textStyles: { + size: textHeight + }, + position: { + top: TOP['top'+(parseInt(i/4)+1)] + iconWidth + icontextSpace, + left: LEFT['left'+(1+i%4)], + width: iconWidth, + height: textHeight + } + }) + }) + + //绘制底部图标菜单的内容 + nvImageMenu.draw([ + { + tag: 'rect',//菜单顶部的分割灰线 + color: '#e7e7e7', + position: { + top: '0px', + height: '1px' + } + }, + { + tag: 'font', + text: cancelText,//底部取消按钮的文字 + textStyles: { + size: '14px' + }, + position: { + bottom: '0px', + height: '44px' + } + }, + { + tag: 'rect',//底部取消按钮的顶部边线 + color: '#e7e7e7', + position: { + bottom: '45px', + height: '1px' + } + }, + ...myList + ]) + nvMask.show() + nvImageMenu.show() //5+应支持从底部向上弹出的动画 + + + + nvImageMenu.addEventListener("click",e=>{ //处理底部图标菜单的点击事件,根据点击位置触发不同的逻辑 + // console.log("click menu"+JSON.stringify(e)); + if (e.screenY > plus.screen.resolutionHeight - 44) { //点击了底部取消按钮 + nvMask.hide(); + nvImageMenu.hide(); + } else if (e.clientX < 5 || e.clientX > screenWidth - 5 || e.clientY < 5) { + //屏幕左右边缘5像素及菜单顶部5像素不处理点击 + } else { //点击了图标按钮 + var iClickIndex = -1 //点击的图标按钮序号,第一个图标按钮的index为0 + var iRow = e.clientY < (top2 - (left1 / 2)) ? 0 : 1 + var iCol = -1 + if (e.clientX < (left2 - (iconSpace / 2))) { + iCol = 0 + } else if (e.clientX < (left3 - (iconSpace / 2))) { + iCol = 1 + } else if (e.clientX < (left4 - (iconSpace / 2))) { + iCol = 2 + } else { + iCol = 3 + } + if (iRow == 0) { + iClickIndex = iCol + } else { + iClickIndex = iCol + 4 + } + // console.log("点击按钮的序号: " + iClickIndex); + // if (iClickIndex >= 0 && iClickIndex <= 5) { //处理具体的点击逻辑,此处也可以自行定义逻辑。如果增减了按钮,此处也需要跟着修改 + // } + callback(iClickIndex) + this.hide() + } + }) + /* nvImageMenu.addEventListener("touchstart", function(e) { + if (e.screenY > (plus.screen.resolutionHeight - 44)) { + //TODO 这里可以处理按下背景变灰的效果 + } + }) + nvImageMenu.addEventListener("touchmove", function(e) { + //TODO 这里可以处理按下背景变灰的效果 + if (e.screenY > plus.screen.resolutionHeight - 44) {} + }) + nvImageMenu.addEventListener("touchend", function(e) { + //TODO 这里可以处理释放背景恢复的效果 + }) + */ + }, + hide(){ + nvMask.hide() + nvImageMenu.hide() + } +} \ No newline at end of file diff --git a/uni_modules/uni-image-menu/package.json b/uni_modules/uni-image-menu/package.json new file mode 100644 index 0000000000000000000000000000000000000000..5938aa931684e417f453e80af0c09ed7e0227d85 --- /dev/null +++ b/uni_modules/uni-image-menu/package.json @@ -0,0 +1,76 @@ +{ + "id": "uni-image-menu", + "displayName": "uni-image-menu", + "version": "1.0.0", + "description": "uni-image-menu", + "keywords": [ + "uni-image-menu" +], + "repository": "", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "JS SDK", + "JS SDK" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "", + "data": "", + "permissions": "" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "u", + "aliyun": "u" + }, + "client": { + "App": { + "app-vue": "u", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "u", + "Android Browser": "u", + "微信浏览器(Android)": "u", + "QQ浏览器(Android)": "u" + }, + "H5-pc": { + "Chrome": "u", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "u", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-image-menu/readme.md b/uni_modules/uni-image-menu/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..ecca1b09d800a6127c4306c266994a4e96a5dbbf --- /dev/null +++ b/uni_modules/uni-image-menu/readme.md @@ -0,0 +1,19 @@ +# uni-image-menu + +使用示例: +``` +import uniImageMenu from 'uni_modules/uni-image-menu/js_sdk/uni-image-menu.js'; +uniImageMenu.show({ + list:[{ + "img": "/static/sharemenu/wechatfriend.png", + "text": "微信好友" + }, + { + "img": "/static/sharemenu/wechatmoments.png", + "text": "微信朋友圈" + }], + cancelText:param.cancelText +}, e => { + console.log(e) +}) +``` \ No newline at end of file diff --git a/uni_modules/uni-indexed-list/changelog.md b/uni_modules/uni-indexed-list/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..7af85e4ce3501afd8417cfadb0dfd90a411b9535 --- /dev/null +++ b/uni_modules/uni-indexed-list/changelog.md @@ -0,0 +1,12 @@ +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.11(2021-05-12) +- 新增 组件示例地址 +## 1.0.10(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.9(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.8(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 支持 PC 端 diff --git a/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list-item.vue b/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..7976f6844df5479ded3fa935f10abe7f380be962 --- /dev/null +++ b/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list-item.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list.vue b/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list.vue new file mode 100644 index 0000000000000000000000000000000000000000..665379ccb2d87394c9a32d93610ad274b671b156 --- /dev/null +++ b/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list.vue @@ -0,0 +1,359 @@ + + + diff --git a/uni_modules/uni-indexed-list/package.json b/uni_modules/uni-indexed-list/package.json new file mode 100644 index 0000000000000000000000000000000000000000..5e1cddc999e39890ecaf6a94e3c838694c9bcd75 --- /dev/null +++ b/uni_modules/uni-indexed-list/package.json @@ -0,0 +1,84 @@ +{ + "id": "uni-indexed-list", + "displayName": "uni-indexed-list 索引列表", + "version": "1.1.0", + "description": "索引列表组件,右侧带索引的列表,方便快速定位到具体内容,通常用于城市/机场选择等场景", + "keywords": [ + "uni-ui", + "索引列表", + "索引", + "列表" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} diff --git a/uni_modules/uni-indexed-list/readme.md b/uni_modules/uni-indexed-list/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..4fb6a0a73ebf75913498860b6f7ffd5d805cc4a1 --- /dev/null +++ b/uni_modules/uni-indexed-list/readme.md @@ -0,0 +1,67 @@ + + +## IndexedList 索引列表 +> **组件名:uni-indexed-list** +> 代码块: `uIndexedList` + + +用于展示索引列表。 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +在 ``template`` 中使用组件 + +```html + +``` + +## API + +### IndexedList Props + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|options |Object |- |索引列表需要的数据对象 | +|showSelect |Boolean|- | 展示模式,true 为展示默认,false 为选择模式,默认为 false | + +**options 数据格式说明** + +```json +[{ + "letter": "A", + "data": [ + "阿克苏机场", + "阿拉山口机场", + "阿勒泰机场", + "阿里昆莎机场", + "安庆天柱山机场", + "澳门国际机场" + ] +}, { + "letter": "B", + "data": [ + "保山机场", + "包头机场", + "北海福成机场", + "北京南苑机场", + "北京首都国际机场" + ] +}] +``` + +### IndexedList Events + +|事件名 |说明 |返回值 | +|:-: |:-: |:-: | +|click |点击列表事件 ,返回当前选择项的事件对象 |- | + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/indexed-list/indexed-list](https://hellouniapp.dcloud.net.cn/pages/extUI/indexed-list/indexed-list) \ No newline at end of file diff --git a/uni_modules/uni-link/changelog.md b/uni_modules/uni-link/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..1d24c16c3dcc93a2dc74a88a5375a051cd1d072a --- /dev/null +++ b/uni_modules/uni-link/changelog.md @@ -0,0 +1,11 @@ +## 0.0.6(2021-07-30) +- 支持自定义插槽 +## 0.0.5(2021-06-21) +- 新增 download 属性,H5平台下载文件名 +## 0.0.4(2021-05-12) +- 新增 组件示例地址 +## 0.0.3(2021-03-09) +- 新增 href 属性支持 tel:|mailto: + +## 0.0.2(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-link/components/uni-link/uni-link.vue b/uni_modules/uni-link/components/uni-link/uni-link.vue new file mode 100644 index 0000000000000000000000000000000000000000..d8aaaf8faf64eb1fc5d083a5dd9dc1c57a5b370e --- /dev/null +++ b/uni_modules/uni-link/components/uni-link/uni-link.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/uni_modules/uni-link/package.json b/uni_modules/uni-link/package.json new file mode 100644 index 0000000000000000000000000000000000000000..96f23445023e0ae325075658bf9f9c91c1fbab69 --- /dev/null +++ b/uni_modules/uni-link/package.json @@ -0,0 +1,83 @@ +{ + "id": "uni-link", + "displayName": "uni-link 超链接", + "version": "0.0.6", + "description": "uni-link是一个外部网页超链接组件,在小程序内复制url,在app内打开外部浏览器,在h5端打", + "keywords": [ + "uni-ui", + "uniui", + "link", + "超链接", + "" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "y", + "联盟": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-link/readme.md b/uni_modules/uni-link/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..0735374c17ef048d6eb466a70545ad299adf42e9 --- /dev/null +++ b/uni_modules/uni-link/readme.md @@ -0,0 +1,48 @@ + + +## Link 链接 +> **组件名:uni-link** +> 代码块: `uLink` + + +uni-link是一个外部网页超链接组件,在小程序内复制url,在app内打开外部浏览器,在h5端打开新网页。 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +在 ``template`` 中使用组件 + +```html + +``` + +## API + +### Link Props + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|href |String |- |链接地址 | +|text |String |- |显示文字 | +|download |String |- |H5平台下载文件名 | +|showUnderLine|Boolean|true |是否显示下划线 | +|copyTips |String |已自动复制网址,请在手机浏览器里粘贴该网址 |在小程序端复制链接时的提示语 | +|color |String |#999999 |链接文字颜色 | +|fontSize |String |14 |链接文字大小,单位px | + + +### Link Slots + +|名称|说明| +|:-:|:-:| +|default|自定义内容,回覆盖原有的内容显示| + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/link/link](https://hellouniapp.dcloud.net.cn/pages/extUI/link/link) \ No newline at end of file diff --git a/uni_modules/uni-list/changelog.md b/uni_modules/uni-list/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..dd6f01d7855d0b0684fa5c2256cf3ed4e88a5b01 --- /dev/null +++ b/uni_modules/uni-list/changelog.md @@ -0,0 +1,15 @@ +## 1.1.3(2021-08-30) +- 修复 在vue3中to属性在发行应用的时候报错的bug +## 1.1.2(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.1.1(2021-07-21) +- 修复 与其他组件嵌套使用时,点击失效的Bug +## 1.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.17(2021-05-12) +- 新增 组件示例地址 +## 1.0.16(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 1.0.15(2021-02-05) +- 调整为uni_modules目录规范 +- 修复 uni-list-chat 角标显示不正常的问题 diff --git a/uni_modules/uni-list/components/uni-list-ad/uni-list-ad.vue b/uni_modules/uni-list/components/uni-list-ad/uni-list-ad.vue new file mode 100644 index 0000000000000000000000000000000000000000..e256e4c5c8e45ca094c0002c19a4928aa9fbcb32 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list-ad/uni-list-ad.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.scss b/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.scss new file mode 100644 index 0000000000000000000000000000000000000000..311f8d9faf070f43c719519076af6a8b29424383 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.scss @@ -0,0 +1,58 @@ +/** + * 这里是 uni-list 组件内置的常用样式变量 + * 如果需要覆盖样式,这里提供了基本的组件样式变量,您可以尝试修改这里的变量,去完成样式替换,而不用去修改源码 + * + */ + +// 背景色 +$background-color : #fff; +// 分割线颜色 +$divide-line-color : #e5e5e5; + +// 默认头像大小,如需要修改此值,注意同步修改 js 中的值 const avatarWidth = xx ,目前只支持方形头像 +// nvue 页面不支持修改头像大小 +$avatar-width : 45px ; + +// 头像边框 +$avatar-border-radius: 5px; +$avatar-border-color: #eee; +$avatar-border-width: 1px; + +// 标题文字样式 +$title-size : 16px; +$title-color : #3b4144; +$title-weight : normal; + +// 描述文字样式 +$note-size : 12px; +$note-color : #999; +$note-weight : normal; + +// 右侧额外内容默认样式 +$right-text-size : 12px; +$right-text-color : #999; +$right-text-weight : normal; + +// 角标样式 +// nvue 页面不支持修改圆点位置以及大小 +// 角标在左侧时,角标的位置,默认为 0 ,负数左/下移动,正数右/上移动 +$badge-left: 0px; +$badge-top: 0px; + +// 显示圆点时,圆点大小 +$dot-width: 10px; +$dot-height: 10px; + +// 显示角标时,角标大小和字体大小 +$badge-size : 18px; +$badge-font : 12px; +// 显示角标时,角标前景色 +$badge-color : #fff; +// 显示角标时,角标背景色 +$badge-background-color : #ff5a5f; +// 显示角标时,角标左右间距 +$badge-space : 6px; + +// 状态样式 +// 选中颜色 +$hover : #f5f5f5; diff --git a/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.vue b/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.vue new file mode 100644 index 0000000000000000000000000000000000000000..70345afc264d6f052b5e618c6b84a84be7c25777 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.vue @@ -0,0 +1,534 @@ + + + + + diff --git a/uni_modules/uni-list/components/uni-list-item/uni-list-item.vue b/uni_modules/uni-list/components/uni-list-item/uni-list-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..167222bcbf685b5cd048f75f418f3ddb30d38e2b --- /dev/null +++ b/uni_modules/uni-list/components/uni-list-item/uni-list-item.vue @@ -0,0 +1,461 @@ + + + + + diff --git a/uni_modules/uni-list/components/uni-list/uni-list.vue b/uni_modules/uni-list/components/uni-list/uni-list.vue new file mode 100644 index 0000000000000000000000000000000000000000..1c85003e5547d3d2b16115037b76d51ae2a1d970 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list/uni-list.vue @@ -0,0 +1,106 @@ + + + + diff --git a/uni_modules/uni-list/components/uni-list/uni-refresh.vue b/uni_modules/uni-list/components/uni-list/uni-refresh.vue new file mode 100644 index 0000000000000000000000000000000000000000..3b4c5a230061935d31d8332663b8a511b5143759 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list/uni-refresh.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/uni_modules/uni-list/components/uni-list/uni-refresh.wxs b/uni_modules/uni-list/components/uni-list/uni-refresh.wxs new file mode 100644 index 0000000000000000000000000000000000000000..818a6b721b1172073d91c1f6a456d2b54d772f77 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list/uni-refresh.wxs @@ -0,0 +1,87 @@ +var pullDown = { + threshold: 95, + maxHeight: 200, + callRefresh: 'onrefresh', + callPullingDown: 'onpullingdown', + refreshSelector: '.uni-refresh' +}; + +function ready(newValue, oldValue, ownerInstance, instance) { + var state = instance.getState() + state.canPullDown = newValue; + // console.log(newValue); +} + +function touchStart(e, instance) { + var state = instance.getState(); + state.refreshInstance = instance.selectComponent(pullDown.refreshSelector); + state.canPullDown = (state.refreshInstance != null && state.refreshInstance != undefined); + if (!state.canPullDown) { + return + } + + // console.log("touchStart"); + + state.height = 0; + state.touchStartY = e.touches[0].pageY || e.changedTouches[0].pageY; + state.refreshInstance.setStyle({ + 'height': 0 + }); + state.refreshInstance.callMethod("onchange", true); +} + +function touchMove(e, ownerInstance) { + var instance = e.instance; + var state = instance.getState(); + if (!state.canPullDown) { + return + } + + var oldHeight = state.height; + var endY = e.touches[0].pageY || e.changedTouches[0].pageY; + var height = endY - state.touchStartY; + if (height > pullDown.maxHeight) { + return; + } + + var refreshInstance = state.refreshInstance; + refreshInstance.setStyle({ + 'height': height + 'px' + }); + + height = height < pullDown.maxHeight ? height : pullDown.maxHeight; + state.height = height; + refreshInstance.callMethod(pullDown.callPullingDown, { + height: height + }); +} + +function touchEnd(e, ownerInstance) { + var state = e.instance.getState(); + if (!state.canPullDown) { + return + } + + state.refreshInstance.callMethod("onchange", false); + + var refreshInstance = state.refreshInstance; + if (state.height > pullDown.threshold) { + refreshInstance.callMethod(pullDown.callRefresh); + return; + } + + refreshInstance.setStyle({ + 'height': 0 + }); +} + +function propObserver(newValue, oldValue, instance) { + pullDown = newValue; +} + +module.exports = { + touchmove: touchMove, + touchstart: touchStart, + touchend: touchEnd, + propObserver: propObserver +} diff --git a/uni_modules/uni-list/package.json b/uni_modules/uni-list/package.json new file mode 100644 index 0000000000000000000000000000000000000000..f7f7ef948f2d7ed1eea95d70a846ca9d92b97604 --- /dev/null +++ b/uni_modules/uni-list/package.json @@ -0,0 +1,91 @@ +{ + "id": "uni-list", + "displayName": "uni-list 列表", + "version": "1.1.3", + "description": "List 组件 ,帮助使用者快速构建列表。", + "keywords": [ + "", + "uni-ui", + "uniui", + "列表", + "", + "list" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-badge", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-list/readme.md b/uni_modules/uni-list/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..e4a5d73d81b4811aee609bc7ba37bdded964cd26 --- /dev/null +++ b/uni_modules/uni-list/readme.md @@ -0,0 +1,347 @@ + + +## List 列表 +> **组件名:uni-list** +> 代码块: `uList`、`uListItem` +> 关联组件:`uni-list-item`、`uni-badge`、`uni-icons`、`uni-list-chat`、`uni-list-ad` + + +List 列表组件,包含基本列表样式、可扩展插槽机制、长列表性能优化、多端兼容。 + +在vue页面里,它默认使用页面级滚动。在app-nvue页面里,它默认使用原生list组件滚动。这样的长列表,在滚动出屏幕外后,系统会回收不可见区域的渲染内存资源,不会造成滚动越长手机越卡的问题。 + +uni-list组件是父容器,里面的核心是uni-list-item子组件,它代表列表中的一个可重复行,子组件可以无限循环。 + +uni-list-item有很多风格,uni-list-item组件通过内置的属性,满足一些常用的场景。当内置属性不满足需求时,可以通过扩展插槽来自定义列表内容。 + +内置属性可以覆盖的场景包括:导航列表、设置列表、小图标列表、通信录列表、聊天记录列表。 + +涉及很多大图或丰富内容的列表,比如类今日头条的新闻列表、类淘宝的电商列表,需要通过扩展插槽实现。 + +下文均有样例给出。 + +uni-list不包含下拉刷新和上拉翻页。上拉翻页另见组件:[uni-load-more](https://ext.dcloud.net.cn/plugin?id=29) + + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - 组件需要依赖 `sass` 插件 ,请自行手动安装 +> - 组件内部依赖 `'uni-icons'` 、`uni-badge` 组件 +> - `uni-list` 和 `uni-list-item` 需要配套使用,暂不支持单独使用 `uni-list-item` +> - 只有开启点击反馈后,会有点击选中效果 +> - 使用插槽时,可以完全自定义内容 +> - note 、rightText 属性暂时没做限制,不支持文字溢出隐藏,使用时应该控制长度显示或通过默认插槽自行扩展 +> - 支付宝小程序平台需要在支付宝小程序开发者工具里开启 component2 编译模式,开启方式: 详情 --> 项目配置 --> 启用 component2 编译 +> - 如果需要修改 `switch`、`badge` 样式,请使用插槽自定义 +> - 在 `HBuilderX` 低版本中,可能会出现组件显示 `undefined` 的问题,请升级最新的 `HBuilderX` 或者 `cli` +> - 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + +### 基本用法 + +- 设置 `title` 属性,可以显示列表标题 +- 设置 `disabled` 属性,可以禁用当前项 + +```html + + + + + +``` + +### 多行内容显示 + +- 设置 `note` 属性 ,可以在第二行显示描述文本信息 + +```html + + + + + +``` + +### 右侧显示角标、switch + +- 设置 `show-badge` 属性 ,可以显示角标内容 +- 设置 `show-switch` 属性,可以显示 switch 开关 + +```html + + + + + +``` + +### 左侧显示略缩图、图标 + +- 设置 `thumb` 属性 ,可以在列表左侧显示略缩图 +- 设置 `show-extra-icon` 属性,并指定 `extra-icon` 可以在左侧显示图标 + +```html + + + + +``` + +### 开启点击反馈和右侧箭头 +- 设置 `clickable` 为 `true` ,则表示这是一个可点击的列表,会默认给一个点击效果,并可以监听 `click` 事件 +- 设置 `link` 属性,会自动开启点击反馈,并给列表右侧添加一个箭头 +- 设置 `to` 属性,可以跳转页面,`link` 的值表示跳转方式,如果不指定,默认为 `navigateTo` + +```html + + + + + + + +``` + + +### 聊天列表示例 +- 设置 `clickable` 为 `true` ,则表示这是一个可点击的列表,会默认给一个点击效果,并可以监听 `click` 事件 +- 设置 `link` 属性,会自动开启点击反馈,`link` 的值表示跳转方式,如果不指定,默认为 `navigateTo` +- 设置 `to` 属性,可以跳转页面 +- `time` 属性,通常会设置成时间显示,但是这个属性不仅仅可以设置时间,你可以传入任何文本,注意文本长度可能会影响显示 +- `avatar` 和 `avatarList` 属性同时只会有一个生效,同时设置的话,`avatarList` 属性的长度大于1 ,`avatar` 属性将失效 +- 可以通过默认插槽自定义列表右侧内容 + +```html + + + + + + + + + + + + + + + + + 刚刚 + + + + + + + +``` + +```javascript + +export default { + components: {}, + data() { + return { + avatarList: [{ + url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png' + }, { + url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png' + }, { + url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png' + }] + } + } +} + +``` + + +```css + +.chat-custom-right { + flex: 1; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + justify-content: space-between; + align-items: flex-end; +} + +.chat-custom-text { + font-size: 12px; + color: #999; +} + +``` + +## API + +### List Props + +属性名 |类型 |默认值 | 说明 +:-: |:-: |:-: | :-: +border |Boolean |true | 是否显示边框 + + +### ListItem Props + +属性名 |类型 |默认值 | 说明 +:-: |:-: |:-: | :-: +title |String |- | 标题 +note |String |- | 描述 +ellipsis |Number |0 | title 是否溢出隐藏,可选值,0:默认; 1:显示一行; 2:显示两行;【nvue 暂不支持】 +thumb |String |- | 左侧缩略图,若thumb有值,则不会显示扩展图标 +thumbSize |String |medium | 略缩图尺寸,可选值,lg:大图; medium:一般; sm:小图; +showBadge |Boolean |false | 是否显示数字角标 +badgeText |String |- | 数字角标内容 +badgeType |String |- | 数字角标类型,参考[uni-icons](https://ext.dcloud.net.cn/plugin?id=21) +rightText |String |- | 右侧文字内容 +disabled |Boolean |false | 是否禁用 +showArrow |Boolean |true | 是否显示箭头图标 +link |String |navigateTo | 新页面跳转方式,可选值见下表 +to |String |- | 新页面跳转地址,如填写此属性,click 会返回页面是否跳转成功 +clickable |Boolean |false | 是否开启点击反馈 +showSwitch |Boolean |false | 是否显示Switch +switchChecked |Boolean |false | Switch是否被选中 +showExtraIcon |Boolean |false | 左侧是否显示扩展图标 +extraIcon |Object |- | 扩展图标参数,格式为 ``{color: '#4cd964',size: '22',type: 'spinner'}``,参考 [uni-icons](https://ext.dcloud.net.cn/plugin?id=28) +direction | String |row | 排版方向,可选值,row:水平排列; column:垂直排列; 3个插槽是水平排还是垂直排,也受此属性控制 + + +#### Link Options + +属性名 | 说明 +:-: | :-: +navigateTo | 同 uni.navigateTo() +redirectTo | 同 uni.reLaunch() +reLaunch | 同 uni.reLaunch() +switchTab | 同 uni.switchTab() + +### ListItem Events + +事件称名 |说明 |返回参数 +:-: |:-: |:-: +click |点击 uniListItem 触发事件,需开启点击反馈 |- +switchChange |点击切换 Switch 时触发,需显示 switch |e={value:checked} + + + +### ListItem Slots + +名称 | 说明 +:-: | :-: +header | 左/上内容插槽,可完全自定义默认显示 +body | 中间内容插槽,可完全自定义中间内容 +footer | 右/下内容插槽,可完全自定义右侧内容 + + +> **通过插槽扩展** +> 需要注意的是当使用插槽时,内置样式将会失效,只保留排版样式,此时的样式需要开发者自己实现 +> 如果 `uni-list-item` 组件内置属性样式无法满足需求,可以使用插槽来自定义uni-list-item里的内容。 +> uni-list-item提供了3个可扩展的插槽:`header`、`body`、`footer` +> - 当 `direction` 属性为 `row` 时表示水平排列,此时 `header` 表示列表的左边部分,`body` 表示列表的中间部分,`footer` 表示列表的右边部分 +> - 当 `direction` 属性为 `column` 时表示垂直排列,此时 `header` 表示列表的上边部分,`body` 表示列表的中间部分,`footer` 表示列表的下边部分 +> 开发者可以只用1个插槽,也可以3个一起使用。在插槽中可自主编写view标签,实现自己所需的效果。 + + +**示例** + +```html + + + + + + + + + 自定义插槽 + + + + +``` + + + + + +### ListItemChat Props + +属性名 |类型 |默认值 | 说明 +:-: |:-: |:-: | :-: +title |String |- | 标题 +note |String |- | 描述 +clickable |Boolean |false | 是否开启点击反馈 +badgeText |String |- | 数字角标内容,设置为 `dot` 将显示圆点 +badgePositon |String |right | 角标位置 +link |String |navigateTo | 是否展示右侧箭头并开启点击反馈,可选值见下表 +clickable |Boolean |false | 是否开启点击反馈 +to |String |- | 跳转页面地址,如填写此属性,click 会返回页面是否跳转成功 +time |String |- | 右侧时间显示 +avatarCircle |Boolean |false | 是否显示圆形头像 +avatar |String |- | 头像地址,avatarCircle 不填时生效 +avatarList |Array |- | 头像组,格式为 [{url:''}] + +#### Link Options + +属性名 | 说明 +:-: | :-: +navigateTo | 同 uni.navigateTo() +redirectTo | 同 uni.reLaunch() +reLaunch | 同 uni.reLaunch() +switchTab | 同 uni.switchTab() + +### ListItemChat Slots + +名称 | 说明 +:- | :- +default | 自定义列表右侧内容(包括时间和角标显示) + +### ListItemChat Events +事件称名 | 说明 | 返回参数 +:-: | :-: | :-: +@click | 点击 uniListChat 触发事件 | {data:{}} ,如有 to 属性,会返回页面跳转信息 + + + + + + +## 基于uni-list扩展的页面模板 + +通过扩展插槽,可实现多种常见样式的列表 + +**新闻列表类** + +1. 云端一体混合布局:[https://ext.dcloud.net.cn/plugin?id=2546](https://ext.dcloud.net.cn/plugin?id=2546) +2. 云端一体垂直布局,大图模式:[https://ext.dcloud.net.cn/plugin?id=2583](https://ext.dcloud.net.cn/plugin?id=2583) +3. 云端一体垂直布局,多行图文混排:[https://ext.dcloud.net.cn/plugin?id=2584](https://ext.dcloud.net.cn/plugin?id=2584) +4. 云端一体垂直布局,多图模式:[https://ext.dcloud.net.cn/plugin?id=2585](https://ext.dcloud.net.cn/plugin?id=2585) +5. 云端一体水平布局,左图右文:[https://ext.dcloud.net.cn/plugin?id=2586](https://ext.dcloud.net.cn/plugin?id=2586) +6. 云端一体水平布局,左文右图:[https://ext.dcloud.net.cn/plugin?id=2587](https://ext.dcloud.net.cn/plugin?id=2587) +7. 云端一体垂直布局,无图模式,主标题+副标题:[https://ext.dcloud.net.cn/plugin?id=2588](https://ext.dcloud.net.cn/plugin?id=2588) + +**商品列表类** + +1. 云端一体列表/宫格视图互切:[https://ext.dcloud.net.cn/plugin?id=2651](https://ext.dcloud.net.cn/plugin?id=2651) +2. 云端一体列表(宫格模式):[https://ext.dcloud.net.cn/plugin?id=2671](https://ext.dcloud.net.cn/plugin?id=2671) +3. 云端一体列表(列表模式):[https://ext.dcloud.net.cn/plugin?id=2672](https://ext.dcloud.net.cn/plugin?id=2672) + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/list/list](https://hellouniapp.dcloud.net.cn/pages/extUI/list/list) \ No newline at end of file diff --git a/uni_modules/uni-load-more/changelog.md b/uni_modules/uni-load-more/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..5b60375335474b0dbc300db3765c0fa90ea61d36 --- /dev/null +++ b/uni_modules/uni-load-more/changelog.md @@ -0,0 +1,10 @@ +## 1.2.1(2021-08-24) +- 新增 支持国际化 +## 1.2.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.8(2021-05-12) +- 新增 组件示例地址 +## 1.1.7(2021-03-30) +- 修复 uni-load-more 在首页使用时,h5 平台报 'uni is not defined' 的 bug +## 1.1.6(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-load-more/components/uni-load-more/i18n/en.json b/uni_modules/uni-load-more/components/uni-load-more/i18n/en.json new file mode 100644 index 0000000000000000000000000000000000000000..a4f14a5454b19c2e6245be1cfdcf6b2f48ac0a34 --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/i18n/en.json @@ -0,0 +1,5 @@ +{ + "uni-load-more.contentdown": "Pull up to show more", + "uni-load-more.contentrefresh": "loading...", + "uni-load-more.contentnomore": "No more data" +} diff --git a/uni_modules/uni-load-more/components/uni-load-more/i18n/index.js b/uni_modules/uni-load-more/components/uni-load-more/i18n/index.js new file mode 100644 index 0000000000000000000000000000000000000000..de7509c87ba5197b9f5d20ca94a0558c7a8e08a9 --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json b/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json new file mode 100644 index 0000000000000000000000000000000000000000..f15d5105033ed020b5286f3c6219bf9102134c72 --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json @@ -0,0 +1,5 @@ +{ + "uni-load-more.contentdown": "上拉显示更多", + "uni-load-more.contentrefresh": "正在加载...", + "uni-load-more.contentnomore": "没有更多数据了" +} diff --git a/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json b/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json new file mode 100644 index 0000000000000000000000000000000000000000..a255c6ded74a6a2422caab02e46a0b41d4a86353 --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json @@ -0,0 +1,5 @@ +{ + "uni-load-more.contentdown": "上拉顯示更多", + "uni-load-more.contentrefresh": "正在加載...", + "uni-load-more.contentnomore": "沒有更多數據了" +} diff --git a/uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue b/uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue new file mode 100644 index 0000000000000000000000000000000000000000..19fbc95f20da09b4b0aa8d51bcef0aebf7dd99f2 --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue @@ -0,0 +1,378 @@ + + + + + diff --git a/uni_modules/uni-load-more/package.json b/uni_modules/uni-load-more/package.json new file mode 100644 index 0000000000000000000000000000000000000000..062ee5deb222929b361aecc31e2dc866ad659e77 --- /dev/null +++ b/uni_modules/uni-load-more/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-load-more", + "displayName": "uni-load-more 加载更多", + "version": "1.2.1", + "description": "LoadMore 组件,常用在列表里面,做滚动加载使用。", + "keywords": [ + "uni-ui", + "uniui", + "加载更多", + "load-more" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-load-more/readme.md b/uni_modules/uni-load-more/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..b781fafd5accab29b9b7cbfe8dfec66618e3594a --- /dev/null +++ b/uni_modules/uni-load-more/readme.md @@ -0,0 +1,70 @@ + + +### LoadMore 加载更多 +> **组件名:uni-load-more** +> 代码块: `uLoadMore` + + +用于列表中,做滚动加载使用,展示 loading 的各种状态。 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 使用方式 + +在 ``template`` 中使用组件 + +```html + +``` + +## API + +### LoadMore Props + +|属性名 |类型 | 可选值 |默认值 |说明 | +|:-: |:-: |:-: |:-: |:-: | +|iconSize |Number |- |24 |指定图标大小 | +|status |String |more/loading/noMore |more |loading 的状态 | +|showIcon |Boolean|- |true |是否显示 loading 图标 | +|iconType |String |snow/circle/auto |auto |指定图标样式| +|color |String |- |#777777 |图标和文字颜色 | +|contentText|Object|- |{contentdown: "上拉显示更多",contentrefresh: "正在加载...",contentnomore: "没有更多数据了"}|各状态文字说明 | + + +#### Status Options +|参数名称 |说明 | +|:-: |:-: | +|more |loading前 | +|loading|loading前中 | +|more |没有更多数据 | + +#### IconType Options +|参数名称 |说明 | +|:-: |:-: | +|snow |ios雪花加载样式 | +|circle |安卓环形加载样式 | +|auto |根据平台自动选择加载样式 | + + + + +> **说明** +> `iconType`为`snow`时,在`APP-NVUE`平台不可设置大小,在非`APP-NVUE`平台不可设置颜色 + + + +### 事件说明 + +|事件名 |说明 |返回值 | +|:-: |:-: |:-: | +|clickLoadMore |点击加载更多时触发 |e.detail={status:'loading'}| + + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/load-more/load-more](https://hellouniapp.dcloud.net.cn/pages/extUI/load-more/load-more) \ No newline at end of file diff --git a/uni_modules/uni-nav-bar/changelog.md b/uni_modules/uni-nav-bar/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..9aaae1fc692dc13c10b0f50a6ed8358e2c91ec49 --- /dev/null +++ b/uni_modules/uni-nav-bar/changelog.md @@ -0,0 +1,19 @@ +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.11(2021-05-12) +- 新增 组件示例地址 +## 1.0.10(2021-04-30) +- 修复 在nvue下fixed为true,宽度不能撑满的Bug +## 1.0.9(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.8(2021-04-14) +- uni-ui 修复 uni-nav-bar 当 fixed 属性为 true 时铺不满屏幕的 bug + +## 1.0.7(2021-02-25) +- 修复 easycom 下,找不到 uni-status-bar 的bug + +## 1.0.6(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.5(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue b/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue new file mode 100644 index 0000000000000000000000000000000000000000..33b843092eb025328a85cc0c6a138bc57cd8fc90 --- /dev/null +++ b/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue @@ -0,0 +1,252 @@ + + + + + diff --git a/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue b/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue new file mode 100644 index 0000000000000000000000000000000000000000..976af6c49e3aa4eb30635f38f923e9722aad43b0 --- /dev/null +++ b/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/uni_modules/uni-nav-bar/package.json b/uni_modules/uni-nav-bar/package.json new file mode 100644 index 0000000000000000000000000000000000000000..3b0f20e78c98660fb4d3d461d1d786d31e631742 --- /dev/null +++ b/uni_modules/uni-nav-bar/package.json @@ -0,0 +1,84 @@ +{ + "id": "uni-nav-bar", + "displayName": "uni-nav-bar 自定义导航栏", + "version": "1.1.0", + "description": "自定义导航栏组件,主要用于头部导航。", + "keywords": [ + "uni-ui", + "导航", + "导航栏", + "自定义导航栏" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-nav-bar/readme.md b/uni_modules/uni-nav-bar/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..fa34c8e6357b1d4ef1e103e037c617a6cb895a8a --- /dev/null +++ b/uni_modules/uni-nav-bar/readme.md @@ -0,0 +1,71 @@ + + +### NavBar 导航栏 +*已经支持在nvue页面中使用* + +导航栏组件,主要用于头部导航,组件名:``uni-nav-bar``,代码块: uNavBar。 + +### 使用方式 + +在 ``script`` 中引用组件 + +```javascript +import uniNavBar from '@/components/uni-nav-bar/uni-nav-bar.vue' +export default { + components: {uniNavBar} +} +``` + +在 ``template`` 中使用组件 + +```html + +``` + +### 属性说明 + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|title |String |- |标题文字 | +|leftText |String |- |左侧按钮文本 | +|rightText |String |- |右侧按钮文本 | +|leftIcon |String |- |左侧按钮图标(图标类型参考 [Icon 图标](http://ext.dcloud.net.cn/plugin?id=28) type 属性) | +|rightIcon |String |- |右侧按钮图标(图标类型参考 [Icon 图标](http://ext.dcloud.net.cn/plugin?id=28) type 属性) | +|color |String |#000000|图标和文字颜色 | +|backgroundColor |String |#FFFFFF|导航栏背景颜色 | +|fixed |Boolean|false |是否固定顶部 | +|statusBar |Boolean|false |是否包含状态栏 | +|shadow |Boolean|false |导航栏下是否有阴影 | + +### 插槽说明 + +开发者使用 NavBar 时,支持向 NavBar 里插入不同内容,以达到自定义的目的。 + +|slot名 |说明 | +|:-: |:-: | +|left |向导航栏左侧插入 | +|right |向导航栏右侧插入 | +|default|向导航栏中间插入 | + +```html + + 标题栏 + left + right + +``` + +### 事件说明 + +|事件名 |说明 |返回值 | +|:-: |:-: |:-: | +|@clickLeft |左侧按钮点击时触发 |- | +|@clickRight |右侧按钮点击时触发 |- | + +### 插件预览地址 + +[https://uniapp.dcloud.io/h5/pages/extUI/nav-bar/nav-bar](https://uniapp.dcloud.io/h5/pages/extUI/nav-bar/nav-bar) + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/nav-bar/nav-bar](https://hellouniapp.dcloud.net.cn/pages/extUI/nav-bar/nav-bar) \ No newline at end of file diff --git a/uni_modules/uni-notice-bar/changelog.md b/uni_modules/uni-notice-bar/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..348245e02e004071dd75e427c7b460b0f13af592 --- /dev/null +++ b/uni_modules/uni-notice-bar/changelog.md @@ -0,0 +1,11 @@ +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.9(2021-05-12) +- 新增 组件示例地址 +## 1.0.8(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.7(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.6(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue b/uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue new file mode 100644 index 0000000000000000000000000000000000000000..878ba7e3806ddef1ad9673690b7fb30f432666ef --- /dev/null +++ b/uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue @@ -0,0 +1,398 @@ + + + + + diff --git a/uni_modules/uni-notice-bar/package.json b/uni_modules/uni-notice-bar/package.json new file mode 100644 index 0000000000000000000000000000000000000000..6b2e26e734c3da27eb7126b1f892d1cbb7f605f3 --- /dev/null +++ b/uni_modules/uni-notice-bar/package.json @@ -0,0 +1,85 @@ +{ + "id": "uni-notice-bar", + "displayName": "uni-notice-bar 通告栏", + "version": "1.1.0", + "description": "NoticeBar 通告栏组件,常用于展示公告信息,可设为滚动公告", + "keywords": [ + "uni-ui", + "uniui", + "通告栏", + "公告", + "跑马灯" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} diff --git a/uni_modules/uni-notice-bar/readme.md b/uni_modules/uni-notice-bar/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..f65c5788b4d88ac33a0b887ad26b8e7caaa2a754 --- /dev/null +++ b/uni_modules/uni-notice-bar/readme.md @@ -0,0 +1,71 @@ + + +## NoticeBar 通告栏 +> **组件名:uni-notice-bar** +> 代码块: `uNoticeBar` + + +通告栏组件 。 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +在 ``template`` 中使用组件 + +```html + + + + + + + + + + + + + + + +``` + +> **注意** +> 如果需要异步获取内容后展示需要使用`v-if`进行控制,`` + + +## NoticeBar API + +### NoticeBar Props + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|speed |Number |100 |文字滚动的速度,默认100px/秒 | +|text |String |- |显示文字 | +|background-color |String |#fffbe8|背景颜色 | +|color |String |#de8c17|文字颜色 | +|moreColor |String |#999999|查看更多文字的颜色 | +|moreText |String |- |设置“查看更多”的文本 | +|single |Boolean|false |是否单行 | +|scrollable |Boolean|false |是否滚动,为true时,NoticeBar为单行 | +|showIcon |Boolean|false |是否显示左侧喇叭图标 | +|showClose |Boolean|false |是否显示左侧关闭按钮 | +|showGetMore |Boolean|false |是否显示右侧查看更多图标,为true时,NoticeBar为单行| + +### NoticeBar Events + +|事件名称 |说明 |返回值 | +|:-: |:-: |:-: | +|@click |点击 NoticeBar 触发事件 |- | +|@close |关闭 NoticeBar 触发事件 |- | +|@getmore |点击”查看更多“时触发事件 |- | + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/notice-bar/notice-bar](https://hellouniapp.dcloud.net.cn/pages/extUI/notice-bar/notice-bar) \ No newline at end of file diff --git a/uni_modules/uni-number-box/changelog.md b/uni_modules/uni-number-box/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..ed3890d1e62dab2c5a37f2631730c10a1d532047 --- /dev/null +++ b/uni_modules/uni-number-box/changelog.md @@ -0,0 +1,18 @@ +## 1.1.1(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-05-12) +- 新增 组件示例地址 +## 1.0.6(2021-04-20) +- 修复 uni-number-box 浮点数运算不精确的 bug +- 修复 uni-number-box change 事件触发不正确的 bug +- 新增 uni-number-box v-model 双向绑定 +## 1.0.5(2021-02-05) +- 调整为uni_modules目录规范 + +## 1.0.7(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 支持 v-model +- 新增 支持 focus、blur 事件 +- 新增 支持 PC 端 diff --git a/uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue b/uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue new file mode 100644 index 0000000000000000000000000000000000000000..e59fa40cda02966a4caecacb8c4b4f0a7a18680a --- /dev/null +++ b/uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue @@ -0,0 +1,231 @@ + + + diff --git a/uni_modules/uni-number-box/package.json b/uni_modules/uni-number-box/package.json new file mode 100644 index 0000000000000000000000000000000000000000..1a0a8599074d464787509a31b17dd58a121f0574 --- /dev/null +++ b/uni_modules/uni-number-box/package.json @@ -0,0 +1,81 @@ +{ + "id": "uni-number-box", + "displayName": "uni-number-box 数字输入框", + "version": "1.1.1", + "description": "NumberBox 带加减按钮的数字输入框组件,用户可以控制每次点击增加的数值,支持小数。", + "keywords": [ + "uni-ui", + "uniui", + "数字输入框" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-number-box/readme.md b/uni_modules/uni-number-box/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..9c951e4840d8caa16fde972bdf4fd61151672fa3 --- /dev/null +++ b/uni_modules/uni-number-box/readme.md @@ -0,0 +1,50 @@ + + +## NumberBox 数字输入框 +> **组件名:uni-number-box** +> 代码块: `uNumberBox` + + +带加减按钮的数字输入框。 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +在 ``template`` 中使用组件 + +```html + + + + +``` + +## API + +### NumberBox Props + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|value/v-model|Number |0 |输入框当前值 | +|min |Number |0 |最小值 | +|max |Number |100 |最大值 | +|step |Number |1 |每次点击改变的间隔大小 | +|disabled |Boolean|false |是否为禁用状态 | + +### NumberBox Events + +|事件名称 |说明 |返回值 | +|:-: |:-: |:-: | +|change |输入框值改变时触发的事件,参数为输入框当前的 value |- | +|focus |输入框聚焦时触发的事件,参数为 event 对象 |- | +|blur |输入框失焦时触发的事件,参数为 event 对象 |- | + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/number-box/number-box](https://hellouniapp.dcloud.net.cn/pages/extUI/number-box/number-box) \ No newline at end of file diff --git a/uni_modules/uni-pagination/changelog.md b/uni_modules/uni-pagination/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..05387e0f4fea3bd6ae1b4ab19c0e024a170935f4 --- /dev/null +++ b/uni_modules/uni-pagination/changelog.md @@ -0,0 +1,11 @@ +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-05-12) +- 新增 组件示例地址 +## 1.0.6(2021-04-12) +- 新增 PC 和 移动端适配不同的 ui +## 1.0.5(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.4(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue b/uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue new file mode 100644 index 0000000000000000000000000000000000000000..a883602f20c57d3c83fc782c61cf44891c93be11 --- /dev/null +++ b/uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue @@ -0,0 +1,404 @@ + + + + + diff --git a/uni_modules/uni-pagination/package.json b/uni_modules/uni-pagination/package.json new file mode 100644 index 0000000000000000000000000000000000000000..1b066ce06900afca2e65b0805caa5b45556140aa --- /dev/null +++ b/uni_modules/uni-pagination/package.json @@ -0,0 +1,82 @@ +{ + "id": "uni-pagination", + "displayName": "uni-pagination 分页器", + "version": "1.1.0", + "description": "Pagination 分页器组件,用于展示页码、请求数据等。", + "keywords": [ + "uni-ui", + "uniui", + "分页器", + "页码" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-icons"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-pagination/readme.md b/uni_modules/uni-pagination/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..c8e47e3ebd4841a2fea259839349c647536c630c --- /dev/null +++ b/uni_modules/uni-pagination/readme.md @@ -0,0 +1,48 @@ + + +## Pagination 分页器 +> **组件名:uni-pagination** +> 代码块: `uPagination` + + +分页器组件,用于展示页码、请求数据等。 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +在 ``template`` 中使用组件 + +```html + + +``` + +## API + +### Pagination Props + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|prevText |String |上一页 |左侧按钮文字 | +|nextText |String |下一页 |右侧按钮文字 | +|current |Number |1 |当前页 | +|total |Number |0 |数据总量 | +|pageSize |Number |10 |每页数据量 | +|showIcon |Boolean|false |是否以 icon 形式展示按钮 | + + +### Pagination Events + +|事件称名 |说明 |返回值 | +|:-: |:-: |:-: | +|@change|点击页码按钮时触发 |e={type,current} current为当前页,type值为:next/prev,表示点击的是上一页还是下一个 | + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/pagination/pagination](https://hellouniapp.dcloud.net.cn/pages/extUI/pagination/pagination) \ No newline at end of file diff --git a/uni_modules/uni-popup/changelog.md b/uni_modules/uni-popup/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..99454baa0b20886272b2bd4010481e5d35f12820 --- /dev/null +++ b/uni_modules/uni-popup/changelog.md @@ -0,0 +1,37 @@ +## 1.6.2(2021-08-24) +- 新增 支持国际化 +## 1.6.1(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.6.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.5.0(2021-06-23) +- 新增 mask-click 遮罩层点击事件 +## 1.4.5(2021-06-22) +- 修复 nvue 平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug +## 1.4.4(2021-06-18) +- 修复 H5平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug +## 1.4.3(2021-06-08) +- 修复 错误的 watch 字段 +- 修复 safeArea 属性不生效的问题 +- 修复 点击内容,再点击遮罩无法关闭的Bug +## 1.4.2(2021-05-12) +- 新增 组件示例地址 +## 1.4.1(2021-04-29) +- 修复 组件内放置 input 、textarea 组件,无法聚焦的问题 +## 1.4.0 (2021-04-29) +- 新增 type 属性的 left\right 值,支持左右弹出 +- 新增 open(String:type) 方法参数 ,可以省略 type 属性 ,直接传入类型打开指定弹窗 +- 新增 backgroundColor 属性,可定义主窗口背景色,默认不显示背景色 +- 新增 safeArea 属性,是否适配底部安全区 +- 修复 App\h5\微信小程序底部安全区占位不对的Bug +- 修复 App 端弹出等待的Bug +- 优化 提升低配设备性能,优化动画卡顿问题 +- 优化 更简单的组件自定义方式 +## 1.2.9(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 1.2.8(2021-02-05) +- 调整为uni_modules目录规范 +## 1.2.7(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 支持 PC 端 +- 新增 uni-popup-message 、uni-popup-dialog扩展组件支持 PC 端 diff --git a/uni_modules/uni-popup/components/uni-popup-dialog/keypress.js b/uni_modules/uni-popup/components/uni-popup-dialog/keypress.js new file mode 100644 index 0000000000000000000000000000000000000000..6ef26a26211ff28976e6fc835fbef5d2ea5b5b1e --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup-dialog/keypress.js @@ -0,0 +1,45 @@ +// #ifdef H5 +export default { + name: 'Keypress', + props: { + disable: { + type: Boolean, + default: false + } + }, + mounted () { + const keyNames = { + esc: ['Esc', 'Escape'], + tab: 'Tab', + enter: 'Enter', + space: [' ', 'Spacebar'], + up: ['Up', 'ArrowUp'], + left: ['Left', 'ArrowLeft'], + right: ['Right', 'ArrowRight'], + down: ['Down', 'ArrowDown'], + delete: ['Backspace', 'Delete', 'Del'] + } + const listener = ($event) => { + if (this.disable) { + return + } + const keyName = Object.keys(keyNames).find(key => { + const keyName = $event.key + const value = keyNames[key] + return value === keyName || (Array.isArray(value) && value.includes(keyName)) + }) + if (keyName) { + // 避免和其他按键事件冲突 + setTimeout(() => { + this.$emit(keyName, {}) + }, 0) + } + } + document.addEventListener('keyup', listener) + this.$once('hook:beforeDestroy', () => { + document.removeEventListener('keyup', listener) + }) + }, + render: () => {} +} +// #endif diff --git a/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue b/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..291634c6ac62c0ea8b1d47c8d690d8f74448505c --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue @@ -0,0 +1,263 @@ + + + + + diff --git a/uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue b/uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue new file mode 100644 index 0000000000000000000000000000000000000000..f4c85e259e5310a01a1b4f6566aacc19b14270b9 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue @@ -0,0 +1,143 @@ + + + + diff --git a/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue b/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue new file mode 100644 index 0000000000000000000000000000000000000000..705902db12585539592262c45239c1b524bc9ac2 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue @@ -0,0 +1,185 @@ + + + + diff --git a/uni_modules/uni-popup/components/uni-popup/i18n/en.json b/uni_modules/uni-popup/components/uni-popup/i18n/en.json new file mode 100644 index 0000000000000000000000000000000000000000..7f1bd06a0380527163901fc369ed810b8e25fc46 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/i18n/en.json @@ -0,0 +1,7 @@ +{ + "uni-popup.cancel": "cancel", + "uni-popup.ok": "ok", + "uni-popup.placeholder": "pleace enter", + "uni-popup.title": "Hint", + "uni-popup.shareTitle": "Share to" +} diff --git a/uni_modules/uni-popup/components/uni-popup/i18n/index.js b/uni_modules/uni-popup/components/uni-popup/i18n/index.js new file mode 100644 index 0000000000000000000000000000000000000000..de7509c87ba5197b9f5d20ca94a0558c7a8e08a9 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json b/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json new file mode 100644 index 0000000000000000000000000000000000000000..5e3003cabc8f40b133736719aaaf8b3f1a226fb1 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json @@ -0,0 +1,7 @@ +{ + "uni-popup.cancel": "取消", + "uni-popup.ok": "确定", + "uni-popup.placeholder": "请输入", + "uni-popup.title": "提示", + "uni-popup.shareTitle": "分享到" +} diff --git a/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json b/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json new file mode 100644 index 0000000000000000000000000000000000000000..13e39eba1045a5cb942bbe1e27ed4162848ecf4f --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json @@ -0,0 +1,7 @@ +{ + "uni-popup.cancel": "取消", + "uni-popup.ok": "確定", + "uni-popup.placeholder": "請輸入", + "uni-popup.title": "提示", + "uni-popup.shareTitle": "分享到" +} diff --git a/uni_modules/uni-popup/components/uni-popup/keypress.js b/uni_modules/uni-popup/components/uni-popup/keypress.js new file mode 100644 index 0000000000000000000000000000000000000000..62dda461bd5d5923a173e541f2cf049d816d612b --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/keypress.js @@ -0,0 +1,45 @@ +// #ifdef H5 +export default { + name: 'Keypress', + props: { + disable: { + type: Boolean, + default: false + } + }, + mounted () { + const keyNames = { + esc: ['Esc', 'Escape'], + tab: 'Tab', + enter: 'Enter', + space: [' ', 'Spacebar'], + up: ['Up', 'ArrowUp'], + left: ['Left', 'ArrowLeft'], + right: ['Right', 'ArrowRight'], + down: ['Down', 'ArrowDown'], + delete: ['Backspace', 'Delete', 'Del'] + } + const listener = ($event) => { + if (this.disable) { + return + } + const keyName = Object.keys(keyNames).find(key => { + const keyName = $event.key + const value = keyNames[key] + return value === keyName || (Array.isArray(value) && value.includes(keyName)) + }) + if (keyName) { + // 避免和其他按键事件冲突 + setTimeout(() => { + this.$emit(keyName, {}) + }, 0) + } + } + document.addEventListener('keyup', listener) + // this.$once('hook:beforeDestroy', () => { + // document.removeEventListener('keyup', listener) + // }) + }, + render: () => {} +} +// #endif diff --git a/uni_modules/uni-popup/components/uni-popup/message.js b/uni_modules/uni-popup/components/uni-popup/message.js new file mode 100644 index 0000000000000000000000000000000000000000..0ff9a0242c1a2ed1899a36629cf6067f13ed29df --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/message.js @@ -0,0 +1,22 @@ +export default { + created() { + if (this.type === 'message') { + // 不显示遮罩 + this.maskShow = false + // 获取子组件对象 + this.childrenMsg = null + } + }, + methods: { + customOpen() { + if (this.childrenMsg) { + this.childrenMsg.open() + } + }, + customClose() { + if (this.childrenMsg) { + this.childrenMsg.close() + } + } + } +} diff --git a/uni_modules/uni-popup/components/uni-popup/popup.js b/uni_modules/uni-popup/components/uni-popup/popup.js new file mode 100644 index 0000000000000000000000000000000000000000..c4e5781dd6834247c297788d5bc4ef1339584143 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/popup.js @@ -0,0 +1,26 @@ + +export default { + data() { + return { + + } + }, + created(){ + this.popup = this.getParent() + }, + methods:{ + /** + * 获取父元素实例 + */ + getParent(name = 'uniPopup') { + let parent = this.$parent; + let parentName = parent.$options.name; + while (parentName !== name) { + parent = parent.$parent; + if (!parent) return false + parentName = parent.$options.name; + } + return parent; + }, + } +} diff --git a/uni_modules/uni-popup/components/uni-popup/share.js b/uni_modules/uni-popup/components/uni-popup/share.js new file mode 100644 index 0000000000000000000000000000000000000000..462bb83ab3cbf468e41f4faa3463e1a68d3d6941 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/share.js @@ -0,0 +1,16 @@ +export default { + created() { + if (this.type === 'share') { + // 关闭点击 + this.mkclick = false + } + }, + methods: { + customOpen() { + console.log('share 打开了'); + }, + customClose() { + console.log('share 关闭了'); + } + } +} diff --git a/uni_modules/uni-popup/components/uni-popup/uni-popup.vue b/uni_modules/uni-popup/components/uni-popup/uni-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..59698b726046f5146a032af1ff95de6a91c19d0e --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/uni-popup.vue @@ -0,0 +1,403 @@ + + + + diff --git a/uni_modules/uni-popup/package.json b/uni_modules/uni-popup/package.json new file mode 100644 index 0000000000000000000000000000000000000000..1385af7e6663843c10894752f92cc4cc6453d83e --- /dev/null +++ b/uni_modules/uni-popup/package.json @@ -0,0 +1,89 @@ +{ + "id": "uni-popup", + "displayName": "uni-popup 弹出层", + "version": "1.6.2", + "description": " Popup 组件,提供常用的弹层", + "keywords": [ + "uni-ui", + "弹出层", + "弹窗", + "popup", + "弹框" + ], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-transition" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} diff --git a/uni_modules/uni-popup/readme.md b/uni_modules/uni-popup/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..d8a089946e61bbda98dbd18e87a8850c458032f8 --- /dev/null +++ b/uni_modules/uni-popup/readme.md @@ -0,0 +1,296 @@ + + +## Popup 弹出层 +> **组件名:uni-popup** +> 代码块: `uPopup` +> 关联组件:`uni-popup-dialog`,`uni-popup-message`,`uni-popup-share`,`uni-transition` + + +弹出层组件,在应用中弹出一个消息提示窗口、提示框等 + + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - 组件需要依赖 `sass` 插件 ,请自行手动安装 +> - `uni-popup-message` 、 `uni-popup-dialog` 等扩展ui组件,需要和 `uni-popup` 配套使用,暂不支持单独使用 +> - `nvue` 中使用 `uni-popup` 时,尽量将组件置于其他元素后面,避免出现层级问题 +> - `uni-popup` 并不能完全阻止页面滚动,可在打开 `uni-popup` 的时候手动去做一些处理,禁止页面滚动 +> - 如果想在页面渲染完毕后就打开 `uni-popup` ,请在 `onReady` 或 `mounted` 生命周期内调用,确保组件渲染完毕 +> - 在微信小程序开发者工具中,启用真机调试,popup 会延时出现,是因为 setTimeout 在真机调试中的延时问题导致的,预览和发布小程序不会出现此问题 +> - 使用 `npm` 方式引入组件,如果确认引用正确,但是提示未注册组件或显示不正常,请尝试重新编译项目 +> - `uni-popup` 中尽量不要使用 `scroll-view` 嵌套过多的内容,可能会影响组件的性能,导致组件无法打开或者打开卡顿 +> - `uni-popup` 不会覆盖原生 tabbar 和原生导航栏 +> - 组件支持 nvue ,需要在 `manifest.json > app-plus` 节点下配置 `"nvueStyleCompiler" : "uni-app"` +> - 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + + +### 基本用法 + +**示例** + +```html + +底部弹出 Popup +``` + +```javascript +export default { + methods:{ + open(){ + // 通过组件定义的ref调用uni-popup方法 ,如果传入参数 ,type 属性将失效 ,仅支持 ['top','left','bottom','right','center'] + this.$refs.popup.open('top') + } + } +} + +``` + +### 设置主窗口背景色 + +在大多数场景下,并不需要设置 `background-color` 属性,因为`uni-popup`的主窗口默认是透明的,在向里面插入内容的时候 ,样式完全交由用户定制,如果设置了背景色 ,例如 `uni-popup-dialog` 中的圆角就很难去实现,不设置背景色,更适合用户去自由发挥。 + +而也有特例,需要我们主动去设置背景色,例如 `type = 'bottom'` 的时候 ,在异型屏中遇到了底部安全区问题(如 iphone 11),因为 `uni-popup`的主要内容避开了安全区(设置`safe-area:true`),导致底部的颜色我们无法自定义,这时候使用 `background-color` 就可以解决这个问题。 + +**示例** + +```html + +底部弹出 Popup +``` + +### 禁用打开动画 +在某些场景 ,可能不希望弹层有动画效果 ,只需要将 `animation` 属性设置为 `false` 即可以关闭动画。 + +**示例** + +```html + +中间弹出 Popup +``` + +### 禁用点击遮罩关闭 +默认情况下,点击遮罩会自动关闭`uni-popup`,如不想点击关闭,只需将`mask-click`设置为`false`,这时候要关闭`uni-popup`,只能手动调用 `close` 方法。 + +**示例** + +```html + + + Popup + + +``` + +```javascript +export default { + data() { + return {} + }, + onReady() {}, + methods: { + open() { + this.$refs.popup.open('top') + }, + close() { + this.$refs.popup.close() + } + } +} + +``` + +## API + +### Popup Props + +|属性名|类型|默认值|说明| +|:-:|:-:|:-:|:-:| +|animation|Boolean|true|是否开启动画| +|type|String|'center'|弹出方式| +|mask-click|Boolean|true|蒙版点击是否关闭弹窗| +|background-color|String|'none'|主窗口背景色| +|safe-area|Boolean|true|是否适配底部安全区| + +#### Type Options + +|属性名|说明| +|:-:| :-:| +|top|顶部弹出 | +|center|居中弹出| +|bottom|底部弹出| +|left|左侧弹出| +|right|右侧弹出| +|message|预置样式 :消息提示| +|dialog|预置样式 :对话框| +|share|预置样式 :底部弹出分享示例 | + + +### Popup Methods + +|方法称名 |说明|参数| +|:-:|:-:|:-:| +|open|打开弹出层|open(String:type) ,如果参数可代替 type 属性| +|close|关闭弹出层 |-| + + +### Popup Events + +|事件称名|说明|返回值| +|:-:|:-:|:-:| +|change|组件状态发生变化触发|e={show: true|false,type:当前模式}| +|maskClick|点击遮罩层触发|-| + + +## 扩展组件说明 +`uni-popup` 其实并没有任何样式,只提供基础的动画效果,给用户一个弹出层解决方案,仅仅是这样并不能满足开发需求,所以我们提供了三种基础扩展样式 + +### uni-popup-message 提示信息 + +将 `uni-popup` 的`type`属性改为 `message`,并引入对应组件即可使用消息提示 ,*该组件不支持单独使用* + +**示例** + +```html + + + +``` + +### PopupMessage Props + +|属性名|类型|默认值|说明| +|:-:|:-:|:-:|:-:| +|type|String|success|消息提示主题| +|message|String|-|消息提示文字| +|duration|Number|3000|消息显示时间,超过显示时间组件自动关闭,设置为0 将不会关闭,需手动调用 close 方法关闭| + +#### Type Options + +|属性名|说明| +|:-:| :-:| +|success|成功 | +|warn|警告| +|error|失败| +|info|消息| + +### PopupMessage Slots + +|名称|说明| +|:-:|:-:| +|default|消息内容,会覆盖 message 属性| + +### uni-popup-dialog 对话框 + +将 `uni-popup` 的`type`属性改为 `dialog`,并引入对应组件即可使用对话框 ,*该组件不支持单独使用* + +**示例** + +```html + + + + +``` + +```javascript +export default { + methods: { + open() { + this.$refs.popup.open() + }, + /** + * 点击取消按钮触发 + * @param {Object} done + */ + close() { + // TODO 做一些其他的事情,before-close 为true的情况下,手动执行 close 才会关闭对话框 + // ... + this.$refs.popup.close() + }, + /** + * 点击确认按钮触发 + * @param {Object} done + * @param {Object} value + */ + confirm(value) { + // 输入框的值 + console.log(value) + // TODO 做一些其他的事情,手动执行 close 才会关闭对话框 + // ... + this.$refs.popup.close() + } + } +} +``` + +### PopupDialog Props + +|属性名|类型|默认值|说明| +|:-:|:-:|:-:|:-:| +|type|String|success|对话框标题主题,可选值: success/warn/info/error| +|mode|String|base| 对话框模式,可选值:base(提示对话框)/input(可输入对话框)| +|title|String|-|对话框标题| +|content|String|-|对话框内容,base模式下生效| +|value| String\Number|-|输入框默认值,input模式下生效| +|placeholder|String|-|输入框提示文字,input模式下生效| +|before-close|Boolean|false | 是否拦截按钮事件,如为true,则不会关闭对话框,关闭需要手动执行 uni-popup 的 close 方法| + +#### PopupDialog Events + +|事件称名 |说明|返回值| +|:-:|:-:|:-:| +|close|点击dialog取消按钮触发|-| +|confirm|点击dialog确定按钮触发|e={value:input模式下输入框的值}| + +### PopupDialog Slots + +|名称|说明| +|:-:|:-:| +|default|自定义内容,回覆盖原有的内容显示| + +### uni-popup-share 分享示例 + +分享示例,不作为最终可使用的组件,只做为样式组件,供用户自行修改,`后续的开发计划是实现实际的分享逻辑,参数可配置`。 + +将 `uni-popup` 的 `type` 属性改为 `share`,并引入对应组件即可使用 ,*该组件不支持单独使用* + +**示例** + +```html + + + +``` + +### PopupShare Props + +|属性名|类型|默认值|说明| +|:-:|:-:|:-:| :-: | +|title|String|-|分享弹窗标题| +|before-close|Boolean|false | 是否拦截按钮事件,如为true,则不会关闭对话框,关闭需要手动执行 uni-popup 的 close 方法| + +### PopupShare Events + +|事件称名|说明|返回值| +|:-:|:-:|:-:| +|select|选择触发|e = {item,index}:所选参数| + +**Tips** +- share 分享组件,只是作为一个扩展示例,如果需要修改数据源,请到组件内修改 + +## 帮助 +在使用中如遇到无法解决的问题,请提 [Issues](https://github.com/dcloudio/uni-ui/issues) 给我们。 + + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/popup/popup](https://hellouniapp.dcloud.net.cn/pages/extUI/popup/popup) \ No newline at end of file diff --git a/uni_modules/uni-rate/changelog.md b/uni_modules/uni-rate/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..efb03b7968d1b3786068534c849f285a74e51728 --- /dev/null +++ b/uni_modules/uni-rate/changelog.md @@ -0,0 +1,18 @@ +## 1.2.1(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.2.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.2(2021-05-12) +- 新增 组件示例地址 +## 1.1.1(2021-04-21) +- 修复 布局变化后 uni-rate 星星计算不准确的 bug +- 优化 添加依赖 uni-icons, 导入 uni-rate 自动下载依赖 +## 1.1.0(2021-04-16) +- 修复 uni-rate 属性 margin 值为 string 组件失效的 bug + +## 1.0.9(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.8(2021-02-05) +- 调整为uni_modules目录规范 +- 支持 pc 端 diff --git a/uni_modules/uni-rate/components/uni-rate/uni-rate.vue b/uni_modules/uni-rate/components/uni-rate/uni-rate.vue new file mode 100644 index 0000000000000000000000000000000000000000..58b513233f5b1f731177ac33aa3336b852e97d68 --- /dev/null +++ b/uni_modules/uni-rate/components/uni-rate/uni-rate.vue @@ -0,0 +1,393 @@ + + + + + diff --git a/uni_modules/uni-rate/package.json b/uni_modules/uni-rate/package.json new file mode 100644 index 0000000000000000000000000000000000000000..c98df3dfc77771aa4f744c3f0de791a4e017ecf8 --- /dev/null +++ b/uni_modules/uni-rate/package.json @@ -0,0 +1,83 @@ +{ + "id": "uni-rate", + "displayName": "uni-rate 评分", + "version": "1.2.1", + "description": "Rate 评分组件,可自定义评分星星图标的大小、间隔、评分数。", + "keywords": [ + "uni-ui", + "uniui", + "评分" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} diff --git a/uni_modules/uni-rate/readme.md b/uni_modules/uni-rate/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..bb088001d559eb61651f9764e1b197af32aa5767 --- /dev/null +++ b/uni_modules/uni-rate/readme.md @@ -0,0 +1,107 @@ + + +## Rate 评分 +> **组件名:uni-rate** +> 代码块: `uRate` +> 关联组件:`uni-icons` + + +评分组件,多用于购买商品后,对商品进行评价等场景 + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的使用说明,可以帮你避免一些错误。 +> - 暂时不支持零星选择 +> - 当前版本暂不支持修改图标,后续版本会继续优化 +> - 绑定值推荐使用 `v-model` 的方式 +> - 如需设置一个星星表示多分,如:显示5个星星,最高分10分。这种情况请在 change 事件监听中自行处理,获取到的值乘以你的基数就可以,默认组件是一星一分 + + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + + +## 基本用法 + +```html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +```javascript + +export default { + components: {}, + data() { + return { + value: 2 + } + }, + methods: { + onChange(e) { + console.log('rate发生改变:' + JSON.stringify(e)) + } + } +} + +``` + +## API +### Rate Props + +属性名 | 类型 | 默认值 | 说明 +:-: | :-: | :-: | :-: +value/v-model | Number | 1 | 当前评分 +color | String | #ececec | 未选中状态的星星颜色 +activeColor | String | #ffca3e | 选中状态的星星颜色 +disabledColor | String | #c0c0c0 | 禁用状态的星星颜色 +size | Number | 24 | 星星的大小 +max | Number | 5 | 最大评分评分数量,目前一分一颗星 +margin | Number | 0 | 星星的间距,单位 px +isFill | Boolean | true | 星星的类型,是否为实心类型 +disabled | Boolean | false | 是否为禁用状态 (之前版本为已读状态,现更正为禁用状态) +readonly | Boolean | false | 是否为只读状态 +allowHalf | Boolean | false | 是否展示半星 +touchable | Boolean | true | 是否支持滑动手势 + +### Rate Events + +事件称名 | 说明 | 返回参数 +:-: | :-: | :-: +@change | 改变 value 的值返回 | e = { value:number } + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/rate/rate](https://hellouniapp.dcloud.net.cn/pages/extUI/rate/rate) \ No newline at end of file diff --git a/uni_modules/uni-row/changelog.md b/uni_modules/uni-row/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..8a269ffb01d44512d9ff25fd2b3c3a07e1efe89d --- /dev/null +++ b/uni_modules/uni-row/changelog.md @@ -0,0 +1,7 @@ +## 0.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 0.0.4(2021-05-12) +- 新增 组件示例地址 +## 0.0.3(2021-02-05) +- 调整为uni_modules目录规范 +- 新增uni-row组件 diff --git a/uni_modules/uni-row/components/uni-col/uni-col.vue b/uni_modules/uni-row/components/uni-col/uni-col.vue new file mode 100644 index 0000000000000000000000000000000000000000..84e2deb4cff6badb4221eaadf23d482aca47ea8d --- /dev/null +++ b/uni_modules/uni-row/components/uni-col/uni-col.vue @@ -0,0 +1,317 @@ + + + + + diff --git a/uni_modules/uni-row/components/uni-row/uni-row.vue b/uni_modules/uni-row/components/uni-row/uni-row.vue new file mode 100644 index 0000000000000000000000000000000000000000..f8e854239a53bc1bd917cde58aa6954a5908a686 --- /dev/null +++ b/uni_modules/uni-row/components/uni-row/uni-row.vue @@ -0,0 +1,190 @@ + + + + + diff --git a/uni_modules/uni-row/package.json b/uni_modules/uni-row/package.json new file mode 100644 index 0000000000000000000000000000000000000000..0251ea7491e54b61880c03a6c706a2c62754baf0 --- /dev/null +++ b/uni_modules/uni-row/package.json @@ -0,0 +1,83 @@ +{ + "id": "uni-row", + "displayName": "uni-row 布局-行", + "version": "0.1.0", + "description": "流式栅格系统,随着屏幕或视口分为 24 份,可以迅速简便地创建布局。", + "keywords": [ + "uni-ui", + "uniui", + "栅格", + "布局", + "layout" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-row/readme.md b/uni_modules/uni-row/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..8013a9e47210e2166e2438e6ffd919df45443646 --- /dev/null +++ b/uni_modules/uni-row/readme.md @@ -0,0 +1,183 @@ +## Layout 布局 + +> **组件名 uni-row、uni-col** +> 代码块: `uRow`、`uCol` + + +流式栅格系统,随着屏幕或视口分为 24 份,可以迅速简便地创建布局。 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - 组件需要依赖 `sass` 插件 ,请自行手动安装 +> - 如使用过程中有任何问题,或者您对 uni-ui 有一些好的建议,欢迎加入 uni-ui 交流群:871950839 +> + + +### 基本用法 + +###### 使用单一分栏创建基础的栅格布局 + +```html + + + + + + + + + + + + + + +``` + +### 分栏偏移 + +###### 支持偏移指定的栏数 + +```html + + + + + + + + + + + + + + + + + + + + +``` + +### 响应式布局 + +###### 共五个响应尺寸:xs、sm、md、lg 和 xl + +```html + + + + + + + + + + + + + + +``` + +###### 使用到的 CSS + +```css +.demo-uni-row { + margin-bottom: 10px; + /* QQ、字节小程序文档写有 :host,但实测不生效 */ + /* 百度小程序没有 :host,需要设置block */ + /* #ifdef MP-TOUTIAO || MP-QQ || MP-BAIDU */ + display: block; + /* #endif */ +} + +/* 支付宝小程序没有 demo-uni-row 层级 */ +/* 微信小程序使用了虚拟化节点,没有 demo-uni-row 层级 */ +/* #ifdef MP-ALIPAY || MP-WEIXIN */ +/deep/ .uni-row { + margin-bottom: 10px; +} +/* #endif */ + +.demo-uni-col { + height: 36px; + border-radius: 4px; +} + +.dark_deep { + background-color: #99a9bf; +} + +.dark { + background-color: #d3dce6; +} + +.light { + background-color: #e5e9f2; +} +``` + +### 平台差异说明 + +### `uni-row` +| 属性名 | App(nvue) | App(vue) | H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 字节跳动小程序 | QQ 小程序 | +| :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | +| gutter | - | √ | √ | √ | √ | √ | √ | √ | + +### `uni-col` + +| 属性名 | App(nvue) | App(vue) | H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 字节跳动小程序 | QQ 小程序 | +| :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | +| span | √ | √ | √ | √ | √ | √ | √ | √ | +| offset | √ | √ | √ | √ | √ | √ | √ | √ | +| push | √ | √ | √ | √ | √ | √ | √ | √ | +| pull | √ | √ | √ | √ | √ | √ | √ | √ | +| xs | - | √ | √ | √ | √ | √ | √ | √ | +| sm | - | √ | √ | √ | √ | √ | √ | √ | +| md | - | √ | √ | √ | √ | √ | √ | √ | +| lg | - | √ | √ | √ | √ | √ | √ | √ | +| xl | - | √ | √ | √ | √ | √ | √ | √ | + +## API + +### Row Props + +`其他平台` + +| 属性名 | 类型 | 可选值 | 默认值 | 必填 | 说明 | +| :-: | :-: | :-: | :-: | :-: | :-: | +| gutter | Number | - | 0 | 否 | 栅格间隔 | + +`nvue平台` + +| 属性名 | 类型 | 可选值 | 默认值 | 必填 | 说明 | +| :-: | :-: | :-: | :-: | :-: | :-: | +| width | Number/String | - | `750rpx` | 否 | nvue 中无百分比 width,使用 span 等属性时,需配置此项`rpx值`。此项不会影响其他平台展示效果 | + +### Col Props + +| 属性名 | 类型 | 可选值 | 默认值 | 必填 | 说明 | +| :-: | :-: | :-: | :-: | :-: | :-: | +| span | Number | - | 24 | 否 | 栅格占据的列数 | +| offset | Number | - | - | 否 | 栅格左侧间隔格数 | +| push | Number | - | - | 否 | 栅格向右偏移格数 | +| pull | Number | - | - | 否 | 栅格向左偏移格数 | +| xs | Number/object | - | - | 否 | 屏幕宽度`<768px`时,要显示的栅格规则,如:`:xs="8"`或`:xs="{span: 8, pull: 4}"` | +| sm | Number/object | - | - | 否 | 屏幕宽度`≥768px`时,要显示的栅格规则 | +| md | Number/object | - | - | 否 | 屏幕宽度`≥992px`时,要显示的栅格规则 | +| lg | Number/object | - | - | 否 | 屏幕宽度`≥1200px`时,要显示的栅格规则 | +| xl | Number/object | - | - | 否 | 屏幕宽度`≥1920px`时,要显示的栅格规则 | + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/row/row](https://hellouniapp.dcloud.net.cn/pages/extUI/row/row) \ No newline at end of file diff --git a/uni_modules/uni-search-bar/changelog.md b/uni_modules/uni-search-bar/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..b71a10d02a28ac130fe6a24f12f41adc35c22310 --- /dev/null +++ b/uni_modules/uni-search-bar/changelog.md @@ -0,0 +1,22 @@ +## 1.1.1(2021-08-24) +- 新增 支持国际化 +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.9(2021-05-12) +- 新增 项目示例地址 +## 1.0.8(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.7(2021-04-15) +- uni-ui 新增 uni-search-bar 的 focus 事件 + +## 1.0.6(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.5(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 支持双向绑定 +- 更改 input 事件的返回值,e={value:Number} --> e=value +- 新增 支持图标插槽 +- 新增 支持 clear、blur 事件 +- 新增 支持 focus 属性 +- 去掉组件背景色 diff --git a/uni_modules/uni-search-bar/components/uni-search-bar/i18n/en.json b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/en.json new file mode 100644 index 0000000000000000000000000000000000000000..dd083a5357aa7576eedcf55fe1b6d0caedace0cd --- /dev/null +++ b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/en.json @@ -0,0 +1,4 @@ +{ + "uni-search-bar.cancel": "cancel", + "uni-search-bar.placeholder": "Search enter content" +} \ No newline at end of file diff --git a/uni_modules/uni-search-bar/components/uni-search-bar/i18n/index.js b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/index.js new file mode 100644 index 0000000000000000000000000000000000000000..de7509c87ba5197b9f5d20ca94a0558c7a8e08a9 --- /dev/null +++ b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hans.json b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hans.json new file mode 100644 index 0000000000000000000000000000000000000000..d4e5c120cd8fd530407da308754b20285f95bef9 --- /dev/null +++ b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hans.json @@ -0,0 +1,4 @@ +{ + "uni-search-bar.cancel": "cancel", + "uni-search-bar.placeholder": "请输入搜索内容" +} diff --git a/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hant.json b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hant.json new file mode 100644 index 0000000000000000000000000000000000000000..318b6ef1b7ec16cb05d7609d093b65be2943ccec --- /dev/null +++ b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hant.json @@ -0,0 +1,4 @@ +{ + "uni-search-bar.cancel": "cancel", + "uni-search-bar.placeholder": "請輸入搜索內容" +} diff --git a/uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue b/uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue new file mode 100644 index 0000000000000000000000000000000000000000..3c67344f055c5b44748d568c79f8545de74dea42 --- /dev/null +++ b/uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue @@ -0,0 +1,282 @@ + + + + + diff --git a/uni_modules/uni-search-bar/package.json b/uni_modules/uni-search-bar/package.json new file mode 100644 index 0000000000000000000000000000000000000000..f9b9bad26627a40a84589f2ce39d8037115f6b7e --- /dev/null +++ b/uni_modules/uni-search-bar/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-search-bar", + "displayName": "uni-search-bar 搜索栏", + "version": "1.1.1", + "description": "搜索栏组件,通常用于搜索商品、文章等", + "keywords": [ + "uni-ui", + "uniui", + "搜索框", + "搜索栏" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-search-bar/readme.md b/uni_modules/uni-search-bar/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..b39d6cfca4c2c188cab00086129e6a3039bdc938 --- /dev/null +++ b/uni_modules/uni-search-bar/readme.md @@ -0,0 +1,86 @@ + + +## SearchBar 搜索栏 + +> **组件名:uni-search-bar** +> 代码块: `uSearchBar` + + +评分组件 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +在 ``template`` 中使用组件 + +```html + + + + + + + + + + + +``` + + +## API +### SearchBar Props + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|value/v-model |StringNumber | |搜索栏绑定值 | +|placeholder |String |搜索 |搜索栏Placeholder | +|radius |Number |10 |搜索栏圆角,单位px | +|clearButton |String |auto |是否显示清除按钮,可选值`always`-一直显示、`auto`-输入框不为空时显示、`none`-一直不显示 | +|cancelButton |String |auto |是否显示取消按钮,可选值`always`-一直显示、`auto`-输入框不为空时显示、`none`-一直不显示 | +|cancelText |String |取消 |取消按钮的文字 | +|bgColor |String |#F8F8F8|输入框背景颜色 | +|maxlength |Number |100 |输入最大长度 | +|focus |Boolean |false | | + + +### SearchBar Events + +|事件称名 |说明 |返回参数 | +|:-: |:-: |:-: | +|@confirm |uniSearchBar 的输入框 confirm 事件,返回参数为uniSearchBar的value |e={value:Number} | +|@input |uniSearchBar 的 value 改变时触发事件,返回参数为uniSearchBar的value|e=value | +|@cancel |点击取消按钮时触发事件,返回参数为uniSearchBar的value |e={value:Number} | +|@clear |点击清除按钮时触发事件,返回参数为uniSearchBar的value |e={value:Number} | +|@focus |input 获取焦点时触发事件,返回参数为uniSearchBar的value |e={value:Number} | +|@blur |input 失去焦点时触发事件,返回参数为uniSearchBar的value |e={value:Number} | + +### 替换 icon 的 slot 插槽 + +|插槽称名 |说明 | +|:-: |:-: | +|searchIcon |替换组件的搜索图标| +|clearIcon |替换组件的清除图标| + +```html + + + + + + + + X + + +``` + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/search-bar/search-bar](https://hellouniapp.dcloud.net.cn/pages/extUI/search-bar/search-bar) \ No newline at end of file diff --git a/uni_modules/uni-segmented-control/changelog.md b/uni_modules/uni-segmented-control/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..e5093b3c3e82ce03e2580689791bbccf2912f1d9 --- /dev/null +++ b/uni_modules/uni-segmented-control/changelog.md @@ -0,0 +1,6 @@ +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.5(2021-05-12) +- 新增 项目示例地址 +## 1.0.4(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-segmented-control/components/uni-segmented-control/uni-segmented-control.vue b/uni_modules/uni-segmented-control/components/uni-segmented-control/uni-segmented-control.vue new file mode 100644 index 0000000000000000000000000000000000000000..b753366eb173e8b41351a9de2c34e04c1da10b10 --- /dev/null +++ b/uni_modules/uni-segmented-control/components/uni-segmented-control/uni-segmented-control.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/uni_modules/uni-segmented-control/package.json b/uni_modules/uni-segmented-control/package.json new file mode 100644 index 0000000000000000000000000000000000000000..b3ccdd2a61dea97cd449ee50266c83804ec33118 --- /dev/null +++ b/uni_modules/uni-segmented-control/package.json @@ -0,0 +1,83 @@ +{ + "id": "uni-segmented-control", + "displayName": "uni-segmented-control 分段器", + "version": "1.1.0", + "description": "分段器由至少 2 个分段控件组成,用作不同视图的显示", + "keywords": [ + "uni-ui", + "uniui", + "分段器", + "segement", + "顶部选择" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-segmented-control/readme.md b/uni_modules/uni-segmented-control/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..477901695e7bd74a349f128f6703a944c1a98215 --- /dev/null +++ b/uni_modules/uni-segmented-control/readme.md @@ -0,0 +1,60 @@ + + +## SegmentedControl 分段器 +> **组件名:uni-segmented-control** +> 代码块: `uSegmentedControl` + + +用作不同视图的显示 + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +### 基本用法 + +在 ``template`` 中的使用 + +```html + +``` + +## API + +### SegmentedControl Props + +|属性名 |类型 |默认值 |说明 | +|:-: |:-: |:-: |:-: | +|current |Number |0 |当前选中的tab索引值,从0计数 | +|styleType |String |button |分段器样式类型,可选值:button(按钮类型),text(文字类型) | +|activeColor |String |#007aff|选中的标签背景色与边框颜色 | +|values |Array |- |选项数组 | + +### SegmentedControl Events + +|事件名 |说明 |返回值 | +|:-: |:-: |:-: | +|@clickItem |组件触发点击事件时触发 |e={currentIndex} | + + + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/segmented-control/segmented-control](https://hellouniapp.dcloud.net.cn/pages/extUI/segmented-control/segmented-control) \ No newline at end of file diff --git a/uni_modules/uni-share/changelog.md b/uni_modules/uni-share/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..e3cd23f13b11f34efdd65ce2bf51b71d19eeb7aa --- /dev/null +++ b/uni_modules/uni-share/changelog.md @@ -0,0 +1,14 @@ +## 2.0.0(2021-10-14) +支持监听返回操作(如:物理返回,全面屏手机侧滑)关闭分享弹窗 +## 1.0.6(2021-08-25) +兼容vue3 +## 1.0.5(2021-08-05) +优化代码实现,修改原来用`eval()`函数实现的逻辑 +## 1.0.4(2021-06-07) +为符合苹果应用市场的审核,只显示存在对应的分享客户端的选项。如:配置包含微信分享,但是用户手机上并没有安装微信,就不显示微信分享。 +## 1.0.2(2021-05-06) +修复错误的提示:“打包时未添加oauth模块” +## 1.0.1(2021-04-30) +新增完整示例 +## 1.0.0(2021-04-28) +第1版发布 diff --git a/uni_modules/uni-share/js_sdk/uni-image-menu.js b/uni_modules/uni-share/js_sdk/uni-image-menu.js new file mode 100644 index 0000000000000000000000000000000000000000..a15a846ef986c4588a3ee1c8a9c9b544d33fefb2 --- /dev/null +++ b/uni_modules/uni-share/js_sdk/uni-image-menu.js @@ -0,0 +1,203 @@ +var nvMask, nvImageMenu; +class NvImageMenu { + constructor(arg) { + this.isShow = false + } + show({ + list, + cancelText + }, callback) { + if (!list) { + list = [{ + "img": "/static/sharemenu/wechatfriend.png", + "text": "图标文字" + }] + } + //以下为计算菜单的nview绘制布局,为固定算法,使用者无关关心 + var screenWidth = plus.screen.resolutionWidth + //以360px宽度屏幕为例,上下左右边距及2排按钮边距留25像素,图标宽度55像素,同行图标间的间距在360宽的屏幕是30px,但需要动态计算,以此原则计算4列图标分别的left位置 + //图标下的按钮文字距离图标5像素,文字大小12像素 + //底部取消按钮高度固定为44px + //TODO 未处理横屏和pad,这些情况6个图标应该一排即可 + var margin = 20, + iconWidth = 60, + icontextSpace = 5, + textHeight = 12 + var left1 = margin / 360 * screenWidth + var iconSpace = (screenWidth - (left1 * 2) - (iconWidth * 4)) / 3 //屏幕宽度减去左右留白间距,再减去4个图标的宽度,就是3个同行图标的间距 + if (iconSpace <= 5) { //屏幕过窄时,缩小边距和图标大小,再算一次 + margin = 15 + iconWidth = 40 + left1 = margin / 360 * screenWidth + iconSpace = (screenWidth - (left1 * 2) - (iconWidth * 4)) / 3 //屏幕宽度减去左右留白间距,再减去4个图标的宽度,就是3个同行图标的间距 + } + var left2 = left1 + iconWidth + iconSpace + var left3 = left1 + (iconWidth + iconSpace) * 2 + var left4 = left1 + (iconWidth + iconSpace) * 3 + var top1 = left1 + var top2 = top1 + iconWidth + icontextSpace + textHeight + left1 + + const TOP = { + top1, + top2 + }, + LEFT = { + left1, + left2, + left3, + left4 + }; + + nvMask = new plus.nativeObj.View("nvMask", { //先创建遮罩层 + top: '0px', + left: '0px', + height: '100%', + width: '100%', + backgroundColor: 'rgba(0,0,0,0.2)' + }); + nvImageMenu = new plus.nativeObj.View("nvImageMenu", { //创建底部图标菜单 + bottom: '0px', + left: '0px', + height: (iconWidth + textHeight + 2 * margin) * Math.ceil(list.length / 4) + 44 + + 'px', //'264px', + width: '100%', + backgroundColor: 'rgb(255,255,255)' + }); + nvMask.addEventListener("click", () => { //处理遮罩层点击 + // console.log('处理遮罩层点击'); + this.hide() + callback({ + event: "clickMask" + }) + }) + let myList = [] + list.forEach((item, i) => { + myList.push({ + tag: 'img', + src: item.img, + position: { + top: TOP['top' + (parseInt(i / 4) + 1)], + left: LEFT['left' + (1 + i % 4)], + width: iconWidth, + height: iconWidth + } + }) + myList.push({ + tag: 'font', + text: item.text, + textStyles: { + size: textHeight + }, + position: { + top: TOP['top' + (parseInt(i / 4) + 1)] + iconWidth + icontextSpace, + left: LEFT['left' + (1 + i % 4)], + width: iconWidth, + height: textHeight + } + }) + }) + + //绘制底部图标菜单的内容 + nvImageMenu.draw([{ + tag: 'rect', //菜单顶部的分割灰线 + color: '#e7e7e7', + position: { + top: '0px', + height: '1px' + } + }, + { + tag: 'font', + text: cancelText, //底部取消按钮的文字 + textStyles: { + size: '14px' + }, + position: { + bottom: '0px', + height: '44px' + } + }, + { + tag: 'rect', //底部取消按钮的顶部边线 + color: '#e7e7e7', + position: { + bottom: '45px', + height: '1px' + } + }, + ...myList + ]) + nvMask.show() + nvImageMenu.show() + // 开始动画 + /* + plus.nativeObj.View.startAnimation({ + type: 'slide-in-bottom', + duration: 300 + }, nvImageMenu, {}, function() { + console.log('plus.nativeObj.View.startAnimation动画结束'); + // 关闭原生动画 + plus.nativeObj.View.clearAnimation(); + nvImageMenu.show() + }); + */ + + + this.isShow = true + nvImageMenu.addEventListener("click", e => { //处理底部图标菜单的点击事件,根据点击位置触发不同的逻辑 + // console.log("click menu"+JSON.stringify(e)); + if (e.screenY > plus.screen.resolutionHeight - 44) { //点击了底部取消按钮 + // callback({event:"clickCancelButton"}) + this.hide() + } else if (e.clientX < 5 || e.clientX > screenWidth - 5 || e.clientY < 5) { + //屏幕左右边缘5像素及菜单顶部5像素不处理点击 + } else { //点击了图标按钮 + var iClickIndex = -1 //点击的图标按钮序号,第一个图标按钮的index为0 + var iRow = e.clientY < (top2 - (left1 / 2)) ? 0 : 1 + var iCol = -1 + if (e.clientX < (left2 - (iconSpace / 2))) { + iCol = 0 + } else if (e.clientX < (left3 - (iconSpace / 2))) { + iCol = 1 + } else if (e.clientX < (left4 - (iconSpace / 2))) { + iCol = 2 + } else { + iCol = 3 + } + if (iRow == 0) { + iClickIndex = iCol + } else { + iClickIndex = iCol + 4 + } + // console.log("点击按钮的序号: " + iClickIndex); + // if (iClickIndex >= 0 && iClickIndex <= 5) { //处理具体的点击逻辑,此处也可以自行定义逻辑。如果增减了按钮,此处也需要跟着修改 + // } + callback({ + event: "clickMenu", + index: iClickIndex + }) + } + }) + /* nvImageMenu.addEventListener("touchstart", function(e) { + if (e.screenY > (plus.screen.resolutionHeight - 44)) { + //TODO 这里可以处理按下背景变灰的效果 + } + }) + nvImageMenu.addEventListener("touchmove", function(e) { + //TODO 这里可以处理按下背景变灰的效果 + if (e.screenY > plus.screen.resolutionHeight - 44) {} + }) + nvImageMenu.addEventListener("touchend", function(e) { + //TODO 这里可以处理释放背景恢复的效果 + }) + */ + } + + hide() { + nvMask.hide() + nvImageMenu.hide() + this.isShow = false + } +} + +export default NvImageMenu diff --git a/uni_modules/uni-share/js_sdk/uni-share.js b/uni_modules/uni-share/js_sdk/uni-share.js new file mode 100644 index 0000000000000000000000000000000000000000..8af863138651e162c3c396c6246fe29ef46cace5 --- /dev/null +++ b/uni_modules/uni-share/js_sdk/uni-share.js @@ -0,0 +1,98 @@ +import UniImageMenu from './uni-image-menu.js'; +class UniShare extends UniImageMenu{ + constructor(arg) { + super() + this.isShow = super.isShow + } + async show(param, callback){ + var menus = [] + plus.share.getServices(services => { //只显示有服务的项目 + services = services.filter(item => item.nativeClient) + let servicesList = services.map(e => e.id) + param.menus.forEach(item => { + if (servicesList.includes(item.share.provider) || typeof(item.share) == 'string') { + menus.push(item) + } + }) + super.show({ + list: menus, + cancelText: param.cancelText + }, e => { + callback(e) + if(e.event == 'clickMenu'){ + if (typeof(menus[e.index]['share']) == 'string') { + this[menus[e.index]['share']](param) + } else { + uni.share({ + ...param.content, + ...menus[e.index].share, + success: res=> { + console.log("success:" + JSON.stringify(res)); + super.hide() + }, + fail: function(err) { + console.log("fail:" + JSON.stringify(err)); + uni.showModal({ + content: JSON.stringify(err), + showCancel: false, + confirmText: "知道了" + }); + } + }) + } + } + }) + }, err => { + uni.showModal({ + title: '获取服务供应商失败:' + JSON.stringify(err), + showCancel: false, + confirmText: '知道了' + }); + console.error('获取服务供应商失败:' + JSON.stringify(err)); + }) + } + hide(){ + super.hide() + } + copyurl(param) { + console.log('copyurl',param); + uni.setClipboardData({ + data: param.content.href, + success: ()=>{ + console.log('success'); + uni.hideToast() //关闭自带的toast + uni.showToast({ + title: '复制成功', + icon: 'none' + }); + super.hide(); + }, + fail: (err) => { + uni.showModal({ + content: JSON.stringify(err), + showCancel: false + }); + } + }); + } + // 使用系统分享发送分享消息 + shareSystem(param) { + console.log('shareSystem',param); + plus.share.sendWithSystem({ + type: 'text', + content: param.content.title + param.content.summary || '', + href: param.content.href, + }, (e)=> { + console.log('分享成功'); + super.hide() + }, (err)=> { + console.log('分享失败:' + JSON.stringify(err)); + uni.showModal({ + title: '获取服务供应商失败:' + JSON.stringify(err), + showCancel: false, + confirmText: '知道了' + }); + }); + } +} +export default UniShare \ No newline at end of file diff --git a/uni_modules/uni-share/package.json b/uni_modules/uni-share/package.json new file mode 100644 index 0000000000000000000000000000000000000000..4a2a3e1206562bf0d732f5e21fbb69acefe75d5c --- /dev/null +++ b/uni_modules/uni-share/package.json @@ -0,0 +1,80 @@ +{ + "id": "uni-share", + "displayName": "uni-share", + "version": "2.0.0", + "description": "底部弹出宫格图标式的分享菜单,可覆盖原生组件。", + "keywords": [ + "分享菜单" +], + "repository": "", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "JS SDK", + "通用 SDK" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "n", + "Android Browser": "n", + "微信浏览器(Android)": "n", + "QQ浏览器(Android)": "n" + }, + "H5-pc": { + "Chrome": "n", + "IE": "n", + "Edge": "n", + "Firefox": "n", + "Safari": "n" + }, + "小程序": { + "微信": "n", + "阿里": "n", + "百度": "n", + "字节跳动": "n", + "QQ": "n" + }, + "快应用": { + "华为": "n", + "联盟": "n" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-share/readme.md b/uni_modules/uni-share/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..a3d201c47620dca66dd1ecd9660c2d0163e7706c --- /dev/null +++ b/uni_modules/uni-share/readme.md @@ -0,0 +1,85 @@ +#### 本功能基于[底部图标菜单](https://ext.dcloud.net.cn/plugin?id=4858)封装而成。 +### 示例代码 +``` + + + +``` \ No newline at end of file diff --git a/uni_modules/uni-sign-in/changelog.md b/uni_modules/uni-sign-in/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..c09f3de224b8e4a24275f7f22d89402b1fc46d12 --- /dev/null +++ b/uni_modules/uni-sign-in/changelog.md @@ -0,0 +1,6 @@ +## 1.0.2(2021-08-25) +修复时区问题 +## 1.0.1(2021-08-23) +调整签到逻辑,支持查出积分的收入支出历史记录 +## 1.0.0(2021-08-05) +1.0.0版本发布 diff --git a/uni_modules/uni-sign-in/components/uni-sign-in/uni-sign-in.vue b/uni_modules/uni-sign-in/components/uni-sign-in/uni-sign-in.vue new file mode 100644 index 0000000000000000000000000000000000000000..99d55917276ed122224efef5fca125d8051a543b --- /dev/null +++ b/uni_modules/uni-sign-in/components/uni-sign-in/uni-sign-in.vue @@ -0,0 +1,232 @@ + + + + + diff --git a/uni_modules/uni-sign-in/package.json b/uni_modules/uni-sign-in/package.json new file mode 100644 index 0000000000000000000000000000000000000000..2751a88e69270bebd04683f1a68e1604791ad65b --- /dev/null +++ b/uni_modules/uni-sign-in/package.json @@ -0,0 +1,82 @@ +{ + "id": "uni-sign-in", + "displayName": "签到插件", + "version": "1.0.2", + "description": "uni-sign-in", + "keywords": [ + "uni-sign-in", + "签到", + "营销" +], + "repository": "", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "uniCloud", + "云端一体页面模板" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "u", + "微信浏览器(Android)": "u", + "QQ浏览器(Android)": "u" + }, + "H5-pc": { + "Chrome": "y", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "y", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-sign-in/pages/demo/demo.vue b/uni_modules/uni-sign-in/pages/demo/demo.vue new file mode 100644 index 0000000000000000000000000000000000000000..4efee8ae08fefca35e071e8eb1eb09693359f3e3 --- /dev/null +++ b/uni_modules/uni-sign-in/pages/demo/demo.vue @@ -0,0 +1,12 @@ +