@ -0,0 +1,17 @@ |
|||
# http://editorconfig.org |
|||
|
|||
root = true |
|||
|
|||
# all files |
|||
[*] |
|||
charset = utf-8 |
|||
indent_style = space |
|||
indent_size = 2 |
|||
end_of_line = crlf |
|||
insert_final_newline = true |
|||
trim_trailing_whitespace = true |
|||
|
|||
# md files |
|||
[*.md] |
|||
insert_final_newline = false |
|||
trim_trailing_whitespace = false |
|||
@ -0,0 +1,45 @@ |
|||
src/core/helpers/html-parser.js |
|||
src/platforms/app-plus-nvue/runtime |
|||
src/platforms/app-plus/service/api/plugin/uts.js |
|||
build/rollup-plugin-require-context |
|||
packages/*/packages/uni-app-plus |
|||
packages/*/packages/uni-app-plus-nvue |
|||
packages/*/packages/uni-app-plus-nvue-v8 |
|||
packages/*/packages/weex-styler |
|||
packages/*/packages/weex-template-compiler |
|||
packages/*/packages/@intervolga |
|||
packages/*/packages/@megalo |
|||
packages/*/packages/@vue |
|||
packages/*/packages/app-vue-style-loader |
|||
packages/*/packages/h5-vue |
|||
packages/*/packages/h5-vue-router |
|||
packages/*/packages/h5-vue-style-loader |
|||
packages/*/packages/megalo |
|||
packages/*/packages/mp-vue |
|||
packages/*/packages/mpvue |
|||
packages/*/packages/mpvue-page-factory |
|||
packages/*/packages/mpvue-template-compiler |
|||
packages/*/packages/postcss-normalize-whitespace |
|||
packages/*/packages/uni-app |
|||
packages/*/packages/uni-push |
|||
packages/*/packages/uni-stat |
|||
packages/*/packages/uni-cloud |
|||
packages/*/packages/vue-loader |
|||
packages/*/packages/vue-template-compiler |
|||
packages/*/packages/webpack-preprocess-loader/preprocess |
|||
packages/*/packages/sass-loader |
|||
packages/*/packages/vuex |
|||
packages/*/packages/vuex3 |
|||
packages/*/template/**/* |
|||
packages/uni-h5/src |
|||
packages/uni-stat |
|||
node_modules |
|||
automator.js |
|||
uni.automator.js |
|||
uni-polyfill.js |
|||
packages/uni-cli-shared/components/ad-fullscreen-video.vue |
|||
packages/uni-cli-shared/components/ad-interactive.vue |
|||
packages/uni-cli-shared/components/ad-interstitial.vue |
|||
packages/uni-cli-shared/components/ad-rewarded-video.vue |
|||
packages/uni-cli-shared/lib/uni_modules/uni_modules.js |
|||
packages/uni-cli-shared/lib/uts/uts.js |
|||
@ -0,0 +1,11 @@ |
|||
node_modules/ |
|||
.project |
|||
unpackage/ |
|||
.vscode/ |
|||
.idea |
|||
.DS_Store |
|||
!packages/uni-app-plus/dist |
|||
package-lock.json |
|||
!packages/vue-cli-plugin-uni/packages/**/* |
|||
tests/package.json |
|||
*.DS_Store |
|||
@ -0,0 +1,202 @@ |
|||
|
|||
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. |
|||
@ -0,0 +1,89 @@ |
|||
# uni-app |
|||
|
|||
<p> |
|||
简体中文 | <a href="https://github.com/dcloudio/uni-app/blob/dev/README_en-US.md">English</a> |
|||
</p> |
|||
|
|||
`uni-app` 是一个使用 `Vue.js` 开发小程序、H5、App的统一前端框架。官网地址:[https://uniapp.dcloud.io](https://uniapp.dcloud.io) |
|||
|
|||
开发者使用 `Vue` 语法编写代码,`uni-app` 框架将其编译到 小程序(微信/支付宝/百度/字节跳动/QQ/快手/钉钉/小红书)、App(iOS/Android)、H5等多个平台,保证其正确运行并达到优秀体验。 |
|||
|
|||
|
|||
# uni-app的特点 |
|||
|
|||
- 开发者和案例更多:HBuilder装机量800万台,开发者社区月活百万,70多个QQ微信群承载10万人。案例众多,uni统计月活超10亿([详见](https://uniapp.dcloud.io/case)) |
|||
- 性能更高(见[评测](https://juejin.im/post/5ca1736af265da30ae314248)) |
|||
- 更丰富的周边生态,[插件市场](https://ext.dcloud.net.cn/)数千款插件 |
|||
- 提供比小程序原生开发更好的开发体验、更高的工程化效率 |
|||
- 跨端抹平度更完善,且各端特色发挥更灵活,可真正实现一套代码多端覆盖,无需各端多头维护升级 |
|||
- 权威认可:阿里小程序官方工具内置uni-app([详见](https://docs.alipay.com/mini/ide/0.70-stable))、腾讯课堂官方自制uni-app培训视频([详见](https://ask.dcloud.net.cn/article/35640)) |
|||
|
|||
## 扫码体验 |
|||
|
|||
开发一次,编译到14个平台。依次扫描14个二维码,亲自体验最全面的跨平台效果! |
|||
|
|||
<div align="center"> |
|||
<img src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-a90b5f95-90ba-4d30-a6a7-cd4d057327db/cf5727bc-fbe6-4d7e-bd92-e0d16e6f53b0.jpg"/> |
|||
</div> |
|||
|
|||
*注: 某些平台不能提交简单demo,补充了一些其他功能。* |
|||
|
|||
## 快速开始 |
|||
|
|||
`uni-app`支持通过`vue-cli`命令行、`HBuilderX`可视化界面两种方式快速创建项目: |
|||
|
|||
- [vue-cli命令行方式](https://uniapp.dcloud.io/quickstart?id=_2-通过vue-cli命令行):不限IDE,适合对node熟悉的开发者,扩展阅读:[在vscode中开发uni-app](https://ask.dcloud.net.cn/article/36286)、[在 WebStorm 中开发 uni-app](https://ask.dcloud.net.cn/article/36307) |
|||
- [HBuilderX可视化界面](https://uniapp.dcloud.io/quickstart?id=_1-通过-hbuilderx-可视化界面):专用IDE,内置相关环境,开箱即用,开发效率更高。 |
|||
|
|||
## 项目案例 |
|||
|
|||
案例展示:[uniapp.dcloud.io/case](https://uniapp.dcloud.io/case) |
|||
|
|||
欢迎提交你的应用,[uni-app案例征集](https://github.com/dcloudio/uni-app/issues/6) |
|||
|
|||
## 需求墙 |
|||
|
|||
`uni-app`计划支持的功能点,会在需求墙上进行展示,征集开发者的投票意见,[前往投票](https://dev.dcloud.net.cn/wish/)。 |
|||
|
|||
## 更新日志 |
|||
|
|||
`uni-app`一直保持高频的更新迭代,详见[正式版更新日志](https://uniapp.dcloud.net.cn/release)、[Alpha版更新日志](https://uniapp.dcloud.net.cn/release-note-alpha)。 |
|||
|
|||
## 论坛 |
|||
|
|||
由于`DCloud`有70多个QQ、微信群,官方已无法维护更多交流群。请开发者到官方论坛交流:[https://ask.dcloud.net.cn/explore/](https://ask.dcloud.net.cn/explore/) 。论坛提供了比issues更专业的工具服务。 |
|||
|
|||
## 插件市场 |
|||
|
|||
`uni-app`有丰富的插件生态,众多开发者提交了数千款组件、sdk、项目模板,详见:[https://ext.dcloud.net.cn/](https://ext.dcloud.net.cn/) |
|||
|
|||
除了众多三方ui库,官方还提供了uni-ui,在性能和跨端兼容方面有更强的优势。详见:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) |
|||
|
|||
## 现有项目如何迁移到uni-app体系 |
|||
|
|||
- 微信小程序转换uni-app指南及转换器:[https://ask.dcloud.net.cn/article/35786](https://ask.dcloud.net.cn/article/35786) |
|||
- vue h5项目转换uni-app指南:[https://ask.dcloud.net.cn/article/36174](https://ask.dcloud.net.cn/article/36174) |
|||
- mpvue 项目(组件)迁移指南、示例及资源汇总: [https://ask.dcloud.net.cn/article/34945](https://ask.dcloud.net.cn/article/34945) |
|||
- wepy转uni-app转换器:[https://github.com/zhangdaren/wepy-to-uniapp](https://github.com/zhangdaren/wepy-to-uniapp) |
|||
|
|||
## 常见疑问 |
|||
|
|||
- 问:不同端有不同的需求、不同的特色,登录支付也不一样,如何统一? |
|||
- 答:差异部分使用条件编译。uni-app提供了灵活强大的[条件编译](https://uniapp.dcloud.io/platform)。可以完美处理复用部分和差异部分。真正一套工程源码。当业务升级时,不再需要多端维护。如果多端维护,经常会因为某些端的流量不大,就一直拖延无法让那些用户享受到最新服务。另外登录支付在客户端部分,已经被uni-app统一成一样的api了。 |
|||
|
|||
|
|||
- 问:多端是不是一种妥协,是否会造成性能下降? |
|||
- 答:good question。多端且不影响性能,确实很难,但uni-app做到了。在h5端,它的性能、包体积与直接使用vue.js开发一致; 在小程序端,它的性能比大多数开发框架更好,uni-app底层自动处理的setdata差量同步机制,比开发者手动写setdata更好,就像使用vue.js更新界面比手动写js修改dom更高效一样; 在App,uni-app支持webview渲染和原生渲染双引擎,启用原生渲染时,css写法受限,但性能是很接近原生开发的效果的,在当前的手机环境下,千万日活以下的应用在App使用uni-app也不会遇到任何压力。当然也可以在已经做好的原生App中将部分页面改为uni-app实现; 此外,我们会把很多跨端处理放在编译期完成的,这样会减少对运行期的效率影响。 |
|||
|
|||
|
|||
- 问:不做多端,是不是不需要uni-app? |
|||
- 答:不是。大量开发者用uni-app只做一个端,详见[案例](https://uniapp.dcloud.io/case)。对于开发者而言,一个优秀工具在手,做什么都不愁。 |
|||
|
|||
- 问:uni-app以后会不会变更开源协议,转向收费? |
|||
- 答:官方承诺永远不会变更开源协议。无论HBuilderX、uni-app、App,面向中国人永久免费。 |
|||
|
|||
## 更多资料 |
|||
|
|||
- 评测:[跨端开发框架深度横评之2020版](https://juejin.im/post/5e8e8d5a6fb9a03c6d3d9f42) |
|||
- 评测:[深入测试一周,主流多端框架大比武](https://mp.weixin.qq.com/s/jIDEHfuMnED6HTfNgjsW4w) |
|||
- [uni-app在App端和flutter、react native的比较](https://ask.dcloud.net.cn/article/36083) |
|||
@ -0,0 +1,86 @@ |
|||
# uni-app |
|||
|
|||
<p> |
|||
<a href="https://github.com/dcloudio/uni-app/blob/dev/README.md">简体中文</a> | English |
|||
</p> |
|||
|
|||
`uni-app` is a unified front-end framework that uses `Vue.js` to develop applet, H5, and App. Official website address: [https://uniapp.dcloud.io](https://uniapp.dcloud.io) |
|||
|
|||
Developers use `Vue` syntax to write code, and `uni-app` framework compiles it into small programs (WeChat/Alipay/Baidu/ByteDance/QQ/Kuishou/Dingding/Xiaohongshu), App (iOS/Android) ), H5 and other platforms to ensure its correct operation and achieve an excellent experience. |
|||
|
|||
# Features of uni-app |
|||
|
|||
- More developers and cases: HBuilder has installed 8 million units, the developer community has one million monthly active users, and more than 70 QQ and WeChat groups carry 100,000 people. There are many cases, and the uni statistics exceed 1 billion monthly active users ([see details](https://uniapp.dcloud.io/case)) |
|||
- Higher performance (see [Review](https://juejin.im/post/5ca1736af265da30ae314248)) |
|||
- Richer surrounding ecology, [Plugin Market](https://ext.dcloud.net.cn/) thousands of plugins |
|||
- Provide a better development experience and higher engineering efficiency than the native development of small programs |
|||
- The smoothness across the ends is more complete, and the characteristics of each end are more flexible, which can truly achieve multi-end coverage of a set of codes, without the need for multi-end maintenance and upgrades at each end |
|||
- Authoritative recognition: Alipay's official tool has built-in uni-app ([see details](https://docs.alipay.com/mini/ide/0.70-stable)), Tencent Classroom's official self-made uni-app training video ([ For details, see](https://ask.dcloud.net.cn/article/35640)) |
|||
|
|||
## Scan code experience |
|||
|
|||
Develop once, compile to 3 platforms. Scan 3 QR codes in sequence to experience the most comprehensive cross-platform effect for yourself! |
|||
|
|||
<div align="center"> |
|||
<img src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-f184e7c3-1912-41b2-b81f-435d1b37c7b4/3cca21df-a9af-48f8-b808-0b795acb2580.jpg"/> |
|||
</div> |
|||
|
|||
## quick start |
|||
|
|||
`uni-app` supports two ways to quickly create projects through the `vue-cli` command line and the `HBuilderX` visual interface: |
|||
|
|||
- [vue-cli command line mode](https://uniapp.dcloud.io/quickstart?id=_2-%E9%80%9A%E8%BF%87vue-cli%E5%91%BD%E4%BB%A4%E8%A1%8C): not limited to IDE, suitable for developers familiar with node, extended reading: [Developing uni-app in vscode](https://ask.dcloud.net.cn/article/36286 ), [Developing uni-app in WebStorm](https://ask.dcloud.net.cn/article/36307) |
|||
- [HBuilderX visual interface](https://uniapp.dcloud.io/quickstart?id=_1-%E9%80%9A%E8%BF%87-hbuilderx-%E5%8F%AF%E8%A7%86%E5%8C%96%E7%95%8C%E9%9D%A2): dedicated IDE, built-in related environment, out-of-the-box, and higher development efficiency. |
|||
|
|||
## Project cases |
|||
|
|||
Case show: [uniapp.dcloud.io/case](https://uniapp.dcloud.io/case) |
|||
|
|||
Welcome to submit your application, [uni-app case collection](https://github.com/dcloudio/uni-app/issues/6) |
|||
|
|||
## Demand Wall |
|||
|
|||
The function points supported by the `uni-app` plan will be displayed on the demand wall, and the voting opinions of developers will be collected. [Go to Vote](https://dev.dcloud.net.cn/wish/). |
|||
|
|||
## Changelog |
|||
|
|||
`uni-app` has always maintained high-frequency update iterations. For details, see [Official version update log](https://uniapp.dcloud.net.cn/release), [Alpha version update log](https://uniapp. dcloud.net.cn/release-note-alpha). |
|||
|
|||
## Forum |
|||
|
|||
Since `DCloud` has more than 70 QQ and WeChat groups, the official has been unable to maintain more communication groups. Please go to the official forum to communicate: [https://ask.dcloud.net.cn/explore/](https://ask.dcloud.net.cn/explore/). Forums provide more professional tools and services than issues. |
|||
|
|||
## Plugin Market |
|||
|
|||
`uni-app` has a rich plugin ecosystem. Many developers have submitted thousands of components, sdk, and project templates. For details, see: [https://ext.dcloud.net.cn/](https://ext.dcloud.net.cn/) |
|||
|
|||
In addition to many third-party ui libraries, the official also provides uni-ui, which has stronger advantages in performance and cross-end compatibility. For details, see: [https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) |
|||
|
|||
## How to migrate existing projects to uni-app system |
|||
|
|||
- WeChat applet conversion uni-app guide and converter: [https://ask.dcloud.net.cn/article/35786](https://ask.dcloud.net.cn/article/35786) |
|||
- Vue h5 project conversion uni-app guide: [https://ask.dcloud.net.cn/article/36174](https://ask.dcloud.net.cn/article/36174) |
|||
- mpvue project (component) migration guide, example and resource summary: [https://ask.dcloud.net.cn/article/34945](https://ask.dcloud.net.cn/article/34945) |
|||
- wepy to uni-app converter: [https://github.com/zhangdaren/wepy-to-uniapp](https://github.com/zhangdaren/wepy-to-uniapp) |
|||
|
|||
## Frequently Asked Questions |
|||
|
|||
- Q: Different terminals have different needs and features, and login and payment are also different. How to unify? |
|||
- A: The difference part uses conditional compilation. uni-app provides flexible and powerful [conditional compilation](https://uniapp.dcloud.io/platform). Can perfectly handle the multiplexed part and the difference part. A real set of project source code. When the business is upgraded, multi-terminal maintenance is no longer required. If there is multi-end maintenance, it is often delayed because the traffic on some ends is not large, so that those users cannot enjoy the latest services. In addition, the login payment in the client part has been unified into the same api by uni-app. |
|||
|
|||
|
|||
- Q: Is multi-terminal a compromise and will it cause performance degradation? |
|||
- A: good question. It's really hard to be multi-terminal without compromising performance, but uni-app does it. On the h5 side, its performance and package size are consistent with those developed directly using vue.js; on the applet side, its performance is better than most development frameworks. It is better for the user to manually write setdata, just as it is more efficient to use vue.js to update the interface than to manually write js to modify the dom; in App, uni-app supports dual engines for webview rendering and native rendering. When native rendering is enabled, css writing is limited, However, the performance is very close to the effect of native development. In the current mobile phone environment, applications with less than ten million daily activities will not encounter any pressure when using uni-app in the App. Of course, it is also possible to change some pages to uni-app in the native app that has already been done; in addition, we will complete a lot of cross-end processing at the compile time, which will reduce the efficiency impact on the runtime. |
|||
|
|||
|
|||
- Q: Do you not need uni-app if you don't do multi-end? |
|||
- A: No. A large number of developers use uni-app as only one end, see [Case](https://uniapp.dcloud.io/case) for details. For developers, with an excellent tool in hand, there is nothing to worry about. |
|||
|
|||
- Q: Will uni-app change the open source protocol and switch to charging in the future? |
|||
- A: The official promise will never change the open source agreement. Regardless of HBuilderX, uni-app, or App, it is always free for Chinese people. |
|||
|
|||
## more info |
|||
|
|||
- Evaluation: [Cross-end Development Framework Deep Heng Evaluation 2020 Edition](https://juejin.im/post/5e8e8d5a6fb9a03c6d3d9f42) |
|||
- Evaluation: [In-depth testing for a week, mainstream multi-terminal frameworks compete](https://mp.weixin.qq.com/s/jIDEHfuMnED6HTfNgjsW4w) |
|||
- [Comparison of uni-app on the App side with flutter and react native](https://ask.dcloud.net.cn/article/36083) |
|||
@ -0,0 +1,17 @@ |
|||
const config = { |
|||
// ignore: [
|
|||
// "./packages",
|
|||
// ],
|
|||
presets: [ |
|||
["@vue/cli-plugin-babel/preset", { |
|||
useBuiltIns: "entry" |
|||
}] |
|||
], |
|||
plugins: [require('./lib/babel-plugin-uni-api/index.js')] |
|||
} |
|||
|
|||
if (process.env.NODE_ENV === 'test') { |
|||
delete config.ignore |
|||
} |
|||
|
|||
module.exports = config |
|||
@ -0,0 +1,93 @@ |
|||
const { |
|||
error |
|||
} = require('@vue/cli-shared-utils') |
|||
|
|||
const Service = require('@vue/cli-service') |
|||
|
|||
const del = require('del') |
|||
const copy = require('copy') |
|||
const path = require('path') |
|||
const jsonfile = require('jsonfile') |
|||
|
|||
const { |
|||
generateApiManifest |
|||
} = require('./manifest') |
|||
|
|||
const fixInnerHTML = require('./fixInnerHTML') |
|||
|
|||
const service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd(), { |
|||
inlineOptions: require('./vue.config.js') |
|||
}) |
|||
// 删除 cache 目录
|
|||
del.sync(['node_modules/.cache']) |
|||
|
|||
let name = 'index' |
|||
let filename = '' |
|||
let entry = './lib/' + process.env.UNI_PLATFORM + '/main.js' |
|||
|
|||
if (process.env.UNI_PLATFORM === 'h5' && process.env.UNI_UI === 'true') { |
|||
entry = './lib/' + process.env.UNI_PLATFORM + '/ui.js' |
|||
} |
|||
|
|||
if (process.env.UNI_PLATFORM === 'app-plus' && process.env.UNI_VIEW === 'true') { |
|||
name = 'uni' |
|||
filename = 'view' |
|||
entry = './lib/' + process.env.UNI_PLATFORM + '/view.js' |
|||
} |
|||
service.run('build', { |
|||
name, |
|||
filename, |
|||
watch: process.env.UNI_WATCH === 'true', |
|||
target: 'lib', |
|||
formats: process.env.UNI_WATCH === 'true' ? 'umd' : 'umd-min', |
|||
entry, |
|||
'inline-vue': !!process.env.UNI_VIEW, |
|||
clean: false, //! process.env.UNI_VIEW,
|
|||
mode: process.env.NODE_ENV |
|||
}).then(function () { |
|||
if ( |
|||
process.env.UNI_WATCH !== 'true' && |
|||
process.env.UNI_UI !== 'true' && |
|||
process.env.UNI_VIEW !== 'true' |
|||
) { |
|||
generateApiManifest( |
|||
JSON.parse(JSON.stringify(process.UNI_SERVICE_API_MANIFEST)), |
|||
JSON.parse(JSON.stringify(process.UNI_SERVICE_API_PROTOCOL)) |
|||
) |
|||
} |
|||
if (process.env.UNI_PLATFORM === 'app-plus' && process.env.UNI_VIEW === 'true' && process.env.UNI_WATCH !== 'true') { |
|||
fixInnerHTML() |
|||
} |
|||
}).catch(err => { |
|||
error(err) |
|||
process.exit(1) |
|||
}) |
|||
|
|||
if (process.env.UNI_PLATFORM === 'h5' && process.env.UNI_WATCH === 'false') { |
|||
const packagePath = path.join(__dirname, `../packages/uni-${process.env.UNI_PLATFORM}`) |
|||
const packageJsonPath = path.join(packagePath, 'package.json') |
|||
del(path.join(packagePath, '{lib,src}')) |
|||
.then(() => { |
|||
copy([path.join(__dirname, '../{lib,src}/**/*')], packagePath, function (err, file) { |
|||
if (err) { |
|||
throw err |
|||
} |
|||
}) |
|||
}) |
|||
jsonfile.readFile(path.join(__dirname, '../package.json')) |
|||
.then(origin => { |
|||
return jsonfile.readFile(packageJsonPath) |
|||
.then(obj => { |
|||
obj.dependencies = origin.dependencies |
|||
return obj |
|||
}) |
|||
}) |
|||
.then(obj => { |
|||
return jsonfile.writeFile(packageJsonPath, obj, { |
|||
spaces: 2 |
|||
}) |
|||
}) |
|||
.catch(err => { |
|||
throw err |
|||
}) |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
const path = require('path') |
|||
const del = require('del') |
|||
|
|||
const { |
|||
error |
|||
} = require('@vue/cli-shared-utils') |
|||
|
|||
const Service = require('@vue/cli-service') |
|||
|
|||
const vueConfig = require('./vue.config.js') |
|||
|
|||
const extendsApiPath = path.resolve(__dirname, '../lib/h5/extends-api') |
|||
|
|||
vueConfig.configureWebpack.resolve.alias['uni-invoke-api'] = extendsApiPath |
|||
|
|||
const service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd(), { |
|||
inlineOptions: vueConfig |
|||
}) |
|||
// 删除 cache 目录
|
|||
del.sync(['node_modules/.cache']) |
|||
|
|||
let pluginDir = process.argv[2] |
|||
if (!pluginDir) { |
|||
console.error('缺少参数') |
|||
process.exit(0) |
|||
} |
|||
|
|||
if (pluginDir.indexOf('/') === -1) { |
|||
pluginDir = path.resolve(__dirname, '../packages/uni-' + pluginDir) |
|||
} |
|||
|
|||
const pkg = require(path.join(pluginDir, 'package.json')) |
|||
if (!pkg['uni-app']) { |
|||
console.error('缺少 uni-app 配置') |
|||
process.exit(0) |
|||
} |
|||
|
|||
service.webpackRawConfigFns.push(function () { |
|||
return { |
|||
resolve: { |
|||
alias: { |
|||
'uni-platform/service/api.js': extendsApiPath, |
|||
'uni-sub-platform': path.resolve(pluginDir, 'src'), |
|||
'uni-platform-api': path.resolve(__dirname, '../src/platforms/h5/service/api'), |
|||
'uni-sub-platform-api': path.resolve(pluginDir, 'src/service/api') |
|||
} |
|||
}, |
|||
module: { |
|||
rules: [{ |
|||
test: path.resolve(__dirname, '../src/core/view/components/index.js'), |
|||
use: [{ |
|||
loader: path.resolve(__dirname, '../lib/extends-component-loader'), |
|||
options: { |
|||
extends: path.resolve(pluginDir, 'src/view/components'), |
|||
base: path.resolve(__dirname, '../src/core/view/components'), |
|||
platform: path.resolve(__dirname, '../src/platforms/h5/view/components') |
|||
} |
|||
}] |
|||
}, { |
|||
test: path.resolve(__dirname, '../src/platforms/h5/service/api/index.js'), |
|||
use: [{ |
|||
loader: path.resolve(__dirname, '../lib/extends-api-loader'), |
|||
options: { |
|||
extends: path.resolve(pluginDir, 'src/service/api'), |
|||
base: path.resolve(__dirname, '../src/platforms/h5/service/api') |
|||
} |
|||
}] |
|||
}] |
|||
} |
|||
} |
|||
}) |
|||
|
|||
service.run('build', { |
|||
name: 'index', |
|||
watch: process.env.UNI_WATCH === 'true', |
|||
target: 'lib', |
|||
formats: process.env.UNI_WATCH === 'true' ? 'umd' : 'umd-min', |
|||
entry: './lib/h5/main.js', |
|||
dest: path.join(pluginDir, 'dist'), |
|||
clean: true, |
|||
mode: process.env.NODE_ENV |
|||
}).then(function () {}).catch(err => { |
|||
error(err) |
|||
process.exit(1) |
|||
}) |
|||
@ -0,0 +1,77 @@ |
|||
const fs = require('fs') |
|||
const del = require('del') |
|||
const path = require('path') |
|||
const copy = require('copy') |
|||
const rollup = require('rollup') |
|||
|
|||
const genConfig = require('./rollup.config.qa') |
|||
|
|||
const filename = 'vue.' + (process.env.NODE_ENV === 'production' ? 'prod' : 'dev') + '.js' |
|||
|
|||
async function build () { |
|||
const bridgeBundle = await rollup.rollup(genConfig('bridge')) |
|||
const { |
|||
output: bridgeOutput |
|||
} = await bridgeBundle.generate({ |
|||
format: 'iife' |
|||
}) |
|||
const bridgeCode = bridgeOutput[0].code |
|||
const appBundle = await rollup.rollup(genConfig('app')) |
|||
const { |
|||
output: appOutput |
|||
} = await appBundle.generate({ |
|||
format: 'iife', |
|||
banner: ` |
|||
dsl.onInitApp(function({ |
|||
inst, |
|||
context, |
|||
instRequireModule |
|||
}) { |
|||
if(!context.quickapp.dock.makeEvaluateBuildScript){ |
|||
context.quickapp.dock.makeEvaluateBuildScript = args => args |
|||
} |
|||
const $app_require$ = instRequireModule; |
|||
`,
|
|||
footer: ` |
|||
});` |
|||
}) |
|||
const appCode = appOutput[0].code |
|||
const pageBundle = await rollup.rollup(genConfig('page')) |
|||
const { |
|||
output: pageOutput |
|||
} = await pageBundle.generate({ |
|||
format: 'iife', |
|||
banner: ` |
|||
dsl.onInitPage(function({ |
|||
$app_require$, |
|||
Vue |
|||
}) { |
|||
`,
|
|||
footer: ` |
|||
});` |
|||
}) |
|||
const pageCode = pageOutput[0].code |
|||
const vueCode = fs.readFileSync(path.resolve(__dirname, '../packages/uni-quickapp-native/assets/' + filename)) |
|||
|
|||
fs.writeFileSync( |
|||
path.resolve(__dirname, '../packages/uni-quickapp-native/dist/' + filename), |
|||
vueCode + bridgeCode + appCode + pageCode, { |
|||
encoding: 'utf8' |
|||
} |
|||
) |
|||
|
|||
if (process.env.NODE_ENV === 'production') { // 命令会执行dev,prod两次,仅prod时执行copy
|
|||
const componentsSrc = path.resolve(__dirname, '../src/platforms/quickapp-native/view/components/**/*') |
|||
const componentsDest = path.resolve(__dirname, '../packages/uni-quickapp-native/components') |
|||
|
|||
del.sync([componentsDest]) |
|||
|
|||
copy(componentsSrc, componentsDest, function (err, file) { |
|||
if (err) { |
|||
throw err |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
|
|||
build() |
|||
@ -0,0 +1,18 @@ |
|||
const path = require('path') |
|||
const fs = require('fs') |
|||
|
|||
module.exports = function () { |
|||
const files = [ |
|||
path.join(__dirname, '../packages/uni-app-plus/dist/view.umd.min.js'), |
|||
path.join(__dirname, '../packages/uni-app-plus/template/v3/__uniappquill.js'), |
|||
path.join(__dirname, '../packages/uni-app-plus/template/v3/__uniappquillimageresize.js') |
|||
] |
|||
files.forEach(filePath => { |
|||
const fileContent = fs.readFileSync(filePath, { |
|||
encoding: 'utf8' |
|||
}) |
|||
fs.writeFileSync(filePath, fileContent.replace(/\.innerHTML\b/g, '["inner"+"HTML"]'), { |
|||
encoding: 'utf8' |
|||
}) |
|||
}) |
|||
} |
|||
@ -0,0 +1,167 @@ |
|||
const fs = require('fs') |
|||
const path = require('path') |
|||
|
|||
const apis = require('../lib/apis') |
|||
|
|||
const AUTO_LOADS = [ |
|||
'upx2px', |
|||
'canIUse', |
|||
|
|||
'getSystemInfo', |
|||
'getSystemInfoSync', |
|||
|
|||
'navigateTo', |
|||
'redirectTo', |
|||
'switchTab', |
|||
'reLaunch', |
|||
'navigateBack' |
|||
] |
|||
|
|||
const TOAST_DEPS = [ |
|||
['/platforms/h5/components/app/popup/toast.vue', 'Toast'], |
|||
['/platforms/h5/components/app/popup/mixins/toast.js', 'ToastMixin'] |
|||
] |
|||
|
|||
// TODO 暂不考虑 head,tabBar 的动态拆分
|
|||
const DEPS = { |
|||
chooseLocation: [ |
|||
['/platforms/h5/view/components/map/index.vue', 'Map'], |
|||
['/core/view/components/input/index.vue', 'Input'], |
|||
['/core/view/components/scroll-view/index.vue', 'ScrollView'], |
|||
['/platforms/h5/service/api/network/request.js', 'request'], |
|||
['/platforms/h5/service/api/location/get-location.js', 'getLocation'], |
|||
['/platforms/h5/components/system-routes/choose-location/index.vue', 'ChooseLocation'] |
|||
], |
|||
openLocation: [ |
|||
['/platforms/h5/view/components/map/index.vue', 'Map'], |
|||
['/platforms/h5/service/api/network/request.js', 'request'], |
|||
['/platforms/h5/service/api/location/get-location.js', 'getLocation'], |
|||
['/platforms/h5/components/system-routes/open-location/index.vue', 'OpenLocation'] |
|||
], |
|||
getLocation: [ |
|||
['/platforms/h5/service/api/network/request.js', 'request'] |
|||
], |
|||
previewImage: [ |
|||
['/core/view/components/swiper/index.vue', 'Swiper'], |
|||
['/core/view/components/swiper-item/index.vue', 'SwiperItem'], |
|||
['/core/view/components/movable-area/index.vue', 'MovableArea'], |
|||
['/core/view/components/movable-view/index.vue', 'MovableView'], |
|||
[ |
|||
'/platforms/h5/components/app/popup/preview-image/index.vue', |
|||
'PreviewImage' |
|||
], |
|||
[ |
|||
'/platforms/h5/components/app/popup/mixins/preview-image.js', |
|||
'PreviewImageMixin' |
|||
] |
|||
], |
|||
showToast: TOAST_DEPS, |
|||
hideToast: TOAST_DEPS, |
|||
showLoading: TOAST_DEPS, |
|||
hideLoading: TOAST_DEPS, |
|||
showModal: [ |
|||
['/platforms/h5/components/app/popup/modal.vue', 'Modal'], |
|||
['/platforms/h5/components/app/popup/mixins/modal.js', 'ModalMixin'] |
|||
], |
|||
showActionSheet: [ |
|||
['/platforms/h5/components/app/popup/actionSheet.vue', 'ActionSheet'], |
|||
['/platforms/h5/components/app/popup/mixins/action-sheet.js', 'ActionSheetMixin'] |
|||
], |
|||
createSelectorQuery: [ |
|||
['/core/view/bridge/subscribe/api/request-component-info.js', 'requestComponentInfo'] |
|||
], |
|||
createIntersectionObserver: [ |
|||
['/core/view/bridge/subscribe/api/request-component-observer.js', 'requestComponentObserver'], |
|||
['/core/view/bridge/subscribe/api/request-component-observer.js', 'destroyComponentObserver'] |
|||
], |
|||
createMediaQueryObserver: [ |
|||
['/core/view/bridge/subscribe/api/request-media-query-observer.js', 'requestMediaQueryObserver'], |
|||
['/core/view/bridge/subscribe/api/request-media-query-observer.js', 'destroyMediaQueryObserver'] |
|||
] |
|||
} |
|||
|
|||
// 检查依赖文件是否存在
|
|||
Object.keys(DEPS).reduce(function (depFiles, name) { |
|||
DEPS[name].forEach(function (dep) { |
|||
depFiles.add(dep[0]) |
|||
}) |
|||
return depFiles |
|||
}, new Set()).forEach(file => { |
|||
if (!fs.existsSync(path.join(__dirname, '../src', file))) { |
|||
console.error(file + ' 不存在') |
|||
process.exit(0) |
|||
} |
|||
}) |
|||
|
|||
function parseApiManifestDeps (manifest, protocol) { |
|||
// 解析 platform 依赖
|
|||
Object.keys(manifest).forEach(name => { |
|||
const deps = manifest[name][1] |
|||
if (deps.length) { |
|||
deps.forEach(dep => { |
|||
if (manifest[dep[1]]) { |
|||
dep[0] = manifest[dep[1]][0] |
|||
} else { |
|||
console.error(`依赖模块[${dep[1]}]不存在,删除 ${name} 接口\n`) |
|||
delete manifest[name] |
|||
} |
|||
}) |
|||
} |
|||
}) |
|||
// 解析 protocol 依赖
|
|||
Object.keys(manifest).forEach(name => { |
|||
const deps = manifest[name][1] |
|||
if (protocol[name]) { |
|||
deps.push([protocol[name], name]) |
|||
} else { |
|||
console.warn(`${name} 缺少 protocol`) |
|||
} |
|||
}) |
|||
// 追加默认依赖
|
|||
Object.keys(DEPS).forEach(name => { |
|||
if (manifest[name]) { |
|||
manifest[name][1].push(...DEPS[name]) |
|||
} else { |
|||
console.error(`缺少 ${name}`) |
|||
} |
|||
}) |
|||
// 设置自动加载标记
|
|||
AUTO_LOADS.forEach(name => { |
|||
if (manifest[name]) { |
|||
manifest[name][2] = true |
|||
} else { |
|||
console.error(`缺少 ${name}`) |
|||
} |
|||
}) |
|||
} |
|||
|
|||
module.exports = { |
|||
generateApiManifest (manifest, protocol) { |
|||
if (!Object.keys(manifest).length) { |
|||
throw new Error('api manifest.json 生成失败') |
|||
} |
|||
parseApiManifestDeps(manifest, protocol) |
|||
|
|||
const manifestJson = Object.create(null) |
|||
const todoApis = [] |
|||
apis.forEach(name => { |
|||
if (manifest[name]) { |
|||
manifestJson[name] = manifest[name] |
|||
} else { |
|||
todoApis.push(name) |
|||
} |
|||
}) |
|||
|
|||
if (todoApis.length) { |
|||
console.log('\n') |
|||
console.warn(`${process.env.UNI_PLATFORM} 平台缺少以下 API 实现(共 ${todoApis.length} 个)`) |
|||
todoApis.forEach(name => { |
|||
console.warn(name) |
|||
}) |
|||
} |
|||
|
|||
fs.writeFileSync(path.resolve(__dirname, '../packages/uni-' + process.env.UNI_PLATFORM + '/manifest.json'), |
|||
JSON.stringify(manifestJson, null, 4) |
|||
) |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
const mediaQuerys = [] |
|||
|
|||
module.exports = { |
|||
splitMediaPlugin: function (root, result) { |
|||
root.walkAtRules(rule => { |
|||
if (rule.params.indexOf('prefers-color-scheme') !== -1) { |
|||
root.removeChild(rule) |
|||
|
|||
mediaQuerys.push(rule) |
|||
} |
|||
}) |
|||
}, |
|||
generateMediaQuerys: function ({ outputDir, filename = 'index.dark.css' }) { |
|||
if (mediaQuerys.length) { |
|||
const fs = require('fs') |
|||
const path = require('path') |
|||
const postcss = require('postcss') |
|||
const uglifycss = require('uglifycss') |
|||
|
|||
const mediaRoot = postcss.root() |
|||
mediaRoot.append(mediaQuerys.sort((a, b) => a.source.input.file > b.source.input.file ? 1 : -1)) |
|||
|
|||
fs.writeFileSync( |
|||
path.resolve(outputDir, filename), |
|||
uglifycss.processString(mediaRoot.toResult().css), |
|||
{ encoding: 'utf-8', flag: 'w+' } |
|||
) |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,107 @@ |
|||
const path = require('path') |
|||
const json = require('@rollup/plugin-json') |
|||
const alias = require('@rollup/plugin-alias') |
|||
const replace = require('@rollup/plugin-replace') |
|||
const nodeResolve = require('@rollup/plugin-node-resolve') |
|||
const commonjs = require('@rollup/plugin-commonjs') |
|||
const requireContext = require('../lib/rollup-plugin-require-context') |
|||
|
|||
let input = 'src/platforms/app-plus/service/framework/create-instance-context.js' |
|||
|
|||
const output = { |
|||
file: 'packages/uni-app-plus-nvue/dist/index.js', |
|||
format: 'es' |
|||
} |
|||
|
|||
const external = [] |
|||
|
|||
// if (process.env.UNI_PLATFORM === 'app-plus-nvue') {
|
|||
// external.push('vue')
|
|||
// output.globals = {
|
|||
// vue: 'Vue'
|
|||
// }
|
|||
// }
|
|||
|
|||
if (process.env.UNI_SERVICE === 'legacy') { |
|||
input = 'src/platforms/app-plus-nvue/services/index.legacy.js' |
|||
output.file = 'packages/uni-app-plus-nvue/dist/index.legacy.js' |
|||
} else { |
|||
input = 'src/platforms/app-plus/service/index.js' |
|||
if (process.env.UNI_PLATFORM === 'app-plus') { |
|||
output.file = 'packages/uni-app-plus/dist/index.v3.js' |
|||
} else { |
|||
output.file = 'packages/uni-app-plus-nvue/dist/index.js' |
|||
} |
|||
output.format = 'iife' |
|||
output.name = 'serviceContext' |
|||
output.banner = |
|||
`export function createServiceContext(Vue, weex, plus, UniServiceJSBridge,instanceContext){
|
|||
var setTimeout = instanceContext.setTimeout |
|||
var clearTimeout = instanceContext.clearTimeout |
|||
var setInterval = instanceContext.setInterval |
|||
var clearInterval = instanceContext.clearInterval |
|||
var __uniConfig = instanceContext.__uniConfig |
|||
var __uniRoutes = instanceContext.__uniRoutes |
|||
` |
|||
output.footer = |
|||
` |
|||
var uni = serviceContext.uni |
|||
var getApp = serviceContext.getApp |
|||
var getCurrentPages = serviceContext.getCurrentPages |
|||
|
|||
var __definePage = serviceContext.__definePage |
|||
var __registerPage = serviceContext.__registerPage |
|||
|
|||
|
|||
return serviceContext \n} |
|||
` |
|||
} |
|||
|
|||
const resolve = dir => path.resolve(__dirname, '../', dir) |
|||
|
|||
module.exports = { |
|||
input, |
|||
output, |
|||
plugins: [ |
|||
alias({ |
|||
entries: [{ |
|||
find: '@dcloudio', |
|||
replacement: resolve('packages') |
|||
}, { |
|||
find: 'uni-core', |
|||
replacement: resolve('src/core') |
|||
}, { |
|||
find: 'uni-platform', |
|||
replacement: resolve('src/platforms/' + process.env.UNI_PLATFORM) |
|||
}, { |
|||
find: 'uni-platforms', |
|||
replacement: resolve('src/platforms') |
|||
}, { |
|||
find: 'uni-shared', |
|||
replacement: resolve('src/shared/index.js') |
|||
}, { |
|||
find: 'uni-helpers', |
|||
replacement: resolve('src/core/helpers') |
|||
}, { |
|||
find: 'uni-invoke-api', |
|||
replacement: resolve('src/platforms/app-plus/service/api') |
|||
}, { |
|||
find: 'uni-service-api', |
|||
replacement: resolve('src/core/service/platform-api') |
|||
}, { |
|||
find: 'uni-api-protocol', |
|||
replacement: resolve('src/core/helpers/protocol') |
|||
}] |
|||
}), |
|||
json(), |
|||
nodeResolve(), |
|||
requireContext(), |
|||
commonjs(), |
|||
replace({ |
|||
__GLOBAL__: 'getGlobalUni()', |
|||
__PLATFORM__: JSON.stringify(process.env.UNI_PLATFORM), |
|||
__PLATFORM_TITLE__: 'app-plus-nvue' |
|||
}) |
|||
], |
|||
external |
|||
} |
|||
@ -0,0 +1,106 @@ |
|||
const path = require('path') |
|||
const json = require('@rollup/plugin-json') |
|||
const alias = require('@rollup/plugin-alias') |
|||
const replace = require('@rollup/plugin-replace') |
|||
|
|||
const PLATFORMS = { |
|||
'mp-weixin': { |
|||
prefix: 'wx', |
|||
title: '微信小程序' |
|||
}, |
|||
'mp-qq': { |
|||
prefix: 'wx', |
|||
title: 'QQ小程序' |
|||
}, |
|||
'mp-alipay': { |
|||
prefix: 'my', |
|||
title: '支付宝小程序' |
|||
}, |
|||
'mp-baidu': { |
|||
prefix: 'swan', |
|||
title: '百度小程序' |
|||
}, |
|||
'mp-toutiao': { |
|||
prefix: 'tt', |
|||
title: '头条小程序' |
|||
}, |
|||
'mp-kuaishou': { |
|||
prefix: 'ks', |
|||
title: '快手小程序' |
|||
}, |
|||
'mp-lark': { |
|||
prefix: 'tt', |
|||
title: '飞书小程序' |
|||
}, |
|||
'mp-jd': { |
|||
prefix: 'jd', |
|||
title: '京东小程序' |
|||
}, |
|||
'mp-xhs': { |
|||
prefix: 'xhs', |
|||
title: '小红书小程序' |
|||
}, |
|||
'quickapp-webview': { |
|||
prefix: 'qa', |
|||
title: '快应用(Webview)版' |
|||
}, |
|||
'app-plus': { |
|||
prefix: 'wx', |
|||
title: 'app-plus' |
|||
} |
|||
} |
|||
|
|||
const platform = PLATFORMS[process.env.UNI_PLATFORM] |
|||
|
|||
let input = 'src/core/runtime/index.js' |
|||
const output = { |
|||
file: `packages/uni-${process.env.UNI_PLATFORM}/dist/index.js`, |
|||
format: 'es' |
|||
} |
|||
|
|||
if (process.env.UNI_MP) { |
|||
input = 'src/core/runtime/mp/index.js' |
|||
output.file = `packages/uni-${process.env.UNI_PLATFORM}/dist/mp.js` |
|||
} |
|||
|
|||
module.exports = { |
|||
input, |
|||
output, |
|||
plugins: [ |
|||
alias({ |
|||
entries: [{ |
|||
find: '@dcloudio', |
|||
replacement: path.resolve(__dirname, '../packages') |
|||
}, { |
|||
find: 'uni-core', |
|||
replacement: path.resolve(__dirname, '../src/core') |
|||
}, { |
|||
find: 'uni-api-protocol', |
|||
replacement: path.resolve(__dirname, '../src/core/helpers/protocol') |
|||
}, { |
|||
find: 'uni-shared/query', |
|||
replacement: path.resolve(__dirname, '../src/shared/query.js') |
|||
}, { |
|||
find: 'uni-shared', |
|||
replacement: path.resolve(__dirname, '../src/shared/util.js') |
|||
}, { |
|||
find: 'uni-platform', |
|||
replacement: path.resolve(__dirname, '../src/platforms/' + process.env.UNI_PLATFORM) |
|||
}, { |
|||
find: 'uni-wrapper', |
|||
replacement: path.resolve(__dirname, '../src/core/runtime/wrapper') |
|||
}, { |
|||
find: 'uni-helpers', |
|||
replacement: path.resolve(__dirname, '../src/core/helpers') |
|||
}] |
|||
}), |
|||
json(), |
|||
replace({ |
|||
__GLOBAL__: platform.prefix, |
|||
__PLATFORM_TITLE__: platform.title, |
|||
__PLATFORM_PREFIX__: JSON.stringify(platform.prefix), |
|||
__PLATFORM__: JSON.stringify(process.env.UNI_PLATFORM) |
|||
}) |
|||
], |
|||
external: ['vue', '@dcloudio/uni-i18n'] |
|||
} |
|||
@ -0,0 +1,94 @@ |
|||
const path = require('path') |
|||
const json = require('@rollup/plugin-json') |
|||
const alias = require('@rollup/plugin-alias') |
|||
const replace = require('@rollup/plugin-replace') |
|||
const nodeResolve = require('@rollup/plugin-node-resolve') |
|||
const commonjs = require('@rollup/plugin-commonjs') |
|||
const terser = require('rollup-plugin-terser') |
|||
const requireContext = require('../lib/rollup-plugin-require-context') |
|||
|
|||
process.env.UNI_PLATFORM = 'quickapp-native' |
|||
|
|||
const external = [] |
|||
|
|||
const resolve = dir => path.resolve(__dirname, '../', dir) |
|||
|
|||
function replaceModuleImport (str) { |
|||
return str.replace( |
|||
/require\s*\(\s*(['"])@([\w$_][\w$-.]*?)\1\)/gm, |
|||
(e, r, p) => `$app_require$(${r}@app-module/${p}${r})` |
|||
).replace( |
|||
/import\s+([\w${}]+?)\s+from\s+(['"])@([\w$_][\w$-.]*?)\2/gm, |
|||
(e, r, p, t) => `var ${r} = $app_require$(${p}@app-module/${t}${p})` |
|||
) |
|||
} |
|||
|
|||
const plugins = [{ |
|||
name: 'replaceModuleImport', |
|||
transform (source) { |
|||
return { |
|||
code: replaceModuleImport(source) |
|||
} |
|||
} |
|||
}, |
|||
alias({ |
|||
entries: [{ |
|||
find: '@dcloudio', |
|||
replacement: resolve('packages') |
|||
}, { |
|||
find: 'uni-core', |
|||
replacement: resolve('src/core') |
|||
}, { |
|||
find: 'uni-platform', |
|||
replacement: resolve('src/platforms/quickapp-native') |
|||
}, { |
|||
find: 'uni-platforms', |
|||
replacement: resolve('src/platforms') |
|||
}, { |
|||
find: 'uni-shared', |
|||
replacement: resolve('src/shared/index.js') |
|||
}, { |
|||
find: 'uni-helpers', |
|||
replacement: resolve('src/core/helpers') |
|||
}, { |
|||
find: 'uni-invoke-api', |
|||
replacement: resolve('src/platforms/quickapp-native/service/invoke-api') |
|||
}, { |
|||
find: 'uni-service-api', |
|||
replacement: resolve('src/platforms/quickapp-native/service/api') |
|||
}, { |
|||
find: 'uni-api-protocol', |
|||
replacement: resolve('src/core/helpers/protocol') |
|||
}] |
|||
}), |
|||
json(), |
|||
nodeResolve(), |
|||
requireContext(), |
|||
commonjs(), |
|||
replace({ |
|||
__PLATFORM__: JSON.stringify(process.env.UNI_PLATFORM), |
|||
__PLATFORM_TITLE__: '快应用(Native)版' |
|||
}) |
|||
] |
|||
|
|||
// if (process.env.NODE_ENV === 'production') {
|
|||
plugins.push(terser.terser()) |
|||
// }
|
|||
|
|||
module.exports = function (type) { |
|||
let input = '' |
|||
|
|||
if (type === 'bridge') { |
|||
input = 'src/platforms/quickapp-native/runtime/bridge.js' |
|||
} else if (type === 'app') { |
|||
input = 'src/platforms/quickapp-native/runtime/app.js' |
|||
} else if (type === 'page') { |
|||
input = 'src/platforms/quickapp-native/runtime/page.js' |
|||
} |
|||
|
|||
return { |
|||
input, |
|||
plugins, |
|||
external |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
const path = require('path') |
|||
const json = require('@rollup/plugin-json') |
|||
const alias = require('@rollup/plugin-alias') |
|||
const replace = require('@rollup/plugin-replace') |
|||
|
|||
module.exports = { |
|||
input: 'src/platforms/app-plus-nvue/services/index.legacy.old.js', |
|||
output: { |
|||
file: 'packages/uni-app-plus-nvue/dist/service.legacy.js', |
|||
format: 'es' |
|||
}, |
|||
plugins: [ |
|||
alias({ |
|||
entries: [{ |
|||
find: '@dcloudio', |
|||
replacement: path.resolve(__dirname, '../packages') |
|||
}, |
|||
{ |
|||
find: 'uni-core', |
|||
replacement: path.resolve(__dirname, '../src/core') |
|||
}, |
|||
{ |
|||
find: 'uni-shared', |
|||
replacement: path.resolve(__dirname, '../src/shared/util.js') |
|||
}, |
|||
{ |
|||
find: 'uni-helpers', |
|||
replacement: path.resolve(__dirname, '../src/core/helpers') |
|||
} |
|||
] |
|||
}), |
|||
json(), |
|||
replace({ |
|||
__GLOBAL__: 'getGlobalUni()', |
|||
__PLATFORM_TITLE__: 'app-plus-nvue' |
|||
}) |
|||
] |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
module.exports = [ |
|||
{ |
|||
input: 'packages/uni-stat/src/index.js', |
|||
output: { |
|||
file: 'packages/uni-stat/dist/index.js', |
|||
format: 'es' |
|||
}, |
|||
external: ['vue', '../package.json'], |
|||
plugins: [] |
|||
}, |
|||
{ |
|||
input: 'packages/uni-cloud-stat/src/index.js', |
|||
output: { |
|||
file: 'packages/uni-cloud-stat/dist/index.js', |
|||
format: 'es' |
|||
}, |
|||
external: ['vue', '../package.json'], |
|||
plugins: [] |
|||
} |
|||
] |
|||
@ -0,0 +1,25 @@ |
|||
const path = require('path') |
|||
const babel = require('rollup-plugin-babel') |
|||
const alias = require('@rollup/plugin-alias') |
|||
const terser = require('rollup-plugin-terser') |
|||
module.exports = { |
|||
input: 'src/core/runtime/web-view/index.js', |
|||
output: { |
|||
name: 'uni', |
|||
file: 'dist/uni.webview.1.5.5.js', |
|||
format: 'umd' |
|||
}, |
|||
plugins: [ |
|||
alias({ |
|||
entries: [{ |
|||
find: 'uni-shared', |
|||
replacement: path.resolve(__dirname, '../src/shared/index.js') |
|||
}, { |
|||
find: 'uni-platforms', |
|||
replacement: path.resolve(__dirname, '../src/platforms') |
|||
}] |
|||
}), |
|||
babel(), |
|||
terser.terser() |
|||
] |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
module.exports = { |
|||
input: 'src/core/runtime/mp/wxs.js', |
|||
output: { |
|||
file: 'packages/uni-mp-weixin/dist/wxs.js', |
|||
format: 'es' |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
const path = require('path') |
|||
|
|||
const resolve = dir => path.resolve(__dirname, '../', dir) |
|||
|
|||
const pkgPath = resolve('package.json') |
|||
|
|||
const { splitMediaPlugin, generateMediaQuerys } = require('./postcssSplitMediaPlugin') |
|||
const webpack = require('webpack') |
|||
|
|||
const webpackConfig = require('./webpack.config.js') |
|||
const postCssConfig = require('../postcss.config') |
|||
|
|||
let outputDir = resolve('./packages/uni-' + process.env.UNI_PLATFORM + '/dist') |
|||
|
|||
if (process.env.UNI_PLATFORM === 'h5' && process.env.UNI_UI === 'true') { |
|||
outputDir = resolve('./packages/uni-' + process.env.UNI_PLATFORM + '-ui/dist') |
|||
} |
|||
|
|||
if (process.env.UNI_PLATFORM === 'app-plus' && process.env.UNI_VIEW === 'true') { |
|||
outputDir = resolve('./packages/uni-' + process.env.UNI_PLATFORM + '/dist') |
|||
} |
|||
|
|||
if (process.env.UNI_PLATFORM === 'h5') { postCssConfig.plugins.push(splitMediaPlugin) } |
|||
|
|||
module.exports = { |
|||
publicPath: '/', |
|||
outputDir, |
|||
lintOnSave: true, // or error
|
|||
runtimeCompiler: false, |
|||
transpileDependencies: ['@dcloudio/uni-i18n'], |
|||
productionSourceMap: false, |
|||
configureWebpack: webpackConfig, |
|||
parallel: process.env.UNI_PLATFORM !== 'h5' || process.env.UNI_WATCH !== 'false' || process.env.UNI_UI === 'true', |
|||
chainWebpack: config => { |
|||
config.devtool('source-map') |
|||
|
|||
config.module |
|||
.rule('eslint') |
|||
.include |
|||
.add(resolve('src')) |
|||
.add(resolve('lib/' + process.env.UNI_PLATFORM)) |
|||
.end() |
|||
.use('eslint-loader') |
|||
.loader(resolve('node_modules/eslint-loader')) |
|||
.options({ |
|||
fix: true, |
|||
configFile: pkgPath |
|||
}) |
|||
config.plugins.delete('hmr') // remove hot module reload
|
|||
|
|||
if (process.env.UNI_PLATFORM === 'h5') { |
|||
config |
|||
.plugin('webpack-build-done') |
|||
.use(webpack.ProgressPlugin, [function (percentage, message, ...args) { |
|||
if (percentage === 1) { |
|||
generateMediaQuerys({ |
|||
outputDir |
|||
}) |
|||
} |
|||
}]) |
|||
} |
|||
}, |
|||
css: { |
|||
extract: true, |
|||
loaderOptions: { |
|||
postcss: postCssConfig |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,80 @@ |
|||
const path = require('path') |
|||
const webpack = require('webpack') |
|||
|
|||
const resolve = dir => path.resolve(__dirname, '../', dir) |
|||
|
|||
const pkg = require('../package.json') |
|||
|
|||
const externals = {} |
|||
|
|||
if (process.env.UNI_VIEW !== 'true') { |
|||
externals.vue = { |
|||
commonjs: 'vue', |
|||
commonjs2: 'vue', |
|||
root: 'Vue' |
|||
} |
|||
externals['vue-router'] = { |
|||
commonjs: 'vue-router', |
|||
commonjs2: 'vue-router', |
|||
root: 'VueRouter' |
|||
} |
|||
externals['@dcloudio/uni-i18n'] = { |
|||
commonjs: '@dcloudio/uni-i18n', |
|||
commonjs2: '@dcloudio/uni-i18n', |
|||
root: '@dcloudio/uni-i18n' |
|||
} |
|||
} |
|||
|
|||
const alias = { |
|||
'@dcloudio': resolve('packages'), |
|||
'uni-core': resolve('src/core'), |
|||
'uni-view': resolve('src/core/view'), |
|||
'uni-service': resolve('src/core/service'), |
|||
'uni-shared': resolve('src/shared'), |
|||
'uni-mixins': resolve('src/core/view/mixins'), |
|||
'uni-helpers': resolve('src/core/helpers'), |
|||
'uni-platform': resolve('src/platforms/' + process.env.UNI_PLATFORM), |
|||
// tree shaking
|
|||
'uni-components': resolve('src/core/view/components'), |
|||
'uni-invoke-api': resolve('src/platforms/' + process.env.UNI_PLATFORM + '/service/api'), |
|||
'uni-service-api': resolve('src/core/service/platform-api'), |
|||
'uni-api-protocol': resolve('src/core/helpers/protocol'), |
|||
'uni-api-subscribe': resolve('src/core/view/bridge/subscribe/api/index'), |
|||
// h5 components
|
|||
'uni-h5-app-components': resolve('src/platforms/h5/components/app/popup/index'), |
|||
'uni-h5-app-mixins': resolve('src/platforms/h5/components/app/popup/mixins/index'), |
|||
'uni-h5-system-routes': resolve('src/platforms/h5/components/system-routes/index') |
|||
} |
|||
|
|||
const provides = { |
|||
console: [resolve('src/core/helpers/console'), 'default'], |
|||
UniViewJSBridge: [resolve('src/core/view/bridge/index')], |
|||
UniServiceJSBridge: [resolve('src/core/service/bridge/index')] |
|||
} |
|||
if (process.env.UNI_VIEW) { // 方便调试
|
|||
delete provides.console |
|||
} |
|||
|
|||
if (process.env.UNI_VIEW === 'true') { |
|||
alias.vue$ = resolve('packages/vue-cli-plugin-uni/packages/h5-vue/dist/vue.runtime.esm.js') |
|||
} |
|||
|
|||
module.exports = { |
|||
mode: 'production', |
|||
devtool: false, |
|||
externals, |
|||
resolve: { |
|||
alias |
|||
}, |
|||
module: { |
|||
rules: [] |
|||
}, |
|||
plugins: [ |
|||
new webpack.DefinePlugin({ |
|||
__VERSION__: JSON.stringify(pkg.version), |
|||
__PLATFORM__: JSON.stringify(process.env.UNI_PLATFORM), |
|||
__VIEW__: JSON.stringify(!!process.env.UNI_VIEW) |
|||
}), |
|||
new webpack.ProvidePlugin(provides) |
|||
] |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
const path = require('path') |
|||
const webpack = require('webpack') |
|||
|
|||
const resolve = dir => path.resolve(__dirname, '../', dir) |
|||
|
|||
const pkg = require('../package.json') |
|||
|
|||
let service = process.VUE_CLI_SERVICE |
|||
if (!service || process.env.VUE_CLI_API_MODE) { |
|||
const Service = require('@vue/cli-service') |
|||
service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd()) |
|||
service.init(process.env.VUE_CLI_MODE || process.env.NODE_ENV) |
|||
} |
|||
|
|||
const config = service.resolveWebpackConfig() |
|||
|
|||
config.resolve.alias = { |
|||
'@': resolve('src'), |
|||
'@dcloudio': resolve('packages'), |
|||
'uni-core': resolve('src/core'), |
|||
'uni-view': resolve('src/core/view'), |
|||
'uni-service': resolve('src/core/service'), |
|||
'uni-shared': resolve('src/shared'), |
|||
'uni-mixins': resolve('src/core/view/mixins'), |
|||
'uni-helpers': resolve('src/core/helpers'), |
|||
'uni-platform': resolve('src/platforms/' + process.env.UNI_PLATFORM), |
|||
// tree shaking
|
|||
'uni-components': resolve('src/core/view/components'), |
|||
'uni-invoke-api': resolve('src/platforms/' + process.env.UNI_PLATFORM + '/service/api'), |
|||
'uni-service-api': resolve('src/core/service/platform-api'), |
|||
'uni-api-protocol': resolve('src/core/helpers/protocol'), |
|||
'uni-api-subscribe': resolve('src/core/view/bridge/subscribe/api/index'), |
|||
// h5 components
|
|||
'uni-h5-app-components': resolve('src/platforms/h5/components/app/popup/index'), |
|||
'uni-h5-app-mixins': resolve('src/platforms/h5/components/app/popup/mixins/index'), |
|||
'uni-h5-system-routes': resolve('src/platforms/h5/components/system-routes/index') |
|||
} |
|||
|
|||
const isEslintLoader = config.module.rules[config.module.rules.length - 1].enforce |
|||
|
|||
if (isEslintLoader) { |
|||
config.module.rules.splice(config.module.rules.length - 1, 1) |
|||
} else { |
|||
throw new Error('eslint-loader is undefined') |
|||
} |
|||
|
|||
config.plugins = config.plugins.concat([ |
|||
new webpack.DefinePlugin({ |
|||
__VERSION__: JSON.stringify(pkg.version), |
|||
__PLATFORM__: JSON.stringify(process.env.UNI_PLATFORM) |
|||
}), |
|||
new webpack.ProvidePlugin({ |
|||
console: [resolve('src/core/helpers/console'), 'default'], |
|||
UniViewJSBridge: [resolve('src/core/view/bridge/index')], |
|||
UniServiceJSBridge: [resolve('src/core/service/bridge/index')] |
|||
}) |
|||
]) |
|||
module.exports = config |
|||
@ -0,0 +1,21 @@ |
|||
const fs = require('fs') |
|||
const path = require('path') |
|||
|
|||
const request = require('request') |
|||
|
|||
const registry = 'https://registry.npmjs.org/@dcloudio/' |
|||
|
|||
const pkgs = fs.readdirSync(path.resolve(__dirname, 'packages')).filter(pkg => pkg.indexOf('.') !== 0) |
|||
|
|||
const tag = process.argv[2] || 'alpha' |
|||
|
|||
pkgs.forEach(pkg => { |
|||
request(registry + pkg, function(error, response, body) { |
|||
if (error) { |
|||
console.log(pkg, error) |
|||
} else { |
|||
const version = JSON.parse(body)['dist-tags'][tag] |
|||
console.log(pkg + ':' + (' '.repeat(80 - (pkg + ':' + version).length)) + version) |
|||
} |
|||
}) |
|||
}) |
|||
@ -0,0 +1,17 @@ |
|||
const fs = require('fs') |
|||
const path = require('path') |
|||
const shellExec = require('shell-exec') |
|||
|
|||
const pkgs = fs.readdirSync(path.resolve(__dirname, 'packages')).filter(pkg => pkg.indexOf('.') !== 0) |
|||
|
|||
const version = process.argv[2] |
|||
if (!version) { |
|||
throw new Error('必须传入 version') |
|||
} |
|||
|
|||
(async function() { |
|||
for (let i = 0; i < pkgs.length; i++) { |
|||
console.log(`npm dist-tag add @dcloudio/${pkgs[i]}@${version} latest`); |
|||
await shellExec(`npm dist-tag add @dcloudio/${pkgs[i]}@${version} latest`) |
|||
} |
|||
})(); |
|||
@ -0,0 +1 @@ |
|||
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e=e||self).uni=n()}(this,(function(){"use strict";try{var e={};Object.defineProperty(e,"passive",{get:function(){!0}}),window.addEventListener("test-passive",null,e)}catch(e){}var n=Object.prototype.hasOwnProperty;function t(e,t){return n.call(e,t)}var i=[],a=function(e,n){var t={options:{timestamp:+new Date},name:e,arg:n};if(window.__dcloud_weex_postMessage||window.__dcloud_weex_){if("postMessage"===e){var a={data:[n]};return window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessage(a):window.__dcloud_weex_.postMessage(JSON.stringify(a))}var o={type:"WEB_INVOKE_APPSERVICE",args:{data:t,webviewIds:i}};window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessageToService(o):window.__dcloud_weex_.postMessageToService(JSON.stringify(o))}if(!window.plus)return window.parent.postMessage({type:"WEB_INVOKE_APPSERVICE",data:t,pageId:""},"*");if(0===i.length){var r=plus.webview.currentWebview();if(!r)throw new Error("plus.webview.currentWebview() is undefined");var d=r.parent(),s="";s=d?d.id:r.id,i.push(s)}if(plus.webview.getWebviewById("__uniapp__service"))plus.webview.postMessageToUniNView({type:"WEB_INVOKE_APPSERVICE",args:{data:t,webviewIds:i}},"__uniapp__service");else{var w=JSON.stringify(t);plus.webview.getLaunchWebview().evalJS('UniPlusBridge.subscribeHandler("'.concat("WEB_INVOKE_APPSERVICE",'",').concat(w,",").concat(JSON.stringify(i),");"))}},o={navigateTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("navigateTo",{url:encodeURI(n)})},navigateBack:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.delta;a("navigateBack",{delta:parseInt(n)||1})},switchTab:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("switchTab",{url:encodeURI(n)})},reLaunch:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("reLaunch",{url:encodeURI(n)})},redirectTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("redirectTo",{url:encodeURI(n)})},getEnv:function(e){window.plus?e({plus:!0}):e({h5:!0})},postMessage:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};a("postMessage",e.data||{})}},r=/uni-app/i.test(navigator.userAgent),d=/Html5Plus/i.test(navigator.userAgent),s=/complete|loaded|interactive/;var w=window.my&&navigator.userAgent.indexOf("AlipayClient")>-1;var u=window.swan&&window.swan.webView&&/swan/i.test(navigator.userAgent);var c=window.qq&&window.qq.miniProgram&&/QQ/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var g=window.tt&&window.tt.miniProgram&&/toutiaomicroapp/i.test(navigator.userAgent);var v=window.wx&&window.wx.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var p=window.qa&&/quickapp/i.test(navigator.userAgent);for(var l,_=function(){window.UniAppJSBridge=!0,document.dispatchEvent(new CustomEvent("UniAppJSBridgeReady",{bubbles:!0,cancelable:!0}))},f=[function(e){if(r||d)return window.__dcloud_weex_postMessage||window.__dcloud_weex_?document.addEventListener("DOMContentLoaded",e):window.plus&&s.test(document.readyState)?setTimeout(e,0):document.addEventListener("plusready",e),o},function(e){if(v)return window.WeixinJSBridge&&window.WeixinJSBridge.invoke?setTimeout(e,0):document.addEventListener("WeixinJSBridgeReady",e),window.wx.miniProgram},function(e){if(c)return window.QQJSBridge&&window.QQJSBridge.invoke?setTimeout(e,0):document.addEventListener("QQJSBridgeReady",e),window.qq.miniProgram},function(e){if(w){document.addEventListener("DOMContentLoaded",e);var n=window.my;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){if(u)return document.addEventListener("DOMContentLoaded",e),window.swan.webView},function(e){if(g)return document.addEventListener("DOMContentLoaded",e),window.tt.miniProgram},function(e){if(p){window.QaJSBridge&&window.QaJSBridge.invoke?setTimeout(e,0):document.addEventListener("QaJSBridgeReady",e);var n=window.qa;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){return document.addEventListener("DOMContentLoaded",e),o}],m=0;m<f.length&&!(l=f[m](_));m++);l||(l={});var E="undefined"!=typeof uni?uni:{};if(!E.navigateTo)for(var b in l)t(l,b)&&(E[b]=l[b]);return E.webView=l,E})); |
|||
@ -0,0 +1,2 @@ |
|||
* [简体中文](https://github.com/dcloudio/unidocs-zh) |
|||
* [English](https://github.com/dcloudio/unidocs-en) |
|||
@ -0,0 +1 @@ |
|||
代码已迁移,请移步[https://github.com/dcloudio/hello-uniapp](https://github.com/dcloudio/hello-uniapp)查看最新代码 |
|||
@ -0,0 +1 @@ |
|||
代码已迁移,请移步[https://github.com/dcloudio/uni-template-picture](https://github.com/dcloudio/uni-template-picture)查看最新代码 |
|||
@ -0,0 +1 @@ |
|||
代码已迁移,请移步[https://github.com/dcloudio/uni-template-login](https://github.com/dcloudio/uni-template-login)查看最新代码 |
|||
@ -0,0 +1,75 @@ |
|||
<script> |
|||
import { mapActions, mapMutations } from 'vuex' |
|||
export default { |
|||
created () { |
|||
this.initPage() |
|||
}, |
|||
methods: { |
|||
...mapMutations('weather', { |
|||
setLocation: 'SET_LOCATION' |
|||
}), |
|||
...mapActions('weather', ['getWeather']), |
|||
async initPage() { |
|||
const location = await this.getLocation() |
|||
this.setLocation({ |
|||
location |
|||
}) |
|||
this.getWeather() |
|||
}, |
|||
async getLocation() { |
|||
return await new Promise((resolve, reject) => { |
|||
wx.getLocation({ |
|||
success(location) { |
|||
resolve(location) |
|||
}, |
|||
fail(err) { |
|||
console.log(err) |
|||
reject(err) |
|||
} |
|||
}) |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.container { |
|||
font-size: 26rpx; |
|||
text-align: center; |
|||
} |
|||
|
|||
.wrapper { |
|||
font-size: 26rpx; |
|||
text-align: left; |
|||
} |
|||
|
|||
* { |
|||
transition: width 2s; |
|||
-moz-transition: width 2s; |
|||
-webkit-transition: width 2s; |
|||
-o-transition: width 2s; |
|||
} |
|||
|
|||
/* font color */ |
|||
* { |
|||
color: #353535; |
|||
} |
|||
.gray { |
|||
color: #808080; |
|||
} |
|||
|
|||
.wxParse { |
|||
text-indent: 2em; |
|||
} |
|||
.wxParse image { |
|||
text-indent: 0; |
|||
} |
|||
.wxParse ._view { |
|||
padding: 0 10rpx; |
|||
text-indent: 0; |
|||
} |
|||
.wxParse .p { |
|||
text-indent: 1em; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,11 @@ |
|||
# uni-one |
|||
|
|||
> 使用uni-app开发的 [「ONE · 一个」](http://wufazhuce.com) |
|||
> 移植自[mpvue-one](https://github.com/feng-fu/mpvue-one/blob/master/README.md) |
|||
> 本项目仅作为mpvue项目移植示例 |
|||
|
|||
## 使用方式 |
|||
将项目拖入[HbuilderX](http://www.dcloud.io/hbuilderx.html),直接运行即可 |
|||
|
|||
## 注意事项 |
|||
* 在小程序中预览提示合法域名校验出错,需打开微信开发者工具内 设置-项目设置,勾选“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”。 |
|||
@ -0,0 +1,64 @@ |
|||
<template> |
|||
<block> |
|||
<view class="title"> |
|||
<text>{{title}}</text> |
|||
</view> |
|||
<view class="author" v-if="user_name"> |
|||
<text>文 / {{user_name}}</text> |
|||
</view> |
|||
<view class="summary" v-if="summary"> |
|||
<view class="line"></view> |
|||
<text>{{summary}}</text> |
|||
</view> |
|||
<wx-parse :content="content"></wx-parse> |
|||
</block> |
|||
</template> |
|||
|
|||
<script> |
|||
import wxParse from './mpvue-wxparse/wxParse.vue' |
|||
export default { |
|||
props: { |
|||
title: String, |
|||
user_name: String, |
|||
content: String, |
|||
summary: String |
|||
}, |
|||
components: { |
|||
wxParse |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
|
|||
<style scoped> |
|||
.title { |
|||
font-size: 36rpx; |
|||
font-weight: 700; |
|||
margin: .8em .8em; |
|||
text-align: center; |
|||
} |
|||
.author { |
|||
color: #cacaca; |
|||
font-size: 24rpx; |
|||
text-align: right; |
|||
padding-right: 2em; |
|||
} |
|||
.summary { |
|||
position: relative; |
|||
color: #b0b0b0; |
|||
font-size: 24rpx; |
|||
white-space: nowrap; |
|||
text-overflow: ellipse; |
|||
padding-left: 40rpx; |
|||
height: 60rpx; |
|||
line-height: 60rpx; |
|||
} |
|||
.summary .line { |
|||
position: absolute; |
|||
top: 14rpx; |
|||
left: 20rpx; |
|||
width: 10rpx; |
|||
height: 32rpx; |
|||
background-color: #d0d0d0; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,89 @@ |
|||
<template> |
|||
<block> |
|||
<view class="list-container" v-if="movie.subtitle"> |
|||
<view class="content"> |
|||
<view class="view-title title"> |
|||
<text>{{movie.title}}</text> |
|||
</view> |
|||
<view class="author"> |
|||
<!-- <text>{{movie.author_list[0].user_name}} —— </text> --> |
|||
<!-- <text>{{movie.author_list[0].desc}}</text> --> |
|||
<text>{{movie.subtitle}}</text> |
|||
</view> |
|||
</view> |
|||
<view> |
|||
<image class="avatar" :src="movie.img_url || 'https://petrify.oss-cn-beijing.aliyuncs.com/arrow-right.png'" /> |
|||
</view> |
|||
</view> |
|||
<block v-else> |
|||
<view class="only-title title"> |
|||
<text>{{movie.title}}</text> |
|||
<image class="arraw" src="https://petrify.oss-cn-beijing.aliyuncs.com/arrow-right.png" /> |
|||
</view> |
|||
</block> |
|||
</block> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
props: { |
|||
movie: Object |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.title { |
|||
font-size: 28rpx; |
|||
line-height: 40rpx; |
|||
margin-bottom: 10rpx; |
|||
font-weight: bold; |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
} |
|||
.only-title { |
|||
height: 100%; |
|||
line-height: 170rpx; |
|||
text-align: left; |
|||
padding-left: 20rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.only-title text { |
|||
width: 680rpx; |
|||
} |
|||
|
|||
.arraw { |
|||
width: 28rpx; |
|||
height: 28rpx; |
|||
} |
|||
|
|||
.view-title { |
|||
text-align: left; |
|||
} |
|||
|
|||
.avatar { |
|||
width: 100rpx; |
|||
height: 100rpx; |
|||
padding-right: 20rpx; |
|||
} |
|||
|
|||
.list-container { |
|||
display: flex; |
|||
align-items: center; |
|||
height: 100%; |
|||
padding-left: 20rpx; |
|||
} |
|||
|
|||
.content { |
|||
width: 600rpx; |
|||
} |
|||
|
|||
.author { |
|||
text-align: left; |
|||
color: #979794; |
|||
} |
|||
</style> |
|||
|
|||
@ -0,0 +1,97 @@ |
|||
<template> |
|||
<image |
|||
class="img" |
|||
:mode="node.image.mode" |
|||
:lazy-load="node.image.lazyLoad" |
|||
:class="node.classStr" |
|||
:style="fitStyleStr" |
|||
:data-src="node.attr.src" |
|||
:src="node.attr.src" |
|||
@tap="wxParseImgTap" |
|||
@load="wxParseImgLoad" |
|||
/> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'wxParseImg', |
|||
data() { |
|||
return { |
|||
realWindowWidth: 0, |
|||
realWindowHeight: 0, |
|||
newStyleStr: '', |
|||
}; |
|||
}, |
|||
props: { |
|||
node: { |
|||
type: Object, |
|||
default() { |
|||
return {}; |
|||
}, |
|||
}, |
|||
}, |
|||
mounted() { |
|||
this.getSysWH(); |
|||
}, |
|||
computed: { |
|||
fitStyleStr() { |
|||
return this.newStyleStr || this.node.styleStr; |
|||
}, |
|||
}, |
|||
methods: { |
|||
getSysWH() { |
|||
// 获取当前设备屏幕宽度和高度, 写在这里只是为了方便调试, 最好是写到 wxParse.vue 再传入 |
|||
wx.getSystemInfo({ |
|||
success: (res) => { |
|||
this.realWindowWidth = res.windowWidth; |
|||
this.realWindowHeight = res.windowHeight; |
|||
}, |
|||
}); |
|||
}, |
|||
wxParseImgTap(e) { |
|||
const { src } = e.target.dataset; |
|||
if (!src) return; |
|||
wx.previewImage({ |
|||
current: src, // 当前显示图片的http链接 |
|||
urls: this.node.image.urls, // 需要预览的图片http链接列表 |
|||
}); |
|||
}, |
|||
// 图片视觉宽高计算函数区 |
|||
wxParseImgLoad(e) { |
|||
const { src } = e.target.dataset; |
|||
if (!src) return; |
|||
if (typeof src !== 'undefined' && src !== '' && src !== null) { |
|||
this.calMoreImageInfo(e); |
|||
} |
|||
}, |
|||
// 假循环获取计算图片视觉最佳宽高 |
|||
calMoreImageInfo(e) { |
|||
const { width, height } = e.mp.detail; |
|||
const recal = this.wxAutoImageCal(width, height); |
|||
const { imageheight, imageWidth } = recal; |
|||
const { padding } = this.node.image; |
|||
this.newStyleStr = `height: ${imageheight}px; width: ${imageWidth}px; padding: 0 ${padding}px;`; |
|||
}, |
|||
// 计算视觉优先的图片宽高 |
|||
wxAutoImageCal(originalWidth, originalHeight) { |
|||
// 获取图片的原始长宽 |
|||
const { padding } = this.node.image; |
|||
const windowWidth = this.realWindowWidth - (2 * padding); |
|||
const results = {}; |
|||
|
|||
// 判断按照那种方式进行缩放 |
|||
if (originalWidth > windowWidth) { |
|||
// 在图片width大于手机屏幕width时候 |
|||
results.imageWidth = windowWidth; |
|||
results.imageheight = windowWidth * (originalHeight / originalWidth); |
|||
} else { |
|||
// 否则展示原来的数据 |
|||
results.imageWidth = originalWidth; |
|||
results.imageheight = originalHeight; |
|||
} |
|||
|
|||
return results; |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
@ -0,0 +1,101 @@ |
|||
<template> |
|||
<!--判断是否是标签节点--> |
|||
<block v-if="node.node == 'element'"> |
|||
<block v-if="node.tag == 'button'"> |
|||
<button type="default" size="mini"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</button> |
|||
</block> |
|||
|
|||
<!--li类型--> |
|||
<block v-else-if="node.tag == 'li'"> |
|||
<view :class="node.classStr" class="li" :style="node.styleStr"> |
|||
<view :class="node.classStr" class="li-inner"> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<view :class="node.classStr" class="li-circle"></view> |
|||
</view> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--video类型--> |
|||
<block v-else-if="node.tag == 'video'"> |
|||
<wx-parse-video :node="node" /> |
|||
</block> |
|||
|
|||
<!--img类型--> |
|||
<block v-else-if="node.tag == 'img'"> |
|||
<wx-parse-img :node="node" /> |
|||
</block> |
|||
|
|||
<!--a类型--> |
|||
<block v-else-if="node.tag == 'a'"> |
|||
<view :class="node.classStr" class="inline a" :data-href="node.attr.href" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--table类型--> |
|||
<block v-else-if="node.tag == 'table'"> |
|||
<view :class="node.classStr" class="table" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--br类型--> |
|||
<block v-else-if="node.tag == 'br'"> |
|||
<text>\n</text> |
|||
</block> |
|||
|
|||
<!--其他块级标签--> |
|||
<block v-else-if="node.tagType == 'block' && node.tag !== 'script'"> |
|||
<view :class="[node.classStr, node.tag]" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--内联标签--> |
|||
<view v-else-if="node.tagType == 'inline' && node.tag !== 'style'" :class="[node.classStr, node.tag]" class="inline" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
|
|||
</block> |
|||
|
|||
<!--判断是否是文本节点--> |
|||
<block v-else-if="node.node == 'text'"> |
|||
{{node.text}} |
|||
</block> |
|||
</template> |
|||
|
|||
<script> |
|||
import wxParseTemplate from './wxParseTemplate1'; |
|||
import wxParseImg from './wxParseImg'; |
|||
import wxParseVideo from './wxParseVideo'; |
|||
|
|||
export default { |
|||
name: 'wxParseTemplate0', |
|||
props: { |
|||
node: {}, |
|||
}, |
|||
components: { |
|||
wxParseTemplate, |
|||
wxParseImg, |
|||
wxParseVideo, |
|||
}, |
|||
}; |
|||
</script> |
|||
@ -0,0 +1,92 @@ |
|||
<template> |
|||
<!--判断是否是标签节点--> |
|||
<block v-if="node.node == 'element'"> |
|||
<block v-if="node.tag == 'button'"> |
|||
<button type="default" size="mini"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</button> |
|||
</block> |
|||
|
|||
<!--li类型--> |
|||
<block v-else-if="node.tag == 'li'"> |
|||
<view :class="node.classStr" class="li" :style="node.styleStr"> |
|||
<view :class="node.classStr" class="li-inner"> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<view :class="node.classStr" class="li-circle"></view> |
|||
</view> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--video类型--> |
|||
<block v-else-if="node.tag == 'video'"> |
|||
<wx-parse-video :node="node" /> |
|||
</block> |
|||
|
|||
<!--img类型--> |
|||
<block v-else-if="node.tag == 'img'"> |
|||
<wx-parse-img :node="node" /> |
|||
</block> |
|||
|
|||
<!--a类型--> |
|||
<block v-else-if="node.tag == 'a'"> |
|||
<view :class="node.classStr" class="inline a" :data-href="node.attr.href" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--br类型--> |
|||
<block v-else-if="node.tag == 'br'"> |
|||
<text>\n</text> |
|||
</block> |
|||
|
|||
<!--其他块级标签--> |
|||
<block v-else-if="node.tagType == 'block' && node.tag !== 'script'"> |
|||
<view :class="[node.classStr, node.tag]" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--内联标签--> |
|||
<view v-else-if="node.tagType == 'inline' && node.tag !== 'style'" :class="[node.classStr, node.tag]" class="inline" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
|
|||
</block> |
|||
|
|||
<!--判断是否是文本节点--> |
|||
<block v-else-if="node.node == 'text'"> |
|||
{{node.text}} |
|||
</block> |
|||
</template> |
|||
|
|||
<script> |
|||
import wxParseTemplate from './wxParseTemplate2'; |
|||
import wxParseImg from './wxParseImg'; |
|||
import wxParseVideo from './wxParseVideo'; |
|||
|
|||
export default { |
|||
name: 'wxParseTemplate1', |
|||
props: { |
|||
node: {}, |
|||
}, |
|||
components: { |
|||
wxParseTemplate, |
|||
wxParseImg, |
|||
wxParseVideo, |
|||
}, |
|||
}; |
|||
</script> |
|||
@ -0,0 +1,92 @@ |
|||
<template> |
|||
<!--判断是否是标签节点--> |
|||
<block v-if="node.node == 'element'"> |
|||
<block v-if="node.tag == 'button'"> |
|||
<button type="default" size="mini"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</button> |
|||
</block> |
|||
|
|||
<!--li类型--> |
|||
<block v-else-if="node.tag == 'li'"> |
|||
<view :class="node.classStr" class="li" :style="node.styleStr"> |
|||
<view :class="node.classStr" class="li-inner"> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<view :class="node.classStr" class="li-circle"></view> |
|||
</view> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--video类型--> |
|||
<block v-else-if="node.tag == 'video'"> |
|||
<wx-parse-video :node="node" /> |
|||
</block> |
|||
|
|||
<!--img类型--> |
|||
<block v-else-if="node.tag == 'img'"> |
|||
<wx-parse-img :node="node" /> |
|||
</block> |
|||
|
|||
<!--a类型--> |
|||
<block v-else-if="node.tag == 'a'"> |
|||
<view :class="node.classStr" class="inline a" :data-href="node.attr.href" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--br类型--> |
|||
<block v-else-if="node.tag == 'br'"> |
|||
<text>\n</text> |
|||
</block> |
|||
|
|||
<!--其他块级标签--> |
|||
<block v-else-if="node.tagType == 'block' && node.tag !== 'script'"> |
|||
<view :class="[node.classStr, node.tag]" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--内联标签--> |
|||
<view v-else-if="node.tagType == 'inline' && node.tag !== 'style'" :class="[node.classStr, node.tag]" class="inline" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
|
|||
</block> |
|||
|
|||
<!--判断是否是文本节点--> |
|||
<block v-else-if="node.node == 'text'"> |
|||
{{node.text}} |
|||
</block> |
|||
</template> |
|||
|
|||
<script> |
|||
import wxParseTemplate from './wxParseTemplate11'; |
|||
import wxParseImg from './wxParseImg'; |
|||
import wxParseVideo from './wxParseVideo'; |
|||
|
|||
export default { |
|||
name: 'wxParseTemplate10', |
|||
props: { |
|||
node: {}, |
|||
}, |
|||
components: { |
|||
wxParseTemplate, |
|||
wxParseImg, |
|||
wxParseVideo, |
|||
}, |
|||
}; |
|||
</script> |
|||
@ -0,0 +1,80 @@ |
|||
<template> |
|||
<!--判断是否是标签节点--> |
|||
<block v-if="node.node == 'element'"> |
|||
<!--button类型--> |
|||
<block v-if="node.tag == 'button'"> |
|||
<button type="default" size="mini"> |
|||
</button> |
|||
</block> |
|||
|
|||
<!--li类型--> |
|||
<block v-else-if="node.tag == 'li'"> |
|||
<view :class="node.classStr" class="li" :style="node.styleStr"> |
|||
<view :class="node.classStr" class="li-inner"> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<view :class="node.classStr" class="li-circle"></view> |
|||
</view> |
|||
<view :class="node.classStr" class="li-text"> |
|||
{{node.text}} |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--video类型--> |
|||
<block v-else-if="node.tag == 'video'"> |
|||
<wx-parse-video :node="node" /> |
|||
</block> |
|||
|
|||
<!--img类型--> |
|||
<block v-else-if="node.tag == 'img'"> |
|||
<wx-parse-img :node="node" /> |
|||
</block> |
|||
|
|||
<!--a类型--> |
|||
<block v-else-if="node.tag == 'a'"> |
|||
<view :class="node.classStr" class="inline a" :data-href="node.attr.href" :style="node.styleStr"> |
|||
{{node.text}} |
|||
</view> |
|||
</block> |
|||
|
|||
<!--br类型--> |
|||
<block v-else-if="node.tag == 'br'"> |
|||
<text>\n</text> |
|||
</block> |
|||
|
|||
<!--其他块级标签--> |
|||
<block v-else-if="node.tagType == 'block' && node.tag !== 'script'"> |
|||
<view :class="[node.classStr, node.tag]" :style="node.styleStr"> |
|||
{{node.text}} |
|||
</view> |
|||
</block> |
|||
|
|||
<!--内联标签--> |
|||
<view v-else-if="node.tagType == 'inline' && node.tag !== 'style'" :class="[node.classStr, node.tag]" class="inline" :style="node.styleStr"> |
|||
{{node.text}} |
|||
</view> |
|||
|
|||
</block> |
|||
|
|||
<!--判断是否是文本节点--> |
|||
<block v-else-if="node.node == 'text'"> |
|||
{{node.text}} |
|||
</block> |
|||
</template> |
|||
|
|||
<script> |
|||
import wxParseImg from './wxParseImg'; |
|||
import wxParseVideo from './wxParseVideo'; |
|||
|
|||
export default { |
|||
name: 'wxParseTemplate11', |
|||
props: { |
|||
node: {}, |
|||
}, |
|||
components: { |
|||
wxParseImg, |
|||
wxParseVideo, |
|||
}, |
|||
}; |
|||
</script> |
|||
@ -0,0 +1,92 @@ |
|||
<template> |
|||
<!--判断是否是标签节点--> |
|||
<block v-if="node.node == 'element'"> |
|||
<block v-if="node.tag == 'button'"> |
|||
<button type="default" size="mini"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</button> |
|||
</block> |
|||
|
|||
<!--li类型--> |
|||
<block v-else-if="node.tag == 'li'"> |
|||
<view :class="node.classStr" class="li" :style="node.styleStr"> |
|||
<view :class="node.classStr" class="li-inner"> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<view :class="node.classStr" class="li-circle"></view> |
|||
</view> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--video类型--> |
|||
<block v-else-if="node.tag == 'video'"> |
|||
<wx-parse-video :node="node" /> |
|||
</block> |
|||
|
|||
<!--img类型--> |
|||
<block v-else-if="node.tag == 'img'"> |
|||
<wx-parse-img :node="node" /> |
|||
</block> |
|||
|
|||
<!--a类型--> |
|||
<block v-else-if="node.tag == 'a'"> |
|||
<view :class="node.classStr" class="inline a" :data-href="node.attr.href" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--br类型--> |
|||
<block v-else-if="node.tag == 'br'"> |
|||
<text>\n</text> |
|||
</block> |
|||
|
|||
<!--其他块级标签--> |
|||
<block v-else-if="node.tagType == 'block' && node.tag !== 'script'"> |
|||
<view :class="[node.classStr, node.tag]" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--内联标签--> |
|||
<view v-else-if="node.tagType == 'inline' && node.tag !== 'style'" :class="[node.classStr, node.tag]" class="inline" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
|
|||
</block> |
|||
|
|||
<!--判断是否是文本节点--> |
|||
<block v-else-if="node.node == 'text'"> |
|||
{{node.text}} |
|||
</block> |
|||
</template> |
|||
|
|||
<script> |
|||
import wxParseTemplate from './wxParseTemplate3'; |
|||
import wxParseImg from './wxParseImg'; |
|||
import wxParseVideo from './wxParseVideo'; |
|||
|
|||
export default { |
|||
name: 'wxParseTemplate2', |
|||
props: { |
|||
node: {}, |
|||
}, |
|||
components: { |
|||
wxParseTemplate, |
|||
wxParseImg, |
|||
wxParseVideo, |
|||
}, |
|||
}; |
|||
</script> |
|||
@ -0,0 +1,92 @@ |
|||
<template> |
|||
<!--判断是否是标签节点--> |
|||
<block v-if="node.node == 'element'"> |
|||
<block v-if="node.tag == 'button'"> |
|||
<button type="default" size="mini"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</button> |
|||
</block> |
|||
|
|||
<!--li类型--> |
|||
<block v-else-if="node.tag == 'li'"> |
|||
<view :class="node.classStr" class="li" :style="node.styleStr"> |
|||
<view :class="node.classStr" class="li-inner"> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<view :class="node.classStr" class="li-circle"></view> |
|||
</view> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--video类型--> |
|||
<block v-else-if="node.tag == 'video'"> |
|||
<wx-parse-video :node="node" /> |
|||
</block> |
|||
|
|||
<!--img类型--> |
|||
<block v-else-if="node.tag == 'img'"> |
|||
<wx-parse-img :node="node" /> |
|||
</block> |
|||
|
|||
<!--a类型--> |
|||
<block v-else-if="node.tag == 'a'"> |
|||
<view :class="node.classStr" class="inline a" :data-href="node.attr.href" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--br类型--> |
|||
<block v-else-if="node.tag == 'br'"> |
|||
<text>\n</text> |
|||
</block> |
|||
|
|||
<!--其他块级标签--> |
|||
<block v-else-if="node.tagType == 'block' && node.tag !== 'script'"> |
|||
<view :class="[node.classStr, node.tag]" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--内联标签--> |
|||
<view v-else-if="node.tagType == 'inline' && node.tag !== 'style'" :class="[node.classStr, node.tag]" class="inline" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
|
|||
</block> |
|||
|
|||
<!--判断是否是文本节点--> |
|||
<block v-else-if="node.node == 'text'"> |
|||
{{node.text}} |
|||
</block> |
|||
</template> |
|||
|
|||
<script> |
|||
import wxParseTemplate from './wxParseTemplate4'; |
|||
import wxParseImg from './wxParseImg'; |
|||
import wxParseVideo from './wxParseVideo'; |
|||
|
|||
export default { |
|||
name: 'wxParseTemplate3', |
|||
props: { |
|||
node: {}, |
|||
}, |
|||
components: { |
|||
wxParseTemplate, |
|||
wxParseImg, |
|||
wxParseVideo, |
|||
}, |
|||
}; |
|||
</script> |
|||
@ -0,0 +1,92 @@ |
|||
<template> |
|||
<!--判断是否是标签节点--> |
|||
<block v-if="node.node == 'element'"> |
|||
<block v-if="node.tag == 'button'"> |
|||
<button type="default" size="mini"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</button> |
|||
</block> |
|||
|
|||
<!--li类型--> |
|||
<block v-else-if="node.tag == 'li'"> |
|||
<view :class="node.classStr" class="li" :style="node.styleStr"> |
|||
<view :class="node.classStr" class="li-inner"> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<view :class="node.classStr" class="li-circle"></view> |
|||
</view> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--video类型--> |
|||
<block v-else-if="node.tag == 'video'"> |
|||
<wx-parse-video :node="node" /> |
|||
</block> |
|||
|
|||
<!--img类型--> |
|||
<block v-else-if="node.tag == 'img'"> |
|||
<wx-parse-img :node="node" /> |
|||
</block> |
|||
|
|||
<!--a类型--> |
|||
<block v-else-if="node.tag == 'a'"> |
|||
<view :class="node.classStr" class="inline a" :data-href="node.attr.href" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--br类型--> |
|||
<block v-else-if="node.tag == 'br'"> |
|||
<text>\n</text> |
|||
</block> |
|||
|
|||
<!--其他块级标签--> |
|||
<block v-else-if="node.tagType == 'block' && node.tag !== 'script'"> |
|||
<view :class="[node.classStr, node.tag]" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--内联标签--> |
|||
<view v-else-if="node.tagType == 'inline' && node.tag !== 'style'" :class="[node.classStr, node.tag]" class="inline" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
|
|||
</block> |
|||
|
|||
<!--判断是否是文本节点--> |
|||
<block v-else-if="node.node == 'text'"> |
|||
{{node.text}} |
|||
</block> |
|||
</template> |
|||
|
|||
<script> |
|||
import wxParseTemplate from './wxParseTemplate5'; |
|||
import wxParseImg from './wxParseImg'; |
|||
import wxParseVideo from './wxParseVideo'; |
|||
|
|||
export default { |
|||
name: 'wxParseTemplate4', |
|||
props: { |
|||
node: {}, |
|||
}, |
|||
components: { |
|||
wxParseTemplate, |
|||
wxParseImg, |
|||
wxParseVideo, |
|||
}, |
|||
}; |
|||
</script> |
|||
@ -0,0 +1,92 @@ |
|||
s<template> |
|||
<!--判断是否是标签节点--> |
|||
<block v-if="node.node == 'element'"> |
|||
<block v-if="node.tag == 'button'"> |
|||
<button type="default" size="mini"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</button> |
|||
</block> |
|||
|
|||
<!--li类型--> |
|||
<block v-else-if="node.tag == 'li'"> |
|||
<view :class="node.classStr" class="li" :style="node.styleStr"> |
|||
<view :class="node.classStr" class="li-inner"> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<view :class="node.classStr" class="li-circle"></view> |
|||
</view> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--video类型--> |
|||
<block v-else-if="node.tag == 'video'"> |
|||
<wx-parse-video :node="node" /> |
|||
</block> |
|||
|
|||
<!--img类型--> |
|||
<block v-else-if="node.tag == 'img'"> |
|||
<wx-parse-img :node="node" /> |
|||
</block> |
|||
|
|||
<!--a类型--> |
|||
<block v-else-if="node.tag == 'a'"> |
|||
<view :class="node.classStr" class="inline a" :data-href="node.attr.href" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--br类型--> |
|||
<block v-else-if="node.tag == 'br'"> |
|||
<text>\n</text> |
|||
</block> |
|||
|
|||
<!--其他块级标签--> |
|||
<block v-else-if="node.tagType == 'block' && node.tag !== 'script'"> |
|||
<view :class="[node.classStr, node.tag]" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--内联标签--> |
|||
<view v-else-if="node.tagType == 'inline' && node.tag !== 'style'" :class="[node.classStr, node.tag]" class="inline" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
|
|||
</block> |
|||
|
|||
<!--判断是否是文本节点--> |
|||
<block v-else-if="node.node == 'text'"> |
|||
{{node.text}} |
|||
</block> |
|||
</template> |
|||
|
|||
<script> |
|||
import wxParseTemplate from './wxParseTemplate6'; |
|||
import wxParseImg from './wxParseImg'; |
|||
import wxParseVideo from './wxParseVideo'; |
|||
|
|||
export default { |
|||
name: 'wxParseTemplate5', |
|||
props: { |
|||
node: {}, |
|||
}, |
|||
components: { |
|||
wxParseTemplate, |
|||
wxParseImg, |
|||
wxParseVideo, |
|||
}, |
|||
}; |
|||
</script> |
|||
@ -0,0 +1,92 @@ |
|||
<template> |
|||
<!--判断是否是标签节点--> |
|||
<block v-if="node.node == 'element'"> |
|||
<block v-if="node.tag == 'button'"> |
|||
<button type="default" size="mini"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</button> |
|||
</block> |
|||
|
|||
<!--li类型--> |
|||
<block v-else-if="node.tag == 'li'"> |
|||
<view :class="node.classStr" class="li" :style="node.styleStr"> |
|||
<view :class="node.classStr" class="li-inner"> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<view :class="node.classStr" class="li-circle"></view> |
|||
</view> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--video类型--> |
|||
<block v-else-if="node.tag == 'video'"> |
|||
<wx-parse-video :node="node" /> |
|||
</block> |
|||
|
|||
<!--img类型--> |
|||
<block v-else-if="node.tag == 'img'"> |
|||
<wx-parse-img :node="node" /> |
|||
</block> |
|||
|
|||
<!--a类型--> |
|||
<block v-else-if="node.tag == 'a'"> |
|||
<view :class="node.classStr" class="inline a" :data-href="node.attr.href" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--br类型--> |
|||
<block v-else-if="node.tag == 'br'"> |
|||
<text>\n</text> |
|||
</block> |
|||
|
|||
<!--其他块级标签--> |
|||
<block v-else-if="node.tagType == 'block' && node.tag !== 'script'"> |
|||
<view :class="[node.classStr, node.tag]" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--内联标签--> |
|||
<view v-else-if="node.tagType == 'inline' && node.tag !== 'style'" :class="[node.classStr, node.tag]" class="inline" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
|
|||
</block> |
|||
|
|||
<!--判断是否是文本节点--> |
|||
<block v-else-if="node.node == 'text'"> |
|||
{{node.text}} |
|||
</block> |
|||
</template> |
|||
|
|||
<script> |
|||
import wxParseTemplate from './wxParseTemplate7'; |
|||
import wxParseImg from './wxParseImg'; |
|||
import wxParseVideo from './wxParseVideo'; |
|||
|
|||
export default { |
|||
name: 'wxParseTemplate6', |
|||
props: { |
|||
node: {}, |
|||
}, |
|||
components: { |
|||
wxParseTemplate, |
|||
wxParseImg, |
|||
wxParseVideo, |
|||
}, |
|||
}; |
|||
</script> |
|||
@ -0,0 +1,92 @@ |
|||
<template> |
|||
<!--判断是否是标签节点--> |
|||
<block v-if="node.node == 'element'"> |
|||
<block v-if="node.tag == 'button'"> |
|||
<button type="default" size="mini"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</button> |
|||
</block> |
|||
|
|||
<!--li类型--> |
|||
<block v-else-if="node.tag == 'li'"> |
|||
<view :class="node.classStr" class="li" :style="node.styleStr"> |
|||
<view :class="node.classStr" class="li-inner"> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<view :class="node.classStr" class="li-circle"></view> |
|||
</view> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--video类型--> |
|||
<block v-else-if="node.tag == 'video'"> |
|||
<wx-parse-video :node="node" /> |
|||
</block> |
|||
|
|||
<!--img类型--> |
|||
<block v-else-if="node.tag == 'img'"> |
|||
<wx-parse-img :node="node" /> |
|||
</block> |
|||
|
|||
<!--a类型--> |
|||
<block v-else-if="node.tag == 'a'"> |
|||
<view :class="node.classStr" class="inline a" :data-href="node.attr.href" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--br类型--> |
|||
<block v-else-if="node.tag == 'br'"> |
|||
<text>\n</text> |
|||
</block> |
|||
|
|||
<!--其他块级标签--> |
|||
<block v-else-if="node.tagType == 'block' && node.tag !== 'script'"> |
|||
<view :class="[node.classStr, node.tag]" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--内联标签--> |
|||
<view v-else-if="node.tagType == 'inline' && node.tag !== 'style'" :class="[node.classStr, node.tag]" class="inline" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
|
|||
</block> |
|||
|
|||
<!--判断是否是文本节点--> |
|||
<block v-else-if="node.node == 'text'"> |
|||
{{node.text}} |
|||
</block> |
|||
</template> |
|||
|
|||
<script> |
|||
import wxParseTemplate from './wxParseTemplate8'; |
|||
import wxParseImg from './wxParseImg'; |
|||
import wxParseVideo from './wxParseVideo'; |
|||
|
|||
export default { |
|||
name: 'wxParseTemplate7', |
|||
props: { |
|||
node: {}, |
|||
}, |
|||
components: { |
|||
wxParseTemplate, |
|||
wxParseImg, |
|||
wxParseVideo, |
|||
}, |
|||
}; |
|||
</script> |
|||
@ -0,0 +1,92 @@ |
|||
<template> |
|||
<!--判断是否是标签节点--> |
|||
<block v-if="node.node == 'element'"> |
|||
<block v-if="node.tag == 'button'"> |
|||
<button type="default" size="mini"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</button> |
|||
</block> |
|||
|
|||
<!--li类型--> |
|||
<block v-else-if="node.tag == 'li'"> |
|||
<view :class="node.classStr" class="li" :style="node.styleStr"> |
|||
<view :class="node.classStr" class="li-inner"> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<view :class="node.classStr" class="li-circle"></view> |
|||
</view> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--video类型--> |
|||
<block v-else-if="node.tag == 'video'"> |
|||
<wx-parse-video :node="node" /> |
|||
</block> |
|||
|
|||
<!--img类型--> |
|||
<block v-else-if="node.tag == 'img'"> |
|||
<wx-parse-img :node="node" /> |
|||
</block> |
|||
|
|||
<!--a类型--> |
|||
<block v-else-if="node.tag == 'a'"> |
|||
<view :class="node.classStr" class="inline a" :data-href="node.attr.href" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--br类型--> |
|||
<block v-else-if="node.tag == 'br'"> |
|||
<text>\n</text> |
|||
</block> |
|||
|
|||
<!--其他块级标签--> |
|||
<block v-else-if="node.tagType == 'block' && node.tag !== 'script'"> |
|||
<view :class="[node.classStr, node.tag]" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--内联标签--> |
|||
<view v-else-if="node.tagType == 'inline' && node.tag !== 'style'" :class="[node.classStr, node.tag]" class="inline" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
|
|||
</block> |
|||
|
|||
<!--判断是否是文本节点--> |
|||
<block v-else-if="node.node == 'text'"> |
|||
{{node.text}} |
|||
</block> |
|||
</template> |
|||
|
|||
<script> |
|||
import wxParseTemplate from './wxParseTemplate9'; |
|||
import wxParseImg from './wxParseImg'; |
|||
import wxParseVideo from './wxParseVideo'; |
|||
|
|||
export default { |
|||
name: 'wxParseTemplate8', |
|||
props: { |
|||
node: {}, |
|||
}, |
|||
components: { |
|||
wxParseTemplate, |
|||
wxParseImg, |
|||
wxParseVideo, |
|||
}, |
|||
}; |
|||
</script> |
|||
@ -0,0 +1,92 @@ |
|||
<template> |
|||
<!--判断是否是标签节点--> |
|||
<block v-if="node.node == 'element'"> |
|||
<block v-if="node.tag == 'button'"> |
|||
<button type="default" size="mini"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</button> |
|||
</block> |
|||
|
|||
<!--li类型--> |
|||
<block v-else-if="node.tag == 'li'"> |
|||
<view :class="node.classStr" class="li" :style="node.styleStr"> |
|||
<view :class="node.classStr" class="li-inner"> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<view :class="node.classStr" class="li-circle"></view> |
|||
</view> |
|||
<view :class="node.classStr" class="li-text"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--video类型--> |
|||
<block v-else-if="node.tag == 'video'"> |
|||
<wx-parse-video :node="node" /> |
|||
</block> |
|||
|
|||
<!--img类型--> |
|||
<block v-else-if="node.tag == 'img'"> |
|||
<wx-parse-img :node="node" /> |
|||
</block> |
|||
|
|||
<!--a类型--> |
|||
<block v-else-if="node.tag == 'a'"> |
|||
<view :class="node.classStr" class="inline a" :data-href="node.attr.href" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--br类型--> |
|||
<block v-else-if="node.tag == 'br'"> |
|||
<text>\n</text> |
|||
</block> |
|||
|
|||
<!--其他块级标签--> |
|||
<block v-else-if="node.tagType == 'block' && node.tag !== 'script'"> |
|||
<view :class="[node.classStr, node.tag]" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
</block> |
|||
|
|||
<!--内联标签--> |
|||
<view v-else-if="node.tagType == 'inline' && node.tag !== 'style'" :class="[node.classStr, node.tag]" class="inline" :style="node.styleStr"> |
|||
<block v-for="node of node.nodes" :key="node.index"> |
|||
<wx-parse-template :node="node" /> |
|||
</block> |
|||
</view> |
|||
|
|||
</block> |
|||
|
|||
<!--判断是否是文本节点--> |
|||
<block v-else-if="node.node == 'text'"> |
|||
{{node.text}} |
|||
</block> |
|||
</template> |
|||
|
|||
<script> |
|||
import wxParseTemplate from './wxParseTemplate10'; |
|||
import wxParseImg from './wxParseImg'; |
|||
import wxParseVideo from './wxParseVideo'; |
|||
|
|||
export default { |
|||
name: 'wxParseTemplate9', |
|||
props: { |
|||
node: {}, |
|||
}, |
|||
components: { |
|||
wxParseTemplate, |
|||
wxParseImg, |
|||
wxParseVideo, |
|||
}, |
|||
}; |
|||
</script> |
|||
@ -0,0 +1,15 @@ |
|||
<template> |
|||
<!--增加video标签支持,并循环添加--> |
|||
<view :class="node.classStr" class="video" :style="node.styleStr"> |
|||
<video :class="node.classStr" class="video-video" :src="node.attr.src"></video> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'wxParseVideo', |
|||
props: { |
|||
node: {}, |
|||
}, |
|||
}; |
|||
</script> |
|||
@ -0,0 +1,252 @@ |
|||
/** |
|||
* html2Json 改造来自: https://github.com/Jxck/html2json
|
|||
* |
|||
* |
|||
* author: Di (微信小程序开发工程师) |
|||
* organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
|
|||
* 垂直微信小程序开发交流社区 |
|||
* |
|||
* github地址: https://github.com/icindy/wxParse
|
|||
* |
|||
* for: 微信小程序富文本解析 |
|||
* detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
|
|||
*/ |
|||
|
|||
import wxDiscode from './wxDiscode'; |
|||
import HTMLParser from './htmlparser'; |
|||
|
|||
const placeImgeUrlHttps = 'https'; |
|||
|
|||
function makeMap(str) { |
|||
const obj = {}; |
|||
const items = str.split(','); |
|||
for (let i = 0; i < items.length; i += 1) obj[items[i]] = true; |
|||
return obj; |
|||
} |
|||
|
|||
// Block Elements - HTML 5
|
|||
const block = makeMap('br,a,code,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video'); |
|||
|
|||
// Inline Elements - HTML 5
|
|||
const inline = makeMap('abbr,acronym,applet,b,basefont,bdo,big,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'); |
|||
|
|||
// Elements that you can, intentionally, leave open
|
|||
// (and which close themselves)
|
|||
const closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr'); |
|||
|
|||
function removeDOCTYPE(html) { |
|||
return html |
|||
.replace(/<\?xml.*\\?>\n/, '') |
|||
.replace(/<.*!doctype.*\\>\n/, '') |
|||
.replace(/<.*!DOCTYPE.*\\>\n/, ''); |
|||
} |
|||
|
|||
function trimHtml(html) { |
|||
return html |
|||
.replace(/<!--.*?-->/gi, '') |
|||
.replace(/\/\*.*?\*\//gi, '') |
|||
.replace(/[ ]+</gi, '<'); |
|||
} |
|||
|
|||
function html2json(html, image, debug) { |
|||
// 处理字符串
|
|||
html = removeDOCTYPE(html); |
|||
html = trimHtml(html); |
|||
html = wxDiscode.strDiscode(html); |
|||
// 生成node节点
|
|||
const bufArray = []; |
|||
const results = { |
|||
nodes: [], |
|||
images: [], |
|||
imageUrls: [], |
|||
}; |
|||
let index = 0; |
|||
|
|||
image.urls = results.imageUrls; |
|||
|
|||
HTMLParser(html, { |
|||
start(tag, attrs, unary) { |
|||
// node for this element
|
|||
const node = { |
|||
node: 'element', |
|||
tag, |
|||
}; |
|||
|
|||
if (bufArray.length === 0) { |
|||
node.index = index.toString(); |
|||
index += 1; |
|||
} else { |
|||
const parent = bufArray[0]; |
|||
if (parent.nodes === undefined) { |
|||
parent.nodes = []; |
|||
} |
|||
node.index = `${parent.index}.${parent.nodes.length}`; |
|||
} |
|||
|
|||
if (block[tag]) { |
|||
node.tagType = 'block'; |
|||
} else if (inline[tag]) { |
|||
node.tagType = 'inline'; |
|||
} else if (closeSelf[tag]) { |
|||
node.tagType = 'closeSelf'; |
|||
} |
|||
|
|||
node.attr = attrs.reduce((pre, attr) => { |
|||
const { name } = attr; |
|||
let { value } = attr; |
|||
if (name === 'class') { |
|||
if (debug) console.dir(value); |
|||
// value = value.join("")
|
|||
node.classStr = value; |
|||
} |
|||
// has multi attibutes
|
|||
// make it array of attribute
|
|||
if (name === 'style') { |
|||
if (debug) console.dir(value); |
|||
// value = value.join("")
|
|||
node.styleStr = value; |
|||
} |
|||
if (value.match(/ /)) { |
|||
value = value.split(' '); |
|||
} |
|||
|
|||
// if attr already exists
|
|||
// merge it
|
|||
if (pre[name]) { |
|||
if (Array.isArray(pre[name])) { |
|||
// already array, push to last
|
|||
pre[name].push(value); |
|||
} else { |
|||
// single value, make it array
|
|||
pre[name] = [pre[name], value]; |
|||
} |
|||
} else { |
|||
// not exist, put it
|
|||
pre[name] = value; |
|||
} |
|||
|
|||
return pre; |
|||
}, {}); |
|||
|
|||
// 对img添加额外数据
|
|||
if (node.tag === 'img') { |
|||
node.imgIndex = results.images.length; |
|||
let imgUrl = node.attr.src; |
|||
imgUrl = wxDiscode.urlToHttpUrl(imgUrl, placeImgeUrlHttps); |
|||
node.attr.src = imgUrl || ''; |
|||
node.image = image; |
|||
if (imgUrl) { |
|||
results.images.push(node); |
|||
results.imageUrls.push(imgUrl); |
|||
} |
|||
} |
|||
|
|||
// 处理a标签属性
|
|||
if (node.tag === 'a') { |
|||
node.attr.href = node.attr.href || ''; |
|||
} |
|||
|
|||
// 处理font标签样式属性
|
|||
if (node.tag === 'font') { |
|||
const fontSize = [ |
|||
'x-small', |
|||
'small', |
|||
'medium', |
|||
'large', |
|||
'x-large', |
|||
'xx-large', |
|||
'-webkit-xxx-large', |
|||
]; |
|||
const styleAttrs = { |
|||
color: 'color', |
|||
face: 'font-family', |
|||
size: 'font-size', |
|||
}; |
|||
if (!node.attr.style) node.attr.style = []; |
|||
if (!node.styleStr) node.styleStr = ''; |
|||
Object.keys(styleAttrs).forEach((key) => { |
|||
if (node.attr[key]) { |
|||
const value = key === 'size' ? fontSize[node.attr[key] - 1] : node.attr[key]; |
|||
node.attr.style.push(styleAttrs[key]); |
|||
node.attr.style.push(value); |
|||
node.styleStr += `${styleAttrs[key]}: ${value};`; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
// 临时记录source资源
|
|||
if (node.tag === 'source') { |
|||
results.source = node.attr.src; |
|||
} |
|||
|
|||
if (unary) { |
|||
// if this tag doesn't have end tag
|
|||
// like <img src="hoge.png"/>
|
|||
// add to parents
|
|||
const parent = bufArray[0] || results; |
|||
if (parent.nodes === undefined) { |
|||
parent.nodes = []; |
|||
} |
|||
parent.nodes.push(node); |
|||
} else { |
|||
bufArray.unshift(node); |
|||
} |
|||
}, |
|||
end(tag) { |
|||
// merge into parent tag
|
|||
const node = bufArray.shift(); |
|||
if (node.tag !== tag && debug) { |
|||
console.error('invalid state: mismatch end tag'); |
|||
} |
|||
|
|||
// 当有缓存source资源时于于video补上src资源
|
|||
if (node.tag === 'video' && results.source) { |
|||
node.attr.src = results.source; |
|||
delete results.source; |
|||
} |
|||
|
|||
if (bufArray.length === 0) { |
|||
results.nodes.push(node); |
|||
} else { |
|||
const parent = bufArray[0]; |
|||
if (parent.nodes === undefined) { |
|||
parent.nodes = []; |
|||
} |
|||
parent.nodes.push(node); |
|||
} |
|||
}, |
|||
chars(text) { |
|||
const node = { |
|||
node: 'text', |
|||
text: text.trim(), |
|||
}; |
|||
|
|||
if (bufArray.length === 0) { |
|||
node.index = index.toString(); |
|||
index += 1; |
|||
results.nodes.push(node); |
|||
} else { |
|||
const parent = bufArray[0]; |
|||
if (parent.nodes === undefined) { |
|||
parent.nodes = []; |
|||
} |
|||
node.index = `${parent.index}.${parent.nodes.length}`; |
|||
parent.nodes.push(node); |
|||
} |
|||
}, |
|||
comment(text) { |
|||
const node = { |
|||
node: 'comment', |
|||
text, |
|||
}; |
|||
const parent = bufArray[0]; |
|||
if (parent.nodes === undefined) { |
|||
parent.nodes = []; |
|||
} |
|||
parent.nodes.push(node); |
|||
}, |
|||
}); |
|||
return results; |
|||
} |
|||
|
|||
export default html2json; |
|||
@ -0,0 +1,188 @@ |
|||
/** |
|||
* |
|||
* htmlParser改造自: https://github.com/blowsie/Pure-JavaScript-HTML5-Parser
|
|||
* |
|||
* author: Di (微信小程序开发工程师) |
|||
* organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
|
|||
* 垂直微信小程序开发交流社区 |
|||
* |
|||
* github地址: https://github.com/icindy/wxParse
|
|||
* |
|||
* for: 微信小程序富文本解析 |
|||
* detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
|
|||
*/ |
|||
// Regular Expressions for parsing tags and attributes
|
|||
|
|||
const startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z0-9_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/; |
|||
const endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/; |
|||
const attr = /([a-zA-Z0-9_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; |
|||
|
|||
function makeMap(str) { |
|||
const obj = {}; |
|||
const items = str.split(','); |
|||
for (let i = 0; i < items.length; i += 1) obj[items[i]] = true; |
|||
return obj; |
|||
} |
|||
|
|||
// Empty Elements - HTML 5
|
|||
const empty = makeMap('area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr'); |
|||
|
|||
// Block Elements - HTML 5
|
|||
const block = makeMap('a,address,code,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video'); |
|||
|
|||
// Inline Elements - HTML 5
|
|||
const inline = makeMap('abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'); |
|||
|
|||
// Elements that you can, intentionally, leave open
|
|||
// (and which close themselves)
|
|||
const closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr'); |
|||
|
|||
// Attributes that have their values filled in disabled="disabled"
|
|||
const fillAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'); |
|||
|
|||
// Special Elements (can contain anything)
|
|||
const special = makeMap('script,style,view,scroll-view,block'); |
|||
|
|||
function HTMLParser(html, handler) { |
|||
let index; |
|||
let chars; |
|||
let match; |
|||
let last = html; |
|||
const stack = []; |
|||
|
|||
stack.last = () => stack[stack.length - 1]; |
|||
|
|||
function parseEndTag(tag, tagName) { |
|||
// If no tag name is provided, clean shop
|
|||
let pos; |
|||
if (!tagName) { |
|||
pos = 0; |
|||
} else { |
|||
// Find the closest opened tag of the same type
|
|||
tagName = tagName.toLowerCase(); |
|||
for (pos = stack.length - 1; pos >= 0; pos -= 1) { |
|||
if (stack[pos] === tagName) break; |
|||
} |
|||
} |
|||
if (pos >= 0) { |
|||
// Close all the open elements, up the stack
|
|||
for (let i = stack.length - 1; i >= pos; i -= 1) { |
|||
if (handler.end) handler.end(stack[i]); |
|||
} |
|||
|
|||
// Remove the open elements from the stack
|
|||
stack.length = pos; |
|||
} |
|||
} |
|||
|
|||
function parseStartTag(tag, tagName, rest, unary) { |
|||
tagName = tagName.toLowerCase(); |
|||
|
|||
if (block[tagName]) { |
|||
while (stack.last() && inline[stack.last()]) { |
|||
parseEndTag('', stack.last()); |
|||
} |
|||
} |
|||
|
|||
if (closeSelf[tagName] && stack.last() === tagName) { |
|||
parseEndTag('', tagName); |
|||
} |
|||
|
|||
unary = empty[tagName] || !!unary; |
|||
|
|||
if (!unary) stack.push(tagName); |
|||
|
|||
if (handler.start) { |
|||
const attrs = []; |
|||
|
|||
rest.replace(attr, function genAttr(matches, name) { |
|||
const value = arguments[2] || arguments[3] || arguments[4] || (fillAttrs[name] ? name : ''); |
|||
|
|||
attrs.push({ |
|||
name, |
|||
value, |
|||
escaped: value.replace(/(^|[^\\])"/g, '$1\\"'), // "
|
|||
}); |
|||
}); |
|||
|
|||
if (handler.start) { |
|||
handler.start(tagName, attrs, unary); |
|||
} |
|||
} |
|||
} |
|||
|
|||
while (html) { |
|||
chars = true; |
|||
|
|||
// Make sure we're not in a script or style element
|
|||
if (!stack.last() || !special[stack.last()]) { |
|||
// Comment
|
|||
if (html.indexOf('<!--') === 0) { |
|||
index = html.indexOf('-->'); |
|||
|
|||
if (index >= 0) { |
|||
if (handler.comment) handler.comment(html.substring(4, index)); |
|||
html = html.substring(index + 3); |
|||
chars = false; |
|||
} |
|||
|
|||
// end tag
|
|||
} else if (html.indexOf('</') === 0) { |
|||
match = html.match(endTag); |
|||
|
|||
if (match) { |
|||
html = html.substring(match[0].length); |
|||
match[0].replace(endTag, parseEndTag); |
|||
chars = false; |
|||
} |
|||
|
|||
// start tag
|
|||
} else if (html.indexOf('<') === 0) { |
|||
match = html.match(startTag); |
|||
|
|||
if (match) { |
|||
html = html.substring(match[0].length); |
|||
match[0].replace(startTag, parseStartTag); |
|||
chars = false; |
|||
} |
|||
} |
|||
|
|||
if (chars) { |
|||
index = html.indexOf('<'); |
|||
let text = ''; |
|||
while (index === 0) { |
|||
text += '<'; |
|||
html = html.substring(1); |
|||
index = html.indexOf('<'); |
|||
} |
|||
text += index < 0 ? html : html.substring(0, index); |
|||
html = index < 0 ? '' : html.substring(index); |
|||
|
|||
if (handler.chars) handler.chars(text); |
|||
} |
|||
} else { |
|||
html = html.replace( |
|||
new RegExp(`([\\s\\S]*?)</${stack.last()}[^>]*>`), |
|||
(all, text) => { |
|||
text = text.replace( |
|||
/<!--([\s\S]*?)-->|<!\[CDATA\[([\s\S]*?)]]>/g, |
|||
'$1$2', |
|||
); |
|||
if (handler.chars) handler.chars(text); |
|||
|
|||
return ''; |
|||
}, |
|||
); |
|||
|
|||
parseEndTag('', stack.last()); |
|||
} |
|||
|
|||
if (html === last) throw new Error(`Parse Error: ${html}`); |
|||
last = html; |
|||
} |
|||
|
|||
// Clean up any remaining tags
|
|||
parseEndTag(); |
|||
} |
|||
|
|||
export default HTMLParser; |
|||
@ -0,0 +1,197 @@ |
|||
// HTML 支持的数学符号
|
|||
function strNumDiscode(str) { |
|||
str = str.replace(/∀/g, '∀'); |
|||
str = str.replace(/∂/g, '∂'); |
|||
str = str.replace(/∃/g, '∃'); |
|||
str = str.replace(/∅/g, '∅'); |
|||
str = str.replace(/∇/g, '∇'); |
|||
str = str.replace(/∈/g, '∈'); |
|||
str = str.replace(/∉/g, '∉'); |
|||
str = str.replace(/∋/g, '∋'); |
|||
str = str.replace(/∏/g, '∏'); |
|||
str = str.replace(/∑/g, '∑'); |
|||
str = str.replace(/−/g, '−'); |
|||
str = str.replace(/∗/g, '∗'); |
|||
str = str.replace(/√/g, '√'); |
|||
str = str.replace(/∝/g, '∝'); |
|||
str = str.replace(/∞/g, '∞'); |
|||
str = str.replace(/∠/g, '∠'); |
|||
str = str.replace(/∧/g, '∧'); |
|||
str = str.replace(/∨/g, '∨'); |
|||
str = str.replace(/∩/g, '∩'); |
|||
str = str.replace(/∪/g, '∪'); |
|||
str = str.replace(/∫/g, '∫'); |
|||
str = str.replace(/∴/g, '∴'); |
|||
str = str.replace(/∼/g, '∼'); |
|||
str = str.replace(/≅/g, '≅'); |
|||
str = str.replace(/≈/g, '≈'); |
|||
str = str.replace(/≠/g, '≠'); |
|||
str = str.replace(/≤/g, '≤'); |
|||
str = str.replace(/≥/g, '≥'); |
|||
str = str.replace(/⊂/g, '⊂'); |
|||
str = str.replace(/⊃/g, '⊃'); |
|||
str = str.replace(/⊄/g, '⊄'); |
|||
str = str.replace(/⊆/g, '⊆'); |
|||
str = str.replace(/⊇/g, '⊇'); |
|||
str = str.replace(/⊕/g, '⊕'); |
|||
str = str.replace(/⊗/g, '⊗'); |
|||
str = str.replace(/⊥/g, '⊥'); |
|||
str = str.replace(/⋅/g, '⋅'); |
|||
return str; |
|||
} |
|||
|
|||
// HTML 支持的希腊字母
|
|||
function strGreeceDiscode(str) { |
|||
str = str.replace(/Α/g, 'Α'); |
|||
str = str.replace(/Β/g, 'Β'); |
|||
str = str.replace(/Γ/g, 'Γ'); |
|||
str = str.replace(/Δ/g, 'Δ'); |
|||
str = str.replace(/Ε/g, 'Ε'); |
|||
str = str.replace(/Ζ/g, 'Ζ'); |
|||
str = str.replace(/Η/g, 'Η'); |
|||
str = str.replace(/Θ/g, 'Θ'); |
|||
str = str.replace(/Ι/g, 'Ι'); |
|||
str = str.replace(/Κ/g, 'Κ'); |
|||
str = str.replace(/Λ/g, 'Λ'); |
|||
str = str.replace(/Μ/g, 'Μ'); |
|||
str = str.replace(/Ν/g, 'Ν'); |
|||
str = str.replace(/Ξ/g, 'Ν'); |
|||
str = str.replace(/Ο/g, 'Ο'); |
|||
str = str.replace(/Π/g, 'Π'); |
|||
str = str.replace(/Ρ/g, 'Ρ'); |
|||
str = str.replace(/Σ/g, 'Σ'); |
|||
str = str.replace(/Τ/g, 'Τ'); |
|||
str = str.replace(/Υ/g, 'Υ'); |
|||
str = str.replace(/Φ/g, 'Φ'); |
|||
str = str.replace(/Χ/g, 'Χ'); |
|||
str = str.replace(/Ψ/g, 'Ψ'); |
|||
str = str.replace(/Ω/g, 'Ω'); |
|||
|
|||
str = str.replace(/α/g, 'α'); |
|||
str = str.replace(/β/g, 'β'); |
|||
str = str.replace(/γ/g, 'γ'); |
|||
str = str.replace(/δ/g, 'δ'); |
|||
str = str.replace(/ε/g, 'ε'); |
|||
str = str.replace(/ζ/g, 'ζ'); |
|||
str = str.replace(/η/g, 'η'); |
|||
str = str.replace(/θ/g, 'θ'); |
|||
str = str.replace(/ι/g, 'ι'); |
|||
str = str.replace(/κ/g, 'κ'); |
|||
str = str.replace(/λ/g, 'λ'); |
|||
str = str.replace(/μ/g, 'μ'); |
|||
str = str.replace(/ν/g, 'ν'); |
|||
str = str.replace(/ξ/g, 'ξ'); |
|||
str = str.replace(/ο/g, 'ο'); |
|||
str = str.replace(/π/g, 'π'); |
|||
str = str.replace(/ρ/g, 'ρ'); |
|||
str = str.replace(/ς/g, 'ς'); |
|||
str = str.replace(/σ/g, 'σ'); |
|||
str = str.replace(/τ/g, 'τ'); |
|||
str = str.replace(/υ/g, 'υ'); |
|||
str = str.replace(/φ/g, 'φ'); |
|||
str = str.replace(/χ/g, 'χ'); |
|||
str = str.replace(/ψ/g, 'ψ'); |
|||
str = str.replace(/ω/g, 'ω'); |
|||
str = str.replace(/ϑ/g, 'ϑ'); |
|||
str = str.replace(/ϒ/g, 'ϒ'); |
|||
str = str.replace(/ϖ/g, 'ϖ'); |
|||
str = str.replace(/·/g, '·'); |
|||
return str; |
|||
} |
|||
|
|||
//
|
|||
|
|||
function strcharacterDiscode(str) { |
|||
// 加入常用解析
|
|||
str = str.replace(/ /g, ' '); |
|||
str = str.replace(/"/g, "'"); |
|||
str = str.replace(/&/g, '&'); |
|||
// str = str.replace(/</g, '‹');
|
|||
// str = str.replace(/>/g, '›');
|
|||
|
|||
str = str.replace(/</g, '<'); |
|||
str = str.replace(/>/g, '>'); |
|||
str = str.replace(/•/g, '•'); |
|||
|
|||
return str; |
|||
} |
|||
|
|||
// HTML 支持的其他实体
|
|||
function strOtherDiscode(str) { |
|||
str = str.replace(/Œ/g, 'Œ'); |
|||
str = str.replace(/œ/g, 'œ'); |
|||
str = str.replace(/Š/g, 'Š'); |
|||
str = str.replace(/š/g, 'š'); |
|||
str = str.replace(/Ÿ/g, 'Ÿ'); |
|||
str = str.replace(/ƒ/g, 'ƒ'); |
|||
str = str.replace(/ˆ/g, 'ˆ'); |
|||
str = str.replace(/˜/g, '˜'); |
|||
str = str.replace(/ /g, ''); |
|||
str = str.replace(/ /g, ''); |
|||
str = str.replace(/ /g, ''); |
|||
str = str.replace(/‌/g, ''); |
|||
str = str.replace(/‍/g, ''); |
|||
str = str.replace(/‎/g, ''); |
|||
str = str.replace(/‏/g, ''); |
|||
str = str.replace(/–/g, '–'); |
|||
str = str.replace(/—/g, '—'); |
|||
str = str.replace(/‘/g, '‘'); |
|||
str = str.replace(/’/g, '’'); |
|||
str = str.replace(/‚/g, '‚'); |
|||
str = str.replace(/“/g, '“'); |
|||
str = str.replace(/”/g, '”'); |
|||
str = str.replace(/„/g, '„'); |
|||
str = str.replace(/†/g, '†'); |
|||
str = str.replace(/‡/g, '‡'); |
|||
str = str.replace(/•/g, '•'); |
|||
str = str.replace(/…/g, '…'); |
|||
str = str.replace(/‰/g, '‰'); |
|||
str = str.replace(/′/g, '′'); |
|||
str = str.replace(/″/g, '″'); |
|||
str = str.replace(/‹/g, '‹'); |
|||
str = str.replace(/›/g, '›'); |
|||
str = str.replace(/‾/g, '‾'); |
|||
str = str.replace(/€/g, '€'); |
|||
str = str.replace(/™/g, '™'); |
|||
|
|||
str = str.replace(/←/g, '←'); |
|||
str = str.replace(/↑/g, '↑'); |
|||
str = str.replace(/→/g, '→'); |
|||
str = str.replace(/↓/g, '↓'); |
|||
str = str.replace(/↔/g, '↔'); |
|||
str = str.replace(/↵/g, '↵'); |
|||
str = str.replace(/⌈/g, '⌈'); |
|||
str = str.replace(/⌉/g, '⌉'); |
|||
|
|||
str = str.replace(/⌊/g, '⌊'); |
|||
str = str.replace(/⌋/g, '⌋'); |
|||
str = str.replace(/◊/g, '◊'); |
|||
str = str.replace(/♠/g, '♠'); |
|||
str = str.replace(/♣/g, '♣'); |
|||
str = str.replace(/♥/g, '♥'); |
|||
|
|||
str = str.replace(/♦/g, '♦'); |
|||
str = str.replace(/'/g, "'"); |
|||
return str; |
|||
} |
|||
|
|||
function strDiscode(str) { |
|||
str = strNumDiscode(str); |
|||
str = strGreeceDiscode(str); |
|||
str = strcharacterDiscode(str); |
|||
str = strOtherDiscode(str); |
|||
return str; |
|||
} |
|||
function urlToHttpUrl(url, rep) { |
|||
const patt1 = new RegExp('^//'); |
|||
const result = patt1.test(url); |
|||
if (result) { |
|||
url = `${rep}:${url}`; |
|||
} |
|||
return url; |
|||
} |
|||
|
|||
export default { |
|||
strDiscode, |
|||
urlToHttpUrl, |
|||
}; |
|||
@ -0,0 +1,257 @@ |
|||
/** |
|||
* author: Di (微信小程序开发工程师) |
|||
* organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com) |
|||
* 垂直微信小程序开发交流社区 |
|||
* |
|||
* github地址: https://github.com/icindy/wxParse |
|||
* |
|||
* for: 微信小程序富文本解析 |
|||
* detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184 |
|||
*/ |
|||
|
|||
.wxParse { |
|||
width: 100%; |
|||
font-family: Helvetica, sans-serif; |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
line-height: 1.8; |
|||
} |
|||
|
|||
view { |
|||
word-break: hyphenate; |
|||
} |
|||
|
|||
.wxParse .inline { |
|||
display: inline; |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
|
|||
/*//标题 */ |
|||
.wxParse .div { |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
|
|||
.wxParse .h1 { |
|||
font-size: 2em; |
|||
margin: 0.67em 0; |
|||
} |
|||
.wxParse .h2 { |
|||
font-size: 1.5em; |
|||
margin: 0.83em 0; |
|||
} |
|||
.wxParse .h3 { |
|||
font-size: 1.17em; |
|||
margin: 1em 0; |
|||
} |
|||
.wxParse .h4 { |
|||
margin: 1.33em 0; |
|||
} |
|||
.wxParse .h5 { |
|||
font-size: 0.83em; |
|||
margin: 1.67em 0; |
|||
} |
|||
.wxParse .h6 { |
|||
font-size: 0.67em; |
|||
margin: 2.33em 0; |
|||
} |
|||
|
|||
.wxParse .h1, |
|||
.wxParse .h2, |
|||
.wxParse .h3, |
|||
.wxParse .h4, |
|||
.wxParse .h5, |
|||
.wxParse .h6, |
|||
.wxParse .b, |
|||
.wxParse .strong { |
|||
font-weight: bolder; |
|||
} |
|||
|
|||
.wxParse .p { |
|||
margin: 1em 0; |
|||
} |
|||
|
|||
.wxParse .i, |
|||
.wxParse .cite, |
|||
.wxParse .em, |
|||
.wxParse .var, |
|||
.wxParse .address { |
|||
font-style: italic; |
|||
} |
|||
|
|||
.wxParse .pre, |
|||
.wxParse .tt, |
|||
.wxParse .code, |
|||
.wxParse .kbd, |
|||
.wxParse .samp { |
|||
font-family: monospace; |
|||
} |
|||
|
|||
.wxParse .pre { |
|||
background: #f5f5f5; |
|||
padding: 16rpx; |
|||
white-space: pre-wrap; |
|||
} |
|||
|
|||
.wxParse .big { |
|||
font-size: 1.17em; |
|||
} |
|||
|
|||
.wxParse .small, |
|||
.wxParse .sub, |
|||
.wxParse .sup { |
|||
font-size: 0.83em; |
|||
} |
|||
|
|||
.wxParse .sub { |
|||
vertical-align: sub; |
|||
} |
|||
.wxParse .sup { |
|||
vertical-align: super; |
|||
} |
|||
|
|||
.wxParse .s, |
|||
.wxParse .strike, |
|||
.wxParse .del { |
|||
text-decoration: line-through; |
|||
} |
|||
|
|||
/*wxparse-自定义个性化的css样式*/ |
|||
/*增加video的css样式*/ |
|||
.wxParse .strong, |
|||
.wxParse .s { |
|||
display: inline; |
|||
} |
|||
|
|||
.wxParse .a { |
|||
color: deepskyblue; |
|||
word-break: break-all; |
|||
overflow: auto; |
|||
} |
|||
|
|||
.wxParse .video { |
|||
text-align: center; |
|||
margin: 20rpx 0; |
|||
} |
|||
|
|||
.wxParse .video-video { |
|||
width: 100%; |
|||
} |
|||
|
|||
.wxParse .img { |
|||
width: 0; |
|||
height: 0; |
|||
max-width: 100%; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.wxParse .blockquote { |
|||
margin: 10rpx 0; |
|||
padding: 20rpx 0 20rpx 20rpx; |
|||
font-family: Courier, Calibri, "宋体"; |
|||
background: #f5f5f5; |
|||
border-left: 6rpx solid #dbdbdb; |
|||
} |
|||
.wxParse .blockquote .p { |
|||
margin: 0; |
|||
} |
|||
|
|||
.wxParse .code { |
|||
display: inline; |
|||
background: #f5f5f5; |
|||
} |
|||
.wxParse .ul { |
|||
margin: 20rpx 10rpx; |
|||
} |
|||
|
|||
.wxParse .li, |
|||
.wxParse .li-inner { |
|||
display: flex; |
|||
align-items: baseline; |
|||
margin: 10rpx 0; |
|||
} |
|||
|
|||
.wxParse .li-text { |
|||
display: flex; |
|||
align-items: center; |
|||
line-height: 40rpx; |
|||
} |
|||
|
|||
.wxParse .li-circle { |
|||
display: inline-flex; |
|||
width: 10rpx; |
|||
height: 10rpx; |
|||
background-color: #333; |
|||
margin-right: 10rpx; |
|||
border-radius: 50%; |
|||
position: relative; |
|||
top: -5rpx; |
|||
} |
|||
|
|||
.wxParse .li-square { |
|||
display: inline-flex; |
|||
width: 10rpx; |
|||
height: 10rpx; |
|||
background-color: #333; |
|||
margin-right: 10rpx; |
|||
} |
|||
.wxParse .li-ring { |
|||
display: inline-flex; |
|||
width: 10rpx; |
|||
height: 10rpx; |
|||
border: 2rpx solid #333; |
|||
border-radius: 50%; |
|||
background-color: #fff; |
|||
margin-right: 10rpx; |
|||
} |
|||
|
|||
.wxParse .table { |
|||
width: 100%; |
|||
border-top: 2rpx solid #e0e0e0; |
|||
} |
|||
.wxParse .thead,.wxParse .tfoot,.wxParse .tr { |
|||
display: flex; |
|||
flex-direction: row; |
|||
} |
|||
.wxParse .th,.wxParse .td { |
|||
display: flex; |
|||
width: 1160rpx; |
|||
overflow: auto; |
|||
} |
|||
|
|||
.wxParse .u { |
|||
text-decoration: underline; |
|||
} |
|||
.wxParse .hide { |
|||
display: none; |
|||
} |
|||
.wxParseText { |
|||
align-items: center; |
|||
} |
|||
.wxParse .tr { |
|||
width:100%; |
|||
display: flex; |
|||
border-right: 2rpx solid #e0e0e0; |
|||
border-bottom: 2rpx solid #e0e0e0; |
|||
} |
|||
.wxParse .th, |
|||
.wxParse .td { |
|||
flex: 1; |
|||
padding: 10rpx; |
|||
border-left: 2rpx solid #e0e0e0; |
|||
word-break: break-all; |
|||
} |
|||
.wxParse .td:last { |
|||
border-top: 2rpx solid #e0e0e0; |
|||
} |
|||
.wxParse .th { |
|||
background: #f0f0f0; |
|||
border-top: 2rpx solid #e0e0e0; |
|||
} |
|||
.wxParse .del { |
|||
display: inline; |
|||
} |
|||
.wxParse .figure { |
|||
overflow: hidden; |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
<!--** |
|||
* author: F-loat <chaimaoyuan@foxmail.com> |
|||
* |
|||
* github地址: https://github.com/F-loat/mpvue-wxParse |
|||
* |
|||
* for: Mpvue框架下 微信小程序富文本解析 |
|||
*/--> |
|||
|
|||
<template> |
|||
<!--基础元素--> |
|||
<div class="wxParse"> |
|||
<block v-for="node of wxParseData.nodes" :key="node.index"> |
|||
<wxParseTemplate :node="node" /> |
|||
</block> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import HtmlToJson from './libs/html2json'; |
|||
import wxParseTemplate from './components/wxParseTemplate0'; |
|||
|
|||
export default { |
|||
name: 'wxParse', |
|||
props: { |
|||
content: { |
|||
type: String, |
|||
default() { |
|||
return '<div class="color:red;">数据不能为空</div>'; |
|||
}, |
|||
}, |
|||
image: { |
|||
type: String, |
|||
default() { |
|||
return { |
|||
mode: 'aspectFit', |
|||
padding: 0, |
|||
lazyLoad: false, |
|||
}; |
|||
}, |
|||
}, |
|||
debug: { |
|||
type: Boolean, |
|||
default() { |
|||
return false; |
|||
}, |
|||
}, |
|||
}, |
|||
components: { |
|||
wxParseTemplate, |
|||
}, |
|||
computed: { |
|||
wxParseData() { |
|||
const { content, image, debug } = this; |
|||
const transData = HtmlToJson(content, image, debug); |
|||
if (debug) console.log(JSON.stringify(transData, null, ' ')); |
|||
return transData; |
|||
}, |
|||
}, |
|||
}; |
|||
|
|||
</script> |
|||
|
|||
<style> |
|||
@import url("./wxParse.css"); |
|||
</style> |
|||
@ -0,0 +1,97 @@ |
|||
<template> |
|||
<view class="read-item"> |
|||
<view class="dot"></view> |
|||
<view class="content"> |
|||
<view class="title"> |
|||
<text>{{content.question_title}}</text> |
|||
</view> |
|||
<view class="guide"> |
|||
<text>{{content.answer_content}}</text> |
|||
</view> |
|||
<view class="author" v-if="content.author_list && content.author_list.length"> |
|||
<text>— {{content.author_list[0].user_name}}</text> |
|||
</view> |
|||
</view> |
|||
<view class="date">{{content.question_makettime}}</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
props: { |
|||
item: Object |
|||
}, |
|||
computed: { |
|||
content() { |
|||
const item = this.item |
|||
item.question_makettime = item.question_makettime.substr(5, 5).split('-').join('月') + '日' |
|||
return item |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.read-item { |
|||
width: 100%; |
|||
height: 300rpx; |
|||
position: relative; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
text-align: left; |
|||
} |
|||
.date { |
|||
width: 160rpx; |
|||
text-indent: 1em; |
|||
white-space: nowrap; |
|||
line-height: 70rpx; |
|||
} |
|||
.title { |
|||
line-height: 80rpx; |
|||
white-space: nowrap; |
|||
text-overflow: ellipsis; |
|||
overflow: hidden; |
|||
font-size: 36rpx; |
|||
} |
|||
.guide { |
|||
line-height: 38rpx; |
|||
height: 76rpx; |
|||
overflow: hidden; |
|||
} |
|||
.author { |
|||
text-align: right; |
|||
margin-right: 20rpx; |
|||
} |
|||
.content { |
|||
width: 540rpx; |
|||
height: 246rpx; |
|||
box-sizing: border-box; |
|||
border-radius: 10rpx; |
|||
background: linear-gradient(to bottom right, #9653fe, #507fff); |
|||
color: #fff; |
|||
position: relative; |
|||
margin-left: 20rpx; |
|||
padding: 2rpx 12rpx 0; |
|||
} |
|||
.content::before { |
|||
content: ''; |
|||
display: block; |
|||
width: 0; |
|||
height: 0; |
|||
border-width: 15rpx 12rpx; |
|||
border-style: solid; |
|||
border-color: transparent transparent transparent #6c6dfe; |
|||
position: absolute; |
|||
top: 20rpx; |
|||
right: -20rpx; |
|||
} |
|||
.dot { |
|||
width: 18rpx; |
|||
height: 18rpx; |
|||
background-color: rgba(108, 109, 254, .6); |
|||
position: absolute; |
|||
top: 26rpx; |
|||
right: 150rpx; |
|||
border-radius: 50%; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,98 @@ |
|||
<template> |
|||
<view class="read-item"> |
|||
<view class="dot"></view> |
|||
<view class="date">{{content.hp_makettime}}</view> |
|||
<view class="content"> |
|||
<view class="title"> |
|||
<text>{{content.hp_title}}</text> |
|||
</view> |
|||
<view class="guide"> |
|||
<text>{{content.guide_word}}</text> |
|||
</view> |
|||
<view class="author" v-if="content.author && content.author.length"> |
|||
<text>— {{content.author[0].user_name}}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
props: { |
|||
item: Object |
|||
}, |
|||
computed: { |
|||
content() { |
|||
const item = this.item |
|||
item.hp_makettime = item.hp_makettime.substr(5, 5).split('-').join('月') + '日' |
|||
return item |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.read-item { |
|||
width: 100%; |
|||
height: 300rpx; |
|||
position: relative; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
text-align: left; |
|||
} |
|||
.date { |
|||
width: 160rpx; |
|||
text-indent: 1em; |
|||
white-space: nowrap; |
|||
line-height: 70rpx; |
|||
} |
|||
.title { |
|||
line-height: 80rpx; |
|||
white-space: nowrap; |
|||
text-overflow: ellipsis; |
|||
overflow: hidden; |
|||
font-size: 36rpx; |
|||
} |
|||
.guide { |
|||
line-height: 38rpx; |
|||
height: 76rpx; |
|||
overflow: hidden; |
|||
} |
|||
.author { |
|||
text-align: right; |
|||
margin-right: 20rpx; |
|||
} |
|||
.content { |
|||
width: 540rpx; |
|||
height: 246rpx; |
|||
box-sizing: border-box; |
|||
border-radius: 10rpx; |
|||
/* background-color: #42adda; */ |
|||
background: linear-gradient(to bottom right, #9653fe, #507fff); |
|||
color: #fff; |
|||
position: relative; |
|||
margin-right: 20rpx; |
|||
padding: 2rpx 12rpx 0; |
|||
} |
|||
.content::before { |
|||
content: ''; |
|||
display: block; |
|||
width: 0; |
|||
height: 0; |
|||
border-width: 15rpx 12rpx; |
|||
border-style: solid; |
|||
border-color: transparent #9653fe transparent transparent; |
|||
position: absolute; |
|||
top: 20rpx; |
|||
left: -20rpx; |
|||
} |
|||
.dot { |
|||
width: 18rpx; |
|||
height: 18rpx; |
|||
background-color: rgba(150, 83, 254, .6); |
|||
position: absolute; |
|||
top: 26rpx; |
|||
left: 150rpx; |
|||
border-radius: 50%; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,13 @@ |
|||
import Vue from 'vue' |
|||
import App from './App' |
|||
import store from '@/store' |
|||
|
|||
Vue.config.productionTip = false |
|||
Vue.prototype.$store = store |
|||
App.mpType = 'app' |
|||
|
|||
const app = new Vue({ |
|||
store, |
|||
...App |
|||
}) |
|||
app.$mount() |
|||
@ -0,0 +1,53 @@ |
|||
{ |
|||
"name" : "uni-one", |
|||
"appid" : "__UNI__04F77E3", |
|||
"description" : "A uni-app project", |
|||
"versionName" : "1.0.0", |
|||
"versionCode" : "100", |
|||
"app-plus" : {/* App特有相关 */ |
|||
"modules" : {/* 模块配置 */ |
|||
|
|||
}, |
|||
"distribute" : {/* 应用发布信息 */ |
|||
"android" : {/* android打包配置 */ |
|||
"permissions" : [ |
|||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", |
|||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", |
|||
"<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>", |
|||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>", |
|||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>", |
|||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", |
|||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", |
|||
"<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>", |
|||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", |
|||
"<uses-permission android:name=\"android.permission.CAMERA\"/>", |
|||
"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>", |
|||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", |
|||
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>", |
|||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", |
|||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", |
|||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", |
|||
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>", |
|||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", |
|||
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>", |
|||
"<uses-feature android:name=\"android.hardware.camera\"/>", |
|||
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>", |
|||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" |
|||
] |
|||
}, |
|||
"ios" : {/* ios打包配置 */ |
|||
|
|||
}, |
|||
"sdkConfigs" : {/* SDK配置 */ |
|||
|
|||
} |
|||
} |
|||
}, |
|||
"quickapp" : {/* 快应用特有相关 */ |
|||
|
|||
}, |
|||
|
|||
"mp-weixin" : {/* 小程序特有相关 */ |
|||
"appid" : "" |
|||
} |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
{ |
|||
"pages": [ |
|||
{ |
|||
"path": "pages/home/main", |
|||
"style": { |
|||
"navigationBarTitleText": "首页" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/read/main", |
|||
"style": { |
|||
"navigationBarTitleText": "文章&问答" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/read/essay/main" |
|||
}, |
|||
{ |
|||
"path": "pages/read/question/main" |
|||
}, |
|||
{ |
|||
"path": "pages/movie/main", |
|||
"style": { |
|||
"navigationBarTitleText": "影评" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/movie/detail/main" |
|||
}, |
|||
{ |
|||
"path": "pages/daily/main", |
|||
"style": { |
|||
"navigationBarTitleText": "日报" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/daily/detail/main", |
|||
"style": { |
|||
"navigationBarTitleText": "日报" |
|||
} |
|||
} |
|||
], |
|||
"globalStyle": { |
|||
"backgroundTextStyle": "light", |
|||
"navigationBarBackgroundColor": "fafafa", |
|||
"navigationBarTitleText": "一个", |
|||
"navigationBarTextStyle": "black" |
|||
}, |
|||
"tabBar": { |
|||
"color": "#130f13", |
|||
"selectedColor": "#0f0f0f", |
|||
"list": [ |
|||
{ |
|||
"pagePath": "pages/home/main", |
|||
"text": "ONE", |
|||
"iconPath": "static/icon/home.png", |
|||
"selectedIconPath": "static/icon/home-active.png" |
|||
}, |
|||
{ |
|||
"pagePath": "pages/read/main", |
|||
"text": "READ", |
|||
"iconPath": "static/icon/read.png", |
|||
"selectedIconPath": "static/icon/read-active.png" |
|||
}, |
|||
{ |
|||
"pagePath": "pages/movie/main", |
|||
"text": "MOVIE", |
|||
"iconPath": "static/icon/movie.png", |
|||
"selectedIconPath": "static/icon/movie-active.png" |
|||
}, |
|||
{ |
|||
"pagePath": "pages/daily/main", |
|||
"text": "DAILY", |
|||
"iconPath": "static/icon/daily.png", |
|||
"selectedIconPath": "static/icon/daily-active.png" |
|||
} |
|||
] |
|||
} |
|||
} |
|||
@ -0,0 +1,145 @@ |
|||
<style scoped> |
|||
.cover { |
|||
width: 300rpx; |
|||
height: 300rpx; |
|||
margin: 20rpx auto 0; |
|||
border-radius: 16rpx; |
|||
box-shadow: 10rpx 10rpx 20rpx rgba(0, 0, 0, .2); |
|||
} |
|||
.control { |
|||
width: 80rpx; |
|||
height: 80rpx; |
|||
background-color: rgba(0, 0, 0, .4); |
|||
position: absolute; |
|||
top: 50%; |
|||
left: 50%; |
|||
border-radius: 50%; |
|||
margin: -60rpx 0 0 -40rpx; |
|||
} |
|||
.feeds_cover { |
|||
width: 100%; |
|||
height: 400rpx; |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
z-index: -1; |
|||
} |
|||
.control img { |
|||
width: 32rpx; |
|||
height: 32rpx; |
|||
position: absolute; |
|||
top: 50%; |
|||
left: 50%; |
|||
transform: translate(-50%, -50%); |
|||
} |
|||
.title { |
|||
font-size: 30rpx; |
|||
font-weight: bold; |
|||
height: 96rpx; |
|||
line-height: 48rpx; |
|||
margin-top: 40rpx; |
|||
text-align: center; |
|||
} |
|||
</style> |
|||
|
|||
<style> |
|||
.meta.div { |
|||
height: 120rpx; |
|||
padding-left: 40rpx; |
|||
position: relative; |
|||
} |
|||
.meta.div::before { |
|||
content: ''; |
|||
display: block; |
|||
position: absolute; |
|||
top: 12rpx; |
|||
left: 10rpx; |
|||
width: 0; |
|||
height: 0; |
|||
border-width: 12rpx 16rpx; |
|||
border-style: solid; |
|||
border-color: transparent transparent transparent #000; |
|||
animation: circle 1s infinite; |
|||
transform-origin: 25% 50%; |
|||
} |
|||
.meta.div::after { |
|||
content: ''; |
|||
display: block; |
|||
position: absolute; |
|||
top: 12rpx; |
|||
left: 10rpx; |
|||
width: 0; |
|||
height: 0; |
|||
border-width: 12rpx 16rpx; |
|||
border-style: solid; |
|||
border-color: transparent transparent transparent #000; |
|||
animation: fadeIn 1s infinite; |
|||
} |
|||
.meta.div .avatar { |
|||
display: none; |
|||
} |
|||
.view-more.div { |
|||
display: none; |
|||
} |
|||
@keyframes circle { |
|||
from { |
|||
transform: translateX(0); |
|||
opacity: 1; |
|||
} |
|||
to { |
|||
transform: translateX(16rpx); |
|||
opacity: 0; |
|||
} |
|||
} |
|||
@keyframes fadeIn { |
|||
from { |
|||
opacity: 0; |
|||
} |
|||
to { |
|||
opacity: 1; |
|||
} |
|||
} |
|||
</style> |
|||
|
|||
|
|||
<template> |
|||
<div class="article-container"> |
|||
<div class="title">{{title}}</div> |
|||
<wx-parse :content="body"></wx-parse> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import API from '@/utils/api' |
|||
import wxParse from '../../../components/mpvue-wxparse/wxParse.vue' |
|||
export default { |
|||
data() { |
|||
return { |
|||
loaded: false, |
|||
isPlay: false, |
|||
audioContext: null, |
|||
body: '', |
|||
title: '' |
|||
} |
|||
}, |
|||
components: { |
|||
wxParse |
|||
}, |
|||
onLoad(query) { |
|||
const id = query.id |
|||
if (!id) console.log('error coured no id find.') |
|||
this.getDetail(id) |
|||
}, |
|||
methods: { |
|||
async getDetail(id) { |
|||
const data = await API.getZhDtl(id) |
|||
this.body = data.body |
|||
this.title = data.title |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
|
|||
</style> |
|||
@ -0,0 +1,141 @@ |
|||
<style scoped> |
|||
li { |
|||
min-height: 160rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
border-bottom: 2rpx solid #fafbff; |
|||
padding: 0 20rpx; |
|||
} |
|||
li:active { |
|||
background-color: #f15549; |
|||
padding-left: 20rpx; |
|||
} |
|||
li:active .title, li:active .subtitle { |
|||
color: #fff; |
|||
} |
|||
li img { |
|||
width: 120rpx; |
|||
height: 120rpx; |
|||
border-radius: 50%; |
|||
display: block; |
|||
} |
|||
.desc { |
|||
width: 480rpx; |
|||
text-align: left; |
|||
line-height: 48rpx; |
|||
padding-left: 40rpx; |
|||
} |
|||
.title { |
|||
color: #7f7f7f; |
|||
font-size: 32rpx; |
|||
} |
|||
.play { |
|||
margin-left: 40rpx; |
|||
} |
|||
.play img { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
} |
|||
.slide-image { |
|||
width: 100%; |
|||
height: 100%; |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
} |
|||
.item-title { |
|||
position: absolute; |
|||
width: 100%; |
|||
box-sizing: border-box; |
|||
padding: 0 40rpx 60rpx; |
|||
font-size: 36rpx; |
|||
text-align: left; |
|||
color: #fff; |
|||
left: 0; |
|||
bottom: 0; |
|||
z-index: 11; |
|||
} |
|||
swiper { |
|||
width: 100%; |
|||
height: 400rpx; |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
} |
|||
swiper-item { |
|||
position: relative; |
|||
} |
|||
.layer { |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
z-index: 1; |
|||
background: linear-gradient(to bottom, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, .3)); |
|||
} |
|||
.list { |
|||
margin-top: 400rpx; |
|||
} |
|||
</style> |
|||
<template> |
|||
<div class="container"> |
|||
<swiper :indicator-dots="true" :autoplay="true" :circular="true"> |
|||
<block v-for="v in top_stories" :key="v.id"> |
|||
<swiper-item @tap="toDtl(v.id)"> |
|||
<image :src="v.image" class="slide-image" mode="center"/> |
|||
<div class="item-title">{{v.title}}</div> |
|||
<div class="layer"></div> |
|||
</swiper-item> |
|||
</block> |
|||
</swiper> |
|||
<ul class="list"> |
|||
<li v-for="v in all_stories" :key="v.id" @tap="toDtl(v.id)"> |
|||
<img :src="v.image" alt="cover"> |
|||
<div class="desc"> |
|||
<div class="title"> |
|||
{{v.title}} |
|||
</div> |
|||
</div> |
|||
<div class="play"> |
|||
<img src="/static/assert/arrow-right.png" alt="play"> |
|||
</div> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import API from '@/utils/api' |
|||
export default { |
|||
data() { |
|||
return { |
|||
top_stories: [], |
|||
stories: [], |
|||
innerAudioContext: '' |
|||
} |
|||
}, |
|||
mounted() { |
|||
this.getDailyList() |
|||
}, |
|||
computed: { |
|||
all_stories() { |
|||
return this.stories.map(v => ({ |
|||
...v, |
|||
image: v.images[0] |
|||
})) |
|||
} |
|||
}, |
|||
methods: { |
|||
async getDailyList() { |
|||
const res = await API.getZhList() |
|||
console.log(res.top_stories) |
|||
this.top_stories = res.top_stories |
|||
this.stories = res.stories |
|||
}, |
|||
toDtl(id) { |
|||
wx.navigateTo({ url: '/pages/daily/detail/main?id=' + id }) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
@ -0,0 +1,65 @@ |
|||
<template> |
|||
<div class="container"> |
|||
<image class="cover" :src="data.hp_img_url" mode="widthFix" /> |
|||
<view class="cover-author"> |
|||
<text class="gray">{{data.hp_author}}</text> |
|||
</view> |
|||
<view class="content"> |
|||
<text>{{content}}</text> |
|||
</view> |
|||
<view class="content-author"> |
|||
<text class="gray">{{data.text_authors}}</text> |
|||
</view> |
|||
<weather :weather="weather" v-if="weather.status === 'ok'"></weather> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { mapState, mapActions } from 'vuex' |
|||
import weather from './weather' |
|||
export default { |
|||
mounted() { |
|||
this.initPage() |
|||
}, |
|||
components: { |
|||
weather |
|||
}, |
|||
computed: { |
|||
...mapState('home', ['data']), |
|||
...mapState('weather', ['weather']), |
|||
content() { |
|||
return this.data.hp_content.split('by')[0] |
|||
} |
|||
}, |
|||
methods: { |
|||
...mapActions('home', ['getNewIds', 'getHomeData']), |
|||
async initPage() { |
|||
await this.getNewIds() |
|||
await this.getHomeData() |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.cover { |
|||
width: 100%; |
|||
} |
|||
.cover-author { |
|||
width: 100%; |
|||
height: 100rpx; |
|||
line-height: 100rpx; |
|||
margin-bottom: 30rpx; |
|||
} |
|||
.content { |
|||
width: 80%; |
|||
margin: 0 auto; |
|||
line-height: 58rpx; |
|||
text-align: left; |
|||
} |
|||
.content-author { |
|||
height: 100rpx; |
|||
line-height: 100rpx; |
|||
font-size: 20rpx; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,56 @@ |
|||
<template> |
|||
<div class="weather"> |
|||
<div class="date">{{day}}</div> |
|||
<div class="location"> |
|||
{{weather.basic.location}} |
|||
</div> |
|||
<img :src="'https://petrify.oss-cn-beijing.aliyuncs.com/weather/' + weather.now.cond_code + '.png'" alt=""> |
|||
<div class="cond-text">{{weather.now.cond_txt}}</div> |
|||
<div class="tmp"><span>{{weather.now.tmp}}°C</span></div> |
|||
<div class="fl">体感:<span>{{weather.now.fl}}°C</span></div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
const DAY_LIST = ['Sun', 'Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat'] |
|||
export default { |
|||
props: { |
|||
weather: Object |
|||
}, |
|||
computed: { |
|||
day() { |
|||
const day = new Date().getDay() |
|||
return DAY_LIST[day] |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.weather { |
|||
height: 170rpx; |
|||
width: 100%; |
|||
display: flex; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
font-size: 36rpx; |
|||
justify-content: space-around; |
|||
} |
|||
.date { |
|||
color: #ecc88e; |
|||
font-size: 48rpx; |
|||
font-weight: 700; |
|||
padding: 0 20rpx; |
|||
} |
|||
.location { |
|||
color: #b4b0ad; |
|||
} |
|||
|
|||
img { |
|||
width: 80rpx; |
|||
height: 80rpx; |
|||
} |
|||
.tmp span, .fl span { |
|||
color: #52b9b6; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,38 @@ |
|||
<template> |
|||
<block> |
|||
<v-article |
|||
:title="currentMovie.title" |
|||
:user_name="currentMovie.user ? currentMovie.user.user_name : ''" |
|||
:content="currentMovie.content || ''" |
|||
:summary="currentMovie.summary || ''" |
|||
></v-article> |
|||
</block> |
|||
</template> |
|||
|
|||
<script> |
|||
import { mapState, mapActions } from 'vuex' |
|||
import article from '@/components/article' |
|||
export default { |
|||
onLoad(query) { |
|||
this.clearMovieDetail() |
|||
const { id } = query |
|||
this.initPage(id) |
|||
}, |
|||
components: { |
|||
'v-article': article |
|||
}, |
|||
computed: { |
|||
...mapState('movie', ['currentMovie']) |
|||
}, |
|||
methods: { |
|||
...mapActions('movie', ['getMovieDetail', 'clearMovieDetail']), |
|||
async initPage(id) { |
|||
await this.getMovieDetail(id) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
|
|||
</style> |
|||
@ -0,0 +1,51 @@ |
|||
<template> |
|||
<div class="container"> |
|||
<navigator |
|||
v-for="v in movies" |
|||
:key="v.item_id" |
|||
:url="'/pages/movie/detail/main?id=' + v.item_id" |
|||
class="item" |
|||
> |
|||
<movie-detail :movie="v"></movie-detail> |
|||
</navigator> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { mapState, mapActions } from 'vuex' |
|||
import movieDetail from '@/components/movieItem' |
|||
export default { |
|||
mounted () { |
|||
this.initPage() |
|||
}, |
|||
components: { |
|||
movieDetail |
|||
}, |
|||
computed: { |
|||
...mapState('movie', ['movies']) |
|||
}, |
|||
methods: { |
|||
...mapActions('movie', ['getMovieList']), |
|||
async initPage() { |
|||
await this.getMovieList() |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
navigator { |
|||
width: 100%; |
|||
} |
|||
.item { |
|||
width: 100%; |
|||
height: 170rpx; |
|||
} |
|||
|
|||
.item:nth-child(odd) { |
|||
background-color: #e5e4df; |
|||
} |
|||
.item:nth-child(even) { |
|||
background-color: #eae9e4; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,34 @@ |
|||
<template> |
|||
<block> |
|||
<v-article |
|||
:title="readContent.essay.hp_title" |
|||
:user_name="readContent.essay.hp_author" |
|||
:content="readContent.essay.hp_content || ''" |
|||
></v-article> |
|||
</block> |
|||
</template> |
|||
|
|||
<script> |
|||
import article from '@/components/article' |
|||
import { mapState, mapActions } from 'vuex' |
|||
export default { |
|||
onLoad(params) { |
|||
this.clearReadContent({type: 'essay'}) |
|||
const { id } = params |
|||
this.getReadContent({ type: 'essay', id }) |
|||
}, |
|||
components: { |
|||
'v-article': article |
|||
}, |
|||
computed: { |
|||
...mapState('read', ['readContent']) |
|||
}, |
|||
methods: { |
|||
...mapActions('read', ['getReadContent', 'clearReadContent']) |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
|
|||
</style> |
|||
@ -0,0 +1,68 @@ |
|||
<template> |
|||
<div class="container"> |
|||
<view class="mode-title" v-if="readList.essay && readList.essay.length"> |
|||
<view class="mode-title-word"> |
|||
<text>阅读</text> |
|||
</view> |
|||
</view> |
|||
<navigator v-for="v in readList.essay" :key="v.content_id" :url="'/pages/read/essay/main?id=' + v.content_id"> |
|||
<read-list :item="v"></read-list> |
|||
</navigator> |
|||
<view class="mode-title" v-if="readList.question && readList.question.length"> |
|||
<view class="mode-title-word"> |
|||
<text>问答</text> |
|||
</view> |
|||
</view> |
|||
<navigator v-for="v in readList.question" :key="v.question_id" :url="'/pages/read/question/main?id=' + v.question_id"> |
|||
<question-list :item="v"></question-list> |
|||
</navigator> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { mapState, mapActions } from 'vuex' |
|||
import readList from '@/components/readList' |
|||
import questionList from '@/components/questionList' |
|||
export default { |
|||
mounted() { |
|||
this.getReadList() |
|||
}, |
|||
components: { |
|||
readList, |
|||
questionList |
|||
}, |
|||
computed: { |
|||
...mapState('read', ['readList']) |
|||
}, |
|||
methods: { |
|||
...mapActions('read', ['getReadList']) |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.mode-title { |
|||
height: 80rpx; |
|||
line-height: 80rpx; |
|||
position: relative; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.mode-title .mode-title-word { |
|||
width: 120rpx; |
|||
margin: 0 auto; |
|||
font-size: 36rpx; |
|||
background-color: #fff; |
|||
} |
|||
.mode-title:before { |
|||
content: ''; |
|||
display: block; |
|||
width: 80%; |
|||
height: 2rpx; |
|||
background-color: #b4b4b5; |
|||
position: absolute; |
|||
top: 50%; |
|||
left: 10%; |
|||
z-index: -1; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,92 @@ |
|||
<template> |
|||
<div class="wrapper"> |
|||
<view class="title"> |
|||
<text>{{detail.question_title}}</text> |
|||
</view> |
|||
<view class="ask"> |
|||
<view class="asker" v-if="detail.answerer"> |
|||
<text>{{detail.asker.user_name}}问:</text> |
|||
</view> |
|||
<view class="asker" v-else> |
|||
<text>网友问:</text> |
|||
</view> |
|||
<wx-parse :content="detail.question_content || ''"></wx-parse> |
|||
</view> |
|||
<view class="divider"></view> |
|||
<view class="answer"> |
|||
<view class="answerer" v-if="detail.answerer"> |
|||
<text>{{detail.answerer.user_name}}答:</text> |
|||
</view> |
|||
<view class="answerer" v-else> |
|||
<text>网友答:</text> |
|||
</view> |
|||
<wx-parse :content="detail.answer_content || ''"></wx-parse> |
|||
</view> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { mapState, mapActions } from 'vuex' |
|||
import wxParse from '../../../components/mpvue-wxparse/wxParse.vue' |
|||
export default { |
|||
onLoad(params) { |
|||
this.clearReadContent({type: 'question'}) |
|||
const { id } = params |
|||
this.getReadContent({ type: 'question', id }) |
|||
}, |
|||
components: { |
|||
wxParse |
|||
}, |
|||
computed: { |
|||
...mapState('read', ['readContent']), |
|||
detail() { |
|||
return this.readContent.question |
|||
} |
|||
}, |
|||
methods: { |
|||
...mapActions('read', ['getReadContent', 'clearReadContent']) |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.title { |
|||
font-size: 32rpx; |
|||
overflow: hidden; |
|||
text-align: center; |
|||
padding: .8em; |
|||
font-weight: bold; |
|||
} |
|||
.asker, .answerer { |
|||
font-size: 30rpx; |
|||
padding-left: 1em; |
|||
height: 60rpx; |
|||
} |
|||
.divider { |
|||
width: 90%; |
|||
margin: 15rpx auto; |
|||
position: relative; |
|||
height: 2rpx; |
|||
background-color: #b4b4b4; |
|||
} |
|||
.divider::before { |
|||
content: ''; |
|||
width: 50rpx; |
|||
height: 2rpx; |
|||
background-color: #fff; |
|||
position: absolute; |
|||
top: 0; |
|||
left: calc(50% - 25rpx); |
|||
} |
|||
.divider::after { |
|||
content: ''; |
|||
display: block; |
|||
width: 10rpx; |
|||
height: 10rpx; |
|||
border-radius: 50%; |
|||
background-color: #b4b4b4; |
|||
position: absolute; |
|||
top: -4rpx; |
|||
left: calc(50% - 5rpx); |
|||
} |
|||
</style> |
|||
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
@ -0,0 +1,33 @@ |
|||
import Vue from 'vue' |
|||
import Vuex from 'vuex' |
|||
|
|||
import home from './modules/home' |
|||
import movie from './modules/movie' |
|||
import read from './modules/read' |
|||
import weather from './modules/weather' |
|||
// import music from './modules/music'
|
|||
|
|||
Vue.use(Vuex) |
|||
|
|||
const store = new Vuex.Store({ |
|||
modules: { |
|||
home: { |
|||
namespaced: true, |
|||
...home |
|||
}, |
|||
movie: { |
|||
namespaced: true, |
|||
...movie |
|||
}, |
|||
read: { |
|||
namespaced: true, |
|||
...read |
|||
}, |
|||
weather: { |
|||
namespaced: true, |
|||
...weather |
|||
} |
|||
} |
|||
}) |
|||
|
|||
export default store |
|||
@ -0,0 +1,38 @@ |
|||
import { CHANGE_HOME_DATA, STORE_ID_LIST } from './../mutations_type' |
|||
import API from '@/utils/api' |
|||
|
|||
const state = { |
|||
ids: [], |
|||
data: { |
|||
hp_content: '', |
|||
hp_img_url: '', |
|||
hp_author: '', |
|||
text_authors: '' |
|||
} |
|||
} |
|||
|
|||
const mutations = { |
|||
[CHANGE_HOME_DATA](state, payload) { |
|||
state.data = payload.data |
|||
}, |
|||
[STORE_ID_LIST](state, payload) { |
|||
state.ids = payload.ids |
|||
} |
|||
} |
|||
|
|||
const actions = { |
|||
async getNewIds({ commit }) { |
|||
const { data } = await API.getNewIds() |
|||
commit(STORE_ID_LIST, { ids: data }) |
|||
}, |
|||
async getHomeData({ commit, state }) { |
|||
const { data } = await API.getHomeData(state.ids[0]) |
|||
commit(CHANGE_HOME_DATA, { data }) |
|||
} |
|||
} |
|||
|
|||
export default { |
|||
state, |
|||
mutations, |
|||
actions |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
import { CHANGE_MOVIE_LIST, CHANGE_CURRENT_MOVIE } from './../mutations_type' |
|||
import API from '@/utils/api' |
|||
|
|||
const state = { |
|||
movies: [], |
|||
currentMovie: {} |
|||
} |
|||
|
|||
const mutations = { |
|||
[CHANGE_MOVIE_LIST](state, payload) { |
|||
state.movies = payload.movies |
|||
}, |
|||
[CHANGE_CURRENT_MOVIE](state, payload) { |
|||
state.currentMovie = payload.data |
|||
} |
|||
} |
|||
|
|||
const actions = { |
|||
async getMovieList({ commit }) { |
|||
const { data } = await API.getMovieList() |
|||
commit(CHANGE_MOVIE_LIST, { movies: data }) |
|||
}, |
|||
async getMovieDetail({ commit, state }, id) { |
|||
const { data: { data } } = await API.getMovieDetail(id) |
|||
commit(CHANGE_CURRENT_MOVIE, { data: data[0] }) |
|||
}, |
|||
async getMovieArticleDetail({ commit, state }, id) { |
|||
const { data: { data } } = await API.getMovieArticleDetail(id) |
|||
commit(CHANGE_CURRENT_MOVIE, { data: data[0] }) |
|||
}, |
|||
clearMovieDetail({ commit }) { |
|||
commit(CHANGE_CURRENT_MOVIE, { data: {} }) |
|||
} |
|||
} |
|||
|
|||
export default { |
|||
state, |
|||
mutations, |
|||
actions |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
import API from '@/utils/api' |
|||
import { CHANGE_CURRENT_READ, CHANGE_READ_LIST } from './../mutations_type' |
|||
|
|||
const state = { |
|||
readList: { |
|||
essay: [], |
|||
question: [], |
|||
serial: [] |
|||
}, |
|||
readContent: { |
|||
essay: {}, |
|||
question: {} |
|||
} |
|||
} |
|||
|
|||
const mutations = { |
|||
[CHANGE_READ_LIST] (state, payload) { |
|||
state.readList = payload.data |
|||
}, |
|||
[CHANGE_CURRENT_READ] (state, payload) { |
|||
state.readContent[payload.type] = payload.data |
|||
} |
|||
} |
|||
|
|||
const actions = { |
|||
async getReadList({ commit, state }) { |
|||
const { data } = await API.getReadList() |
|||
commit(CHANGE_READ_LIST, { data }) |
|||
}, |
|||
async getReadContent({ commit, state }, { type, id }) { |
|||
const { data } = await API.getReadDetail(type, id) |
|||
commit(CHANGE_CURRENT_READ, { type, data }) |
|||
}, |
|||
async clearReadContent({ commit, state }, { type }) { |
|||
commit(CHANGE_CURRENT_READ, { type, data: {} }) |
|||
} |
|||
// async getReadComment({ commit, state }, id) {
|
|||
// // const { data } = await API.getReadComment(id)
|
|||
// // commit(CHANGE_CURRENT_READ, { data })
|
|||
// }
|
|||
} |
|||
|
|||
export default { |
|||
state, |
|||
mutations, |
|||
actions |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
import API from '@/utils/api' |
|||
import { SET_LOCATION, SET_WEATHER } from '../mutations_type' |
|||
const state = { |
|||
location: { |
|||
latitude: '', |
|||
longitude: '' |
|||
}, |
|||
weather: { |
|||
basic: {}, |
|||
now: {}, |
|||
update: {}, |
|||
status: '' |
|||
} |
|||
} |
|||
|
|||
const mutations = { |
|||
[SET_LOCATION] (state, payload) { |
|||
state.location = payload.location |
|||
}, |
|||
[SET_WEATHER] (state, payload) { |
|||
state.weather = payload.weather |
|||
} |
|||
} |
|||
|
|||
const actions = { |
|||
async getWeather({ state, commit }) { |
|||
console.log('getWeather') |
|||
const location = state.location |
|||
const data = await API.getWeather(`${location.latitude},${location.longitude}`) |
|||
commit(SET_WEATHER, { weather: data.result }) |
|||
} |
|||
} |
|||
|
|||
export default { |
|||
state, |
|||
mutations, |
|||
actions |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
export const CHANGE_HOME_DATA = 'CHANGE_HOME_DATA' |
|||
export const STORE_ID_LIST = 'STORE_ID_LIST' |
|||
|
|||
export const CHANGE_MOVIE_LIST = 'CHANGE_MOVIE_LIST' |
|||
export const CHANGE_CURRENT_MOVIE = 'CHANGE_CURRENT_MOVIE' |
|||
|
|||
export const CHANGE_READ_LIST = 'CHANGE_READ_LIST' |
|||
export const CHANGE_CURRENT_READ = 'CHANGE_CURRENT_READ' |
|||
|
|||
export const CHANGE_MUSIC_LIST = 'CHANGE_MUSIC_LIST' |
|||
export const CHANGE_CURRENT_MUSIC = 'CHANGE_CURRENT_MUSIC' |
|||
|
|||
export const SET_LOCATION = 'SET_LOCATION' |
|||
export const SET_WEATHER = 'SET_WEATHER' |
|||
@ -0,0 +1,29 @@ |
|||
import request from './request' |
|||
|
|||
const baseURL = `https://petrify.cc` |
|||
// const baseURL = 'http://192.168.29.238:7001'
|
|||
request.config.baseURL = baseURL |
|||
|
|||
const dailyRequest = (id) => request.post('/v1/daily', { |
|||
url: `/api/4/news/${id}` |
|||
}) |
|||
|
|||
const api = { |
|||
// picture
|
|||
getNewIds: () => request.get(`/v1/one?${encodeURI('url=/api/hp/idlist/0?version=3.5.0&platform=android')}`), |
|||
getHomeData: (id) => request.get(`/v1/one?${encodeURI('url=/api/hp/detail/' + id + '?version=3.5.0&platform=android')}`), |
|||
// read
|
|||
getReadList: () => request.get(`/v1/one?${encodeURI('url=/api/reading/index/?version=3.5.0&platform=android')}`), |
|||
getReadDetail: (type, id) => request.get(`/v1/one?${encodeURI('url=/api/' + type + '/' + id + '?version=3.5.0&platform=android')}`), |
|||
getReadComment: (id) => request.get(`/v1/one?${encodeURI('url=/api/comment/praiseandtime/essay/' + id + '/0?version=3.5.0&platform=android')}`), |
|||
getMovieList: () => request.post('/v1/two', { |
|||
url: '/api/channel/movie/more/0?channel=wdj&version=4.0.2&uuid=ffffffff-a90e-706a-63f7-ccf973aae5ee&platform=android' |
|||
}), |
|||
getMovieDetail: (id) => request.get(`/v1/one?${encodeURI('url=/api/movie/' + id + '/story/1/0?version=3.5.0&platform=android')}`), |
|||
getWeather: (location) => request.get(`/v1/weather?location=${location}`), |
|||
// 知乎日报
|
|||
getZhList: () => dailyRequest('latest'), |
|||
getZhDtl: (id) => dailyRequest(id) |
|||
} |
|||
|
|||
export default api |
|||
@ -0,0 +1,29 @@ |
|||
import Fly from 'flyio/dist/npm/wx' |
|||
|
|||
const request = new Fly() |
|||
|
|||
const errorPrompt = (err) => { |
|||
wx.showToast({ |
|||
title: err.message || 'fetch data error.', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
|
|||
request.interceptors.request.use((request) => { |
|||
wx.showNavigationBarLoading() |
|||
return request |
|||
}) |
|||
|
|||
request.interceptors.response.use((response, promise) => { |
|||
wx.hideNavigationBarLoading() |
|||
// if (!(response && response.data && response.data.res === 0)) {
|
|||
// errorPrompt(response)
|
|||
// }
|
|||
return promise.resolve(response.data) |
|||
}, (err, promise) => { |
|||
wx.hideNavigationBarLoading() |
|||
errorPrompt(err) |
|||
return promise.reject(err) |
|||
}) |
|||
|
|||
export default request |
|||
@ -0,0 +1,25 @@ |
|||
<script> |
|||
export default { |
|||
onLaunch: function () { |
|||
console.log('App Launch') |
|||
}, |
|||
onShow: function () { |
|||
console.log('App Show') |
|||
}, |
|||
onHide: function () { |
|||
console.log('App Hide') |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
/*每个页面公共css */ |
|||
page { |
|||
min-height: 100%; |
|||
background: #ECECEC; |
|||
} |
|||
.title{ |
|||
padding: 10px; |
|||
text-align: center; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,12 @@ |
|||
# 小程序自定义组件使用示例 |
|||
|
|||
## 注意事项 |
|||
* 小程序自定义组件需放在wxcomponents目录 |
|||
* 使用的组件需添加到pages.json文件内 |
|||
* 原组件内事件名称需修改为驼峰命名,如: |
|||
```js |
|||
// wxcomponents/vant-weapp/dist/nav-bar/index.js |
|||
this.$emit('click-left'); |
|||
// 改为 |
|||
this.$emit('clickLeft'); |
|||
``` |
|||
@ -0,0 +1,11 @@ |
|||
import Vue from 'vue' |
|||
import App from './App' |
|||
|
|||
Vue.config.productionTip = false |
|||
|
|||
App.mpType = 'app' |
|||
|
|||
const app = new Vue({ |
|||
...App |
|||
}) |
|||
app.$mount() |
|||
@ -0,0 +1,55 @@ |
|||
{ |
|||
"name" : "wxcomponents-template", |
|||
"appid" : "__UNI__773C51E", |
|||
"description": "", |
|||
"versionName": "1.0.0", |
|||
"versionCode": "100", |
|||
"transformPx":false, |
|||
"app-plus": { /* App特有相关 */ |
|||
"modules": { /* 模块配置 */ |
|||
|
|||
}, |
|||
"distribute": { /* 应用发布信息 */ |
|||
"android": { /* android打包配置 */ |
|||
"permissions": ["<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", |
|||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", |
|||
"<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>", |
|||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>", |
|||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>", |
|||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", |
|||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", |
|||
"<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>", |
|||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", |
|||
"<uses-permission android:name=\"android.permission.CAMERA\"/>", |
|||
"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>", |
|||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", |
|||
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>", |
|||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", |
|||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", |
|||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", |
|||
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>", |
|||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", |
|||
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>", |
|||
"<uses-feature android:name=\"android.hardware.camera\"/>", |
|||
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>", |
|||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" |
|||
] |
|||
}, |
|||
"ios": { /* ios打包配置 */ |
|||
|
|||
}, |
|||
"sdkConfigs": { /* SDK配置 */ |
|||
|
|||
} |
|||
} |
|||
}, |
|||
"quickapp": { /* 快应用特有相关 */ |
|||
|
|||
}, |
|||
"mp-weixin": { /* 小程序特有相关 */ |
|||
"appid": "", |
|||
"setting" : { |
|||
"urlCheck" : true |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
{ |
|||
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages |
|||
{ |
|||
"path": "pages/index/index", |
|||
"style": { |
|||
"navigationBarTitleText": "小程序组件示例" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/vant/vant", |
|||
"style": { |
|||
"navigationBarTitleText": "vant组件示例", |
|||
"usingComponents": { |
|||
"van-nav-bar": "/wxcomponents/vant-weapp/dist/nav-bar/index", |
|||
"van-icon": "/wxcomponents/vant-weapp/dist/icon/index" |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/wux/wux", |
|||
"style": { |
|||
"navigationBarTitleText": "wux组件示例", |
|||
"usingComponents": { |
|||
"wux-calendar": "/wxcomponents/wux-weapp/dist/calendar/index", |
|||
"wux-cell-group": "/wxcomponents/wux-weapp/dist/cell-group/index", |
|||
"wux-cell": "/wxcomponents/wux-weapp/dist/cell/index" |
|||
} |
|||
} |
|||
} |
|||
], |
|||
"globalStyle": { |
|||
"navigationBarTextStyle": "black", |
|||
"navigationBarTitleText": "小程序组件示例", |
|||
"navigationBarBackgroundColor": "#F8F8F8", |
|||
"backgroundColor": "#F8F8F8" |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
<template> |
|||
<view class="content"> |
|||
<navigator url="../vant/vant"> |
|||
<button type="default">vant组件示例</button> |
|||
</navigator> |
|||
<navigator url="../wux/wux"> |
|||
<button type="default">wux组件示例</button> |
|||
</navigator> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
button { |
|||
margin-top: 20px; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,32 @@ |
|||
<template> |
|||
<view> |
|||
<view class="title">导航栏</view> |
|||
<van-nav-bar title="标题" left-text="返回" left-arrow @clickLeft="onClickLeft" @clickRight="onClickRight"> |
|||
<van-icon name="search" slot="right" custom-class="icon" /> |
|||
</van-nav-bar> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
methods: { |
|||
onClickLeft() { |
|||
wx.showToast({ |
|||
title: '点击返回', |
|||
icon: 'none' |
|||
}); |
|||
}, |
|||
|
|||
onClickRight() { |
|||
wx.showToast({ |
|||
title: '点击按钮', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
|
|||
</style> |
|||
@ -0,0 +1,80 @@ |
|||
<template> |
|||
<view> |
|||
<view class="title">日历组件</view> |
|||
<wux-calendar id="wux-calendar" /> |
|||
|
|||
<wux-cell-group title="Calendar"> |
|||
<wux-cell title="单选" :extra="value1" @click="openCalendar1"></wux-cell> |
|||
<wux-cell title="多选" :extra="value2" @click="openCalendar2"></wux-cell> |
|||
<wux-cell title="Direction = Vertical" :extra="value3" @click="openCalendar3"></wux-cell> |
|||
<wux-cell title="MinDate & MaxDate" :extra="value4" @click="openCalendar4"></wux-cell> |
|||
</wux-cell-group> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { |
|||
$wuxCalendar |
|||
} from '@/wxcomponents/wux-weapp/dist/index.js' |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
value1: [], |
|||
value2: [], |
|||
value3: [], |
|||
value4: [], |
|||
} |
|||
}, |
|||
methods: { |
|||
openCalendar1() { |
|||
$wuxCalendar().open({ |
|||
value: this.value1, |
|||
onChange: (values, displayValues) => { |
|||
console.log('onChange', values, displayValues) |
|||
this.value1 = displayValues |
|||
}, |
|||
}) |
|||
}, |
|||
openCalendar2() { |
|||
$wuxCalendar().open({ |
|||
value: this.value2, |
|||
multiple: true, |
|||
onChange: (values, displayValues) => { |
|||
console.log('onChange', values, displayValues) |
|||
this.value2 = displayValues |
|||
}, |
|||
}) |
|||
}, |
|||
openCalendar3() { |
|||
$wuxCalendar().open({ |
|||
value: this.value3, |
|||
direction: 'vertical', |
|||
onChange: (values, displayValues) => { |
|||
console.log('onChange', values, displayValues) |
|||
this.value3 = displayValues |
|||
}, |
|||
}) |
|||
}, |
|||
openCalendar4() { |
|||
const now = new Date() |
|||
const minDate = now.getTime() |
|||
const maxDate = now.setDate(now.getDate() + 7) |
|||
|
|||
$wuxCalendar().open({ |
|||
value: this.value4, |
|||
minDate, |
|||
maxDate, |
|||
onChange: (values, displayValues) => { |
|||
console.log('onChange', values, displayValues) |
|||
this.value4 = displayValues |
|||
}, |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
|
|||
</style> |
|||
@ -0,0 +1,29 @@ |
|||
const hasOwn = {}.hasOwnProperty; |
|||
|
|||
export function classNames() { |
|||
const classes = []; |
|||
|
|||
for (let i = 0; i < arguments.length; i++) { |
|||
const arg = arguments[i]; |
|||
if (!arg) continue; |
|||
|
|||
const argType = typeof arg; |
|||
|
|||
if (argType === 'string' || argType === 'number') { |
|||
classes.push(arg); |
|||
} else if (Array.isArray(arg) && arg.length) { |
|||
const inner = classNames.apply(null, arg); |
|||
if (inner) { |
|||
classes.push(inner); |
|||
} |
|||
} else if (argType === 'object') { |
|||
for (const key in arg) { |
|||
if (hasOwn.call(arg, key) && arg[key]) { |
|||
classes.push(key); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
return classes.join(' '); |
|||
}; |
|||