初始化my文件
6
uni_modules/UniDevTools/LICENSE
Normal file
@@ -0,0 +1,6 @@
|
||||
个人可免费商用
|
||||
企业可免费商用
|
||||
如果你只是想分享本组件库及其教程或者知识点,请保留版权链接 dev.api0.cn
|
||||
不可以复制本工具库的任何文档至第三方站点,以此形成第三方文档库。这样会扰乱正常官网的声誉,因为文档是不定期的经常更新。
|
||||
贡献者提供的组件,一并适用第1,2点。
|
||||
请尊重作者的辛苦,本工具库耗费大量时间维护。
|
||||
201
uni_modules/UniDevTools/LICENSE.txt
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
108
uni_modules/UniDevTools/README.MD
Normal file
@@ -0,0 +1,108 @@
|
||||
# UniDevTools - UniApp调试工具
|
||||
|
||||
><b>在线文档 - 完整版使用教程:<br>
|
||||
><a href="https://dev.api0.cn" target="_blank">https://dev.api0.cn</a>
|
||||
|
||||
----
|
||||
|
||||
不知道大家是否和我一样已经受够了使用Uniapp开发APP时需要插USB线连着HbuilderX才能看到console打印,各项指标数据全是黑盒,开发APP时无法选择调试节点等各类头疼问题。Uniapp官方一直没有一个标准的调试工具,为什么不能像Chrome调试工具一样调试App呢?为此我们开发了一个插件,把这些痛点问题依次解决!
|
||||
|
||||
UniDevTools是一个UniApp工具库,包含console打印日志、request请求记录、storage缓存管理、vuex状态管理、框架报错记录、文件管理等多功能调试工具合集:
|
||||
- `Tools` 常用工具(重启、请求构建、跳转指定页面、注入VConsole、Eruda)、自定义工具页
|
||||
- `Error` 全局报错拦截:vue模板报错、uniapp框架报错
|
||||
- `Console` 打印日志记录
|
||||
- `Network` request请求记录、重发请求
|
||||
- `JsRunner` 执行js代码、支持App和H5端
|
||||
- `Storage` 缓存管理,支持localStorage、cookie、sessionStorage
|
||||
- `Pages` 路由页面管理、日活时间记录
|
||||
- `Vuex` 状态管理,支持`Vuex`、`Pinia`、`globalData`
|
||||
- `Logs` 框架运行日志、框架api调用日志、自定义上报的日志记录
|
||||
- `Info` 当前设备参数、App启动参数、运行时信息、权限列表
|
||||
- `UniBus` uni框架事件总线调用记录
|
||||
- `FileSys` 本地文件管理系统
|
||||
- `Setting` DevTools工具设置、清空全部缓存、导出全部日志
|
||||
|
||||
兼容框架:
|
||||
| Vue2+js+vuex | Vue3+ts+vuex(pinia)|
|
||||
| -- | -- |
|
||||
| √ | √ |
|
||||
|
||||
兼容平台:
|
||||
| H5 | APP | 微信小程序 | APP-NVUE | 其他小程序 | UniAppX |
|
||||
| -- | -- | -- | -- | -- | -- |
|
||||
| √| √ | √ | √(大部分功能支持) | 未测试 | × (待办中) |
|
||||
|
||||
><b>本工具支持在<span style="color: red;">生产环境</span>中使用</b><br>
|
||||
><span style="font-size: 13px;">生产环境使用时建议隐藏调试浮窗,可设置通过特定方法进入调试页</span>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 下载运行本示例项目使用说明:
|
||||
|
||||
>注意该项目使用Cli模式搭建,请勿导入HBuilderX中运行<br>
|
||||
>开始前请先准备好环境:`node18` + `pnpm`
|
||||
|
||||
1. 克隆项目到本地:
|
||||
```cmd
|
||||
git clone https://github.com/1615958039/UniDevTools.git
|
||||
```
|
||||
2. 进入项目
|
||||
```cmd
|
||||
cd ./UniDevTools
|
||||
```
|
||||
3. 安装依赖
|
||||
```
|
||||
pnpm i
|
||||
```
|
||||
4. 启动预览H5
|
||||
```
|
||||
pnpm run dev:h5
|
||||
```
|
||||
|
||||
说明:示例基于UniApp+Vue3+ts+Vite,UI框架为TM-UI3.1
|
||||
|
||||
|
||||
|
||||
## 把调试工具引入自己项目:
|
||||
1. 下载最新源码包`v3.5.0`
|
||||
>GitHub: [releases](https://github.com/1615958039/UniDevTools/releases) <br>
|
||||
>Gitee: [releases](https://gitee.com/t1zf/UniAppDevTools/releases/) <br>
|
||||
>备用:[v3.5.0_r.zip](https://dev.api0.cn/releases/v3.5.0_r.zip)
|
||||
1. 引入项目(请移步至在线文档):[https://dev.api0.cn/guide/install](https://dev.api0.cn/guide/install)
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 示例项目
|
||||
>《斗图助手APP》表情包制作工具<br>
|
||||
>该项目基于Uniapp+Vue2,已集成DevTools工具<br>
|
||||
>打开APP后在搜索页输入` __devtools__ `即可打开调试弹窗查看请求和日志数据
|
||||
<div class="qrCodeList" style="display: flex;flex-direction: row; align-items: center;gap: 30px;flex-wrap: wrap;">
|
||||
<div class="codeItem">
|
||||
<img src="https://dev.api0.cn/qrCode/iosAppStore.png" style="width: 160px;height: 160px;">
|
||||
<div class="codeTitle">苹果AppStore</div>
|
||||
</div>
|
||||
<br><br><br>
|
||||
<div class="codeItem">
|
||||
<img src="https://dev.api0.cn/qrCode/androidYYB.png" style="width: 160px;height: 160px;">
|
||||
<div class="codeTitle">安卓应用宝</div>
|
||||
</div>
|
||||
<br><br><br>
|
||||
<div class="codeItem">
|
||||
<img src="https://dev.api0.cn/qrCode/wxmp.jpg" style="width: 160px;height: 160px;">
|
||||
<div class="codeTitle">微信小程序</div>
|
||||
</div>
|
||||
<br><br><br>
|
||||
<div class="codeItem">
|
||||
<img src="https://dev.api0.cn/qrCode/web.png" style="width: 160px;height: 160px;">
|
||||
<div class="codeTitle">H5</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
18
uni_modules/UniDevTools/index.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
|
||||
<title></title>
|
||||
<!--preload-links-->
|
||||
<!--app-context-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
11402
uni_modules/UniDevTools/package-lock.json
generated
Normal file
81
uni_modules/UniDevTools/package.json
Normal file
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"name": "UniDevTools开发调试神器支持vue2和3支持vuex和pinia",
|
||||
"version": "3.6",
|
||||
"scripts": {
|
||||
"dabao": "node buildTmui/build.js 3.1.09",
|
||||
"dev:app": "uni -p app",
|
||||
"dev:custom": "uni -p",
|
||||
"dev:h5": "uni",
|
||||
"dev:h5:ssr": "uni --ssr",
|
||||
"dev:mp-alipay": "uni -p mp-alipay",
|
||||
"dev:mp-baidu": "uni -p mp-baidu",
|
||||
"dev:mp-kuaishou": "uni -p mp-kuaishou",
|
||||
"dev:mp-lark": "uni -p mp-lark",
|
||||
"dev:mp-qq": "uni -p mp-qq",
|
||||
"dev:mp-toutiao": "uni -p mp-toutiao",
|
||||
"dev:mp-weixin": "uni -p mp-weixin",
|
||||
"dev:quickapp-webview": "uni -p quickapp-webview",
|
||||
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
|
||||
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
|
||||
"build:app": "uni build -p app",
|
||||
"build:custom": "uni build -p",
|
||||
"build:h5": "uni build",
|
||||
"build:h5:ssr": "uni build --ssr",
|
||||
"build:mp-alipay": "uni build -p mp-alipay",
|
||||
"build:mp-baidu": "uni build -p mp-baidu",
|
||||
"build:mp-kuaishou": "uni build -p mp-kuaishou",
|
||||
"build:mp-lark": "uni build -p mp-lark",
|
||||
"build:mp-qq": "uni build -p mp-qq",
|
||||
"build:mp-toutiao": "uni build -p mp-toutiao",
|
||||
"build:mp-weixin": "uni build -p mp-weixin",
|
||||
"build:quickapp-webview": "uni build -p quickapp-webview",
|
||||
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
|
||||
"build:quickapp-webview-union": "uni build -p quickapp-webview-union"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dcloudio/uni-app": "3.0.0-3090520231028001",
|
||||
"@dcloudio/uni-app-plus": "3.0.0-3090520231028001",
|
||||
"@dcloudio/uni-components": "3.0.0-3090520231028001",
|
||||
"@dcloudio/uni-h5": "3.0.0-3090520231028001",
|
||||
"@dcloudio/uni-helper-json": "^1.0.13",
|
||||
"@dcloudio/uni-mp-alipay": "3.0.0-3090520231028001",
|
||||
"@dcloudio/uni-mp-baidu": "3.0.0-3090520231028001",
|
||||
"@dcloudio/uni-mp-kuaishou": "3.0.0-3090520231028001",
|
||||
"@dcloudio/uni-mp-lark": "3.0.0-3090520231028001",
|
||||
"@dcloudio/uni-mp-qq": "3.0.0-3090520231028001",
|
||||
"@dcloudio/uni-mp-toutiao": "3.0.0-3090520231028001",
|
||||
"@dcloudio/uni-mp-weixin": "3.0.0-3090520231028001",
|
||||
"@dcloudio/uni-quickapp-webview": "3.0.0-3090520231028001",
|
||||
"echarts": "5.4.2",
|
||||
"pinia": "2.0.36",
|
||||
"sass": "^1.77.6",
|
||||
"vue": "3.2.47",
|
||||
"vue-i18n": "9.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dcloudio/types": "3.3.2",
|
||||
"@dcloudio/uni-automator": "3.0.0-3090520231028001",
|
||||
"@dcloudio/uni-cli-shared": "3.0.0-3090520231028001",
|
||||
"@dcloudio/vite-plugin-uni": "3.0.0-3090520231028001",
|
||||
"@types/node": "^17.0.35",
|
||||
"autoprefixer": "10.4.14",
|
||||
"unplugin-vue-components": "0.24.1",
|
||||
"vite": "4.2.1"
|
||||
},
|
||||
"id": "jie-UniDevTools",
|
||||
"displayName": "UniDevTools开发调试神器支持vue2和3支持vuex和pinia",
|
||||
"description": "UniDevTools跨平台多功能免费调试工具,包含console打印日志、request请求记录、storage缓存管理、vuex状态管理、框架报错记录、文件管理等多功能调试工具合集",
|
||||
"keywords": [
|
||||
"console",
|
||||
"调试工具",
|
||||
"vconsole",
|
||||
"debug",
|
||||
"devtools"
|
||||
],
|
||||
"dcloudext": {
|
||||
"category": [
|
||||
"uni-app前端模板",
|
||||
"uni-app前端项目模板"
|
||||
]
|
||||
}
|
||||
}
|
||||
7415
uni_modules/UniDevTools/pnpm-lock.yaml
generated
Normal file
9
uni_modules/UniDevTools/postcss.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const {
|
||||
uniPostcssPlugin
|
||||
} = require('@dcloudio/uni-cli-shared')
|
||||
module.exports = {
|
||||
plugins: [
|
||||
uniPostcssPlugin(),
|
||||
require('autoprefixer')()
|
||||
]
|
||||
}
|
||||
74
uni_modules/UniDevTools/src/App.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
globalData: {
|
||||
test: {
|
||||
a: 1,
|
||||
b: [1,2,3,4]
|
||||
},
|
||||
b: "红红火火恍恍惚惚",
|
||||
c: Math.random(),
|
||||
t: new Date().getTime(),
|
||||
testArray: [
|
||||
Math.random(),
|
||||
[Math.random(),Math.random(),Math.random(),Math.random(),],
|
||||
Math.random(),Math.random(),Math.random(),
|
||||
{
|
||||
[Math.random()]: 123
|
||||
},
|
||||
{
|
||||
a: [
|
||||
{
|
||||
b: [
|
||||
{
|
||||
c: [
|
||||
{
|
||||
d: [
|
||||
{
|
||||
e: [
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onError, onLaunch } from '@dcloudio/uni-app';
|
||||
onError((err)=>{
|
||||
try {
|
||||
// 挂载devTools全局报错拦截
|
||||
uni.$dev.errorReport(err, "at App.vue onError", "oe");
|
||||
} catch (error) {}
|
||||
})
|
||||
onLaunch((ctx) => {
|
||||
try {
|
||||
// 挂载APP启动日志提交
|
||||
uni.$dev.logReport("appOnLaunch>" + JSON.stringify(ctx));
|
||||
} catch (error) {}
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
<style>
|
||||
/* #ifdef APP-PLUS-NVUE */
|
||||
@import './tmui/scss/nvue.css';
|
||||
/* #endif */
|
||||
/* #ifndef APP-PLUS-NVUE */
|
||||
@import './tmui/scss/noNvue.css';
|
||||
/* #endif */
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
3
uni_modules/UniDevTools/src/androidPrivacy.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"prompt": "template"
|
||||
}
|
||||
435
uni_modules/UniDevTools/src/demo/index.vue
Normal file
@@ -0,0 +1,435 @@
|
||||
<template>
|
||||
<view class="demoIndex">
|
||||
<view class="hello">Hello</view>
|
||||
|
||||
<tm-card title="DevTools">
|
||||
<template #status>
|
||||
<view @click="goUrl('https://github.com/1615958039/UniDevTools')">
|
||||
<tm-text label="GitHub" :font-size="26" color="orange"></tm-text>
|
||||
</view>
|
||||
</template>
|
||||
<template #content>
|
||||
<view class="vertical">
|
||||
<view @click="goUrl('https://dev.api0.cn')">
|
||||
<text class="row">在线文档:<text style="color: #ff2d55">https://dev.api0.cn</text></text>
|
||||
</view>
|
||||
<view>
|
||||
<tm-text
|
||||
label="这是一个开发给UniApp框架兼容Vue2、Vue3的调试工具库,支持在线上生产环境中使用!"
|
||||
:font-size="26"
|
||||
color="cyan"
|
||||
></tm-text>
|
||||
</view>
|
||||
<view>
|
||||
<tm-text label="只需要简单几步即可在你的项目中引入本工具,详情请移步在线文档" :font-size="26" color="orange"></tm-text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<template #action>
|
||||
<view class="flex flex-row flex-row-center-end flex-1">
|
||||
<tm-button
|
||||
@click="goUrl('https://dev.api0.cn')"
|
||||
:round="24"
|
||||
color="red"
|
||||
label="在线文档"
|
||||
:font-size="24"
|
||||
:width="180"
|
||||
:height="64"
|
||||
></tm-button>
|
||||
<tm-button @click="goQun" :round="24" :margin="[24, 0]" label="QQ群" :font-size="24" :width="120" :height="64"></tm-button>
|
||||
</view>
|
||||
</template>
|
||||
</tm-card>
|
||||
|
||||
<tm-card title="Start 开始">
|
||||
<template #content>
|
||||
<view class="btnList">
|
||||
<tm-button label="一键生成全部测数据" @click="rand()" :width="290" size="small" :round="20"></tm-button>
|
||||
<tm-button label="打开Dev弹窗" @click="open" :width="220" color="blue" size="small" :round="20"></tm-button>
|
||||
</view>
|
||||
</template>
|
||||
</tm-card>
|
||||
|
||||
<tm-card title="Error 错误拦截示例">
|
||||
<template #content>
|
||||
<view class="btnList">
|
||||
<tm-button label="常规js运行报错" @click="showError" :width="290" color="orange" size="small" :round="20"></tm-button>
|
||||
<tm-button label="vue渲染报错" @click="showTemplateError" :width="290" color="red" size="small" :round="20"></tm-button>
|
||||
<template v-if="isShowTemplateError">
|
||||
<view :test="testModel" />
|
||||
</template>
|
||||
<tm-text label="打开调试页后可看到具体报错日志" :font-size="20" color="red"></tm-text>
|
||||
</view>
|
||||
</template>
|
||||
</tm-card>
|
||||
|
||||
<tm-card title="Console 打印示例">
|
||||
<template #content>
|
||||
<view class="btnList">
|
||||
<tm-button label="打印普通对象" @click="print('def')" :width="290" size="small" :round="20"></tm-button>
|
||||
<tm-button label="console.info()" @click="print('info')" :width="290" color="grey" size="small" :round="20"></tm-button>
|
||||
<tm-button label="console.warn()" @click="print('warn')" :width="290" color="orange" size="small" :round="20"></tm-button>
|
||||
<tm-button label="console.error()" @click="print('error')" :width="290" color="red" size="small" :round="20"></tm-button>
|
||||
|
||||
<tm-button label="打印数组" @click="print('array')" color="red" size="small" :round="20"></tm-button>
|
||||
<tm-button label="打印对象" @click="print('object')" color="pink" size="small" :round="20"></tm-button>
|
||||
<tm-button label="打印JSON字符串" @click="print('json')" color="orange" :width="220" size="small" :round="20"></tm-button>
|
||||
</view>
|
||||
</template>
|
||||
</tm-card>
|
||||
|
||||
<tm-card title="Storage 本地缓存">
|
||||
<template #content>
|
||||
<view class="btnList">
|
||||
<tm-button label="随机添加一个KEY" @click="storage('add')" :width="290" size="small" :round="20"></tm-button>
|
||||
</view>
|
||||
</template>
|
||||
</tm-card>
|
||||
|
||||
<tm-card title="Network 网络请求">
|
||||
<template #content>
|
||||
<view class="btnList">
|
||||
<tm-button label="发送GET请求" @click="sendReq('get')" :width="290" size="small" :round="20"></tm-button>
|
||||
<tm-button label="POST" @click="sendReq('post')" :width="290" color="red" size="small" :round="20"></tm-button>
|
||||
</view>
|
||||
</template>
|
||||
</tm-card>
|
||||
|
||||
<tm-card title="Pinia 全局变量">
|
||||
<template #content>
|
||||
<view class="vertical">
|
||||
<tm-text :label="'Pinia: test.model = ' + testStore.model" :font-size="24" color="red"></tm-text>
|
||||
<tm-button label="更改model的值" @click="changeModel" :width="290" size="small" :round="20"></tm-button>
|
||||
</view>
|
||||
</template>
|
||||
</tm-card>
|
||||
|
||||
<tm-card title="EventBus 事件总线监听">
|
||||
<template #content>
|
||||
<view class="vertical">
|
||||
<tm-button label="生成测试数据" @click="eventBusTest" :width="290" size="small" :round="20"></tm-button>
|
||||
</view>
|
||||
</template>
|
||||
</tm-card>
|
||||
|
||||
<view class="divider">
|
||||
<tm-divider :border="8" label="分割线(下方为tm-ui示例项目)"></tm-divider>
|
||||
</view>
|
||||
|
||||
<tm-notification placement="top" ref="msg" label="消息提醒"></tm-notification>
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useTestStore } from '@/stores/test'
|
||||
import { ref } from 'vue'
|
||||
const msg = ref()
|
||||
|
||||
/**
|
||||
* 测试Pinia
|
||||
*/
|
||||
const testStore = useTestStore()
|
||||
|
||||
const isShowTemplateError = ref(false)
|
||||
|
||||
let isCanTips = true
|
||||
function tips(data = '') {
|
||||
if (!isCanTips) return
|
||||
// @ts-ignore
|
||||
msg.value.show({
|
||||
label: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开调试弹窗
|
||||
*/
|
||||
function open() {
|
||||
// @ts-ignore
|
||||
uni.$dev.show()
|
||||
}
|
||||
|
||||
/**
|
||||
* 报错示例
|
||||
*/
|
||||
function showError(e = '') {
|
||||
const ee = () => {
|
||||
// @ts-ignore
|
||||
aaaa()
|
||||
}
|
||||
try {
|
||||
ee()
|
||||
} catch (error: any) {
|
||||
tips('运行成功:' + error?.message ?? 'js运行出错')
|
||||
}
|
||||
ee()
|
||||
}
|
||||
|
||||
function showTemplateError() {
|
||||
tips('操作成功!请进入调试工具Error中查看')
|
||||
isShowTemplateError.value = true
|
||||
setTimeout(() => {
|
||||
isShowTemplateError.value = false
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
/**
|
||||
* console 打印
|
||||
*/
|
||||
function print(type = '') {
|
||||
let obj = {
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: 3,
|
||||
d: [{ e: 1, f: [{ s: '0' }] }],
|
||||
[Math.random()]: Math.random(),
|
||||
[Math.random()]: Math.random(),
|
||||
[Math.random()]: Math.random(),
|
||||
[Math.random()]: Math.random(),
|
||||
[Math.random()]: Math.random(),
|
||||
[Math.random()]: Math.random(),
|
||||
[Math.random()]: Math.random(),
|
||||
[Math.random()]: Math.random(),
|
||||
[Math.random()]: Math.random(),
|
||||
[Math.random()]: Math.random(),
|
||||
[Math.random()]: Math.random(),
|
||||
[Math.random()]: Math.random(),
|
||||
[Math.random()]: Math.random(),
|
||||
[Math.random()]: Math.random(),
|
||||
[Math.random()]: Math.random(),
|
||||
[Math.random()]: Math.random(),
|
||||
testFun: (args: any) => {
|
||||
console.log('args', args)
|
||||
},
|
||||
other: null as any,
|
||||
set: new Set(),
|
||||
map: new Map(),
|
||||
Symbol: Symbol()
|
||||
}
|
||||
try {
|
||||
if (Blob) {
|
||||
obj.other = new Blob()
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
switch (type) {
|
||||
case 'def':
|
||||
console.log('示例打印:' + Math.random())
|
||||
break
|
||||
case 'info':
|
||||
console.info('这是一条info打印:' + new Date().getTime())
|
||||
break
|
||||
case 'warn':
|
||||
console.warn('这是一条warn打印:' + new Date().getTime())
|
||||
break
|
||||
case 'error':
|
||||
console.error('这是一条error打印:' + new Date().getTime())
|
||||
break
|
||||
case 'array':
|
||||
console.log(['array1', 'array2', 'array3', 'array4', 'array5'])
|
||||
break
|
||||
case 'object':
|
||||
console.log(obj)
|
||||
break
|
||||
case 'json':
|
||||
console.log(JSON.stringify(['json字符串', '1', '2', obj]))
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
tips('操作成功!')
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送请求
|
||||
*/
|
||||
function sendReq(type = '') {
|
||||
switch (type) {
|
||||
case 'get':
|
||||
uni.request({
|
||||
method: 'GET',
|
||||
url: 'https://api.oioweb.cn/api/SimpWords',
|
||||
success(res) {
|
||||
console.log('GET 请求成功! res=>', res)
|
||||
// @ts-ignore
|
||||
tips(res.data.result.content)
|
||||
},
|
||||
fail() {
|
||||
tips('请求失败')
|
||||
}
|
||||
})
|
||||
break
|
||||
case 'post':
|
||||
uni.request({
|
||||
method: 'POST',
|
||||
url: 'https://api.oioweb.cn/api/qq/getQQLevelInfo',
|
||||
data: {
|
||||
qq: Number((Math.random() * 1000000000).toString().replace('0.', '')).toFixed(0)
|
||||
},
|
||||
header: {
|
||||
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
||||
},
|
||||
success(res) {
|
||||
console.log('POST 请求成功! res=>', res)
|
||||
// @ts-ignore
|
||||
tips(res.data.msg)
|
||||
},
|
||||
fail() {
|
||||
tips('请求失败')
|
||||
}
|
||||
})
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存管理
|
||||
*/
|
||||
function storage(type = '') {
|
||||
switch (type) {
|
||||
case 'add':
|
||||
uni.setStorageSync('test_' + Math.random(), Math.random())
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
tips('操作成功!')
|
||||
}
|
||||
|
||||
function changeModel() {
|
||||
testStore.model = Math.random().toString()
|
||||
tips('操作成功!')
|
||||
}
|
||||
|
||||
function eventBusTest() {
|
||||
let eventName = 'test_' + Math.random()
|
||||
uni.$on(eventName, (opts) => {})
|
||||
uni.$emit(eventName, Math.random())
|
||||
uni.$emit(eventName, { t: new Date().toString() })
|
||||
uni.$emit(eventName, { s: [new Date().getTime()] })
|
||||
uni.$off(eventName)
|
||||
uni.$once(eventName, () => {})
|
||||
uni.$emit(eventName, Math.random())
|
||||
tips('操作成功!')
|
||||
}
|
||||
|
||||
function rand() {
|
||||
isCanTips = false
|
||||
uni.showLoading({
|
||||
title: '模拟数据生成中'
|
||||
})
|
||||
try {
|
||||
print('def')
|
||||
print('info')
|
||||
print('warn')
|
||||
print('error')
|
||||
print('array')
|
||||
print('object')
|
||||
print('json')
|
||||
print('def')
|
||||
print('info')
|
||||
print('warn')
|
||||
print('error')
|
||||
print('array')
|
||||
print('object')
|
||||
print('json')
|
||||
storage('add')
|
||||
storage('add')
|
||||
sendReq('get')
|
||||
sendReq('post')
|
||||
sendReq('get')
|
||||
sendReq('post')
|
||||
changeModel()
|
||||
changeModel()
|
||||
eventBusTest()
|
||||
eventBusTest()
|
||||
} catch (error) {}
|
||||
setTimeout(() => {
|
||||
isCanTips = true
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
icon: 'success',
|
||||
title: '随机数据生成成功!'
|
||||
})
|
||||
}, 1500)
|
||||
showTemplateError()
|
||||
showError()
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转指定URL
|
||||
*/
|
||||
function goUrl(url = '') {
|
||||
// #ifdef H5
|
||||
window.open(url)
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
uni.setClipboardData({
|
||||
data: url
|
||||
})
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
plus.runtime.openURL(url)
|
||||
// #endif
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转QQ群
|
||||
*/
|
||||
function goQun() {
|
||||
// #ifdef H5 || APP-PLUS
|
||||
goUrl('https://qm.qq.com/q/2mf5R4Ar7q')
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
uni.setClipboardData({
|
||||
data: '894584115',
|
||||
success() {
|
||||
uni.showToast({
|
||||
icon: 'success',
|
||||
title: '已复制QQ群号!'
|
||||
})
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
|
||||
if (uni.getStorageSync('hasTestData') != '1') {
|
||||
setTimeout(() => {
|
||||
uni.setStorageSync('hasTestData', '1')
|
||||
rand()
|
||||
}, 200)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.demoIndex {
|
||||
margin-top: 100rpx;
|
||||
}
|
||||
.divider {
|
||||
margin-top: 100rpx;
|
||||
padding: 100rpx 0;
|
||||
}
|
||||
.btnList {
|
||||
width: 650rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
.vertical {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rpx;
|
||||
}
|
||||
.hello {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 120rpx;
|
||||
padding-bottom: 50rpx;
|
||||
}
|
||||
</style>
|
||||
BIN
uni_modules/UniDevTools/src/devTools/UniDevTools_v3.6_r.zip
Normal file
62
uni_modules/UniDevTools/src/devTools/config.js
Normal file
@@ -0,0 +1,62 @@
|
||||
let config = {
|
||||
status: true, //调试工具总开关
|
||||
route: "/devTools/page/index", // 调试页面的路由,不建议更改
|
||||
bubble: { //调试弹窗气泡设置
|
||||
status: true, // 气泡标签是否显示,生产环境建议关闭
|
||||
text: "DevTools", // 气泡上展示的文字
|
||||
color: "#ffffff", // 气泡文字颜色
|
||||
bgColor: "rgba(250, 53, 52,0.7)", // 气泡背景颜色
|
||||
},
|
||||
|
||||
// 注意: 以下配置不建议更改
|
||||
|
||||
pageStatistics: {// 页面统计开关
|
||||
status: true, // 统计状态开关
|
||||
size: 1024 * 100, // 缓存上限,单位byte
|
||||
dayOnlineRowMax: 30, // 活跃数据缓存天数
|
||||
},
|
||||
console: { //console日志配置
|
||||
status: true, //功能总开关
|
||||
isOutput: true, //打印的日志是否对外输出到浏览器调试界面,建议在生产环境时关闭
|
||||
cache: {
|
||||
status: true, //是否启用本地缓存
|
||||
size: 512 * 1024, //缓存上限,单位byte
|
||||
rowSize: 1024 * 4,//单条记录缓存上限,单位byte
|
||||
},
|
||||
},
|
||||
uniBus: { // uni event bus 监听设置
|
||||
status: true,
|
||||
cache: {
|
||||
status: true,
|
||||
size: 1024 * 512, // bus调用日志上限 byte
|
||||
rowSize: 1024 * 10,
|
||||
countMaxSize: 1024 * 10, // bus统计上限 byte
|
||||
},
|
||||
},
|
||||
error: { //报错拦截配置
|
||||
status: true,
|
||||
cache: {
|
||||
status: true,
|
||||
size: 512 * 1024,
|
||||
rowSize: 1024 * 4,
|
||||
},
|
||||
},
|
||||
network: { //请求拦截配置
|
||||
status: true,
|
||||
cache: {
|
||||
status: true,
|
||||
size: 512 * 1024,
|
||||
rowSize: 1024 * 4,
|
||||
},
|
||||
},
|
||||
logs: { //运行日志
|
||||
status: true,
|
||||
cache: {
|
||||
status: true,
|
||||
size: 512 * 1024,
|
||||
rowSize: 1024 * 4,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<view
|
||||
v-if="isMp && options && options.status && options.bubble.status"
|
||||
class="mpDevBubble"
|
||||
:style="{
|
||||
left: `${tagConfig.x}px`,
|
||||
top: `${tagConfig.y}px`,
|
||||
'background-color': options.bubble.bgColor,
|
||||
'box-shadow': `0px 0px 6px ${options.bubble.bgColor}`,
|
||||
}"
|
||||
@touchstart.stop="touchstart"
|
||||
@touchmove.stop="touchmove"
|
||||
@touchend.stop="touchend"
|
||||
>
|
||||
<text
|
||||
class="mpDevBubbleText"
|
||||
:style="{
|
||||
color: options.bubble.color,
|
||||
'font-size': '20rpx',
|
||||
}"
|
||||
>
|
||||
{{ options.bubble.text }}
|
||||
</text>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import devOptions from "../libs/devOptions";
|
||||
|
||||
let options = devOptions.getOptions();
|
||||
let sysInfo = uni.getSystemInfoSync();
|
||||
let tagConfig = uni.getStorageSync("devTools_tagConfig");
|
||||
if (!tagConfig) {
|
||||
tagConfig = {};
|
||||
}
|
||||
|
||||
tagConfig = Object.assign(
|
||||
{
|
||||
x: sysInfo.screenWidth - 150,
|
||||
y: sysInfo.screenHeight - 240,
|
||||
},
|
||||
tagConfig
|
||||
);
|
||||
|
||||
// 拖动范围限制
|
||||
let dragLimit = {
|
||||
min: { x: 0, y: 0 },
|
||||
max: {
|
||||
x: sysInfo.screenWidth - 70,
|
||||
y: sysInfo.screenHeight - 24,
|
||||
},
|
||||
};
|
||||
|
||||
tagConfig.x = Math.min(Math.max(tagConfig.x, dragLimit.min.x), dragLimit.max.x);
|
||||
tagConfig.y = Math.min(Math.max(tagConfig.y, dragLimit.min.y), dragLimit.max.y);
|
||||
|
||||
let isTouch = false;
|
||||
let touchStartPoint = {
|
||||
clientX: 0,
|
||||
clientY: 0,
|
||||
tagX: tagConfig.x,
|
||||
tagY: tagConfig.y,
|
||||
hasMove: false,
|
||||
};
|
||||
|
||||
let isMp = false;
|
||||
// #ifdef MP
|
||||
isMp = true;
|
||||
// #endif
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isMp,
|
||||
/**
|
||||
* 标签参数
|
||||
*/
|
||||
options,
|
||||
/**
|
||||
* 标签坐标信息配置
|
||||
*/
|
||||
tagConfig,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// console.log("调试开始zzzzzzzzzzzzzzzz");
|
||||
},
|
||||
methods: {
|
||||
touchstart(e) {
|
||||
if (isTouch) return;
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
let clientX = e.touches[0].clientX;
|
||||
let clientY = e.touches[0].clientY;
|
||||
touchStartPoint.clientX = clientX;
|
||||
touchStartPoint.clientY = clientY;
|
||||
touchStartPoint.tagX = tagConfig.x;
|
||||
touchStartPoint.tagY = tagConfig.y;
|
||||
touchStartPoint.hasMove = false;
|
||||
isTouch = true;
|
||||
},
|
||||
touchmove(e) {
|
||||
if (!isTouch) return;
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
let clientX = e.touches[0].clientX;
|
||||
let clientY = e.touches[0].clientY;
|
||||
touchStartPoint.hasMove = true;
|
||||
let offsetX = touchStartPoint.clientX - clientX;
|
||||
let offsetY = touchStartPoint.clientY - clientY;
|
||||
let tx = touchStartPoint.tagX - offsetX;
|
||||
let ty = touchStartPoint.tagY - offsetY;
|
||||
tx = Math.min(Math.max(tx, dragLimit.min.x), dragLimit.max.x);
|
||||
ty = Math.min(Math.max(ty, dragLimit.min.y), dragLimit.max.y);
|
||||
tagConfig.x = tx;
|
||||
tagConfig.y = ty;
|
||||
this.tagConfig.x = tx;
|
||||
this.tagConfig.y = ty;
|
||||
},
|
||||
touchend(e) {
|
||||
if (!isTouch) return;
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
isTouch = false;
|
||||
uni.setStorageSync("devTools_tagConfig", tagConfig);
|
||||
if (!touchStartPoint.hasMove) {
|
||||
let pages = getCurrentPages();
|
||||
let route = options.route.substring(1, options.route.length - 2);
|
||||
if (pages[pages.length - 1].route == route) {
|
||||
// 已经处于debug页面,不响应点击事件
|
||||
return;
|
||||
}
|
||||
this.$devTools.show();
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.mpDevBubble {
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
z-index: 9999999;
|
||||
width: 70px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px;
|
||||
border-radius: 6px;
|
||||
font-size: 10px;
|
||||
}
|
||||
</style>
|
||||
142
uni_modules/UniDevTools/src/devTools/core/libs/createH5Bubble.js
Normal file
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
*! 创建h5页面上拖动的气泡标签
|
||||
*/
|
||||
function createH5Bubble(options, devTools) {
|
||||
|
||||
let tagConfig = localStorage.getItem("devTools_tagConfig");
|
||||
if (!tagConfig) {
|
||||
tagConfig = {}
|
||||
} else {
|
||||
tagConfig = JSON.parse(tagConfig)
|
||||
}
|
||||
|
||||
tagConfig = Object.assign({
|
||||
show: options.bubble.status,
|
||||
x: window.innerWidth - 90,
|
||||
y: window.innerHeight - 90,
|
||||
}, tagConfig);
|
||||
|
||||
tagConfig.show = options.bubble.status;
|
||||
|
||||
// 拖动范围限制
|
||||
let dragLimit = {
|
||||
min: { x: 0, y: 0, },
|
||||
max: {
|
||||
x: window.innerWidth - 70,
|
||||
y: window.innerHeight - 24,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tagConfig.x = Math.min(Math.max(tagConfig.x, dragLimit.min.x), dragLimit.max.x)
|
||||
tagConfig.y = Math.min(Math.max(tagConfig.y, dragLimit.min.y), dragLimit.max.y)
|
||||
|
||||
let tag = document.createElement("div");
|
||||
tag.style = `
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
z-index: 9999999;
|
||||
left: ${tagConfig.x}px;
|
||||
top: ${tagConfig.y}px;
|
||||
width: 70px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px;
|
||||
border-radius: 6px;
|
||||
background-color: ${options.bubble.bgColor};
|
||||
color: ${options.bubble.color};
|
||||
font-size: 10px;
|
||||
cursor: grab;
|
||||
box-shadow: 0px 0px 6px ${options.bubble.bgColor};
|
||||
backdrop-filter: blur(1px);
|
||||
`;
|
||||
tag.innerHTML = options.bubble.text;
|
||||
tag.setAttribute("id", "debugTag")
|
||||
|
||||
if (tagConfig.show) {
|
||||
document.body.appendChild(tag)
|
||||
}
|
||||
|
||||
/**
|
||||
* 标签单击事件
|
||||
*/
|
||||
function tagClick() {
|
||||
let pages = getCurrentPages()
|
||||
let route = options.route.substring(1, options.route.length - 2);
|
||||
if (pages[pages.length - 1].route == route) {
|
||||
// 已经处于debug页面,不响应点击事件
|
||||
return;
|
||||
}
|
||||
devTools.show()
|
||||
}
|
||||
|
||||
let isTouch = false;
|
||||
let touchStartPoint = {
|
||||
clientX: 0,
|
||||
clientY: 0,
|
||||
tagX: tagConfig.x,
|
||||
tagY: tagConfig.y,
|
||||
hasMove: false,
|
||||
}
|
||||
|
||||
function touchStart(e) {
|
||||
if (isTouch) return;
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault()
|
||||
}
|
||||
let clientX = e.clientX ? e.clientX : e.targetTouches[0].clientX;
|
||||
let clientY = e.clientX ? e.clientY : e.targetTouches[0].clientY;
|
||||
touchStartPoint.clientX = clientX;
|
||||
touchStartPoint.clientY = clientY;
|
||||
touchStartPoint.tagX = tagConfig.x;
|
||||
touchStartPoint.tagY = tagConfig.y;
|
||||
touchStartPoint.hasMove = false;
|
||||
isTouch = true;
|
||||
}
|
||||
function touchMove(e) {
|
||||
if (!isTouch) return;
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault()
|
||||
}
|
||||
let clientX = e.clientX ? e.clientX : e.targetTouches[0].clientX;
|
||||
let clientY = e.clientX ? e.clientY : e.targetTouches[0].clientY;
|
||||
touchStartPoint.hasMove = true;
|
||||
|
||||
let offsetX = touchStartPoint.clientX - clientX;
|
||||
let offsetY = touchStartPoint.clientY - clientY;
|
||||
let tx = touchStartPoint.tagX - offsetX;
|
||||
let ty = touchStartPoint.tagY - offsetY;
|
||||
tx = Math.min(Math.max(tx, dragLimit.min.x), dragLimit.max.x)
|
||||
ty = Math.min(Math.max(ty, dragLimit.min.y), dragLimit.max.y)
|
||||
tag.style.left = `${tx}px`;
|
||||
tag.style.top = `${ty}px`;
|
||||
tagConfig.x = tx;
|
||||
tagConfig.y = ty;
|
||||
}
|
||||
function touchEnd(e) {
|
||||
if (!isTouch) return;
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault()
|
||||
}
|
||||
isTouch = false;
|
||||
localStorage.setItem("devTools_tagConfig", JSON.stringify(tagConfig))
|
||||
if (!touchStartPoint.hasMove) {
|
||||
tagClick()
|
||||
}
|
||||
}
|
||||
tag.addEventListener("touchstart", touchStart)
|
||||
tag.addEventListener("touchmove", touchMove)
|
||||
tag.addEventListener("touchend", touchEnd)
|
||||
|
||||
tag.addEventListener("mousedown", touchStart)
|
||||
document.addEventListener("mousemove", touchMove)
|
||||
document.addEventListener("mouseup", touchEnd)
|
||||
|
||||
localStorage.setItem("devTools_tagConfig", JSON.stringify(tagConfig))
|
||||
}
|
||||
|
||||
|
||||
export default createH5Bubble;
|
||||
129
uni_modules/UniDevTools/src/devTools/core/libs/devCache.js
Normal file
@@ -0,0 +1,129 @@
|
||||
import devOptions from "./devOptions"
|
||||
/**
|
||||
* dev工具缓存管理
|
||||
*/
|
||||
export default {
|
||||
/**
|
||||
* 存储的键开始名称
|
||||
*/
|
||||
cacheKey: "devTools_v3_",
|
||||
options: null,
|
||||
/**
|
||||
* 临时缓存对象
|
||||
*/
|
||||
tempCache: {
|
||||
errorReport: [],
|
||||
logReport: [],
|
||||
console: [],
|
||||
request: [],
|
||||
uniBus: [],
|
||||
},
|
||||
/**
|
||||
* 临时数据存放
|
||||
*/
|
||||
tempData: {},
|
||||
/**
|
||||
* 向缓存内写入数据
|
||||
*/
|
||||
set(key, value) {
|
||||
try {
|
||||
if (['errorReport', 'logReport', 'console', 'request', 'uniBus'].indexOf(key) != -1) {
|
||||
let setting = this.getLongListSetting(key)
|
||||
if (!setting.status) return;
|
||||
if (!setting.cache.status) {
|
||||
// !不使用缓存
|
||||
this.tempCache[key] = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
key = `${this.cacheKey}${key}`
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
let pages = getCurrentPages()
|
||||
if (pages[pages.length - 1].route == "devTools/page/index") {
|
||||
// devtools 页面直接走设置缓存
|
||||
return uni.setStorageSync(key, value);
|
||||
}
|
||||
// #endif
|
||||
|
||||
this.tempData[key] = value;
|
||||
} catch (error) {
|
||||
console.log("devCache.set error", error);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 同步读取缓存数据
|
||||
*/
|
||||
get(key) {
|
||||
try {
|
||||
if (['errorReport', 'logReport', 'console', 'request', 'uniBus'].indexOf(key) != -1) {
|
||||
let setting = this.getLongListSetting(key)
|
||||
if (!setting.status) return [];
|
||||
if (!setting.cache.status) {
|
||||
// !不使用缓存
|
||||
return this.tempCache[key];
|
||||
}
|
||||
}
|
||||
key = `${this.cacheKey}${key}`
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
let pages = getCurrentPages()
|
||||
if (pages[pages.length - 1].route == "devTools/page/index") {
|
||||
// devtools 页面直接走设置缓存
|
||||
return uni.getStorageSync(key);
|
||||
}
|
||||
// #endif
|
||||
|
||||
if (this.tempData.hasOwnProperty(key)) {
|
||||
return this.tempData[key];
|
||||
} else {
|
||||
let value = uni.getStorageSync(key);
|
||||
this.tempData[key] = value;
|
||||
return value;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("devCache.get error", error);
|
||||
return "";
|
||||
}
|
||||
},
|
||||
getLongListSetting(key) {
|
||||
let optionsKey = {
|
||||
errorReport: 'error',
|
||||
logReport: 'logs',
|
||||
console: 'console',
|
||||
request: 'network',
|
||||
uniBus: 'uniBus',
|
||||
}
|
||||
if (this.options) return this.options[optionsKey[key]];
|
||||
this.options = devOptions.getOptions()
|
||||
return this.options[optionsKey[key]];
|
||||
},
|
||||
/**
|
||||
* 同步本地缓存
|
||||
*/
|
||||
syncLocalCache() {
|
||||
let that = this;
|
||||
setTimeout(() => {
|
||||
try {
|
||||
let waitSetKeys = Object.keys(that.tempData);
|
||||
for (let i = 0; i < waitSetKeys.length; i++) {
|
||||
const key = waitSetKeys[i];
|
||||
uni.setStorage({
|
||||
key,
|
||||
data: that.tempData[key],
|
||||
success() {
|
||||
// console.log("set " + key + " success,length=" + that.tempData[key].length);
|
||||
delete that.tempData[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("devCache error: ", error);
|
||||
}
|
||||
setTimeout(() => {
|
||||
that.syncLocalCache();
|
||||
}, 500);
|
||||
}, Math.round(Math.random() * 3 * 1000) + 2000);
|
||||
},
|
||||
}
|
||||
|
||||
191
uni_modules/UniDevTools/src/devTools/core/libs/devOptions.js
Normal file
@@ -0,0 +1,191 @@
|
||||
import devCache from "./devCache";
|
||||
|
||||
/**
|
||||
* 设置各端大小 kb
|
||||
*/
|
||||
const defSize = (h5, app, mp) => {
|
||||
let r = 0;
|
||||
// #ifdef H5
|
||||
r = h5;
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
r = mp;
|
||||
// #endif
|
||||
// #ifdef APP-PLUS || APP-NVUE
|
||||
r = app;
|
||||
// #endif
|
||||
return Math.ceil(r * 1024);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
*/
|
||||
export default {
|
||||
/**
|
||||
* 配置缓存key
|
||||
*/
|
||||
cacheKey: "options_v8",
|
||||
/**
|
||||
* 默认配置项
|
||||
*/
|
||||
defaultOptions: {
|
||||
version: 3.6,
|
||||
status: false, //调试工具总开关
|
||||
route: "/devTools/page/index", // 调试页面的路由,不建议更改
|
||||
bubble: { //调试弹窗气泡设置
|
||||
status: false, // 气泡标签是否显示,生产环境建议关闭
|
||||
text: "调试工具", // 气泡上展示的文字
|
||||
color: "#ffffff", // 气泡文字颜色
|
||||
bgColor: "rgba(250, 53, 52,0.7)", // 气泡背景颜色
|
||||
},
|
||||
console: {
|
||||
status: true, // 开关
|
||||
isOutput: true, //打印的日志是否对外输出到浏览器调试界面,建议在生产环境时开启
|
||||
cache: {
|
||||
status: true, //是否启用console缓存
|
||||
size: defSize(512, 1024 * 2, 512),
|
||||
rowSize: defSize(5.12, 20, 10),
|
||||
},
|
||||
},
|
||||
error: {
|
||||
status: true,
|
||||
cache: {
|
||||
status: true,
|
||||
size: defSize(512, 1024 * 2, 512),
|
||||
rowSize: defSize(5.12, 20, 10),
|
||||
},
|
||||
},
|
||||
network: {
|
||||
status: true,
|
||||
cache: {
|
||||
status: true,
|
||||
size: defSize(512, 1024 * 2, 512),
|
||||
rowSize: defSize(5.12, 20, 10),
|
||||
},
|
||||
},
|
||||
logs: {
|
||||
status: true,
|
||||
cache: {
|
||||
status: true,
|
||||
size: defSize(512, 1024 * 2, 512),
|
||||
rowSize: defSize(0.4, 0.4, 0.4),
|
||||
},
|
||||
},
|
||||
// 页面统计开关
|
||||
pageStatistics: {
|
||||
status: true, // 统计状态开关
|
||||
size: defSize(200, 1024 * 2, 512),
|
||||
// #ifdef H5
|
||||
dayOnlineRowMax: 30, // 日活跃时间的保存条数
|
||||
// #endif
|
||||
// #ifdef APP-PLUS || APP-NVUE
|
||||
dayOnlineRowMax: 90, // 日活跃时间的保存条数
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
dayOnlineRowMax: 60, // 日活跃时间的保存条数
|
||||
// #endif
|
||||
},
|
||||
// uni event bus 监听设置
|
||||
uniBus: {
|
||||
status: true,
|
||||
cache: {
|
||||
status: true,
|
||||
size: defSize(512, 1024 * 2, 512),
|
||||
rowSize: defSize(5.12, 20, 10),
|
||||
countMaxSize: defSize(512, 1024 * 2, 512), // bus统计上限 kb
|
||||
},
|
||||
},
|
||||
},
|
||||
/**
|
||||
* 获取配置信息
|
||||
*/
|
||||
getOptions() {
|
||||
try {
|
||||
let options = devCache.get(this.cacheKey)
|
||||
if (!options) {
|
||||
return {
|
||||
status: false, //默认关闭调试工具
|
||||
}
|
||||
}
|
||||
let r = String(options.route)
|
||||
// ! 增加 devRoute 参数
|
||||
options.devRoute = r.substring(1, r.length)
|
||||
return options;
|
||||
} catch (error) {
|
||||
console.log("devOptions.getOptions error: ", error);
|
||||
return {
|
||||
status: false, //默认关闭调试工具
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 保存配置项
|
||||
*/
|
||||
setOptions(options) {
|
||||
try {
|
||||
if (!options) {
|
||||
options = this.defaultOptions;
|
||||
}
|
||||
|
||||
if (options.status) {
|
||||
if (
|
||||
!options.route
|
||||
|| typeof options.route != "string"
|
||||
|| options.route.indexOf("/") != 0
|
||||
) {
|
||||
return this.outputError(`devTools 调试工具配置出错: [route] 参数配置错误!`)
|
||||
}
|
||||
}
|
||||
|
||||
let data = deepMerge(this.defaultOptions, options)
|
||||
|
||||
devCache.set(this.cacheKey, data)
|
||||
} catch (error) {
|
||||
console.log("devOptions.setOptions error: ", error);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 弹出错误信息
|
||||
*/
|
||||
outputError(msg) {
|
||||
console.log('%c' + msg, `
|
||||
padding: 4px;
|
||||
background-color: red;
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
`)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 深度合并对象
|
||||
*/
|
||||
function deepMerge(target, ...sources) {
|
||||
try {
|
||||
if (!sources.length) return target; // 如果没有源对象则直接返回目标对象
|
||||
|
||||
const source = sources[0];
|
||||
|
||||
for (let key in source) {
|
||||
if (source.hasOwnProperty(key)) {
|
||||
if (typeof source[key] === 'object' && typeof target[key] !== undefined) {
|
||||
target[key] = deepMerge({}, target[key], source[key]); // 若属性值为对象类型且目标对象已存在该属性,则递归调用deepMerge函数进行合并
|
||||
} else {
|
||||
target[key] = source[key]; // 否则将源对象的属性赋值到目标对象上
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return deepMerge(target, ...sources.slice(1)); // 处理完第一个源对象后再次调用deepMerge函数处理其他源对象
|
||||
} catch (error) {
|
||||
console.log("deepMerge error", error);
|
||||
return {}
|
||||
}
|
||||
}
|
||||
117
uni_modules/UniDevTools/src/devTools/core/libs/drawView.js
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* 绘制调试工具
|
||||
*/
|
||||
|
||||
/**
|
||||
* 入口文件
|
||||
*/
|
||||
function init(options, devTools) {
|
||||
|
||||
let sysInfo = uni.getSystemInfoSync()
|
||||
|
||||
let tagConfig = uni.getStorageSync("devTools_tagConfig");
|
||||
if (!tagConfig) {
|
||||
tagConfig = {}
|
||||
}
|
||||
|
||||
tagConfig = Object.assign({
|
||||
show: options.bubble.status,
|
||||
x: sysInfo.screenWidth - 90,
|
||||
y: sysInfo.screenHeight - 90,
|
||||
}, tagConfig)
|
||||
tagConfig.show = options.bubble.status;
|
||||
|
||||
// 拖动范围限制
|
||||
let dragLimit = {
|
||||
min: { x: 0, y: 0, },
|
||||
max: {
|
||||
x: sysInfo.screenWidth - 70,
|
||||
y: sysInfo.screenHeight - 24,
|
||||
}
|
||||
}
|
||||
|
||||
let view = new plus.nativeObj.View('debugTag', {
|
||||
top: tagConfig.y + 'px',
|
||||
left: tagConfig.x + 'px',
|
||||
height: '24px',
|
||||
width: '70px',
|
||||
backgroundColor: options.bubble.bgColor,
|
||||
});
|
||||
view.drawText(options.bubble.text, {}, {
|
||||
size: '12px',
|
||||
color: options.bubble.color,
|
||||
weight: 'bold'
|
||||
});
|
||||
|
||||
if (tagConfig.show) {
|
||||
view.show();
|
||||
}
|
||||
|
||||
let isTouch = false;
|
||||
|
||||
|
||||
let touchStart = {
|
||||
l: 0,
|
||||
t: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
time: 0,
|
||||
hasMove: false,
|
||||
}
|
||||
|
||||
view.addEventListener("touchstart", (e) => {
|
||||
isTouch = true;
|
||||
touchStart.l = e.clientX;
|
||||
touchStart.t = e.clientY;
|
||||
touchStart.time = new Date().getTime();
|
||||
touchStart.hasMove = false;
|
||||
})
|
||||
|
||||
view.addEventListener("touchmove", (e) => {
|
||||
if (!isTouch) return;
|
||||
if (!touchStart.hasMove) {
|
||||
touchStart.hasMove = true;
|
||||
}
|
||||
let x = e.screenX - touchStart.l;
|
||||
let y = e.screenY - touchStart.t;
|
||||
x = Math.min(Math.max(x, dragLimit.min.x), dragLimit.max.x)
|
||||
y = Math.min(Math.max(y, dragLimit.min.y), dragLimit.max.y)
|
||||
|
||||
view.setStyle({
|
||||
top: y + 'px',
|
||||
left: x + 'px',
|
||||
})
|
||||
touchStart.x = x;
|
||||
touchStart.y = y;
|
||||
})
|
||||
|
||||
view.addEventListener("touchend", (e) => {
|
||||
isTouch = false;
|
||||
if (
|
||||
!touchStart.hasMove
|
||||
|| touchStart.time > (new Date().getTime() - 300)
|
||||
) {// 单击事件
|
||||
|
||||
let pages = getCurrentPages()
|
||||
let route = options.route.substring(1, options.route.length - 2);
|
||||
if (pages[pages.length - 1].route == route) {
|
||||
// 已经处于debug页面,不响应点击事件
|
||||
return;
|
||||
}
|
||||
devTools.show()
|
||||
|
||||
} else { //拖拽结束事件
|
||||
|
||||
tagConfig.x = touchStart.x;
|
||||
tagConfig.y = touchStart.y;
|
||||
|
||||
uni.setStorageSync("devTools_tagConfig", tagConfig)
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
uni.setStorageSync("devTools_tagConfig", tagConfig)
|
||||
|
||||
}
|
||||
|
||||
export default init;
|
||||
@@ -0,0 +1,64 @@
|
||||
import devCache from "./devCache";
|
||||
import devOptions from "./devOptions";
|
||||
import jsonCompress from "./jsonCompress";
|
||||
/**
|
||||
* ! vue报错捕获
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* * vue错误日志上报
|
||||
* @param {'ve'|'vw'|'oe'|'n'} type 错误类型
|
||||
*/
|
||||
function errorReport(msg, trace, type = "n") {
|
||||
try {
|
||||
if (!msg) return false;
|
||||
|
||||
if (msg instanceof Error) {
|
||||
msg = msg.message;
|
||||
}
|
||||
let options = devOptions.getOptions()
|
||||
if (!options.error.status) return;
|
||||
|
||||
let page = "未知";
|
||||
try {
|
||||
let pages = getCurrentPages()
|
||||
let item = pages[pages.length - 1];
|
||||
if (item && item.route) {
|
||||
page = item.route;
|
||||
}
|
||||
} catch (error) { }
|
||||
|
||||
let logs = devCache.get("errorReport");
|
||||
if (!logs) logs = [];
|
||||
if (logs.length >= options.error.cache.rowMax) {
|
||||
logs = logs.splice(0, options.error.cache.rowMax)
|
||||
}
|
||||
|
||||
msg = String(msg)
|
||||
msg = jsonCompress.compressObject(msg, options.error.cache.rowSize / 2)
|
||||
trace = String(trace)
|
||||
trace = jsonCompress.compressObject(trace, options.error.cache.rowSize / 2)
|
||||
|
||||
logs.unshift({
|
||||
t: new Date().getTime(),
|
||||
m: msg,
|
||||
tr: trace,
|
||||
p: page,
|
||||
type,
|
||||
});
|
||||
|
||||
console.error("__ignoreReport__", msg, trace)
|
||||
|
||||
logs = jsonCompress.compressArray(logs, "end", options.error.cache.size)
|
||||
|
||||
devCache.set("errorReport", logs)
|
||||
} catch (error) {
|
||||
console.log("errorReport error: ", error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default errorReport;
|
||||
237
uni_modules/UniDevTools/src/devTools/core/libs/jsonCompress.js
Normal file
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* json压缩工具
|
||||
*/
|
||||
export default {
|
||||
/**
|
||||
* 压缩js对象成json字符串,并控制json字节大小,多余部分裁剪
|
||||
*/
|
||||
compressObject(obj = {}, maxSize = 1024 * 10.24) {
|
||||
try {
|
||||
let t = new Date().getTime()
|
||||
if (obj === undefined || obj === null) return obj;
|
||||
if (typeof obj == "string") {
|
||||
return this.truncateStrBySize(obj, maxSize);
|
||||
}
|
||||
if (typeof obj != "object") {
|
||||
return obj;
|
||||
}
|
||||
if (maxSize < 2) return {};
|
||||
|
||||
let addEndOut = false;
|
||||
if (maxSize > 50) {
|
||||
let objSize = this.calculateStringByteSize(obj);
|
||||
if (objSize > maxSize) {
|
||||
maxSize = maxSize - 50;
|
||||
addEndOut = true;
|
||||
}
|
||||
}
|
||||
|
||||
let sizeCount = 2;
|
||||
let str = this.safeJsonStringify(obj, (key, value) => {
|
||||
let keySize = this.calculateStringByteSize(key)
|
||||
if (typeof value == "object") {
|
||||
if (sizeCount + keySize + 6 > maxSize) {
|
||||
return;
|
||||
}
|
||||
sizeCount = sizeCount + keySize + 6;
|
||||
return value;
|
||||
}
|
||||
let valueSize = this.calculateStringByteSize(value)
|
||||
let rowSize = keySize + valueSize + 6;
|
||||
if (rowSize + sizeCount > maxSize) return;
|
||||
sizeCount = sizeCount + rowSize;
|
||||
return value;
|
||||
})
|
||||
let outPut = JSON.parse(str)
|
||||
if (addEndOut) {
|
||||
if (Array.isArray(outPut)) {
|
||||
outPut.push('(已截断其余部分)')
|
||||
} else if (typeof outPut == "object") {
|
||||
outPut["*注意"] = "(已截断其余部分)";
|
||||
}
|
||||
}
|
||||
// console.log("compressObject use time: " + (new Date().getTime() - t));
|
||||
return outPut;
|
||||
} catch (error) {
|
||||
console.log("compressObject error", error);
|
||||
return "";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 压缩数组不超过特定大小
|
||||
* @param {any[]} [arr=[]] 需要处理的数组
|
||||
* @param {string} [delType='start'] 数组超出后删除的开始位置
|
||||
* @param {number} [maxSize=1024 * 972] 数组最大字节数
|
||||
*/
|
||||
compressArray(arr = [], delType = "start", maxSize = 1024 * 972) {
|
||||
let t = new Date().getTime()
|
||||
try {
|
||||
if (!arr || arr.length == 0 || !arr[0]) return [];
|
||||
let i = 0;
|
||||
while (true) {
|
||||
i = i + 1;
|
||||
if (i > 999999) return arr;
|
||||
if (!arr || arr.length == 0) {
|
||||
return [];
|
||||
}
|
||||
if (this.calculateStringByteSize(arr) <= maxSize) {
|
||||
// consoleLog("compressArray t=>" + (new Date().getTime() - t) + " i=>" + i)
|
||||
return arr;
|
||||
}
|
||||
if (delType == "start") {
|
||||
arr.splice(0, 1);
|
||||
} else {
|
||||
arr.splice(arr.length - 1, 1);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("compressArray error", error);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 计算对象或字符串占用的字节大小,传入对象将自动转json
|
||||
*/
|
||||
calculateStringByteSize(str) {
|
||||
try {
|
||||
let type = typeof str;
|
||||
if (
|
||||
type == "bigint"
|
||||
|| type == "number"
|
||||
|| type == "boolean"
|
||||
) {
|
||||
return str.toString().length;
|
||||
} else if (type == "function") {
|
||||
str = str.toString().length
|
||||
} else if (str === null || str === undefined) {
|
||||
return 0;
|
||||
} else {
|
||||
try {
|
||||
str = this.safeJsonStringify(str);
|
||||
if (str && str.hasOwnProperty) {
|
||||
return str.length;
|
||||
} else {
|
||||
return 1024 * 20;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("calculateStringByteSize error", error);
|
||||
return 1024 * 20;
|
||||
}
|
||||
}
|
||||
let size = 0;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const charCode = str.charCodeAt(i);
|
||||
if (charCode < 0x0080) {
|
||||
size += 1;
|
||||
} else if (charCode < 0x0800) {
|
||||
size += 2;
|
||||
} else if (charCode >= 0xD800 && charCode <= 0xDFFF) {
|
||||
size += 4;
|
||||
i++;
|
||||
} else {
|
||||
size += 3;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
} catch (error) {
|
||||
console.log("calculateStringByteSize error", error);
|
||||
return 1024 * 1024;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 安全的js对象转字符串
|
||||
*/
|
||||
safeJsonStringify(obj, handleValue) {
|
||||
if (!obj) return "{}";
|
||||
try {
|
||||
if (handleValue) {
|
||||
return JSON.stringify(obj, (key, value) => {
|
||||
return handleValue(key, value)
|
||||
})
|
||||
} else {
|
||||
return JSON.stringify(obj, (key, value) => {
|
||||
return value;
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
// 尝试解析json失败,可能是变量循环引用的问题,继续尝试增加WeakSet解析
|
||||
}
|
||||
|
||||
try {
|
||||
let seen = new WeakSet();
|
||||
let jsonStr = JSON.stringify(obj, (key, value) => {
|
||||
if (typeof value == "object") {
|
||||
try {
|
||||
if (value instanceof File) {
|
||||
value = "js:File";
|
||||
}
|
||||
if (value && value.constructor && value.constructor.name && typeof value.constructor.name == "string") {
|
||||
let className = value.constructor.name;
|
||||
if (className == "VueComponent") {
|
||||
return "js:Object:VueComponent";
|
||||
}
|
||||
}
|
||||
} catch (error) { }
|
||||
}
|
||||
if (typeof value == "function") {
|
||||
try {
|
||||
value = value.toString();
|
||||
} catch (error) {
|
||||
value = "js:function";
|
||||
}
|
||||
}
|
||||
if (typeof value === "object" && value !== null) {
|
||||
// 处理循环引用问题
|
||||
if (seen.has(value)) {
|
||||
return;
|
||||
}
|
||||
seen.add(value);
|
||||
}
|
||||
if (handleValue && typeof handleValue == "function") {
|
||||
try {
|
||||
return handleValue(key, value);
|
||||
} catch (error) {
|
||||
console.log("handleValue error", error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
return value;
|
||||
});
|
||||
seen = null;
|
||||
return jsonStr;
|
||||
} catch (error) {
|
||||
return "{}";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 根据限制的字节大小截取字符串
|
||||
*/
|
||||
truncateStrBySize(str = "", size = 20 * 1024) {
|
||||
try {
|
||||
if (size < 1) return "";
|
||||
if (this.calculateStringByteSize(str) <= size) return str;
|
||||
let endStr = "";
|
||||
if (size > 30) {
|
||||
endStr = "(已截断多余部分)"
|
||||
size = size - 30;
|
||||
}
|
||||
let low = 0, high = str.length, mid;
|
||||
while (low < high) {
|
||||
mid = Math.floor((low + high) / 2);
|
||||
let currentSize = this.calculateStringByteSize(str.substring(0, mid));
|
||||
if (currentSize > size) {
|
||||
// 如果大于限制值,减小高边界
|
||||
high = mid;
|
||||
} else {
|
||||
// 如果小于或等于限制值,增加低边界
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
// 返回截断的字符串,注意low-1是因为low是最后一次检查超出大小时的位置
|
||||
return str.substring(0, low - 1) + endStr;
|
||||
} catch (error) {
|
||||
console.log("truncateStrBySize error", error);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
67
uni_modules/UniDevTools/src/devTools/core/libs/logReport.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import devCache from "./devCache";
|
||||
import devOptions from "./devOptions";
|
||||
import jsonCompress from "./jsonCompress";
|
||||
/**
|
||||
* ! 运行日志提交工具
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 日志上报
|
||||
*/
|
||||
function logReport(msg) {
|
||||
try {
|
||||
if (!msg) return false;
|
||||
let options = devOptions.getOptions()
|
||||
if (!options.status) {
|
||||
console.error("日志上报失败!dev工具未启用 msg:" + msg);
|
||||
return;
|
||||
}
|
||||
if (!options.logs.status) {
|
||||
console.error("日志上报失败!dev logs未启用 msg:" + msg);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let pages = getCurrentPages()
|
||||
if (pages[pages.length - 1].route == options.devRoute) {
|
||||
// 不记录调试工具报出的日志
|
||||
return false;
|
||||
}
|
||||
} catch (error) { }
|
||||
|
||||
if (typeof msg == "object") {
|
||||
try {
|
||||
msg = JSON.stringify(msg)
|
||||
} catch (error) {
|
||||
msg = "logReport:error"
|
||||
}
|
||||
}
|
||||
|
||||
let log = {
|
||||
t: new Date().getTime(),
|
||||
m: "",
|
||||
}
|
||||
let logSize = jsonCompress.calculateStringByteSize(log)
|
||||
|
||||
msg = String(msg);
|
||||
msg = jsonCompress.compressObject(msg, options.logs.cache.rowSize - logSize)
|
||||
log.m = msg;
|
||||
|
||||
let logs = devCache.get("logReport");
|
||||
if (!logs) logs = [];
|
||||
|
||||
logs.unshift(log);
|
||||
|
||||
logs = jsonCompress.compressArray(logs, "end", options.logs.cache.size)
|
||||
|
||||
devCache.set("logReport", logs)
|
||||
} catch (error) {
|
||||
console.log("logReport error", error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default logReport;
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
/**
|
||||
* !页面统计:访问次数、停留时长
|
||||
*/
|
||||
|
||||
import devCache from "./devCache"
|
||||
import devOptions from "./devOptions";
|
||||
import jsonCompress from "./jsonCompress";
|
||||
import { timeFormat } from "./timeFormat";
|
||||
|
||||
/**
|
||||
* 页面注销时提交
|
||||
*/
|
||||
function pageStatisticsReport(
|
||||
route, activeTime,
|
||||
) {
|
||||
try {
|
||||
if (!route) return false;
|
||||
let options = devOptions.getOptions()
|
||||
if (!options.pageStatistics.status) return; //! 配置文件关闭页面统计
|
||||
let logs = devCache.get("pageCount");
|
||||
if (!logs) logs = [];
|
||||
let pageIndex = logs.findIndex(x => x.route == route)
|
||||
if (pageIndex == -1) {
|
||||
logs.push({
|
||||
route,
|
||||
activeTimeCount: activeTime,
|
||||
})
|
||||
} else {
|
||||
logs[pageIndex].activeTimeCount = activeTime + logs[pageIndex].activeTimeCount;
|
||||
}
|
||||
logs = jsonCompress.compressArray(logs, "end", options.pageStatistics.size)
|
||||
devCache.set("pageCount", logs)
|
||||
|
||||
let now = new Date().getTime();
|
||||
let date = timeFormat(now, "yyyy-mm-dd");
|
||||
let dayOnline = devCache.get("dayOnline");
|
||||
if (!dayOnline) dayOnline = [];
|
||||
let i = dayOnline.findIndex(x => x.date == date);
|
||||
if (i == -1) {
|
||||
dayOnline.unshift({
|
||||
date,
|
||||
activeTimeCount: activeTime,
|
||||
page: [
|
||||
{
|
||||
r: route,
|
||||
t: activeTime,
|
||||
}
|
||||
]
|
||||
})
|
||||
} else {
|
||||
dayOnline[i].activeTimeCount = dayOnline[i].activeTimeCount + activeTime;
|
||||
let pi = dayOnline[i].page.findIndex(x => x.r == route);
|
||||
if (pi == -1) {
|
||||
dayOnline[i].page.push({
|
||||
r: route,
|
||||
t: activeTime,
|
||||
})
|
||||
} else {
|
||||
dayOnline[i].page[pi].t = dayOnline[i].page[pi].t + activeTime;
|
||||
}
|
||||
}
|
||||
if (dayOnline.length > options.pageStatistics.dayOnlineRowMax) {
|
||||
dayOnline = dayOnline.splice(0, options.pageStatistics.dayOnlineRowMax)
|
||||
}
|
||||
|
||||
dayOnline = jsonCompress.compressArray(dayOnline, "end", options.pageStatistics.size)
|
||||
devCache.set("dayOnline", dayOnline)
|
||||
} catch (error) {
|
||||
console.log("pageStatistics error", error);
|
||||
}
|
||||
}
|
||||
|
||||
export default pageStatisticsReport;
|
||||
94
uni_modules/UniDevTools/src/devTools/core/libs/timeFormat.js
Normal file
@@ -0,0 +1,94 @@
|
||||
// padStart 的 polyfill,因为某些机型或情况,还无法支持es7的padStart,比如电脑版的微信小程序
|
||||
// 所以这里做一个兼容polyfill的兼容处理
|
||||
try {
|
||||
if (!String.prototype.padStart) {
|
||||
// 为了方便表示这里 fillString 用了ES6 的默认参数,不影响理解
|
||||
String.prototype.padStart = function (maxLength, fillString = ' ') {
|
||||
if (Object.prototype.toString.call(fillString) !== "[object String]") throw new TypeError(
|
||||
'fillString must be String')
|
||||
let str = this
|
||||
// 返回 String(str) 这里是为了使返回的值是字符串字面量,在控制台中更符合直觉
|
||||
if (str.length >= maxLength) return String(str)
|
||||
|
||||
let fillLength = maxLength - str.length,
|
||||
times = Math.ceil(fillLength / fillString.length)
|
||||
while (times >>= 1) {
|
||||
fillString += fillString
|
||||
if (times === 1) {
|
||||
fillString += fillString
|
||||
}
|
||||
}
|
||||
return fillString.slice(0, fillLength) + str;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("timeFormat fillString error", error);
|
||||
}
|
||||
|
||||
// 其他更多是格式化有如下:
|
||||
// yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合
|
||||
export function timeFormat(dateTime = null, fmt = 'yyyy-mm-dd hh:MM:ss') {
|
||||
try {
|
||||
// 如果为null,则格式化当前时间
|
||||
if (!dateTime) dateTime = Number(new Date());
|
||||
// 如果dateTime长度为10或者13,则为秒和毫秒的时间戳,如果超过13位,则为其他的时间格式
|
||||
if (dateTime.toString().length == 10) dateTime *= 1000;
|
||||
let date = new Date(dateTime);
|
||||
let ret;
|
||||
let opt = {
|
||||
"y+": date.getFullYear().toString(), // 年
|
||||
"m+": (date.getMonth() + 1).toString(), // 月
|
||||
"d+": date.getDate().toString(), // 日
|
||||
"h+": date.getHours().toString(), // 时
|
||||
"M+": date.getMinutes().toString(), // 分
|
||||
"s+": date.getSeconds().toString() // 秒
|
||||
// 有其他格式化字符需求可以继续添加,必须转化成字符串
|
||||
};
|
||||
for (let k in opt) {
|
||||
ret = new RegExp("(" + k + ")").exec(fmt);
|
||||
if (ret) {
|
||||
fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))
|
||||
};
|
||||
};
|
||||
return fmt;
|
||||
} catch (error) {
|
||||
console.log("timeFormat error", error);
|
||||
return "unknown error"
|
||||
}
|
||||
}
|
||||
|
||||
export function timeFromNow(timestamp) {
|
||||
try {
|
||||
const now = new Date().getTime();
|
||||
let diff = timestamp - now;
|
||||
|
||||
// 确定是过去还是未来
|
||||
const suffix = diff > 0 ? "后" : "前";
|
||||
diff = Math.abs(diff);
|
||||
|
||||
// 计算时间差异
|
||||
const seconds = Math.floor(diff / 1000);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const days = Math.floor(hours / 24);
|
||||
const months = Math.floor(days / 30);
|
||||
const years = Math.floor(days / 365);
|
||||
|
||||
// 根据时间差异返回相应的字符串
|
||||
if (seconds < 60) {
|
||||
return `${seconds}秒${suffix}`;
|
||||
} else if (minutes < 60) {
|
||||
return `${minutes}分钟${suffix}`;
|
||||
} else if (hours < 24) {
|
||||
return `${hours}小时${suffix}`;
|
||||
} else if (days < 30) {
|
||||
return `${days}天${suffix}`;
|
||||
} else if (months < 12) {
|
||||
return `${months}个月${suffix}`;
|
||||
} else {
|
||||
return `${years}年${suffix}`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("timeFromNow error", error);
|
||||
}
|
||||
}
|
||||
430
uni_modules/UniDevTools/src/devTools/core/proxy/console.js
Normal file
@@ -0,0 +1,430 @@
|
||||
import devCache from "../libs/devCache";
|
||||
import devOptions from "../libs/devOptions";
|
||||
import jsonCompress from "../libs/jsonCompress";
|
||||
|
||||
export default {
|
||||
logList: [],
|
||||
options: null,
|
||||
/**
|
||||
* 挂载打印拦截器
|
||||
*/
|
||||
install() {
|
||||
let that = this;
|
||||
|
||||
this.options = devOptions.getOptions()
|
||||
if (!this.options.console.status) return;
|
||||
|
||||
this.logList = devCache.get("console");
|
||||
if (!this.logList) this.logList = [];
|
||||
this.syncReqData(); //同步缓存
|
||||
|
||||
if (uni.__log__) {
|
||||
// ! VUE3在app端时有这个特殊的方法
|
||||
that.mountUniConsole()
|
||||
} else {
|
||||
that.mountJsConsole()
|
||||
}
|
||||
|
||||
//! 删除指定记录
|
||||
uni.$on("devTools_delConsoleItem", (item) => {
|
||||
let i = that.logList.findIndex(
|
||||
(x) => {
|
||||
let t = JSON.stringify(x.list)
|
||||
return t == JSON.stringify(item.list) &&
|
||||
x.time == item.time &&
|
||||
x.page == item.page &&
|
||||
x.type == item.type
|
||||
}
|
||||
);
|
||||
if (i != -1) {
|
||||
that.logList.splice(i, 1);
|
||||
}
|
||||
that.saveData()
|
||||
});
|
||||
|
||||
//! 清空console日志
|
||||
uni.$on("devTools_delConsoleAll", () => {
|
||||
that.logList = []
|
||||
that.saveData()
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 同步请求信息到缓存数据中
|
||||
*/
|
||||
syncReqData() {
|
||||
let that = this;
|
||||
setTimeout(() => {
|
||||
try {
|
||||
that.saveData()
|
||||
} catch (error) {
|
||||
console.log("console.syncReqData error", error);
|
||||
}
|
||||
that.syncReqData()
|
||||
}, 3000);
|
||||
},
|
||||
/**
|
||||
* 同步数据到缓存
|
||||
*/
|
||||
saveData() {
|
||||
let that = this;
|
||||
that.logList = jsonCompress.compressArray(that.logList, 'end', that.options.console.cache.size)
|
||||
devCache.set("console", that.logList)
|
||||
},
|
||||
/**
|
||||
* 挂载监听js自带的console函数
|
||||
*/
|
||||
mountJsConsole() {
|
||||
let that = this;
|
||||
try {
|
||||
|
||||
let l = console.log;
|
||||
try {
|
||||
globalThis.consoleLog = function () {
|
||||
console.log(...arguments)
|
||||
};
|
||||
} catch (error) { }
|
||||
try {
|
||||
window.consoleLog = function () {
|
||||
console.log(...arguments)
|
||||
};
|
||||
} catch (error) { }
|
||||
console.log = function () {
|
||||
replaceConsole("log", arguments)
|
||||
};
|
||||
let e = console.error;
|
||||
function _error() {
|
||||
try {
|
||||
let args = [...arguments]
|
||||
if (
|
||||
args[0]
|
||||
&& typeof args[0] == "string"
|
||||
&& (
|
||||
args[0] == "__ignoreReport__" //! 忽略错误日志上报
|
||||
|| args[0].indexOf("__ignoreReport__") == 0
|
||||
)
|
||||
) {
|
||||
let _args = []
|
||||
if (args.length > 1) {
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (i != 0) {
|
||||
_args.push(args[i])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_args[0] = args[0];
|
||||
_args[0] = _args[0].replace("__ignoreReport__", "");
|
||||
}
|
||||
if (that.options.console.isOutput) {
|
||||
e(..._args)
|
||||
}
|
||||
return;
|
||||
}
|
||||
replaceConsole("error", args)
|
||||
} catch (error) {
|
||||
e("监听console.error出错", error)
|
||||
}
|
||||
}
|
||||
console.error = _error;
|
||||
let w = console.warn;
|
||||
console.warn = function () {
|
||||
replaceConsole("warn", arguments)
|
||||
};
|
||||
let i = console.info;
|
||||
console.info = function () {
|
||||
replaceConsole("info", arguments)
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 替换系统打印函数
|
||||
*/
|
||||
function replaceConsole(type, args) {
|
||||
try {
|
||||
let data = []
|
||||
if (args && args.length > 0) {
|
||||
|
||||
let argList = args;
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
if (args.length == 1) {
|
||||
argList = args[0].split("---COMMA---");
|
||||
|
||||
let endItem = argList[argList.length - 1];
|
||||
if (
|
||||
endItem
|
||||
&& typeof endItem == "string"
|
||||
&& endItem.indexOf(" at ") > -1
|
||||
&& endItem.indexOf(":") > -1
|
||||
) { // 可能包含路径信息
|
||||
let endList = endItem.split(" at ");
|
||||
if (endList.length == 2) {
|
||||
argList.pop()
|
||||
argList.push(endList[0])
|
||||
argList.push("at " + endList[1])
|
||||
}
|
||||
}
|
||||
|
||||
argList = argList.map((item, index) => {
|
||||
try {
|
||||
if (typeof item == "string") {
|
||||
if (item.indexOf("---BEGIN") > -1) {
|
||||
let isJson = item.indexOf("---BEGIN:JSON---") > -1;
|
||||
item = item.replace(/---BEGIN:.*?---/g, '')
|
||||
item = item.replace(/---END:.*?---/g, '')
|
||||
if (isJson) {
|
||||
item = JSON.parse(item);
|
||||
}
|
||||
} else if (item == "---NULL---") {
|
||||
item = null;
|
||||
} else if (item == "---UNDEFINED---") {
|
||||
item = undefined;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("replaceConsole 尝试解析对象出错:", error);
|
||||
}
|
||||
return item;
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
|
||||
let oneSize = that.options.console.cache.rowSize / argList.length;
|
||||
for (let i = 0; i < argList.length; i++) {
|
||||
let row = jsonCompress.compressObject(argList[i], oneSize)
|
||||
data.push(row)
|
||||
}
|
||||
} else {
|
||||
data = []
|
||||
}
|
||||
|
||||
let page = "未知";
|
||||
try {
|
||||
let pages = getCurrentPages()
|
||||
let item = pages[pages.length - 1];
|
||||
if (item && item.route) {
|
||||
page = item.route;
|
||||
}
|
||||
} catch (error) { }
|
||||
that.logList.unshift({
|
||||
list: data,
|
||||
time: new Date().getTime(),
|
||||
page,
|
||||
type,
|
||||
})
|
||||
if (that.options.console.isOutput) {
|
||||
if (type == "error") {
|
||||
e(...args)
|
||||
} else if (type == "warn") {
|
||||
w(...args)
|
||||
} else if (type == "info") {
|
||||
i(...args)
|
||||
} else {
|
||||
l(...args)
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (that.options.console.isOutput) {
|
||||
e("监听console出错", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.log("console.install error", error);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 挂载监听uni自带的打印函数
|
||||
*/
|
||||
mountUniConsole() {
|
||||
let that = this;
|
||||
try {
|
||||
|
||||
let uniSysConsole = uni.__log__;
|
||||
try {
|
||||
globalThis.consoleLog = function () {
|
||||
uni.__log__("log", "未知来源", ...arguments)
|
||||
};
|
||||
} catch (error) { }
|
||||
try {
|
||||
window.consoleLog = function () {
|
||||
uni.__log__("log", "未知来源", ...arguments)
|
||||
};
|
||||
} catch (error) { }
|
||||
|
||||
uni.__log__ = function (type, line, ...args) {
|
||||
try {
|
||||
|
||||
// 处理特殊情况 "__ignoreReport__" 忽略错误日志上报
|
||||
if (type == "error") {
|
||||
if (
|
||||
args[0]
|
||||
&& typeof args[0] == "string"
|
||||
&& (
|
||||
args[0] == "__ignoreReport__" //! 忽略错误日志上报
|
||||
|| args[0].indexOf("__ignoreReport__") == 0
|
||||
)
|
||||
) {
|
||||
let _args = []
|
||||
if (args.length > 1) {
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (i != 0) {
|
||||
_args.push(args[i])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_args[0] = args[0];
|
||||
_args[0] = _args[0].replace("__ignoreReport__", "");
|
||||
}
|
||||
if (that.options.console.isOutput) {
|
||||
uniSysConsole(type, line, ..._args)
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let data = []
|
||||
if (args && args.length > 0) {
|
||||
let argList = args;
|
||||
let oneSize = that.options.console.cache.rowSize / argList.length;
|
||||
for (let i = 0; i < argList.length; i++) {
|
||||
let row = jsonCompress.compressObject(argList[i], oneSize)
|
||||
data.push(row)
|
||||
}
|
||||
} else {
|
||||
data = []
|
||||
}
|
||||
|
||||
let page = "未知";
|
||||
try {
|
||||
let pages = getCurrentPages()
|
||||
let item = pages[pages.length - 1];
|
||||
if (item && item.route) {
|
||||
page = item.route;
|
||||
}
|
||||
} catch (error) { }
|
||||
|
||||
data.push(line)
|
||||
|
||||
that.logList.unshift({
|
||||
list: data,
|
||||
time: new Date().getTime(),
|
||||
page,
|
||||
type,
|
||||
})
|
||||
if (that.options.console.isOutput) {
|
||||
uniSysConsole(type, line, ...data)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (that.options.console.isOutput) {
|
||||
uniSysConsole("error", "监听console出错", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 替换系统打印函数
|
||||
*/
|
||||
function replaceConsole(type, args) {
|
||||
try {
|
||||
let data = []
|
||||
if (args && args.length > 0) {
|
||||
|
||||
let argList = args;
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
if (args.length == 1) {
|
||||
argList = args[0].split("---COMMA---");
|
||||
|
||||
let endItem = argList[argList.length - 1];
|
||||
if (
|
||||
endItem
|
||||
&& typeof endItem == "string"
|
||||
&& endItem.indexOf(" at ") > -1
|
||||
&& endItem.indexOf(":") > -1
|
||||
) { // 可能包含路径信息
|
||||
let endList = endItem.split(" at ");
|
||||
if (endList.length == 2) {
|
||||
argList.pop()
|
||||
argList.push(endList[0])
|
||||
argList.push("at " + endList[1])
|
||||
}
|
||||
}
|
||||
|
||||
argList = argList.map((item, index) => {
|
||||
try {
|
||||
if (typeof item == "string") {
|
||||
if (item.indexOf("---BEGIN") > -1) {
|
||||
let isJson = item.indexOf("---BEGIN:JSON---") > -1;
|
||||
item = item.replace(/---BEGIN:.*?---/g, '')
|
||||
item = item.replace(/---END:.*?---/g, '')
|
||||
if (isJson) {
|
||||
item = JSON.parse(item);
|
||||
}
|
||||
} else if (item == "---NULL---") {
|
||||
item = null;
|
||||
} else if (item == "---UNDEFINED---") {
|
||||
item = undefined;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("replaceConsole 尝试解析对象出错:", error);
|
||||
}
|
||||
return item;
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
|
||||
let oneSize = that.options.console.cache.rowSize / argList.length;
|
||||
for (let i = 0; i < argList.length; i++) {
|
||||
let row = jsonCompress.compressObject(argList[i], oneSize)
|
||||
data.push(row)
|
||||
}
|
||||
} else {
|
||||
data = []
|
||||
}
|
||||
|
||||
let page = "未知";
|
||||
try {
|
||||
let pages = getCurrentPages()
|
||||
let item = pages[pages.length - 1];
|
||||
if (item && item.route) {
|
||||
page = item.route;
|
||||
}
|
||||
} catch (error) { }
|
||||
that.logList.unshift({
|
||||
list: data,
|
||||
time: new Date().getTime(),
|
||||
page,
|
||||
type,
|
||||
})
|
||||
if (that.options.console.isOutput) {
|
||||
if (type == "error") {
|
||||
e(...args)
|
||||
} else if (type == "warn") {
|
||||
w(...args)
|
||||
} else if (type == "info") {
|
||||
i(...args)
|
||||
} else {
|
||||
l(...args)
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (that.options.console.isOutput) {
|
||||
e("监听console出错", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.log("console.install error", error);
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
36
uni_modules/UniDevTools/src/devTools/core/proxy/index.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import devCache from "../libs/devCache";
|
||||
import console from "./console";
|
||||
import request from "./request";
|
||||
import storage from "./storage";
|
||||
import uniBus from "./uniBus";
|
||||
import uniListen from "./uniListen";
|
||||
|
||||
/**
|
||||
* dev调试工具初始化
|
||||
*/
|
||||
export default function devToolsProxyInstall(options) {
|
||||
|
||||
try {
|
||||
if (options.network && options.network.status) {
|
||||
request.install()
|
||||
}
|
||||
if (options.console && options.console.status) {
|
||||
console.install()
|
||||
}
|
||||
if (options.logs && options.logs.status) {
|
||||
uniListen.install()
|
||||
}
|
||||
|
||||
storage.install()
|
||||
|
||||
if (options.uniBus && options.uniBus.status) {
|
||||
uniBus.install()
|
||||
}
|
||||
|
||||
devCache.syncLocalCache();
|
||||
|
||||
} catch (error) {
|
||||
console.log("devToolsProxyInstall error", error);
|
||||
}
|
||||
|
||||
}
|
||||
196
uni_modules/UniDevTools/src/devTools/core/proxy/request.js
Normal file
@@ -0,0 +1,196 @@
|
||||
import devCache from "../libs/devCache";
|
||||
import devOptions from "../libs/devOptions";
|
||||
import jsonCompress from "../libs/jsonCompress";
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 请求日志示例
|
||||
*/
|
||||
ajaxLogData: {
|
||||
id: 0, //请求id
|
||||
type: 0, // 0发起请求中 1请求成功 2请求失败
|
||||
sendTime: 0, //发送请求的时间
|
||||
responseTime: 0, //响应时间
|
||||
useTime: 0, //请求总耗时
|
||||
|
||||
url: "", //请求地址
|
||||
header: "", //请求头
|
||||
method: "get", //请求方式
|
||||
data: "", //请求参数
|
||||
|
||||
responseBody: "", //响应主体
|
||||
responseHeader: "", //响应头
|
||||
responseStatus: "", //响应编码
|
||||
responseMsg: "", //响应报错信息
|
||||
},
|
||||
options: null,
|
||||
/**
|
||||
* 请求的数据列表
|
||||
*/
|
||||
ajaxData: [],
|
||||
/**
|
||||
* 挂载请求拦截器
|
||||
*/
|
||||
install() {
|
||||
let that = this;
|
||||
|
||||
try {
|
||||
this.options = devOptions.getOptions()
|
||||
if (!this.options.network.status) return;
|
||||
|
||||
this.ajaxData = devCache.get("request");
|
||||
if (!this.ajaxData) this.ajaxData = [];
|
||||
this.syncReqData(); //同步缓存
|
||||
|
||||
uni.addInterceptor('request', {
|
||||
/**
|
||||
* 入参
|
||||
*/
|
||||
invoke(args) {
|
||||
|
||||
try {
|
||||
args._id_ = new Date().getTime() + '_' + Number(Math.random().toString().replace("0.", ""));
|
||||
|
||||
let copyData = JSON.parse(JSON.stringify(that.ajaxLogData))
|
||||
copyData.id = args._id_;
|
||||
copyData.sendTime = new Date().getTime();
|
||||
copyData.url = that.dataCopy(args.url);
|
||||
copyData.header = that.dataCopy(args.header);
|
||||
if (!args.method) {
|
||||
copyData.method = "get"
|
||||
} else {
|
||||
copyData.method = that.dataCopy(args.method);
|
||||
}
|
||||
|
||||
let cSize = jsonCompress.calculateStringByteSize(copyData)
|
||||
if (cSize > that.options.network.cache.rowSize) {
|
||||
|
||||
copyData = jsonCompress.compressObject(copyData, that.options.network.cache.rowSize)
|
||||
|
||||
} else {
|
||||
|
||||
let data = jsonCompress.compressObject(args.data, that.options.network.cache.rowSize - cSize)
|
||||
try {
|
||||
data = JSON.parse(data)
|
||||
} catch (error) { }
|
||||
copyData.data = data;
|
||||
|
||||
}
|
||||
that.ajaxData.unshift(copyData)
|
||||
|
||||
} catch (error) {
|
||||
console.error("request拦截器invoke出错", error)
|
||||
}
|
||||
|
||||
},
|
||||
success(response, request) {
|
||||
try {
|
||||
let item = that.ajaxData.find(x => x.id == request._id_);
|
||||
if (!item) return;
|
||||
|
||||
item.responseBodySize = jsonCompress.calculateStringByteSize(response.data);
|
||||
item.responseMsg = response.errMsg;
|
||||
item.responseStatus = response.statusCode;
|
||||
item.responseHeader = response.header;
|
||||
item.type = 1;
|
||||
item.responseTime = new Date().getTime();
|
||||
item.useTime = ((item.responseTime - item.sendTime) / 1000).toFixed(3);
|
||||
|
||||
let size = jsonCompress.calculateStringByteSize(item)
|
||||
if (size > that.options.network.cache.rowSize) {
|
||||
|
||||
item.responseBody = "[内容太长已截断多余部分]"
|
||||
let data = jsonCompress.compressObject(item, that.options.network.cache.rowSize)
|
||||
that.ajaxData[that.ajaxData.findIndex(x => x.id == request._id_)] = data;
|
||||
|
||||
} else {
|
||||
|
||||
let json = response.data;
|
||||
try {
|
||||
json = JSON.parse(JSON.stringify(json))
|
||||
} catch (error) { }
|
||||
item.responseBody = jsonCompress.compressObject(json, that.options.network.cache.rowSize - size)
|
||||
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("request拦截器success出错", error)
|
||||
}
|
||||
|
||||
},
|
||||
fail(err, request) {
|
||||
try {
|
||||
let item = that.ajaxData.find(x => x.id == request._id_);
|
||||
if (!item) return;
|
||||
|
||||
item.type = 2;
|
||||
item.responseTime = new Date().getTime();
|
||||
item.useTime = ((item.responseTime - item.sendTime) / 1000).toFixed(3);
|
||||
|
||||
item.responseMsg = err.errMsg;
|
||||
} catch (error) {
|
||||
console.error("request拦截器fail出错", error)
|
||||
}
|
||||
},
|
||||
complete(res) {
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
// ! 删除指定请求记录
|
||||
uni.$on("devTools_delNetworkItemById", (id) => {
|
||||
let i = this.ajaxData.findIndex(x => x.id == id)
|
||||
if (i != -1) {
|
||||
this.ajaxData.splice(i, 1)
|
||||
}
|
||||
this.saveData()
|
||||
})
|
||||
|
||||
// ! 清空请求记录
|
||||
uni.$on("devTools_delNetworkAll", () => {
|
||||
this.ajaxData = []
|
||||
this.saveData()
|
||||
})
|
||||
} catch (error) {
|
||||
console.log("request.install error", error);
|
||||
}
|
||||
|
||||
},
|
||||
/**
|
||||
* 同步请求信息到缓存数据中
|
||||
*/
|
||||
syncReqData() {
|
||||
let that = this;
|
||||
setTimeout(() => {
|
||||
try {
|
||||
that.saveData()
|
||||
} catch (error) {
|
||||
console.log("request.syncReqData", error);
|
||||
}
|
||||
that.syncReqData()
|
||||
}, 4000);
|
||||
},
|
||||
/**
|
||||
* 保存数据到缓存中
|
||||
*/
|
||||
saveData() {
|
||||
let that = this;
|
||||
that.ajaxData = jsonCompress.compressArray(that.ajaxData, that.options.network.cache.size)
|
||||
devCache.set("request", that.ajaxData)
|
||||
},
|
||||
/**
|
||||
* 复制对象
|
||||
*/
|
||||
dataCopy(data) {
|
||||
try {
|
||||
if (typeof data == "object") {
|
||||
return JSON.parse(JSON.stringify([data]))[0]
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("request.dataCopy", error);
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
113
uni_modules/UniDevTools/src/devTools/core/proxy/storage.js
Normal file
@@ -0,0 +1,113 @@
|
||||
import devCache from "../libs/devCache";
|
||||
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 挂载缓存监听
|
||||
*/
|
||||
install() {
|
||||
try {
|
||||
// #ifdef MP
|
||||
let that = this;
|
||||
|
||||
let _setStorage = uni.setStorage;
|
||||
uni.setStorage = setStorage;
|
||||
function setStorage() {
|
||||
try {
|
||||
if (
|
||||
arguments[0]
|
||||
&& arguments[0].key
|
||||
&& arguments[0].key.indexOf("devTools_") != 0
|
||||
) {
|
||||
that.addCacheKey(arguments[0].key)
|
||||
}
|
||||
} catch (error) { }
|
||||
return _setStorage(...arguments)
|
||||
}
|
||||
|
||||
let _setStorageSync = uni.setStorageSync;
|
||||
uni.setStorageSync = setStorageSync;
|
||||
function setStorageSync() {
|
||||
try {
|
||||
if (
|
||||
arguments[0]
|
||||
&& arguments[0].indexOf("devTools_") != 0
|
||||
) {
|
||||
that.addCacheKey(arguments[0])
|
||||
}
|
||||
} catch (error) { }
|
||||
return _setStorageSync(...arguments)
|
||||
}
|
||||
|
||||
let _removeStorage = uni.removeStorage;
|
||||
uni.removeStorage = removeStorage;
|
||||
function removeStorage() {
|
||||
try {
|
||||
if (
|
||||
arguments[0]
|
||||
&& arguments[0].key
|
||||
&& arguments[0].key.indexOf("devTools_") != 0
|
||||
) {
|
||||
that.delCacheKey(arguments[0].key)
|
||||
}
|
||||
} catch (error) { }
|
||||
return _removeStorage(...arguments)
|
||||
}
|
||||
|
||||
let _removeStorageSync = uni.removeStorageSync;
|
||||
uni.removeStorageSync = removeStorageSync;
|
||||
function removeStorageSync() {
|
||||
try {
|
||||
if (
|
||||
arguments[0]
|
||||
&& arguments[0].indexOf("devTools_") != 0
|
||||
) {
|
||||
that.delCacheKey(arguments[0])
|
||||
}
|
||||
} catch (error) { }
|
||||
return _removeStorageSync(...arguments)
|
||||
}
|
||||
|
||||
// #endif
|
||||
} catch (error) {
|
||||
console.log("devTools storage.install error", error);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 添加缓存key
|
||||
*/
|
||||
addCacheKey(key) {
|
||||
try {
|
||||
if (key && typeof key == "string") {
|
||||
|
||||
let storageList = devCache.get("storage")
|
||||
if (!storageList) storageList = [];
|
||||
if (storageList.indexOf(key) == -1) {
|
||||
storageList.push(key)
|
||||
devCache.set("storage", storageList)
|
||||
}
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("devTools storage.addCacheKey error", error);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 删除指定缓存key
|
||||
*/
|
||||
delCacheKey(key) {
|
||||
try {
|
||||
if (key && typeof key == "string") {
|
||||
let storageList = devCache.get("storage")
|
||||
if (!storageList) storageList = [];
|
||||
let index = storageList.indexOf(key);
|
||||
if (index > -1) {
|
||||
storageList.splice(index, 1)
|
||||
devCache.set("storage", storageList)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("devTools storage.delCacheKey error", error);
|
||||
}
|
||||
},
|
||||
}
|
||||
151
uni_modules/UniDevTools/src/devTools/core/proxy/uniBus.js
Normal file
@@ -0,0 +1,151 @@
|
||||
import devCache from "../libs/devCache";
|
||||
import devOptions from "../libs/devOptions";
|
||||
import jsonCompress from "../libs/jsonCompress";
|
||||
|
||||
|
||||
export default {
|
||||
logList: [],
|
||||
busCount: [],
|
||||
|
||||
options: null,
|
||||
/**
|
||||
* 挂载打印拦截器
|
||||
*/
|
||||
install() {
|
||||
try {
|
||||
let that = this;
|
||||
|
||||
this.options = devOptions.getOptions()
|
||||
if (!this.options.uniBus.status) return;
|
||||
|
||||
this.logList = devCache.get("uniBus");
|
||||
if (!this.logList) this.logList = [];
|
||||
|
||||
this.busCount = devCache.get("busCount");
|
||||
if (!this.busCount) this.busCount = [];
|
||||
|
||||
|
||||
this.syncReqData(); //同步缓存
|
||||
|
||||
let now = () => new Date().getTime();
|
||||
|
||||
const _on = uni.$on;
|
||||
uni.$on = function () {
|
||||
try {
|
||||
let n = arguments[0];
|
||||
if (n && typeof n == "string" && n.length < 200 && n.indexOf("devTools_") == -1) {
|
||||
that.logList.unshift({
|
||||
t: now(),
|
||||
e: jsonCompress.compressObject(`on>${n}`, that.options.uniBus.cache.rowMax)
|
||||
})
|
||||
addCount(n, "on")
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("uni.$on出错", error);
|
||||
}
|
||||
_on(...arguments)
|
||||
}
|
||||
|
||||
const _once = uni.$once;
|
||||
uni.$once = function () {
|
||||
try {
|
||||
let n = arguments[0];
|
||||
if (n && typeof n == "string" && n.length < 200 && n.indexOf("devTools_") == -1) {
|
||||
that.logList.unshift({
|
||||
t: now(),
|
||||
e: jsonCompress.compressObject(`once>${n}`, that.options.uniBus.cache.rowMax)
|
||||
})
|
||||
addCount(n, "once")
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("uni.$once出错", error);
|
||||
}
|
||||
_once(...arguments)
|
||||
}
|
||||
|
||||
const _emit = uni.$emit;
|
||||
uni.$emit = function () {
|
||||
try {
|
||||
let n = arguments[0];
|
||||
let p = arguments[1];
|
||||
if (n && typeof n == "string" && n.length < 200 && n.indexOf("devTools_") == -1) {
|
||||
that.logList.unshift({
|
||||
t: now(),
|
||||
e: jsonCompress.compressObject(`emit>${n}` + (p ? ('>' + JSON.stringify(p)) : ''), that.options.uniBus.cache.rowMax)
|
||||
})
|
||||
addCount(n, "emit")
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("uni.$emit出错", error);
|
||||
}
|
||||
_emit(...arguments)
|
||||
}
|
||||
|
||||
const _off = uni.$off;
|
||||
uni.$off = function () {
|
||||
try {
|
||||
let n = arguments[0];
|
||||
if (n && typeof n == "string" && n.length < 200 && n.indexOf("devTools_") == -1) {
|
||||
that.logList.unshift({
|
||||
t: now(),
|
||||
e: jsonCompress.compressObject(`off>${n}` + arguments[0], that.options.uniBus.cache.rowMax)
|
||||
})
|
||||
addCount(n, "off")
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("uni.$off出错", error);
|
||||
}
|
||||
_off(...arguments)
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计总次数
|
||||
*/
|
||||
function addCount(name, type = "on") {
|
||||
let i = that.busCount.findIndex(x => x.e == name)
|
||||
if (i == -1) {
|
||||
let item = {
|
||||
e: name,
|
||||
on: 0,
|
||||
off: 0,
|
||||
emit: 0,
|
||||
once: 0,
|
||||
};
|
||||
item[type] = item[type] + 1;
|
||||
that.busCount.push(item)
|
||||
} else {
|
||||
that.busCount[i][type] = that.busCount[i][type] + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// ! 清空全部记录
|
||||
uni.$on("devTools_delUniBusAll", () => {
|
||||
that.logList = []
|
||||
that.busCount = []
|
||||
})
|
||||
} catch (error) {
|
||||
console.log("devTools uniBus.install error", error);
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
/**
|
||||
* 同步请求信息到缓存数据中
|
||||
*/
|
||||
syncReqData() {
|
||||
let that = this;
|
||||
setTimeout(() => {
|
||||
try {
|
||||
|
||||
that.logList = jsonCompress.compressArray(that.logList, "end", that.options.uniBus.cache.rowMax)
|
||||
devCache.set("uniBus", that.logList)
|
||||
|
||||
that.busCount = jsonCompress.compressArray(that.busCount, "end", that.options.uniBus.cache.countMaxSize)
|
||||
devCache.set("busCount", that.busCount)
|
||||
} catch (error) {
|
||||
console.log("devTools uniBus.syncReqData error", error);
|
||||
}
|
||||
that.syncReqData()
|
||||
}, 5000);
|
||||
},
|
||||
}
|
||||
192
uni_modules/UniDevTools/src/devTools/core/proxy/uniListen.js
Normal file
@@ -0,0 +1,192 @@
|
||||
import logReport from "../libs/logReport";
|
||||
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 挂载uni大部分api监听器
|
||||
*/
|
||||
install() {
|
||||
try {
|
||||
this.addDefUniApiListen()
|
||||
this.onNetworkStatusChange()
|
||||
this.scanCodeListen()
|
||||
this.onLocaleChange()
|
||||
} catch (error) {
|
||||
console.log("uniListen error", error);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 批量挂载api调用日志
|
||||
*/
|
||||
addDefUniApiListen() {
|
||||
/**
|
||||
* 需要挂载监听的api列表
|
||||
*/
|
||||
let diyListenApi = {
|
||||
downloadFile(args) {
|
||||
logReport("downloadFile>" + (args && args.url ? args.url : ''))
|
||||
},
|
||||
connectSocket(args) {
|
||||
logReport("connectSocket>" + args.url)
|
||||
},
|
||||
makePhoneCall(args) {
|
||||
logReport("makePhoneCall>" + args.phoneNumber)
|
||||
},
|
||||
addPhoneContact(args) {
|
||||
logReport("addPhoneContact>" + args.name)
|
||||
},
|
||||
showToast(args) {
|
||||
logReport("showToast>" + args.title)
|
||||
},
|
||||
showModal(args) {
|
||||
logReport("showModal>" + args.title + '>' + args.content)
|
||||
},
|
||||
setLocale(args) {
|
||||
logReport("setLocale>" + args)
|
||||
},
|
||||
saveFile(args) {
|
||||
logReport("saveFile>" + args.tempFilePath)
|
||||
},
|
||||
login(args) {
|
||||
logReport("login>" + JSON.stringify(args))
|
||||
},
|
||||
share(args) {
|
||||
logReport("share>" + JSON.stringify(args))
|
||||
},
|
||||
shareWithSystem(args) {
|
||||
logReport("shareWithSystem>" + JSON.stringify(args))
|
||||
},
|
||||
requestPayment(args) {
|
||||
logReport("requestPayment>" + JSON.stringify(args))
|
||||
},
|
||||
authorize(args) {
|
||||
logReport("requestPayment>" + args.scope)
|
||||
},
|
||||
navigateToMiniProgram(args) {
|
||||
logReport("navigateToMiniProgram>" + args.appId + '>' + args.path)
|
||||
},
|
||||
openDocument(args) {
|
||||
logReport("openDocument>" + args.filePath)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 需要监听打印日志的api名称列表
|
||||
*/
|
||||
let waitListenApiNames = [
|
||||
"uploadFile",
|
||||
"closeSocket",
|
||||
"getLocation",
|
||||
"chooseLocation",
|
||||
"openLocation",
|
||||
"chooseImage",
|
||||
"previewImage",
|
||||
"saveImageToPhotosAlbum",
|
||||
"chooseFile",
|
||||
"chooseVideo",
|
||||
"chooseMedia",
|
||||
"saveVideoToPhotosAlbum",
|
||||
"openVideoEditor",
|
||||
"openAppAuthorizeSetting",
|
||||
"startAccelerometer",
|
||||
"startCompass",
|
||||
"startGyroscope",
|
||||
"setScreenBrightness",
|
||||
"vibrate",
|
||||
"vibrateLong",
|
||||
"vibrateShort",
|
||||
"openBluetoothAdapter",
|
||||
"startBeaconDiscovery",
|
||||
"startSoterAuthentication",
|
||||
"hideKeyboard",
|
||||
"showActionSheet",
|
||||
"startPullDownRefresh",
|
||||
"showShareMenu",
|
||||
"startFacialRecognitionVerify",
|
||||
"openSetting",
|
||||
"chooseAddress",
|
||||
"chooseInvoiceTitle",
|
||||
"openEmbeddedMiniProgram",
|
||||
|
||||
]
|
||||
|
||||
for (const key in diyListenApi) {
|
||||
uni.addInterceptor(key, {
|
||||
invoke(_args) {
|
||||
try {
|
||||
diyListenApi[key](_args)
|
||||
} catch (error) {
|
||||
console.error("addInterceptor=>" + key, error);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
waitListenApiNames.map(key => {
|
||||
uni.addInterceptor(key, {
|
||||
invoke(args) {
|
||||
try {
|
||||
logReport(key)
|
||||
} catch (error) {
|
||||
console.error("addInterceptor>" + key, error);
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
},
|
||||
/**
|
||||
* 添加网络状态监听
|
||||
*/
|
||||
onNetworkStatusChange() {
|
||||
uni.onNetworkStatusChange((res) => {
|
||||
try {
|
||||
logReport("onNetworkStatusChange>isConnected:" + (res.isConnected ? 'true' : 'false') + '>networkType:' + res.networkType)
|
||||
} catch (error) {
|
||||
console.log("onNetworkStatusChange", error);
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 添加系统主题切换监听
|
||||
*/
|
||||
onThemeChange() {
|
||||
uni.onThemeChange((res) => {
|
||||
try {
|
||||
logReport("onThemeChange>" + res.theme)
|
||||
} catch (error) {
|
||||
console.log("onThemeChange", error);
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 监听扫码结果
|
||||
*/
|
||||
scanCodeListen() {
|
||||
uni.addInterceptor('scanCode', {
|
||||
success(res) {
|
||||
try {
|
||||
logReport("scanCodeSuccess>" + JSON.stringify({
|
||||
scanType: res.scanType,
|
||||
result: res.result,
|
||||
}))
|
||||
} catch (error) {
|
||||
console.log("scanCode", error);
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 监听系统语言切换
|
||||
*/
|
||||
onLocaleChange() {
|
||||
uni.onLocaleChange((locale) => {
|
||||
try {
|
||||
logReport("onLocaleChange>" + locale)
|
||||
} catch (error) {
|
||||
console.log("onLocaleChange", error);
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
157
uni_modules/UniDevTools/src/devTools/core/proxy/vueMixin.js
Normal file
@@ -0,0 +1,157 @@
|
||||
import devOptions from "../libs/devOptions";
|
||||
import logReport from "../libs/logReport";
|
||||
import pageStatisticsReport from "../libs/pageStatistics";
|
||||
|
||||
|
||||
/**
|
||||
* ! Vue页面混入,监听生命周期
|
||||
*/
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 挂载dev页面对象
|
||||
*/
|
||||
devTools_pageData: {
|
||||
route: '', // 页面路径
|
||||
isOnShow: false, // 是否处于展示状态
|
||||
activeTime: 0, //活跃时间
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* *页面载入事件
|
||||
*/
|
||||
onLoad(pageInitParams) {
|
||||
let that = this;
|
||||
|
||||
|
||||
// ! 注入 Eruda
|
||||
let isInjectEruda = uni.getStorageSync("devTools_isInjectEruda") == "yes";
|
||||
if (isInjectEruda) {
|
||||
let ErudaCode = `
|
||||
if(!window.isInjectEruda){
|
||||
window.isInjectEruda = true;
|
||||
var script = document.createElement('script');
|
||||
script.src="https://cdn.jsdelivr.net/npm/eruda";
|
||||
document.body.append(script);
|
||||
script.onload = function () {
|
||||
eruda.init();
|
||||
}
|
||||
}
|
||||
`
|
||||
let fun = 'e' + ['v'][0] + 'a' + ['l'][0];
|
||||
try {
|
||||
// #ifdef H5
|
||||
window[fun](ErudaCode);
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
let endPageWebView = getCurrentPages().pop();
|
||||
if (endPageWebView) {
|
||||
let nowPageWebview = endPageWebView.$getAppWebview();
|
||||
if (nowPageWebview && !nowPageWebview.nvue) {
|
||||
nowPageWebview[fun + 'JS'](ErudaCode)
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
} catch (error) {
|
||||
console.log("devTools mixin onLoad injectEruda error ", error);
|
||||
}
|
||||
}
|
||||
|
||||
// ! 注入 vConsole
|
||||
let isInjectVConsole = uni.getStorageSync("devTools_isInjectVConsole") == "yes";
|
||||
if (isInjectVConsole) {
|
||||
let vConsoleCode = `
|
||||
if(!window.isInjectVConsole){
|
||||
window.isInjectVConsole = true;
|
||||
var script = document.createElement('script');
|
||||
script.src="https://cdn.jsdelivr.net/npm/vconsole@latest/dist/vconsole.min.js";
|
||||
document.body.append(script);
|
||||
script.onload = function () {
|
||||
let vConsoleObj = new window.VConsole();
|
||||
}
|
||||
}
|
||||
`
|
||||
let fun = 'e' + ['v'][0] + 'a' + ['l'][0];
|
||||
try {
|
||||
// #ifdef H5
|
||||
window[fun](vConsoleCode);
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
let endPageWebView = getCurrentPages().pop();
|
||||
if (endPageWebView) {
|
||||
let nowPageWebview = endPageWebView.$getAppWebview();
|
||||
if (nowPageWebview && !nowPageWebview.nvue) {
|
||||
nowPageWebview[fun + 'JS'](vConsoleCode)
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
} catch (error) {
|
||||
console.log("devTools mixin onLoad injectVConsole error ", error);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let pages = getCurrentPages();
|
||||
let pageItem = pages && pages.length > 0 ? pages[pages.length - 1] : null;
|
||||
if (pageItem) {
|
||||
let devSetting = devOptions.getOptions()
|
||||
if (pageItem.route == devSetting.devRoute) {
|
||||
that.devTools_pageData = false;
|
||||
} else {
|
||||
that.devTools_pageData.route = pageItem.route;
|
||||
logReport(`onLoad>${pageItem.route}>` + (pageInitParams ? JSON.stringify(pageInitParams) : ''))
|
||||
setInterval(() => {
|
||||
if (that.devTools_pageData && that.devTools_pageData.isOnShow) {
|
||||
that.devTools_pageData.activeTime = that.devTools_pageData.activeTime + 1;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("devTools mixin onLoad error ", error);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* *页面展示事件
|
||||
*/
|
||||
onShow() {
|
||||
try {
|
||||
let that = this;
|
||||
if (that.devTools_pageData) {
|
||||
that.devTools_pageData.isOnShow = true;
|
||||
that.devTools_pageData.activeTime = 0;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("devTools mixin onShow error ", error);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* *页面隐藏事件
|
||||
*/
|
||||
onHide() {
|
||||
try {
|
||||
let that = this;
|
||||
if (that.devTools_pageData) {
|
||||
that.devTools_pageData.isOnShow = false;
|
||||
pageStatisticsReport(that.devTools_pageData.route, that.devTools_pageData.activeTime);
|
||||
that.devTools_pageData.activeTime = 0;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("devTools mixin onHide error ", error);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* * 页面卸载事件
|
||||
*/
|
||||
onUnload() {
|
||||
try {
|
||||
let that = this;
|
||||
logReport(`onUnload>${that.devTools_pageData.route}`)
|
||||
that.devTools_pageData = null;
|
||||
} catch (error) {
|
||||
console.log("devTools mixin onUnload error ", error);
|
||||
}
|
||||
},
|
||||
}
|
||||
177
uni_modules/UniDevTools/src/devTools/index.js
Normal file
@@ -0,0 +1,177 @@
|
||||
|
||||
import drawView from "./core/libs/drawView";
|
||||
import logReport from "./core/libs/logReport";
|
||||
import errorReport from "./core/libs/errorReport";
|
||||
import devOptions from "./core/libs/devOptions";
|
||||
import createH5Bubble from "./core/libs/createH5Bubble";
|
||||
import vueMixin from "./core/proxy/vueMixin";
|
||||
import devToolsProxyInstall from "./core/proxy/index";
|
||||
|
||||
|
||||
/**
|
||||
* @type {Vue}
|
||||
*/
|
||||
let that;
|
||||
|
||||
const devTools = {
|
||||
options: null,
|
||||
/**
|
||||
* vue2挂载安装
|
||||
*/
|
||||
install(vm, options) {
|
||||
try {
|
||||
that = vm;
|
||||
let _this = this;
|
||||
|
||||
if(vm && vm.config && vm.config.globalProperties){
|
||||
vm.config.globalProperties.$logReport = logReport;
|
||||
}else{
|
||||
vm.prototype.$logReport = logReport;
|
||||
}
|
||||
|
||||
//! 初始化配置项
|
||||
devOptions.setOptions(options)
|
||||
options = devOptions.getOptions()
|
||||
_this.options = options;
|
||||
|
||||
if (!options || !options.status) {
|
||||
return console.log("%c devTools 调试工具未运行!", 'padding: 4px;background-color: red;color: #fff;font-size: 15px;');
|
||||
}
|
||||
|
||||
//! 挂载dev工具
|
||||
if(vm && vm.config && vm.config.globalProperties){
|
||||
vm.config.globalProperties.$devTools = devTools;
|
||||
}else{
|
||||
vm.prototype.$devTools = devTools;
|
||||
}
|
||||
|
||||
if (options.error.status) {
|
||||
|
||||
//! 挂载vue报错
|
||||
vm.config.errorHandler = (err, vm, trace) => {
|
||||
errorReport(err, trace, "ve")
|
||||
};
|
||||
|
||||
//! 挂载vue警告
|
||||
vm.config.warnHandler = (err, vm, trace) => {
|
||||
errorReport(err, trace, "vw")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//!混入生命周期监听器
|
||||
vm.mixin(vueMixin)
|
||||
|
||||
//!绘制环境变量小标签
|
||||
// #ifdef APP-PLUS
|
||||
drawView(options, devTools)
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
createH5Bubble(options, devTools)
|
||||
// #endif
|
||||
|
||||
//!调试工具全局拦截器挂载
|
||||
devToolsProxyInstall(options)
|
||||
|
||||
//! 注册dev弹窗打开事件
|
||||
uni.$on("devTools_showDialog", () => {
|
||||
_this.show()
|
||||
})
|
||||
|
||||
//! 注册dev弹窗关闭事件
|
||||
uni.$on("devTools_closeDialog", (options) => {
|
||||
_this.hide(options)
|
||||
})
|
||||
|
||||
//! 挂载uni对象
|
||||
uni.$dev = {
|
||||
show() {
|
||||
_this.show()
|
||||
},
|
||||
hide() {
|
||||
_this.hide()
|
||||
},
|
||||
errorReport,
|
||||
logReport,
|
||||
}
|
||||
|
||||
//! 注册jsRunner执行事件
|
||||
uni.$on("devTools_jsRunner", (code) => {
|
||||
let result = undefined;
|
||||
try {
|
||||
let fun = (("ev" + "__混淆__" + "al").replace("__混淆__", ""));
|
||||
result = globalThis[fun](code);
|
||||
// result = eval(code);
|
||||
} catch (error) {
|
||||
if (error && error.message) {
|
||||
result = error.message;
|
||||
}
|
||||
}
|
||||
|
||||
uni.$emit("devTools_jsRunnerCallback", result)
|
||||
})
|
||||
} catch (error) {
|
||||
console.log("devTools install error", error);
|
||||
}
|
||||
|
||||
},
|
||||
/**
|
||||
* 打开调试弹窗
|
||||
*/
|
||||
show() {
|
||||
let pages = getCurrentPages();
|
||||
|
||||
//! 已经打开了调试工具,不要重复显示
|
||||
if (pages[pages.length - 1].route == this.options.devRoute) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: this.options.route,
|
||||
animationType: 'none',
|
||||
animationDuration: 0,
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 隐藏调试弹窗
|
||||
*/
|
||||
hide(options) {
|
||||
// #ifdef APP-PLUS
|
||||
uni.$emit("devTools_closeDevToolsPanel")
|
||||
let isBack = false;
|
||||
uni.$once("devTools_panelHideSuccess", () => {
|
||||
if (!isBack) {
|
||||
isBack = true;
|
||||
uni.navigateBack();
|
||||
}
|
||||
})
|
||||
setTimeout(() => {
|
||||
if (!isBack) {
|
||||
isBack = true;
|
||||
uni.navigateBack();
|
||||
}
|
||||
}, 500);
|
||||
// #endif
|
||||
// #ifndef APP-PLUS
|
||||
uni.navigateBack()
|
||||
// #endif
|
||||
|
||||
if (options && options.navigateToUrl) {
|
||||
let t = 600;
|
||||
// #ifndef APP-PLUS
|
||||
t = 200;
|
||||
// #endif
|
||||
setTimeout(() => {
|
||||
uni.navigateTo({
|
||||
url: options.navigateToUrl,
|
||||
})
|
||||
}, t);
|
||||
}
|
||||
|
||||
},
|
||||
errorReport,
|
||||
logReport,
|
||||
}
|
||||
|
||||
|
||||
export default devTools;
|
||||
@@ -0,0 +1,975 @@
|
||||
<template>
|
||||
<view
|
||||
v-if="isShow"
|
||||
class="bottomTools"
|
||||
:style="{
|
||||
'padding-bottom': pb,
|
||||
}"
|
||||
>
|
||||
<!-- Error -->
|
||||
<template v-if="tabTitle == 'Error'">
|
||||
<view
|
||||
class="miniBtn mr warn"
|
||||
@click="emptyLogs('error')"
|
||||
>
|
||||
<text class="miniBtnText">清空 x</text>
|
||||
</view>
|
||||
<btnTabs
|
||||
:list="errorTypeList"
|
||||
:value="errorTypeIndex"
|
||||
@indexChange="errorTypeIndexChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Console -->
|
||||
<template v-if="tabTitle == 'Console'">
|
||||
<view
|
||||
class="miniBtn mr warn"
|
||||
@click="emptyLogs('console')"
|
||||
>
|
||||
<text class="miniBtnText">清空 x</text>
|
||||
</view>
|
||||
<btnTabs
|
||||
:list="consoleTypeList"
|
||||
:value="consoleTypeListIndex"
|
||||
@indexChange="consoleTypeIndexChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Network -->
|
||||
<template v-if="tabTitle == 'Network'">
|
||||
<view
|
||||
class="miniBtn mr warn"
|
||||
@click="emptyLogs('network')"
|
||||
>
|
||||
<text class="miniBtnText">清空 x</text>
|
||||
</view>
|
||||
<btnTabs
|
||||
:list="networkFilterType"
|
||||
:value="networkTypeListIndex"
|
||||
@indexChange="networkTypeIndexChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Pages -->
|
||||
<template v-if="tabTitle == 'Pages'">
|
||||
<view
|
||||
class="miniBtn mr warn"
|
||||
@click="emptyLogs('pages_1')"
|
||||
>
|
||||
<text class="miniBtnText">清空停留统计</text>
|
||||
</view>
|
||||
<view
|
||||
class="miniBtn mr warn"
|
||||
@click="emptyLogs('pages_2')"
|
||||
>
|
||||
<text class="miniBtnText">清空日活统计</text>
|
||||
</view>
|
||||
<view
|
||||
class="miniBtn mr primary"
|
||||
@click="goPage"
|
||||
>
|
||||
<text class="miniBtnText">跳转页面</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- Logs -->
|
||||
<template v-if="tabTitle == 'Logs'">
|
||||
<view
|
||||
class="miniBtn mr warn"
|
||||
@click="emptyLogs('logs')"
|
||||
>
|
||||
<text class="miniBtnText">清空 x</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- Storage -->
|
||||
<template v-if="tabTitle == 'Storage'">
|
||||
<view
|
||||
class="miniBtn mr warn"
|
||||
@click="emptyLogs('storage')"
|
||||
>
|
||||
<text class="miniBtnText">清空 x</text>
|
||||
</view>
|
||||
<!-- #ifdef H5 -->
|
||||
<btnTabs
|
||||
:list="storageFilterTypeList"
|
||||
:value="storageTypeListIndex"
|
||||
@indexChange="storageTypeIndexChange"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
<view
|
||||
class="miniBtn primary ml"
|
||||
@click="addStorage"
|
||||
>
|
||||
<text class="miniBtnText">新增数据+</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- UniBus -->
|
||||
<template v-if="tabTitle == 'UniBus'">
|
||||
<view
|
||||
class="miniBtn mr warn"
|
||||
@click="emptyLogs('UniBus')"
|
||||
>
|
||||
<text class="miniBtnText">清空 x</text>
|
||||
</view>
|
||||
<btnTabs
|
||||
:list="busFilterType"
|
||||
:value="busTypeListIndex"
|
||||
@indexChange="busTypeIndexChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- FileSys -->
|
||||
<template v-if="tabTitle == 'FileSys'">
|
||||
<!-- #ifdef APP-PLUS || MP-WEIXIN -->
|
||||
<scroll-view
|
||||
scroll-x
|
||||
class="dirList"
|
||||
>
|
||||
<view class="dirScrollItem">
|
||||
<text
|
||||
class="dirName"
|
||||
style="color: #999"
|
||||
@click="$emit('goChildDir', '_goIndex_0')"
|
||||
>
|
||||
{{ options.fileSysDirType }}
|
||||
</text>
|
||||
<text class="delimiter">/</text>
|
||||
<view
|
||||
v-for="(item, index) in options.fileSysDirList"
|
||||
:key="index"
|
||||
class="dirItem"
|
||||
>
|
||||
<text
|
||||
v-if="index != 0"
|
||||
class="delimiter"
|
||||
>
|
||||
/
|
||||
</text>
|
||||
<text
|
||||
class="dirName"
|
||||
@click="$emit('goChildDir', '_goIndex_' + (index + 1))"
|
||||
>
|
||||
{{ item }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view
|
||||
class="miniBtn mr warn"
|
||||
@click="emptyFolder"
|
||||
>
|
||||
<text class="miniBtnText">清空 x</text>
|
||||
</view>
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<btnTabs
|
||||
:list="dirTypeList"
|
||||
:value="fileTypeListIndex"
|
||||
@indexChange="$emit('changeFileDirType', dirTypeList[$event].type)"
|
||||
/>
|
||||
<view style="width: 20rpx"></view>
|
||||
<!-- #endif -->
|
||||
<view
|
||||
class="miniBtn primary"
|
||||
@click="createDir"
|
||||
>
|
||||
<text class="miniBtnText">新建文件(夹)</text>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<!-- JsRunner -->
|
||||
<template v-if="tabTitle == 'JsRunner'">
|
||||
<view class="jsRunnerTools">
|
||||
<view class="runOptions">
|
||||
<view class="radiusList">
|
||||
<text class="runType">运行环境:</text>
|
||||
<radio-group
|
||||
@change="jsRunType = $event.detail.value"
|
||||
class="radiusList"
|
||||
style="display: flex;flex-direction: row;"
|
||||
>
|
||||
<view
|
||||
v-for="(item, index) in jsRunTypeList"
|
||||
:key="index"
|
||||
class="radiusItem"
|
||||
@click="jsRunType = item"
|
||||
>
|
||||
<radio
|
||||
class="radiusRadio"
|
||||
:value="item"
|
||||
:checked="jsRunType == item"
|
||||
color="#ff2d55"
|
||||
/>
|
||||
<text
|
||||
class="radiusText"
|
||||
:style="{
|
||||
color: jsRunType == item ? '#ff2d55' : '#333',
|
||||
}"
|
||||
>
|
||||
{{ item }}
|
||||
</text>
|
||||
</view>
|
||||
</radio-group>
|
||||
</view>
|
||||
<view
|
||||
class="hisEmpty"
|
||||
@click="$emit('emptyCodeHis')"
|
||||
v-if="options.codeHisLength > 0"
|
||||
>
|
||||
<image
|
||||
class="hisEmptyIcon"
|
||||
src="@/devTools/page/static/delete.png"
|
||||
/>
|
||||
<text class="hisEmptyText">清空记录</text>
|
||||
</view>
|
||||
<view
|
||||
class="logList"
|
||||
@click="showHisCode"
|
||||
>
|
||||
<text class="hisText">历史代码</text>
|
||||
<image
|
||||
class="unfold"
|
||||
src="@/devTools/page/static/unfold.png"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="code">
|
||||
<textarea
|
||||
v-model="waitSendCode"
|
||||
class="codeInput"
|
||||
placeholder="js code ..."
|
||||
maxlength="-1"
|
||||
/>
|
||||
<view
|
||||
class="codeSend"
|
||||
@click="runJs"
|
||||
>
|
||||
<text class="codeSendText">run</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- Vuex -->
|
||||
<template v-if="tabTitle == 'Vuex'">
|
||||
<btnTabs
|
||||
:list="stateTypeList"
|
||||
:value="stateTypeListIndex"
|
||||
@indexChange="stateTypeIndexChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
||||
<codeHisPicker ref="codeHisPicker" />
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import devCache from "../../core/libs/devCache";
|
||||
import appDelDir from "./libs/appDelDir";
|
||||
import btnTabs from "./ui/btnTabs.vue";
|
||||
import codeHisPicker from "./ui/codeHisPicker.vue";
|
||||
export default {
|
||||
components: {
|
||||
btnTabs,
|
||||
codeHisPicker,
|
||||
},
|
||||
props: {
|
||||
/**
|
||||
* 列表索引
|
||||
*/
|
||||
tabIndex: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
/**
|
||||
* 当前标题
|
||||
*/
|
||||
tabTitle: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
/**
|
||||
* 配置项
|
||||
*/
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
errorFilterType: "",
|
||||
consoleFilterType: "",
|
||||
networkFilterType: "",
|
||||
busFilterType: "",
|
||||
fileSysDirList: [],
|
||||
fileSysDirType: "",
|
||||
storageType: "",
|
||||
}),
|
||||
},
|
||||
/**
|
||||
* 是否渲染
|
||||
*/
|
||||
isShow: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* Vuex变量类型
|
||||
*/
|
||||
stateType: {
|
||||
type: String,
|
||||
default: "vuex"
|
||||
},
|
||||
},
|
||||
data() {
|
||||
let pb = "20px";
|
||||
// #ifdef H5
|
||||
pb = "8px";
|
||||
// #endif
|
||||
let sys = uni.getSystemInfoSync();
|
||||
if (sys.platform == "ios") {
|
||||
pb = "40px";
|
||||
}
|
||||
let jsRunTypeList = [];
|
||||
// #ifdef H5
|
||||
jsRunTypeList = ["h5"];
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
jsRunTypeList = ["nvue", "webview"];
|
||||
// #endif
|
||||
return {
|
||||
/**
|
||||
* 底部边距
|
||||
*/
|
||||
pb,
|
||||
/**
|
||||
* 错误类型列表
|
||||
*/
|
||||
errorTypeList: [
|
||||
{ title: "全部", type: "" },
|
||||
{ title: "error", type: "ve" },
|
||||
{ title: "warn", type: "vw" },
|
||||
{ title: "jsError", type: "oe" },
|
||||
{ title: "unknown", type: "n" },
|
||||
],
|
||||
/**
|
||||
* console过滤类型
|
||||
*/
|
||||
consoleTypeList: [
|
||||
{ title: "全部", type: "" },
|
||||
{ title: "log", type: "log" },
|
||||
{ title: "info", type: "info" },
|
||||
{ title: "warn", type: "warn" },
|
||||
{ title: "error", type: "error" },
|
||||
],
|
||||
/**
|
||||
* 请求过滤类型
|
||||
*/
|
||||
networkFilterType: [
|
||||
{ title: "全部", type: "" },
|
||||
{ title: "请求失败", type: "请求失败" },
|
||||
{ title: "10s+", type: "10s+" },
|
||||
{ title: "500KB+", type: "500KB+" },
|
||||
{ title: "get", type: "get" },
|
||||
{ title: "post", type: "post" },
|
||||
{ title: "other", type: "other" },
|
||||
],
|
||||
/**
|
||||
* uni bus 过滤类型
|
||||
*/
|
||||
busFilterType: [
|
||||
{ title: "全部", type: "" },
|
||||
{ title: "on", type: "on" },
|
||||
{ title: "once", type: "once" },
|
||||
{ title: "emit", type: "emit" },
|
||||
{ title: "off", type: "off" },
|
||||
],
|
||||
/**
|
||||
* 文件类型
|
||||
*/
|
||||
dirTypeList: [
|
||||
{ title: "_doc", type: "PRIVATE_DOC" },
|
||||
{ title: "_www", type: "PRIVATE_WWW" },
|
||||
{ title: "公共文档", type: "PUBLIC_DOCUMENTS" },
|
||||
{ title: "公共下载", type: "PUBLIC_DOWNLOADS" },
|
||||
],
|
||||
/**
|
||||
* 缓存类型
|
||||
*/
|
||||
storageFilterTypeList: [
|
||||
{ title: "localStorage", type: "localStorage" },
|
||||
{ title: "sessionStorage", type: "sessionStorage" },
|
||||
{ title: "cookie", type: "cookie" },
|
||||
],
|
||||
/**
|
||||
* 等待执行的js代码
|
||||
*/
|
||||
waitSendCode: "",
|
||||
/**
|
||||
* js运行类型
|
||||
*/
|
||||
jsRunType: jsRunTypeList[0],
|
||||
jsRunTypeList,
|
||||
/**
|
||||
* Vuex变量类型
|
||||
*/
|
||||
stateTypeList: [
|
||||
{ title: "vuex", type: "vuex" },
|
||||
{ title: "pinia", type: "pinia" },
|
||||
{ title: "globalData", type: "globalData" },
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* 错误筛选分类index
|
||||
*/
|
||||
errorTypeIndex() {
|
||||
return this.errorTypeList.findIndex((x) => x.type == this.options.errorFilterType);
|
||||
},
|
||||
/**
|
||||
* 日志分类索引
|
||||
*/
|
||||
consoleTypeListIndex() {
|
||||
return this.consoleTypeList.findIndex((x) => x.type == this.options.consoleFilterType);
|
||||
},
|
||||
/**
|
||||
* 网络筛选索引
|
||||
*/
|
||||
networkTypeListIndex() {
|
||||
return this.networkFilterType.findIndex((x) => x.type == this.options.networkFilterType);
|
||||
},
|
||||
/**
|
||||
* bus分类索引
|
||||
*/
|
||||
busTypeListIndex() {
|
||||
return this.busFilterType.findIndex((x) => x.type == this.options.busFilterType);
|
||||
},
|
||||
/**
|
||||
* 文件分类索引
|
||||
*/
|
||||
fileTypeListIndex() {
|
||||
return this.dirTypeList.findIndex((x) => x.type == this.options.fileSysDirType);
|
||||
},
|
||||
/**
|
||||
* 缓存类型索引
|
||||
*/
|
||||
storageTypeListIndex() {
|
||||
return this.storageFilterTypeList.findIndex((x) => x.type == this.options.storageType);
|
||||
},
|
||||
/**
|
||||
* Vuex变量类型
|
||||
*/
|
||||
stateTypeListIndex(){
|
||||
return this.stateTypeList.findIndex(x=>x.type == this.stateType)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 过滤类型改变
|
||||
*/
|
||||
filterTypeChange(type) {
|
||||
this.$emit("filterTypeChange", type);
|
||||
},
|
||||
/**
|
||||
* 错误类型索引改变
|
||||
*/
|
||||
errorTypeIndexChange(e) {
|
||||
this.filterTypeChange(this.errorTypeList[e].type);
|
||||
},
|
||||
/**
|
||||
* 日志分类索引改变
|
||||
*/
|
||||
consoleTypeIndexChange(e) {
|
||||
this.filterTypeChange(this.consoleTypeList[e].type);
|
||||
},
|
||||
/**
|
||||
* 网络状态筛选改变事件
|
||||
*/
|
||||
networkTypeIndexChange(e) {
|
||||
this.filterTypeChange(this.networkFilterType[e].type);
|
||||
},
|
||||
/**
|
||||
* bus筛选改变事件
|
||||
*/
|
||||
busTypeIndexChange(e) {
|
||||
this.filterTypeChange(this.busFilterType[e].type);
|
||||
},
|
||||
/**
|
||||
* 文件分类改变事件
|
||||
*/
|
||||
fileTypeIndexChange(e) {
|
||||
this.$emit("changeFileDirType", this.dirTypeList[e].type);
|
||||
},
|
||||
/**
|
||||
* 缓存类型改变事件
|
||||
*/
|
||||
storageTypeIndexChange(e) {
|
||||
this.$emit("changeStorageType", this.storageFilterTypeList[e].type);
|
||||
},
|
||||
/**
|
||||
* Vuex变量类型改变事件
|
||||
*/
|
||||
stateTypeIndexChange(e) {
|
||||
this.$emit("changeStateType", this.stateTypeList[e].type);
|
||||
},
|
||||
/**
|
||||
* 清空日志
|
||||
*/
|
||||
emptyLogs(type) {
|
||||
let that = this;
|
||||
let title = {
|
||||
error: "报错记录",
|
||||
console: "console",
|
||||
network: "请求日志",
|
||||
pages_1: "页面停留统计",
|
||||
pages_2: "页面日活统计",
|
||||
logs: "Logs",
|
||||
UniBus: "UniBus",
|
||||
storage: "Storage",
|
||||
};
|
||||
// #ifdef H5
|
||||
if (type == "storage") {
|
||||
title[type] = this.options.storageType;
|
||||
}
|
||||
// #endif
|
||||
|
||||
uni.showModal({
|
||||
title: "警告",
|
||||
content: `是否确认清空${title[type]}全部数据?`,
|
||||
success(e) {
|
||||
if (e.confirm) {
|
||||
uni.showLoading({
|
||||
title: "处理中...",
|
||||
});
|
||||
|
||||
if (type == "error") {
|
||||
devCache.set("errorReport", []);
|
||||
} else if (type == "console") {
|
||||
uni.$emit("devTools_delConsoleAll");
|
||||
} else if (type == "network") {
|
||||
uni.$emit("devTools_delNetworkAll");
|
||||
} else if (type == "logs") {
|
||||
devCache.set("logReport", []);
|
||||
} else if (type == "UniBus") {
|
||||
uni.$emit("devTools_delUniBusAll");
|
||||
} else if (type == "pages_1") {
|
||||
devCache.set("pageCount", []);
|
||||
} else if (type == "pages_2") {
|
||||
devCache.set("dayOnline", []);
|
||||
} else if (type == "storage") {
|
||||
that.delStorage();
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
that.$emit("getPage");
|
||||
}, 5500);
|
||||
setTimeout(() => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "清空成功!",
|
||||
icon: "success",
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 清空全部缓存
|
||||
*/
|
||||
delStorage() {
|
||||
// #ifdef APP-PLUS
|
||||
let keys = plus.storage.getAllKeys();
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = String(keys[i]);
|
||||
if (key.indexOf("devTools_") == 0) {
|
||||
continue;
|
||||
}
|
||||
uni.removeStorageSync(key);
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
if (this.options.storageType == "localStorage") {
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
let key = String(localStorage.key(i));
|
||||
if (key.indexOf("devTools_") == 0) {
|
||||
continue;
|
||||
}
|
||||
uni.removeStorageSync(key);
|
||||
}
|
||||
} else if (this.options.storageType == "sessionStorage") {
|
||||
for (let i = 0; i < sessionStorage.length; i++) {
|
||||
let key = String(sessionStorage.key(i));
|
||||
if (key.indexOf("devTools_") == 0) {
|
||||
continue;
|
||||
}
|
||||
sessionStorage.removeItem(key);
|
||||
}
|
||||
} else if (this.options.storageType == "cookie") {
|
||||
let keys = [];
|
||||
document.cookie.split(";").forEach((cookieStr) => {
|
||||
const [name, value] = cookieStr.trim().split("=");
|
||||
keys.push(name);
|
||||
});
|
||||
keys.map((k) => {
|
||||
document.cookie = `${k}=;expires=` + new Date(new Date().getTime() + 200).toGMTString() + ";path=/";
|
||||
});
|
||||
}
|
||||
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
let keyList = devCache.get("storage");
|
||||
if (!keyList) keyList = [];
|
||||
for (let i = 0; i < keyList.length; i++) {
|
||||
const key = keyList[i];
|
||||
if (key.indexOf("devTools_") == 0) {
|
||||
continue;
|
||||
}
|
||||
uni.removeStorageSync(key);
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
/**
|
||||
* 清空文件夹
|
||||
*/
|
||||
emptyFolder() {
|
||||
let that = this;
|
||||
if (that.options.fileSysDirType == "PRIVATE_WWW") {
|
||||
return uni.showToast({
|
||||
title: "该目录不可删除",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
uni.showModal({
|
||||
title: "提示",
|
||||
content: "是否确认清空全部文件?",
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({
|
||||
title: "清空中",
|
||||
});
|
||||
let path = "";
|
||||
switch (that.options.fileSysDirType) {
|
||||
case "wx":
|
||||
path = wx.env.USER_DATA_PATH;
|
||||
break;
|
||||
case "PRIVATE_DOC":
|
||||
path = "_doc";
|
||||
break;
|
||||
case "PUBLIC_DOCUMENTS":
|
||||
path = "_documents";
|
||||
break;
|
||||
case "PUBLIC_DOWNLOADS":
|
||||
path = "_downloads";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// #ifdef APP-PLUS
|
||||
appDelDir(path + "/", false)
|
||||
.then(() => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "清空成功!",
|
||||
icon: "success",
|
||||
});
|
||||
that.$emit("getPage");
|
||||
})
|
||||
.catch(() => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "清空失败!",
|
||||
icon: "none",
|
||||
});
|
||||
});
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
let fs = wx.getFileSystemManager();
|
||||
fs.rmdir({
|
||||
dirPath: path + "/",
|
||||
recursive: true,
|
||||
success() {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "清空成功!",
|
||||
icon: "success",
|
||||
});
|
||||
that.$emit("getPage");
|
||||
},
|
||||
fail() {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "清空失败!",
|
||||
icon: "none",
|
||||
});
|
||||
},
|
||||
});
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 创建文件夹
|
||||
*/
|
||||
createDir() {
|
||||
let that = this;
|
||||
let menu = [
|
||||
{
|
||||
text: `新建文件`,
|
||||
click() {
|
||||
that.$emit("editDirName", {
|
||||
isEdit: false,
|
||||
isDir: false,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
text: `新建文件夹`,
|
||||
click() {
|
||||
that.$emit("editDirName", {
|
||||
isEdit: false,
|
||||
isDir: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
uni.showActionSheet({
|
||||
itemList: menu.map((x) => x.text),
|
||||
success({ tapIndex }) {
|
||||
menu[tapIndex].click();
|
||||
},
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 新增缓存
|
||||
*/
|
||||
addStorage() {
|
||||
uni.$emit("devTools_showAddStorageDialog");
|
||||
},
|
||||
/**
|
||||
* 执行js
|
||||
*/
|
||||
runJs() {
|
||||
let that = this;
|
||||
if (this.waitSendCode == "") {
|
||||
return uni.showToast({
|
||||
title: "请先输入需要执行的js代码",
|
||||
icon: "none",
|
||||
});
|
||||
} else {
|
||||
let code = String(this.waitSendCode);
|
||||
this.$emit("runJs", { code, type: that.jsRunType });
|
||||
this.waitSendCode = "";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 获取历史代码运行记录
|
||||
*/
|
||||
showHisCode() {
|
||||
let that = this;
|
||||
let his = devCache.get("codeRunHis");
|
||||
if (!his) his = [];
|
||||
if (his.length == 0) {
|
||||
return uni.showToast({
|
||||
title: "暂无记录!",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
that.$refs.codeHisPicker.show(his).then((res) => {
|
||||
that.waitSendCode = res;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 跳转指定页面
|
||||
*/
|
||||
goPage() {
|
||||
uni.$emit("devTools_showRouteDialog");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.bottomTools {
|
||||
position: fixed;
|
||||
z-index: 3;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 750rpx;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
background-color: #fff;
|
||||
padding-top: 15rpx;
|
||||
padding-left: 20rpx;
|
||||
padding-right: 20rpx;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
.mr {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
.mt {
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
.ml {
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
.miniBtn {
|
||||
height: 40rpx;
|
||||
border-radius: 8rpx;
|
||||
padding: 0 10rpx;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
&.warn {
|
||||
background-color: rgb(255, 251, 229);
|
||||
}
|
||||
&.primary {
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
.miniBtnText {
|
||||
font-size: 20rpx;
|
||||
height: 40rpx;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dirList {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 710rpx;
|
||||
height: 34rpx;
|
||||
margin-bottom: 10rpx;
|
||||
white-space: nowrap;
|
||||
.dirScrollItem {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
height: 34rpx;
|
||||
}
|
||||
.dirItem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
.delimiter {
|
||||
color: #999;
|
||||
margin: 0 6rpx;
|
||||
font-size: 22rpx;
|
||||
line-height: 34rpx;
|
||||
}
|
||||
.dirName {
|
||||
color: #333;
|
||||
font-size: 22rpx;
|
||||
line-height: 34rpx;
|
||||
height: 34rpx;
|
||||
padding: 0 6rpx;
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
.jsRunnerTools {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 710rpx;
|
||||
.runOptions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 710rpx;
|
||||
justify-content: space-between;
|
||||
padding-bottom: 10rpx;
|
||||
.radiusList {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
.runType {
|
||||
font-size: 20rpx;
|
||||
line-height: 24rpx;
|
||||
color: #333;
|
||||
}
|
||||
.radiusItem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-left: 10rpx;
|
||||
.radiusText {
|
||||
font-size: 20rpx;
|
||||
line-height: 24rpx;
|
||||
color: #333;
|
||||
}
|
||||
.radiusRadio {
|
||||
transform: scale(0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
.hisEmpty {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
.hisEmptyIcon {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
}
|
||||
.hisEmptyText {
|
||||
font-size: 20rpx;
|
||||
margin-left: 5rpx;
|
||||
color: #ff2d55;
|
||||
}
|
||||
}
|
||||
.logList {
|
||||
// margin-right: 120rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
.hisText {
|
||||
font-size: 20rpx;
|
||||
color: #777;
|
||||
list-style: 24rpx;
|
||||
margin-right: 5rpx;
|
||||
}
|
||||
.unfold {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.code {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 710rpx;
|
||||
.codeInput {
|
||||
width: 590rpx;
|
||||
height: 100rpx;
|
||||
font-size: 20rpx;
|
||||
line-height: 28rpx;
|
||||
color: #333;
|
||||
padding: 10rpx;
|
||||
background-color: rgba(0, 0, 0, 0.03);
|
||||
border-radius: 15rpx;
|
||||
}
|
||||
.codeSend {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 15rpx;
|
||||
border: 1px solid #ff2d55;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.codeSendText {
|
||||
color: #ff2d55;
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
.codeSend:active {
|
||||
background-color: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,203 @@
|
||||
<template>
|
||||
<view>
|
||||
<view
|
||||
class="editMask"
|
||||
v-if="isShow"
|
||||
:style="{
|
||||
height: height + 'px',
|
||||
}"
|
||||
@click.stop
|
||||
>
|
||||
<view
|
||||
class="editDialog"
|
||||
@click.stop
|
||||
>
|
||||
<text class="title">新增缓存</text>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="请输入Key"
|
||||
class="input"
|
||||
v-model="key"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="请输入Value"
|
||||
class="input"
|
||||
v-model="value"
|
||||
/>
|
||||
<view class="btnGroup">
|
||||
<view
|
||||
class="btnItem left"
|
||||
@click="hide"
|
||||
>
|
||||
<text class="btnText">取消</text>
|
||||
</view>
|
||||
<view
|
||||
class="btnItem right"
|
||||
@click="save"
|
||||
>
|
||||
<text class="btnText">提交</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
let success, error;
|
||||
export default {
|
||||
props: {
|
||||
/**
|
||||
* 默认缓存类型
|
||||
*/
|
||||
storageType: {
|
||||
type: String,
|
||||
default: "localStorage",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 是否展示
|
||||
*/
|
||||
isShow: false,
|
||||
/**
|
||||
* 键名称
|
||||
*/
|
||||
key: "",
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
value: "",
|
||||
/**
|
||||
* 屏幕高度
|
||||
*/
|
||||
height: uni.getSystemInfoSync().windowHeight,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
let that = this;
|
||||
/**
|
||||
* 挂载弹窗打开事件
|
||||
*/
|
||||
uni.$on("devTools_showAddStorageDialog", () => {
|
||||
that.key = "";
|
||||
that.value = "";
|
||||
that.isShow = true;
|
||||
});
|
||||
},
|
||||
unmounted() {
|
||||
uni.$off("devTools_showAddStorageDialog");
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 关闭弹窗
|
||||
*/
|
||||
hide() {
|
||||
this.isShow = false;
|
||||
},
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
save() {
|
||||
let that = this;
|
||||
if (that.key == "") {
|
||||
return uni.showToast({
|
||||
title: "请输入key",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
if (that.storageType == "localStorage") {
|
||||
uni.setStorageSync(that.key, that.value);
|
||||
} else if (that.storageType == "sessionStorage") {
|
||||
sessionStorage.setItem(that.key, that.value);
|
||||
} else if (that.storageType == "cookie") {
|
||||
let key = encodeURIComponent(that.key);
|
||||
let val = encodeURIComponent(that.value);
|
||||
let cookie =
|
||||
`${key}=${val}; path=/; expires=` + new Date(new Date().getTime() + 86400 * 1000 * 365).toGMTString();
|
||||
document.cookie = cookie;
|
||||
}
|
||||
uni.showToast({
|
||||
title: "添加成功!",
|
||||
icon: "success",
|
||||
});
|
||||
that.hide();
|
||||
setTimeout(() => {
|
||||
that.$emit("getPage");
|
||||
}, 300);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.editMask {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
width: 750rpx;
|
||||
flex: 1;
|
||||
/* #ifndef APP-PLUS */
|
||||
height: 100vh;
|
||||
backdrop-filter: blur(1px);
|
||||
/* #endif */
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
.editDialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 690rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 300rpx;
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
height: 50rpx;
|
||||
line-height: 50rpx;
|
||||
}
|
||||
.input {
|
||||
margin-top: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
width: 640rpx;
|
||||
height: 70rpx;
|
||||
padding: 5rpx;
|
||||
border-radius: 8rpx;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.btnGroup {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 640rpx;
|
||||
justify-content: space-between;
|
||||
.btnItem {
|
||||
height: 64rpx;
|
||||
border-radius: 10rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.btnText {
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
}
|
||||
&.left {
|
||||
width: 160rpx;
|
||||
background-color: #8799a3;
|
||||
}
|
||||
&.right {
|
||||
width: 450rpx;
|
||||
background-color: #3cbb45;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,365 @@
|
||||
<template>
|
||||
<view>
|
||||
<view
|
||||
class="editMask"
|
||||
v-if="isShow"
|
||||
:style="{
|
||||
height: height + 'px',
|
||||
}"
|
||||
@click.stop
|
||||
>
|
||||
<view
|
||||
class="editDialog"
|
||||
@click.stop
|
||||
>
|
||||
<text class="title">{{ title }}</text>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="请输入"
|
||||
class="input"
|
||||
v-model="value"
|
||||
/>
|
||||
<view class="btnGroup">
|
||||
<view
|
||||
class="btn left"
|
||||
@click="hide"
|
||||
>
|
||||
<text class="btnText">取消</text>
|
||||
</view>
|
||||
<view
|
||||
class="btn right"
|
||||
@click="save"
|
||||
>
|
||||
<text class="btnText">提交</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
let success, error;
|
||||
export default {
|
||||
props: {
|
||||
/**
|
||||
* 路径列表
|
||||
*/
|
||||
dirList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
/**
|
||||
* 路径类型
|
||||
*/
|
||||
dirType: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 是否展示
|
||||
*/
|
||||
isShow: false,
|
||||
/**
|
||||
* 是否为操作文件夹
|
||||
*/
|
||||
isDir: false,
|
||||
/**
|
||||
* 是否编辑模式
|
||||
*/
|
||||
isEdit: true,
|
||||
/**
|
||||
* 输入框的值
|
||||
*/
|
||||
value: "",
|
||||
/**
|
||||
* 屏幕高度
|
||||
*/
|
||||
height: uni.getSystemInfoSync().windowHeight,
|
||||
/**
|
||||
* 更改前名称
|
||||
*/
|
||||
oldValue: "",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* 获取标题
|
||||
*/
|
||||
title() {
|
||||
if (this.isEdit) {
|
||||
return this.isDir ? "更改文件夹名称" : "更改文件名称";
|
||||
} else {
|
||||
return this.isDir ? "创建文件夹" : "创建文件";
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 展示弹窗
|
||||
*/
|
||||
show(options) {
|
||||
let that = this;
|
||||
return new Promise((yes, err) => {
|
||||
success = yes;
|
||||
error = err;
|
||||
|
||||
this.isDir = options.isDir;
|
||||
this.isEdit = options.isEdit;
|
||||
this.value = String(options.name ? options.name : "");
|
||||
this.oldValue = String(options.name ? options.name : "");
|
||||
|
||||
that.isShow = true;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 关闭弹窗
|
||||
*/
|
||||
hide() {
|
||||
this.isShow = false;
|
||||
},
|
||||
/**
|
||||
* 获取当前文件绝对路径
|
||||
*/
|
||||
getPath() {
|
||||
let that = this;
|
||||
let path = "";
|
||||
switch (that.dirType) {
|
||||
case "wx":
|
||||
path = wx.env.USER_DATA_PATH;
|
||||
break;
|
||||
case "PRIVATE_DOC":
|
||||
path = "_doc";
|
||||
break;
|
||||
case "PRIVATE_WWW":
|
||||
path = "_www";
|
||||
break;
|
||||
case "PUBLIC_DOCUMENTS":
|
||||
path = "_documents";
|
||||
break;
|
||||
case "PUBLIC_DOWNLOADS":
|
||||
path = "_downloads";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
that.dirList.map((x) => {
|
||||
path += "/" + x;
|
||||
});
|
||||
return path + "/";
|
||||
},
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
save() {
|
||||
let that = this;
|
||||
that.value = that.value.replace(" ", "");
|
||||
if (that.value == "") {
|
||||
return uni.showToast({
|
||||
title: "请输入...",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
let path = that.getPath();
|
||||
|
||||
function buildSuccess() {
|
||||
uni.showToast({
|
||||
title: "操作成功!",
|
||||
icon: "success",
|
||||
});
|
||||
that.isShow = false;
|
||||
that.$emit("getPage");
|
||||
}
|
||||
|
||||
function buildError(e) {
|
||||
let msg = "";
|
||||
if (e && e.message) {
|
||||
msg = e.message;
|
||||
}
|
||||
uni.showToast({
|
||||
title: "重命名失败!" + msg,
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
if (1) {
|
||||
let fs = wx.getFileSystemManager();
|
||||
if (that.isEdit) {
|
||||
if (that.isDir) {
|
||||
// ! 重命名文件夹
|
||||
// ! 小程序不支持重命名文件夹
|
||||
} else {
|
||||
// ! 重命名文件
|
||||
fs.rename({
|
||||
oldPath: path + that.oldValue,
|
||||
newPath: path + that.value,
|
||||
success: buildSuccess,
|
||||
fail: buildError,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (that.isDir) {
|
||||
// ! 创建目录
|
||||
fs.mkdir({
|
||||
dirPath: path + that.value,
|
||||
recursive: false,
|
||||
success: buildSuccess,
|
||||
fail: buildError,
|
||||
});
|
||||
} else {
|
||||
// ! 创建文件
|
||||
// fs.open({
|
||||
// filePath: path + that.value,
|
||||
// flag: "wx+",
|
||||
// success({ fd }) {
|
||||
// fs.closeSync({ fd });
|
||||
// buildSuccess();
|
||||
// },
|
||||
// fail: buildError,
|
||||
// });
|
||||
fs.writeFile({
|
||||
filePath: path + that.value,
|
||||
data: "",
|
||||
encoding: "utf8",
|
||||
success() {
|
||||
buildSuccess();
|
||||
},
|
||||
fail: buildError,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
// #endif
|
||||
|
||||
if (that.isEdit) {
|
||||
if (that.isDir) {
|
||||
// ! 重命名文件夹
|
||||
plus.io.resolveLocalFileSystemURL(
|
||||
path + that.oldValue,
|
||||
(entry) => {
|
||||
plus.io.resolveLocalFileSystemURL(
|
||||
path,
|
||||
(faEntry) => {
|
||||
entry.moveTo(faEntry, that.value, buildSuccess, buildError);
|
||||
},
|
||||
buildError
|
||||
);
|
||||
},
|
||||
buildError
|
||||
);
|
||||
} else {
|
||||
// ! 重命名文件
|
||||
plus.io.resolveLocalFileSystemURL(
|
||||
path + that.oldValue,
|
||||
(entry) => {
|
||||
plus.io.resolveLocalFileSystemURL(
|
||||
path,
|
||||
(faEntry) => {
|
||||
entry.moveTo(faEntry, that.value, buildSuccess, buildError);
|
||||
},
|
||||
buildError
|
||||
);
|
||||
},
|
||||
buildError
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (that.isDir) {
|
||||
// ! 创建文件夹
|
||||
plus.io.resolveLocalFileSystemURL(
|
||||
path,
|
||||
(entry) => {
|
||||
entry.getDirectory(that.value, { create: true, exclusive: false }, buildSuccess, buildError);
|
||||
},
|
||||
buildError
|
||||
);
|
||||
} else {
|
||||
// ! 创建文件
|
||||
plus.io.resolveLocalFileSystemURL(
|
||||
path,
|
||||
(entry) => {
|
||||
entry.getFile(that.value, { create: true }, buildSuccess, buildError);
|
||||
},
|
||||
buildError
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.editMask {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
width: 750rpx;
|
||||
flex: 1;
|
||||
/* #ifndef APP-PLUS */
|
||||
height: 100vh;
|
||||
backdrop-filter: blur(1px);
|
||||
/* #endif */
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
.editDialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 690rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 300rpx;
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
line-height: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
.input {
|
||||
margin-top: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
width: 640rpx;
|
||||
height: 70rpx;
|
||||
padding: 5rpx;
|
||||
border-radius: 8rpx;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.btnGroup {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 640rpx;
|
||||
justify-content: space-between;
|
||||
.btn {
|
||||
height: 64rpx;
|
||||
border-radius: 10rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.btnText {
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.left {
|
||||
width: 160rpx;
|
||||
background-color: #8799a3;
|
||||
}
|
||||
.right {
|
||||
width: 450rpx;
|
||||
background-color: #3cbb45;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<view>
|
||||
<view
|
||||
class="dialogMask"
|
||||
v-if="isShow"
|
||||
:style="{
|
||||
height: height + 'px',
|
||||
}"
|
||||
@click.stop="hide"
|
||||
>
|
||||
<view
|
||||
class="dialogContent"
|
||||
@click.stop
|
||||
>
|
||||
<view
|
||||
class="dialogHead"
|
||||
@click="hide"
|
||||
>
|
||||
<view>
|
||||
<text class="title">{{ item.date + " " + item.timeCount }}</text>
|
||||
</view>
|
||||
<view>
|
||||
<image
|
||||
src="@/devTools/page/static/unfold.png"
|
||||
class="fold"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="scrollList"
|
||||
>
|
||||
<view
|
||||
v-for="(row, index) in item.page"
|
||||
:key="index"
|
||||
class="pageLogItem"
|
||||
@click.stop="showMenu(row)"
|
||||
>
|
||||
<text class="p">页面:{{ row.r }}</text>
|
||||
<text class="t">活跃:{{ row.timeCount }}</text>
|
||||
</view>
|
||||
<view style="height: 100rpx"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
let success, error;
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 是否展示
|
||||
*/
|
||||
isShow: false,
|
||||
/**
|
||||
* 屏幕高度
|
||||
*/
|
||||
height: uni.getSystemInfoSync().windowHeight,
|
||||
/**
|
||||
* 详情列表
|
||||
*/
|
||||
item: {
|
||||
date: "",
|
||||
timeCount: "",
|
||||
page: [
|
||||
{
|
||||
r: "",
|
||||
timeCount: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 展示弹窗
|
||||
*/
|
||||
show(item) {
|
||||
let that = this;
|
||||
return new Promise((yes, err) => {
|
||||
success = yes;
|
||||
error = err;
|
||||
that.item.date = item.date;
|
||||
that.item.timeCount = item.timeCount;
|
||||
that.item.page = item.page;
|
||||
that.isShow = true;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 关闭弹窗
|
||||
*/
|
||||
hide() {
|
||||
this.isShow = false;
|
||||
error();
|
||||
},
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
save() {
|
||||
this.isShow = false;
|
||||
success(this.value);
|
||||
},
|
||||
/**
|
||||
* 展示菜单
|
||||
*/
|
||||
showMenu(row) {
|
||||
let that = this;
|
||||
|
||||
let r = String(row.r).substring(0, 10) + "...";
|
||||
|
||||
let menu = [
|
||||
{
|
||||
text: `复制路径(${r})`,
|
||||
click() {
|
||||
uni.setClipboardData({
|
||||
data: row.r,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
text: `复制时间(${row.timeCount})`,
|
||||
click() {
|
||||
uni.setClipboardData({
|
||||
data: row.timeCount,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
text: `跳转至此页面`,
|
||||
click() {
|
||||
uni.$emit("devTools_showRouteDialog", row.r);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
uni.showActionSheet({
|
||||
itemList: menu.map((x) => x.text),
|
||||
success({ tapIndex }) {
|
||||
menu[tapIndex].click();
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.dialogMask {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
width: 750rpx;
|
||||
flex: 1;
|
||||
/* #ifndef APP-PLUS */
|
||||
height: 100vh;
|
||||
backdrop-filter: blur(1px);
|
||||
/* #endif */
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
.dialogContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 750rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
.dialogHead {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 80rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
width: 750rpx;
|
||||
.title {
|
||||
margin-left: 20rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
color: #333;
|
||||
}
|
||||
.fold {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
.scrollList {
|
||||
width: 750rpx;
|
||||
height: 750rpx;
|
||||
.pageLogItem {
|
||||
width: 750rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10rpx 20rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
&:active {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.p {
|
||||
font-size: 20rpx;
|
||||
color: #333;
|
||||
}
|
||||
.t {
|
||||
margin-top: 4rpx;
|
||||
font-size: 20rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<view>
|
||||
<view
|
||||
class="editMask"
|
||||
v-if="isShow"
|
||||
:style="{
|
||||
height: height + 'px',
|
||||
}"
|
||||
@click.stop
|
||||
>
|
||||
<view
|
||||
class="editDialog"
|
||||
@click.stop
|
||||
>
|
||||
<view>
|
||||
<text class="title">{{ title }}</text>
|
||||
</view>
|
||||
<textarea
|
||||
v-model="value"
|
||||
type="text"
|
||||
placeholder="请输入..."
|
||||
class="textarea"
|
||||
/>
|
||||
<view class="btnGroup">
|
||||
<view
|
||||
class="btnItem left"
|
||||
@click="hide"
|
||||
>
|
||||
<text class="btnText">取消</text>
|
||||
</view>
|
||||
<view
|
||||
class="btnItem right"
|
||||
@click="save"
|
||||
>
|
||||
<text class="btnText">提交</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
let success, error;
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 是否展示
|
||||
*/
|
||||
isShow: false,
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
title: "",
|
||||
/**
|
||||
* 输入框的值
|
||||
*/
|
||||
value: "",
|
||||
/**
|
||||
* 屏幕高度
|
||||
*/
|
||||
height: uni.getSystemInfoSync().windowHeight,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
let that = this;
|
||||
/**
|
||||
* 使用uni.$on打开弹窗
|
||||
*/
|
||||
uni.$on("devTools_showEditDialog", (options) => {
|
||||
that
|
||||
.show(options.title, options.value)
|
||||
.then((val) => {
|
||||
uni.$emit("devTools_editDialogSaveSuccess", val);
|
||||
})
|
||||
.catch(() => {
|
||||
uni.$emit("devTools_editDialogClose");
|
||||
});
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
uni.$off("devTools_showEditDialog");
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 展示弹窗
|
||||
*/
|
||||
show(title = "标题", value = "") {
|
||||
let that = this;
|
||||
return new Promise((yes, err) => {
|
||||
success = yes;
|
||||
error = err;
|
||||
that.title = title;
|
||||
that.value = value ? value : "";
|
||||
that.isShow = true;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 关闭弹窗
|
||||
*/
|
||||
hide() {
|
||||
this.isShow = false;
|
||||
error();
|
||||
},
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
save() {
|
||||
this.isShow = false;
|
||||
success(this.value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.editMask {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
width: 750rpx;
|
||||
flex: 1;
|
||||
/* #ifndef APP-PLUS */
|
||||
height: 100vh;
|
||||
backdrop-filter: blur(1px);
|
||||
/* #endif */
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
.editDialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 690rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
line-height: 28rpx;
|
||||
color: #333;
|
||||
max-width: 600rpx;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.textarea {
|
||||
margin-top: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
width: 640rpx;
|
||||
min-height: 200rpx;
|
||||
max-height: 750rpx;
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
padding: 10rpx;
|
||||
}
|
||||
.btnGroup {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 640rpx;
|
||||
justify-content: space-between;
|
||||
.btnItem {
|
||||
height: 64rpx;
|
||||
border-radius: 10rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.btnText {
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
}
|
||||
&.left {
|
||||
width: 160rpx;
|
||||
background-color: #8799a3;
|
||||
}
|
||||
&.right {
|
||||
width: 450rpx;
|
||||
background-color: #3cbb45;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<view>
|
||||
<view
|
||||
class="routeDialogMask"
|
||||
v-if="isShow"
|
||||
:style="{
|
||||
height: height + 'px',
|
||||
}"
|
||||
@click.stop
|
||||
>
|
||||
<view
|
||||
class="routeDialog"
|
||||
@click.stop
|
||||
>
|
||||
<view>
|
||||
<text class="title">跳转至指定页面</text>
|
||||
</view>
|
||||
<textarea
|
||||
v-model="path"
|
||||
type="text"
|
||||
placeholder="请输入..."
|
||||
class="textarea"
|
||||
/>
|
||||
<view class="btnGroup">
|
||||
<view
|
||||
class="btnItem left"
|
||||
@click="hide"
|
||||
>
|
||||
<text class="btnText">取消</text>
|
||||
</view>
|
||||
<view
|
||||
class="btnItem right"
|
||||
@click="goPath"
|
||||
>
|
||||
<text class="btnText">跳转</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 是否展示
|
||||
*/
|
||||
isShow: false,
|
||||
/**
|
||||
* 输入框的值
|
||||
*/
|
||||
path: "",
|
||||
/**
|
||||
* 屏幕高度
|
||||
*/
|
||||
height: uni.getSystemInfoSync().windowHeight,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
let that = this;
|
||||
/**
|
||||
* 使用uni.$on打开弹窗
|
||||
*/
|
||||
uni.$on("devTools_showRouteDialog", (path) => {
|
||||
that.path = path ? path : "";
|
||||
that.isShow = true;
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
uni.$off("devTools_showRouteDialog");
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 关闭弹窗
|
||||
*/
|
||||
hide() {
|
||||
this.isShow = false;
|
||||
},
|
||||
/**
|
||||
* 执行跳转
|
||||
*/
|
||||
goPath() {
|
||||
let that = this;
|
||||
let path = String(that.path);
|
||||
path = path.replace(/[\r\n\s]+/g, "");
|
||||
if (path.substring(0, 1) !== "/") {
|
||||
return uni.showToast({
|
||||
title: "页面路径需以“/”开头!",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
if (path.length < 2) {
|
||||
return uni.showToast({
|
||||
title: "请输入正确的路径!",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: path,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.routeDialogMask {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
width: 750rpx;
|
||||
flex: 1;
|
||||
/* #ifndef APP-PLUS */
|
||||
height: 100vh;
|
||||
backdrop-filter: blur(1px);
|
||||
/* #endif */
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
.routeDialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 690rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
line-height: 28rpx;
|
||||
color: #333;
|
||||
max-width: 600rpx;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.textarea {
|
||||
margin-top: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
width: 640rpx;
|
||||
min-height: 200rpx;
|
||||
max-height: 750rpx;
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
padding: 10rpx;
|
||||
}
|
||||
.btnGroup {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 640rpx;
|
||||
justify-content: space-between;
|
||||
.btnItem {
|
||||
height: 64rpx;
|
||||
border-radius: 10rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.btnText {
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
}
|
||||
&.left {
|
||||
width: 160rpx;
|
||||
background-color: #8799a3;
|
||||
}
|
||||
&.right {
|
||||
width: 450rpx;
|
||||
background-color: #3cbb45;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,499 @@
|
||||
<template>
|
||||
<view>
|
||||
<view
|
||||
class="dialogMask"
|
||||
v-if="isShow"
|
||||
:style="{
|
||||
height: height + 'px',
|
||||
}"
|
||||
@click.stop
|
||||
>
|
||||
<view
|
||||
class="dialogContent"
|
||||
@click.stop
|
||||
>
|
||||
<view
|
||||
class="dialogHead"
|
||||
@click="hide(2)"
|
||||
>
|
||||
<view>
|
||||
<text class="title">请求构建工具</text>
|
||||
</view>
|
||||
<view>
|
||||
<image
|
||||
src="@/devTools/page/static/unfold.png"
|
||||
class="fold"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view
|
||||
@click.stop
|
||||
scroll-y
|
||||
class="scrollList"
|
||||
:style="{
|
||||
height: dialogHeight + 'px',
|
||||
}"
|
||||
>
|
||||
<subTitleBar
|
||||
title="请求地址(url):"
|
||||
:showArrow="false"
|
||||
/>
|
||||
<view class="inputRow">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="请输入url地址"
|
||||
class="input"
|
||||
v-model="request.url"
|
||||
maxlength="-1"
|
||||
/>
|
||||
<text
|
||||
class="del"
|
||||
@click="request.url = ''"
|
||||
>
|
||||
x
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<subTitleBar
|
||||
title="请求方式[method]:"
|
||||
:showArrow="false"
|
||||
/>
|
||||
<view class="inputRow">
|
||||
<picker
|
||||
@change="request.method = requestMethods[$event.detail.value]"
|
||||
:value="requestMethods.findIndex((x) => x == request.method)"
|
||||
:range="requestMethods"
|
||||
>
|
||||
<view class="method">
|
||||
<text class="methodName">{{ request.method }}</text>
|
||||
<image
|
||||
class="unfold"
|
||||
src="@/devTools/page/static/unfold.png"
|
||||
/>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<subTitleBar
|
||||
title="请求参数[data]:(json对象)"
|
||||
:showArrow="false"
|
||||
/>
|
||||
<view class="inputRow">
|
||||
<textarea
|
||||
placeholder="请输入JSON对象"
|
||||
class="textarea"
|
||||
v-model="request.data"
|
||||
maxlength="-1"
|
||||
/>
|
||||
<text
|
||||
class="del"
|
||||
@click="request.data = ''"
|
||||
>
|
||||
x
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<subTitleBar
|
||||
title="请求头(header):"
|
||||
:showArrow="false"
|
||||
/>
|
||||
<view class="inputRow">
|
||||
<textarea
|
||||
placeholder="请输入JSON对象"
|
||||
class="textarea"
|
||||
v-model="request.header"
|
||||
maxlength="-1"
|
||||
/>
|
||||
<text
|
||||
class="del"
|
||||
@click="request.header = ''"
|
||||
>
|
||||
x
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
:class="[send.status ? 'loading' : '']"
|
||||
class="sendBtn"
|
||||
@click="sendRequest"
|
||||
>
|
||||
<text
|
||||
v-if="send.status"
|
||||
class="sendBtnText"
|
||||
>
|
||||
发送中[{{ send.t }}ms]
|
||||
<text class="msg">(点击可取消)</text>
|
||||
</text>
|
||||
<text
|
||||
v-else
|
||||
class="sendBtnText"
|
||||
>
|
||||
发送
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<template v-if="ajaxHasRes">
|
||||
<subTitleBar
|
||||
title="响应结果:"
|
||||
:showArrow="false"
|
||||
/>
|
||||
<view class="inputRow">
|
||||
<objectAnalysis
|
||||
:data="ajaxRes"
|
||||
:width="710"
|
||||
:isOpenFirst="true"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<view style="height: 100rpx"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import objectAnalysis from "../listItem/objectAnalysis.vue";
|
||||
import subTitleBar from "../ui/subTitleBar.vue";
|
||||
let success, error;
|
||||
|
||||
/**
|
||||
* 转json字符串并格式化
|
||||
*/
|
||||
function toJsonStr(obj) {
|
||||
return JSON.stringify(obj, null, 2);
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
subTitleBar,
|
||||
objectAnalysis,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 是否展示
|
||||
*/
|
||||
isShow: false,
|
||||
/**
|
||||
* 屏幕高度
|
||||
*/
|
||||
height: uni.getSystemInfoSync().windowHeight,
|
||||
dialogHeight: Math.ceil(uni.getSystemInfoSync().windowHeight * 0.85),
|
||||
|
||||
requestMethods: ["get", "post", "put", "delete", "connect", "head", "options", "trace"],
|
||||
/**
|
||||
* 请求构建对象
|
||||
*/
|
||||
request: {
|
||||
url: "", //请求地址
|
||||
header: "", //请求头
|
||||
method: "get", //请求方式
|
||||
data: "", //请求参数
|
||||
},
|
||||
/**
|
||||
* 是否有响应结果
|
||||
*/
|
||||
ajaxHasRes: false,
|
||||
/**
|
||||
* 响应结果对象
|
||||
*/
|
||||
ajaxRes: {},
|
||||
|
||||
/**
|
||||
* 是否状态
|
||||
*/
|
||||
send: {
|
||||
status: false, //是否处于发送中状态
|
||||
t: 0, //等待时间 ms
|
||||
time: 0, //发送时的时间
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
let that = this;
|
||||
setInterval(() => {
|
||||
if (that.send.status) {
|
||||
that.send.t = new Date().getTime() - that.send.time;
|
||||
}
|
||||
}, 1000 / 24);
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 展示弹窗
|
||||
*/
|
||||
show(item, needSend = false) {
|
||||
let that = this;
|
||||
|
||||
if (that.send.status) {
|
||||
that.send.status = false;
|
||||
}
|
||||
if (that.ajaxHasRes) {
|
||||
that.ajaxHasRes = false;
|
||||
}
|
||||
|
||||
return new Promise((yes, err) => {
|
||||
success = yes;
|
||||
error = err;
|
||||
if (item && item.url && item.method) {
|
||||
that.request.url = item.url;
|
||||
if (typeof item.method == "string") {
|
||||
that.request.method = item.method.toLocaleLowerCase();
|
||||
} else {
|
||||
that.request.method = "get";
|
||||
}
|
||||
|
||||
try {
|
||||
let data = toJsonStr(item.data);
|
||||
if (Object.keys(data).length == 0) {
|
||||
data = "";
|
||||
} else {
|
||||
that.request.data = data;
|
||||
}
|
||||
} catch (error) {
|
||||
that.request.data = "";
|
||||
}
|
||||
try {
|
||||
that.request.header = toJsonStr(item.header);
|
||||
} catch (error) {
|
||||
that.request.header = toJsonStr({
|
||||
"content-type": "application/x-www-form-urlencoded",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
that.request.url = "";
|
||||
that.request.data = "";
|
||||
that.request.method = "get";
|
||||
that.ajaxHasRes = false;
|
||||
that.request.header = toJsonStr({
|
||||
"content-type": "application/x-www-form-urlencoded",
|
||||
});
|
||||
}
|
||||
that.send.status = false;
|
||||
that.isShow = true;
|
||||
if (needSend) {
|
||||
that.sendRequest();
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 关闭弹窗
|
||||
*/
|
||||
hide() {
|
||||
this.isShow = false;
|
||||
error();
|
||||
},
|
||||
/**
|
||||
* 发送请求
|
||||
*/
|
||||
sendRequest() {
|
||||
let that = this;
|
||||
|
||||
if (that.send.status) {
|
||||
return uni.showModal({
|
||||
title: "提示",
|
||||
content: "请求还在进行,是否确认取消请求?",
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
if (that.send.status) {
|
||||
that.send.status = false;
|
||||
that.ajaxHasRes = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
let tw = (m) =>
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: m,
|
||||
});
|
||||
if (that.request.url == "" || typeof that.request.url != "string") return tw("请输入url");
|
||||
if (that.request.url.indexOf("http") != 0) return tw("请输入正确的url地址");
|
||||
if (that.request.url.indexOf("://") == -1) return tw("请输入正确的url地址");
|
||||
if (that.request.url.length < 10) return tw("请输入正确的url地址");
|
||||
|
||||
let data = {};
|
||||
if (that.request.data != "") {
|
||||
try {
|
||||
data = JSON.parse(that.request.data);
|
||||
} catch (error) {
|
||||
return tw("请求参数json解析失败!");
|
||||
}
|
||||
}
|
||||
|
||||
let header = {};
|
||||
if (that.request.header != "") {
|
||||
try {
|
||||
header = JSON.parse(that.request.header);
|
||||
} catch (error) {
|
||||
return tw("请求头json解析失败!");
|
||||
}
|
||||
}
|
||||
|
||||
that.send.t = 0;
|
||||
that.send.time = new Date().getTime();
|
||||
that.send.status = true;
|
||||
that.ajaxHasRes = false;
|
||||
|
||||
uni.request({
|
||||
url: that.request.url,
|
||||
method: that.request.method,
|
||||
data,
|
||||
header,
|
||||
success(res) {
|
||||
if (!that.send.status || !that.isShow) return;
|
||||
that.send.status = false;
|
||||
res["请求用时"] = new Date().getTime() - that.send.time + "ms";
|
||||
that.$set(that, "ajaxRes", res);
|
||||
that.ajaxHasRes = true;
|
||||
uni.showToast({
|
||||
title: "请求响应成功",
|
||||
icon: "success",
|
||||
});
|
||||
},
|
||||
fail(msg, request) {
|
||||
if (!that.send.status || !that.isShow) return;
|
||||
let res = {
|
||||
fail: msg,
|
||||
request,
|
||||
请求用时: new Date().getTime() - that.send.time + "ms",
|
||||
};
|
||||
that.send.status = false;
|
||||
that.$set(that, "ajaxRes", res);
|
||||
that.ajaxHasRes = true;
|
||||
uni.showToast({
|
||||
title: "请求响应失败",
|
||||
icon: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.dialogMask {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
width: 750rpx;
|
||||
flex: 1;
|
||||
/* #ifndef APP-PLUS */
|
||||
height: 100vh;
|
||||
backdrop-filter: blur(1px);
|
||||
/* #endif */
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
.dialogContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 750rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
.dialogHead {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 80rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
width: 750rpx;
|
||||
.title {
|
||||
margin-left: 20rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 28rpx;
|
||||
height: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
.fold {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
.scrollList {
|
||||
width: 750rpx;
|
||||
.inputRow {
|
||||
width: 750rpx;
|
||||
padding: 0rpx 20rpx;
|
||||
position: relative;
|
||||
.input {
|
||||
width: 710rpx;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
padding-left: 5rpx;
|
||||
padding-top: 5rpx;
|
||||
padding-bottom: 5rpx;
|
||||
padding-right: 50rpx;
|
||||
font-size: 24rpx;
|
||||
border-radius: 6rpx;
|
||||
height: 60rpx;
|
||||
}
|
||||
.textarea {
|
||||
width: 710rpx;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
padding-left: 5rpx;
|
||||
padding-top: 5rpx;
|
||||
padding-bottom: 5rpx;
|
||||
padding-right: 50rpx;
|
||||
font-size: 24rpx;
|
||||
border-radius: 6rpx;
|
||||
height: 140rpx;
|
||||
}
|
||||
.del {
|
||||
position: absolute;
|
||||
right: 24rpx;
|
||||
top: 10rpx;
|
||||
height: 40rpx;
|
||||
background-color: #fff;
|
||||
padding: 0 10rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
.method {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
.methodName {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
}
|
||||
.unfold {
|
||||
margin-left: 10rpx;
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.sendBtn {
|
||||
width: 710rpx;
|
||||
margin-left: 20rpx;
|
||||
margin-top: 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 10rpx;
|
||||
background-color: rgb(255, 45, 85);
|
||||
&.loading {
|
||||
background-color: rgba(255, 45, 85, 0.5);
|
||||
}
|
||||
.sendBtnText {
|
||||
color: #fff;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
.msg {
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,374 @@
|
||||
<template>
|
||||
<view>
|
||||
<view
|
||||
class="dialogMask"
|
||||
v-if="isShow"
|
||||
:style="{
|
||||
height: height + 'px',
|
||||
}"
|
||||
>
|
||||
<view
|
||||
class="dialogContent"
|
||||
@click.stop
|
||||
>
|
||||
<view
|
||||
class="dialogHead"
|
||||
@click="hide"
|
||||
>
|
||||
<view>
|
||||
<text class="title">{{ title }}</text>
|
||||
</view>
|
||||
<view>
|
||||
<image
|
||||
src="@/devTools/page/static/unfold.png"
|
||||
class="fold"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="scrollList"
|
||||
:style="{
|
||||
height: dialogHeight + 'px',
|
||||
}"
|
||||
>
|
||||
<textarea
|
||||
:style="{
|
||||
height: dialogHeight - (canSave ? 90 : 40) + 'px',
|
||||
}"
|
||||
v-model="value"
|
||||
type="text"
|
||||
placeholder="请输入..."
|
||||
class="fileEditInput"
|
||||
maxlength="-1"
|
||||
/>
|
||||
<view
|
||||
class="saveBtn"
|
||||
v-if="canSave"
|
||||
@click="saveFile"
|
||||
>
|
||||
<text class="saveBtnText">保存</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
let success, error;
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 是否展示
|
||||
*/
|
||||
isShow: false,
|
||||
/**
|
||||
* 屏幕高度
|
||||
*/
|
||||
height: uni.getSystemInfoSync().windowHeight,
|
||||
dialogHeight: uni.getSystemInfoSync().windowHeight * 0.8,
|
||||
/**
|
||||
* 弹窗标题
|
||||
*/
|
||||
title: "",
|
||||
/**
|
||||
* 文本内容
|
||||
*/
|
||||
value: "",
|
||||
/**
|
||||
* 是否为文件编辑模式
|
||||
*/
|
||||
isFileEdit: true,
|
||||
/**
|
||||
* 文件路径
|
||||
*/
|
||||
path: "",
|
||||
/**
|
||||
* 是否允许保存
|
||||
*/
|
||||
canSave: false,
|
||||
/**
|
||||
* 是否为新建文件
|
||||
*/
|
||||
isNewFile: false,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
let that = this;
|
||||
uni.$on("devTools_showTextEditDialog", (options) => {
|
||||
that
|
||||
.show(options)
|
||||
.then((val) => {
|
||||
uni.$emit("devTools_showTextEditDialogSave", val);
|
||||
})
|
||||
.catch(() => {
|
||||
uni.$emit("devTools_showTextEditDialogHide");
|
||||
});
|
||||
});
|
||||
},
|
||||
unmounted() {
|
||||
uni.$off("devTools_showTextEditDialog");
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 展示弹窗
|
||||
*/
|
||||
show(options) {
|
||||
let that = this;
|
||||
return new Promise((yes, err) => {
|
||||
success = yes;
|
||||
error = err;
|
||||
that.title = options.title;
|
||||
that.canSave = Boolean(options.canSave);
|
||||
that.isShow = true;
|
||||
|
||||
if (options.isFileEdit === false) {
|
||||
// 仅为文件编辑模式
|
||||
that.isFileEdit = false;
|
||||
try {
|
||||
that.value = JSON.stringify(JSON.parse(options.value), null, 2);
|
||||
} catch (error) {
|
||||
that.value = options.value;
|
||||
}
|
||||
return;
|
||||
}
|
||||
that.isFileEdit = true;
|
||||
that.value = "文件读取中...";
|
||||
that.path = options.path;
|
||||
that.isNewFile = Boolean(options.isNewFile);
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
if (that.isNewFile) {
|
||||
that.value = "";
|
||||
} else {
|
||||
plus.io.resolveLocalFileSystemURL(
|
||||
that.path,
|
||||
(entry) => {
|
||||
// 可通过entry对象操作test.html文件
|
||||
entry.file((file) => {
|
||||
var fileReader = new plus.io.FileReader();
|
||||
fileReader.readAsText(file, "utf-8");
|
||||
fileReader.onloadend = function (evt) {
|
||||
let res = "";
|
||||
try {
|
||||
res = JSON.stringify(JSON.parse(evt.target.result), null, 2);
|
||||
} catch (error) {
|
||||
res = evt.target.result;
|
||||
}
|
||||
that.value = res;
|
||||
};
|
||||
fileReader.onerror = function () {
|
||||
that.value = `[${that.path}]文件读取失败!_code_2`;
|
||||
};
|
||||
});
|
||||
},
|
||||
function (e) {
|
||||
that.value = `[${that.path}]文件读取失败!` + e.message;
|
||||
}
|
||||
);
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
let fs = wx.getFileSystemManager();
|
||||
if (options.size != 0) {
|
||||
fs.readFile({
|
||||
filePath: that.path,
|
||||
encoding: "utf8",
|
||||
position: 0,
|
||||
length: options.size,
|
||||
success({ data }) {
|
||||
try {
|
||||
that.value = JSON.stringify(JSON.parse(data), null, 2);
|
||||
} catch (error) {
|
||||
that.value = data;
|
||||
}
|
||||
},
|
||||
fail(e) {
|
||||
console.log(e);
|
||||
that.value = `[${that.path}]文件读取失败!` + e;
|
||||
},
|
||||
});
|
||||
} else {
|
||||
that.value = "";
|
||||
}
|
||||
// #endif
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 关闭弹窗
|
||||
*/
|
||||
hide() {
|
||||
this.isShow = false;
|
||||
error();
|
||||
},
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
save() {
|
||||
this.isShow = false;
|
||||
success(this.value);
|
||||
},
|
||||
/**
|
||||
* 保存文件
|
||||
*/
|
||||
saveFile() {
|
||||
let that = this;
|
||||
|
||||
if (!that.isFileEdit) {
|
||||
// 非文件编辑模式
|
||||
|
||||
that.isShow = false;
|
||||
success(that.value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
uni.showLoading({
|
||||
title: "保存中",
|
||||
});
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
|
||||
let fileName = that.path.split("/").pop();
|
||||
let path = that.path.substring(0, that.path.length - fileName.length);
|
||||
|
||||
plus.io.resolveLocalFileSystemURL(
|
||||
path,
|
||||
(entry) => {
|
||||
entry.getFile(
|
||||
fileName,
|
||||
{
|
||||
create: true,
|
||||
},
|
||||
(fileEntry) => {
|
||||
fileEntry.createWriter((writer) => {
|
||||
writer.onwrite = (e) => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "文件保存成功!",
|
||||
icon: "success",
|
||||
});
|
||||
that.isShow = false;
|
||||
that.$emit("getPage");
|
||||
};
|
||||
writer.onerror = () => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "文件保存失败!_写入文件失败",
|
||||
icon: "none",
|
||||
});
|
||||
};
|
||||
writer.write(that.value);
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
() => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "文件保存失败!_打开目录失败",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
);
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
|
||||
let fs = wx.getFileSystemManager();
|
||||
fs.writeFile({
|
||||
filePath: that.path,
|
||||
encoding: "utf-8",
|
||||
data: that.value,
|
||||
success() {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "文件保存成功!",
|
||||
icon: "success",
|
||||
});
|
||||
that.isShow = false;
|
||||
that.$emit("getPage");
|
||||
},
|
||||
fail() {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "文件保存失败!_打开目录失败",
|
||||
icon: "none",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// #endif
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.dialogMask {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
width: 750rpx;
|
||||
flex: 1;
|
||||
/* #ifndef APP-PLUS */
|
||||
height: 100vh;
|
||||
backdrop-filter: blur(1px);
|
||||
/* #endif */
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
.dialogContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 750rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
.dialogHead {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 80rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
width: 750rpx;
|
||||
.title {
|
||||
margin-left: 20rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
color: #333;
|
||||
}
|
||||
.fold {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
.scrollList {
|
||||
width: 750rpx;
|
||||
padding: 20rpx;
|
||||
.fileEditInput {
|
||||
font-size: 20rpx;
|
||||
}
|
||||
.saveBtn {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 20rpx;
|
||||
height: 35px;
|
||||
width: 710rpx;
|
||||
border-radius: 10rpx;
|
||||
background-color: #ff2d55;
|
||||
.saveBtnText {
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,86 @@
|
||||
import dirReader from "./dirReader"
|
||||
|
||||
/**
|
||||
* 遍历删除整个文件夹,以“/”结尾
|
||||
*/
|
||||
export default function appDelDir(path, needDelSelf = true) {
|
||||
return new Promise(async (yes, err) => {
|
||||
let dirList = await dirReader.getDirFileList(path)
|
||||
for (let i = 0; i < dirList.length; i++) {
|
||||
let item = dirList[i];
|
||||
try {
|
||||
if (item.type == "dir") {
|
||||
let info = await getMeteInfo(path + item.name + "/")
|
||||
if (
|
||||
info.directoryCount > 0
|
||||
|| info.fileCount > 0
|
||||
) {
|
||||
await appDelDir(path + item.name + "/")
|
||||
} else {
|
||||
await delDir(path + item.name + "/")
|
||||
}
|
||||
} else {
|
||||
await delFile(path + item.name)
|
||||
}
|
||||
} catch (error) { }
|
||||
}
|
||||
try {
|
||||
if (needDelSelf) {
|
||||
await delDir(path)
|
||||
}
|
||||
} catch (error) { }
|
||||
yes()
|
||||
})
|
||||
}
|
||||
|
||||
function delFile(path) {
|
||||
return new Promise((yes, err) => {
|
||||
plus.io.resolveLocalFileSystemURL(
|
||||
path,
|
||||
(entry) => {
|
||||
entry.remove(
|
||||
yes,
|
||||
err
|
||||
)
|
||||
},
|
||||
err
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
function delDir(path) {
|
||||
return new Promise((yes, err) => {
|
||||
plus.io.resolveLocalFileSystemURL(
|
||||
path,
|
||||
(entry) => {
|
||||
entry.remove(
|
||||
yes,
|
||||
err
|
||||
)
|
||||
},
|
||||
err
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件夹内信息
|
||||
* @returns {Promise<PlusIoMetadata>}
|
||||
*/
|
||||
function getMeteInfo(path) {
|
||||
return new Promise((yes, err) => {
|
||||
plus.io.resolveLocalFileSystemURL(
|
||||
path,
|
||||
(entry) => {
|
||||
if (entry.isDirectory) {
|
||||
entry.getMetadata((metadata) => {
|
||||
yes(metadata)
|
||||
}, err)
|
||||
} else {
|
||||
err()
|
||||
}
|
||||
},
|
||||
err
|
||||
);
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
const iconConfig = [
|
||||
{
|
||||
type: ["", undefined, null],
|
||||
mime: "",
|
||||
icon: "/devTools/page/static/fileSys/weizhiwenjian.png",
|
||||
},
|
||||
{
|
||||
type: ["dwg"],
|
||||
mime: "dwg",
|
||||
icon: "/devTools/page/static/fileSys/DWG.png",
|
||||
},
|
||||
{
|
||||
type: ["xls", "xlsx", "csv"],
|
||||
mime: "xls",
|
||||
icon: "/devTools/page/static/fileSys/excel.png",
|
||||
},
|
||||
{
|
||||
type: ["exe"],
|
||||
mime: "exe",
|
||||
icon: "/devTools/page/static/fileSys/EXE.png",
|
||||
},
|
||||
{
|
||||
type: ["gif"],
|
||||
mime: "gif",
|
||||
icon: "/devTools/page/static/fileSys/GIF.png",
|
||||
},
|
||||
{
|
||||
type: ["html"],
|
||||
mime: "html",
|
||||
icon: "/devTools/page/static/fileSys/HTML.png",
|
||||
},
|
||||
{
|
||||
type: ["pdf"],
|
||||
mime: "pdf",
|
||||
icon: "/devTools/page/static/fileSys/pdf.png",
|
||||
},
|
||||
{
|
||||
type: ["ppt"],
|
||||
mime: "ppt",
|
||||
icon: "/devTools/page/static/fileSys/pptl.png",
|
||||
},
|
||||
{
|
||||
type: ["psd"],
|
||||
mime: "psd",
|
||||
icon: "/devTools/page/static/fileSys/PSD.png",
|
||||
},
|
||||
{
|
||||
type: ["rvt"],
|
||||
mime: "rvt",
|
||||
icon: "/devTools/page/static/fileSys/RVT.png",
|
||||
},
|
||||
{
|
||||
type: [
|
||||
"mp4",
|
||||
"avi",
|
||||
"wmv",
|
||||
"mpg",
|
||||
"mpeg",
|
||||
"mov",
|
||||
"flv",
|
||||
"3gp",
|
||||
"mp3gp",
|
||||
"mkv",
|
||||
"rmvb",
|
||||
],
|
||||
mime: "mp4",
|
||||
icon: "/devTools/page/static/fileSys/shipin.png",
|
||||
},
|
||||
{
|
||||
type: ["skp"],
|
||||
mime: "skp",
|
||||
icon: "/devTools/page/static/fileSys/SKP.png",
|
||||
},
|
||||
{
|
||||
type: ["svg"],
|
||||
mime: "svg",
|
||||
icon: "/devTools/page/static/fileSys/SVG.png",
|
||||
},
|
||||
{
|
||||
type: ["png", "jpeg", "jpg", "webp", "bmp"],
|
||||
mime: "img",
|
||||
icon: "/devTools/page/static/fileSys/tupian.png",
|
||||
},
|
||||
{
|
||||
type: ["txt", "sql", "js", "css", "log", "json"],
|
||||
mime: "txt",
|
||||
icon: "/devTools/page/static/fileSys/txt.png",
|
||||
},
|
||||
{
|
||||
type: ["word"],
|
||||
mime: "word",
|
||||
icon: "/devTools/page/static/fileSys/word.png",
|
||||
},
|
||||
{
|
||||
type: ["zip", "rar", "gz", "7z"],
|
||||
mime: "zip",
|
||||
icon: "/devTools/page/static/fileSys/yasuo.png",
|
||||
},
|
||||
{
|
||||
type: ["mp3", "wma", "wav", "aac", "flac"],
|
||||
mime: "",
|
||||
icon: "/devTools/page/static/fileSys/yinpin.png",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取文件和目录列表
|
||||
*/
|
||||
getDirFileList(path) {
|
||||
return new Promise((yes) => {
|
||||
// #ifdef APP-PLUS
|
||||
plus.io.resolveLocalFileSystemURL(path, function (entry) {
|
||||
if (entry.isDirectory) {
|
||||
let reader = entry.createReader();
|
||||
reader.readEntries(
|
||||
async (entries) => {
|
||||
let dirList = [];
|
||||
let fileList = [];
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
/**
|
||||
* @type {PlusIoDirectoryEntry}
|
||||
*/
|
||||
const item = entries[i];
|
||||
let meta = await getMetaData(item)
|
||||
let row = {
|
||||
type: item.isDirectory ? 'dir' : 'file',
|
||||
name: item.name,
|
||||
fileType: getFileType(item.name),
|
||||
...meta,
|
||||
}
|
||||
if (item.isDirectory) {
|
||||
dirList.push(row)
|
||||
} else {
|
||||
fileList.push(row)
|
||||
}
|
||||
}
|
||||
|
||||
dirList = dirList.sort((a, b) => a.time > b.time)
|
||||
fileList = fileList.sort((a, b) => a.time > b.time)
|
||||
|
||||
yes([
|
||||
...dirList,
|
||||
...fileList,
|
||||
])
|
||||
},
|
||||
(e) => {
|
||||
console.log("readEntries error", e);
|
||||
uni.showToast({
|
||||
title: "文件读取失败: " + e.message,
|
||||
icon: "none",
|
||||
});
|
||||
yes([])
|
||||
}
|
||||
);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: "路径读取失败_b",
|
||||
icon: "none",
|
||||
});
|
||||
yes([])
|
||||
}
|
||||
}, () => {
|
||||
uni.showToast({
|
||||
title: "路径读取失败_a",
|
||||
icon: "none",
|
||||
});
|
||||
yes([])
|
||||
});
|
||||
// #endif
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 获取文件图片
|
||||
*/
|
||||
getFileIcon(type) {
|
||||
for (let i = 0; i < iconConfig.length; i++) {
|
||||
const item = iconConfig[i];
|
||||
for (let _i = 0; _i < item.type.length; _i++) {
|
||||
const typeName = item.type[_i];
|
||||
if (type == typeName) {
|
||||
return item.icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "/devTools/page/static/fileSys/weizhiwenjian.png";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PlusIoDirectoryEntry} entry
|
||||
*/
|
||||
function getMetaData(entry) {
|
||||
return new Promise((yes) => {
|
||||
entry.getMetadata(function (metadata) {
|
||||
yes({
|
||||
size: metadata.size,
|
||||
time: metadata.modificationTime.getTime(),
|
||||
fileCount: metadata.fileCount,
|
||||
directoryCount: metadata.directoryCount,
|
||||
})
|
||||
}, function () {
|
||||
yes({
|
||||
size: 0,
|
||||
time: 0,
|
||||
fileCount: 0,
|
||||
directoryCount: 0,
|
||||
})
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function getFileType(name) {
|
||||
if (typeof name == "string") {
|
||||
let tList = name.split(".");
|
||||
if (tList.length > 1) {
|
||||
return tList.pop().toLocaleLowerCase()
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
export default {
|
||||
/**
|
||||
* 获取字节大小,b转kb mb
|
||||
*/
|
||||
getByteSize(size) {
|
||||
if (null == size || size == '') return "0 B";
|
||||
var unitArr = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
var index = 0;
|
||||
var srcsize = parseFloat(size);
|
||||
index = Math.floor(Math.log(srcsize) / Math.log(1024));
|
||||
var size = srcsize / Math.pow(1024, index);
|
||||
size = size.toFixed(2);//保留的小数位数
|
||||
return size + unitArr[index];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* 获取当前运行时环境信息
|
||||
*/
|
||||
export default function getRuntimeInfo() {
|
||||
return new Promise(async (yes) => {
|
||||
let data = {
|
||||
系统信息: uni.getSystemInfoSync(),
|
||||
设备基础信息: uni.getDeviceInfo ? uni.getDeviceInfo() : null,
|
||||
窗口信息: uni.getWindowInfo ? uni.getWindowInfo() : null,
|
||||
APP基础信息: uni.getAppBaseInfo ? uni.getAppBaseInfo() : null,
|
||||
APP授权设置: uni.getAppAuthorizeSetting ? uni.getAppAuthorizeSetting() : null,
|
||||
设备设置: uni.getSystemSetting ? uni.getSystemSetting() : null,
|
||||
网络状态: await getNetworkType(),
|
||||
启动参数: uni.getLaunchOptionsSync(),
|
||||
// #ifdef APP-PLUS
|
||||
其他信息: await getAppOtherInfo(),
|
||||
// #endif
|
||||
};
|
||||
yes(data)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取网络状态
|
||||
*/
|
||||
function getNetworkType() {
|
||||
return new Promise((yes, err) => {
|
||||
if (!uni.getNetworkType) {
|
||||
return yes(null);
|
||||
}
|
||||
uni.getNetworkType({
|
||||
success(res) {
|
||||
yes(res.networkType);
|
||||
},
|
||||
fail() {
|
||||
yes("error");
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 获取APP端设备其他信息
|
||||
*/
|
||||
function getAppOtherInfo() {
|
||||
return new Promise(async (yes) => {
|
||||
let info = {};
|
||||
|
||||
let getDevice = () =>
|
||||
new Promise((yes) => {
|
||||
plus.device.getInfo({
|
||||
success: yes,
|
||||
fail() {
|
||||
yes("plus.device.getInfo() fail");
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
let getOAID = () =>
|
||||
new Promise((yes) => {
|
||||
plus.device.getOAID({
|
||||
success: yes,
|
||||
fail() {
|
||||
yes("plus.device.getOAID() fail");
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
let getAAID = () =>
|
||||
new Promise((yes) => {
|
||||
plus.device.getOAID({
|
||||
success: yes,
|
||||
fail() {
|
||||
yes("plus.device.getOAID() fail");
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
let getDeviceId = () =>
|
||||
new Promise((yes) => {
|
||||
try {
|
||||
let id = plus.device.getDeviceId();
|
||||
yes(id);
|
||||
} catch (error) {
|
||||
yes("plus.device.getDeviceId() fail");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
info.getDevice = await getDevice();
|
||||
info.getOAID = await getOAID();
|
||||
info.getAAID = await getAAID();
|
||||
info.getDeviceId = await getDeviceId();
|
||||
yes(info);
|
||||
} catch (error) {
|
||||
console.log("getDeviceInfoFail", error);
|
||||
yes("获取设备信息失败!");
|
||||
}
|
||||
|
||||
plus.device.getInfo({
|
||||
success(e) {
|
||||
info = Object.assign(info, e);
|
||||
|
||||
plus.device.getOAID({
|
||||
success(e) {
|
||||
info = Object.assign(info, e);
|
||||
|
||||
plus.device.getVAID({
|
||||
success(e) { },
|
||||
fail() {
|
||||
yes(
|
||||
Object.assign(info, {
|
||||
errMsg: "plus.device.getVAID 获取失败!",
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
fail() {
|
||||
yes(
|
||||
Object.assign(info, {
|
||||
errMsg: "plus.device.getOAID 获取失败!",
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
fail() {
|
||||
yes({ errMsg: "plus.device.getInfo 获取失败!" });
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
<template>
|
||||
<view
|
||||
class="consoleItem"
|
||||
:class="['type-' + item.type]"
|
||||
@longpress.stop="consoleLongpress"
|
||||
>
|
||||
<view class="content">
|
||||
<view
|
||||
v-for="(row, index) in item.list"
|
||||
:key="index"
|
||||
>
|
||||
<template v-if="isObj(row)">
|
||||
<objectAnalysis
|
||||
:data="row"
|
||||
:width="610"
|
||||
:canLongpress="false"
|
||||
@onLongpress="consoleLongpress"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<view>
|
||||
<text
|
||||
class="context"
|
||||
:class="[getTypeClass(row)]"
|
||||
>
|
||||
{{ getText(row) }}
|
||||
</text>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<view class="msgBar">
|
||||
<text class="time">{{ item.date }}</text>
|
||||
<text
|
||||
class="logType"
|
||||
:class="'type-' + item.type"
|
||||
>
|
||||
{{ item.type }}
|
||||
</text>
|
||||
<text class="page">{{ item.page }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="tools">
|
||||
<view
|
||||
class="copyBtn"
|
||||
@click="copyList"
|
||||
>
|
||||
<image
|
||||
src="@/devTools/page/static/copy.png"
|
||||
class="copyIcon"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import objectAnalysis from "./objectAnalysis.vue";
|
||||
export default {
|
||||
components: {
|
||||
objectAnalysis,
|
||||
},
|
||||
props: {
|
||||
/**
|
||||
* console单行数据
|
||||
*/
|
||||
item: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
list: [],
|
||||
date: "", // 打印的日期
|
||||
page: "", // 打印的页面
|
||||
type: "", // 打印类型
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 是否为对象类型
|
||||
*/
|
||||
isObj(data) {
|
||||
return typeof data == "object" && data != null && data != undefined;
|
||||
},
|
||||
/**
|
||||
* 获取对应的类型样式
|
||||
*/
|
||||
getTypeClass(obj) {
|
||||
try {
|
||||
let type = typeof obj;
|
||||
|
||||
if (type == "string") {
|
||||
if (obj.indexOf("at ") == 0) {
|
||||
return "t-line";
|
||||
} else if (obj === undefined || obj == "undefined") {
|
||||
return "t-undefined";
|
||||
} else if (obj === null || obj == "null") {
|
||||
return "t-null";
|
||||
} else if (obj == "true" || obj == "false") {
|
||||
return "t-boolean";
|
||||
} else if (Number.isFinite(Number(obj))) {
|
||||
return "t-number";
|
||||
}
|
||||
}
|
||||
return "t-" + type;
|
||||
} catch (error) {}
|
||||
return "t-string";
|
||||
},
|
||||
/**
|
||||
* 获取数据文字
|
||||
*/
|
||||
getText(data) {
|
||||
switch (typeof data) {
|
||||
case "string":
|
||||
// return data.replace(/\n/g, "");
|
||||
return data;
|
||||
case "boolean":
|
||||
return data ? "true" : "false";
|
||||
case "undefined":
|
||||
return "undefined";
|
||||
case "function":
|
||||
return "js:function";
|
||||
case "symbol":
|
||||
return "js:symbol";
|
||||
default:
|
||||
return data;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 复制列表
|
||||
*/
|
||||
copyList() {
|
||||
uni.setClipboardData({
|
||||
data: JSON.stringify(this.item.list),
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 长按事件
|
||||
*/
|
||||
consoleLongpress() {
|
||||
let that = this;
|
||||
|
||||
let menu = [
|
||||
{
|
||||
text: `复制日志信息`,
|
||||
click() {
|
||||
uni.setClipboardData({
|
||||
data: JSON.stringify(that.item),
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
text: `删除此记录`,
|
||||
click() {
|
||||
uni.$emit("devTools_delConsoleItem", that.item);
|
||||
uni.showToast({
|
||||
title: "删除成功!",
|
||||
icon: "success",
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
uni.showActionSheet({
|
||||
itemList: menu.map((x) => x.text),
|
||||
success({ tapIndex }) {
|
||||
menu[tapIndex].click();
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.consoleItem:active {
|
||||
background-color: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
.consoleItem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
width: 750rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
&.type-warn {
|
||||
background-color: rgb(255, 251, 229);
|
||||
}
|
||||
&.type-error {
|
||||
background-color: rgb(255, 240, 240);
|
||||
}
|
||||
&.type-info {
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
.content {
|
||||
width: 610rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.context {
|
||||
font-size: 20rpx;
|
||||
color: #333;
|
||||
line-height: 24rpx;
|
||||
&.t-number {
|
||||
color: rgb(8, 66, 160);
|
||||
}
|
||||
&.t-boolean {
|
||||
color: rgb(133, 2, 255);
|
||||
}
|
||||
&.t-string {
|
||||
color: #333;
|
||||
}
|
||||
&.t-undefined {
|
||||
color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
&.t-null {
|
||||
color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
&.t-line {
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
.msgBar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-top: 4rpx;
|
||||
.time {
|
||||
font-size: 16rpx;
|
||||
color: #888;
|
||||
min-width: 90rpx;
|
||||
}
|
||||
.page {
|
||||
font-size: 16rpx;
|
||||
color: #888;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
.logType {
|
||||
margin-left: 20rpx;
|
||||
font-size: 16rpx;
|
||||
padding: 0px 6rpx;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.type-log {
|
||||
color: #fff;
|
||||
background-color: #a8abb3;
|
||||
}
|
||||
.type-info {
|
||||
color: #fff;
|
||||
background-color: #747474;
|
||||
}
|
||||
.type-warn {
|
||||
color: #fff;
|
||||
background-color: #ff9900;
|
||||
}
|
||||
.type-error {
|
||||
color: #fff;
|
||||
background-color: #fa3534;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tools {
|
||||
width: 100rpx;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
margin-top: 6rpx;
|
||||
.copy {
|
||||
font-size: 20rpx;
|
||||
color: #333;
|
||||
line-height: 24rpx;
|
||||
}
|
||||
.copy:active {
|
||||
background-color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
.copyBtn:active {
|
||||
background-color: rgba(103, 194, 58, 0.6);
|
||||
}
|
||||
.copyBtn {
|
||||
padding: 5rpx;
|
||||
border-radius: 999rpx;
|
||||
overflow: hidden;
|
||||
background-color: #67c23a;
|
||||
.copyIcon {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<view
|
||||
class="dayOnlineItem"
|
||||
@click.stop="$emit('click')"
|
||||
@longpress.stop="logLongpress"
|
||||
>
|
||||
<view class="info">
|
||||
<text class="text-xs">{{ item.date }}</text>
|
||||
<text class="text-xs margin-left">{{ item.timeCount }}</text>
|
||||
</view>
|
||||
<view class="arrow">
|
||||
<image
|
||||
class="icon"
|
||||
src="@/devTools/page/static/fold.png"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
/**
|
||||
* logs单行数据
|
||||
*/
|
||||
item: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
date: "", //日期
|
||||
timeCount: "", //活跃时间
|
||||
page: [], //页面详细数据
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 长按事件
|
||||
*/
|
||||
logLongpress() {
|
||||
let that = this;
|
||||
|
||||
let menu = [
|
||||
{
|
||||
text: `复制日志信息`,
|
||||
click() {
|
||||
uni.setClipboardData({
|
||||
data: JSON.stringify(that.item),
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
uni.showActionSheet({
|
||||
itemList: menu.map((x) => x.text),
|
||||
success({ tapIndex }) {
|
||||
menu[tapIndex].click();
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.dayOnlineItem:active {
|
||||
background-color: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
.dayOnlineItem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
width: 750rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
.info {
|
||||
width: 610rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
.text-xs {
|
||||
font-size: 20rpx;
|
||||
}
|
||||
.margin-left {
|
||||
margin-left: 30rpx;
|
||||
}
|
||||
}
|
||||
.arrow {
|
||||
.icon {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,274 @@
|
||||
<template>
|
||||
<view
|
||||
class="errorItem"
|
||||
:class="['type-' + item.type]"
|
||||
@longpress.stop="errorLongpress"
|
||||
>
|
||||
<view class="content">
|
||||
<view
|
||||
v-for="(row, index) in [item.m, item.tr]"
|
||||
:key="index"
|
||||
>
|
||||
<template v-if="isObj(row)">
|
||||
<objectAnalysis
|
||||
:data="row"
|
||||
:width="610"
|
||||
:canLongpress="false"
|
||||
@onLongpress="errorLongpress"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<view>
|
||||
<text class="context">{{ getText(row) }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<view class="msgBar">
|
||||
<text class="time">{{ item.date }}</text>
|
||||
<text
|
||||
class="logType"
|
||||
:class="['type-' + item.type]"
|
||||
>
|
||||
{{ getType(item.type) }}
|
||||
</text>
|
||||
<text class="page">{{ item.p }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="tools">
|
||||
<view
|
||||
class="copyBtn"
|
||||
@click="copyList"
|
||||
>
|
||||
<image
|
||||
src="@/devTools/page/static/copy.png"
|
||||
class="copyIcon"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import devCache from "../../../core/libs/devCache";
|
||||
import objectAnalysis from "./objectAnalysis.vue";
|
||||
export default {
|
||||
components: {
|
||||
objectAnalysis,
|
||||
},
|
||||
props: {
|
||||
/**
|
||||
* console单行数据
|
||||
*/
|
||||
item: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
m: "",
|
||||
tr: "",
|
||||
date: "", // 打印的日期
|
||||
p: "", // 打印的页面
|
||||
type: "", // 打印类型
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 是否为对象类型
|
||||
*/
|
||||
isObj(data) {
|
||||
return typeof data == "object";
|
||||
},
|
||||
/**
|
||||
* 获取数据文字
|
||||
*/
|
||||
getText(data) {
|
||||
switch (typeof data) {
|
||||
case "string":
|
||||
return data;
|
||||
case "boolean":
|
||||
return data ? "true" : "false";
|
||||
case "undefined":
|
||||
return "undefined";
|
||||
case "function":
|
||||
return "js:function";
|
||||
case "symbol":
|
||||
return "js:symbol";
|
||||
default:
|
||||
return data;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 复制列表
|
||||
*/
|
||||
copyList() {
|
||||
let that = this;
|
||||
uni.setClipboardData({
|
||||
data: JSON.stringify([that.item.m, that.item.tr]),
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 获取类型
|
||||
*/
|
||||
getType(type) {
|
||||
let t = {
|
||||
ve: "vue error",
|
||||
vw: "vue warn",
|
||||
oe: "App.vue onError",
|
||||
n: "unknown",
|
||||
};
|
||||
return t[type];
|
||||
},
|
||||
/**
|
||||
* 长按事件
|
||||
*/
|
||||
errorLongpress() {
|
||||
let that = this;
|
||||
|
||||
let menu = [
|
||||
{
|
||||
text: `复制日志信息`,
|
||||
click() {
|
||||
uni.setClipboardData({
|
||||
data: JSON.stringify(that.item),
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
text: `删除此记录`,
|
||||
click() {
|
||||
uni.$emit("devTools_delError", that.item);
|
||||
let logs = devCache.get("errorReport");
|
||||
if (!logs) logs = [];
|
||||
let i = logs.findIndex(
|
||||
(x) =>
|
||||
x.type == that.item.type &&
|
||||
x.t == that.item.t &&
|
||||
x.m == that.item.m &&
|
||||
x.tr == that.item.tr &&
|
||||
x.p == that.item.p
|
||||
);
|
||||
if (i != -1) {
|
||||
logs.splice(i, 1);
|
||||
devCache.set("errorReport", logs);
|
||||
}
|
||||
uni.showToast({
|
||||
title: "删除成功!",
|
||||
icon: "success",
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
uni.showActionSheet({
|
||||
itemList: menu.map((x) => x.text),
|
||||
success({ tapIndex }) {
|
||||
menu[tapIndex].click();
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.errorItem:active {
|
||||
background-color: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
.errorItem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
width: 750rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
&.type-vw {
|
||||
background-color: rgb(255, 251, 229);
|
||||
}
|
||||
&.type-ve {
|
||||
background-color: rgb(255, 240, 240);
|
||||
}
|
||||
&.type-oe {
|
||||
background-color: rgb(255, 240, 240);
|
||||
}
|
||||
.content {
|
||||
width: 670rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.context {
|
||||
font-size: 20rpx;
|
||||
color: #333;
|
||||
line-height: 24rpx;
|
||||
}
|
||||
.msgBar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
.time {
|
||||
font-size: 16rpx;
|
||||
color: #888;
|
||||
min-width: 90rpx;
|
||||
}
|
||||
.page {
|
||||
font-size: 16rpx;
|
||||
color: #888;
|
||||
margin-left: 20rpx;
|
||||
lines: 1;
|
||||
max-width: 450rpx;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.logType {
|
||||
margin-left: 20rpx;
|
||||
font-size: 16rpx;
|
||||
padding: 0px 6rpx;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.type-ve {
|
||||
color: #fff;
|
||||
background-color: #fd0add;
|
||||
}
|
||||
.type-n {
|
||||
color: #fff;
|
||||
background-color: #82848a;
|
||||
}
|
||||
.type-vw {
|
||||
color: #fff;
|
||||
background-color: #ff9900;
|
||||
}
|
||||
.type-oe {
|
||||
color: #fff;
|
||||
background-color: #ff0000;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tools {
|
||||
width: 40rpx;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
margin-top: 6rpx;
|
||||
.copy {
|
||||
font-size: 20rpx;
|
||||
color: #333;
|
||||
line-height: 24rpx;
|
||||
}
|
||||
.copy:active {
|
||||
background-color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
.copyBtn:active {
|
||||
background-color: rgba(103, 194, 58, 0.6);
|
||||
}
|
||||
.copyBtn {
|
||||
padding: 5rpx;
|
||||
border-radius: 999rpx;
|
||||
overflow: hidden;
|
||||
background-color: #67c23a;
|
||||
.copyIcon {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,388 @@
|
||||
<template>
|
||||
<view
|
||||
class="fileItem"
|
||||
@click="fileClick"
|
||||
@longpress.stop="longpress"
|
||||
>
|
||||
<view class="icon">
|
||||
<image
|
||||
class="iconImg"
|
||||
:src="icon"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<view class="fileInfo">
|
||||
<text
|
||||
class="fileName"
|
||||
:style="{
|
||||
color: item.type == 'back' ? '#999' : '#333',
|
||||
}"
|
||||
>
|
||||
{{ item.name }}
|
||||
</text>
|
||||
<view
|
||||
v-if="item.type != 'back'"
|
||||
class="fileMeta"
|
||||
>
|
||||
<text class="textItem">{{ item.date }}</text>
|
||||
<text
|
||||
v-if="item.type == 'file'"
|
||||
class="textItem"
|
||||
>
|
||||
{{ item.sizeText }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import appDelDir from "../libs/appDelDir";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
/**
|
||||
* 当前文件路径类型
|
||||
*/
|
||||
dirType: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
/**
|
||||
* 文件路径列表
|
||||
*/
|
||||
dirList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
/**
|
||||
* 单个列表对象
|
||||
*/
|
||||
item: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
type: "", // 对象类型 dir back file
|
||||
name: "",
|
||||
fileType: "", // 文件后缀名称
|
||||
size: "", // 文件大小
|
||||
icon: "", //图标
|
||||
time: "", // 最后更改日期
|
||||
date: "",
|
||||
fileCount: 0, //文件夹下的文件数量
|
||||
directoryCount: 0, //文件夹下的目录数量
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
icon() {
|
||||
// #ifdef APP-PLUS
|
||||
// IOS端直接访问本地图片会报跨域,所以仅支持安卓预览图片
|
||||
if (
|
||||
uni.getSystemInfoSync().platform == "android" &&
|
||||
this.item.type == "file" &&
|
||||
["jpg", "jpeg", "png", "gif", "webp", "bmp"].indexOf(this.item.fileType) > -1
|
||||
) {
|
||||
let path = this.getPath();
|
||||
return path;
|
||||
}
|
||||
// #endif
|
||||
return this.item.icon;
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 点击事件
|
||||
*/
|
||||
fileClick() {
|
||||
let that = this;
|
||||
if (this.item.type == "dir") {
|
||||
this.$emit("goChildDir", this.item.name);
|
||||
} else if (this.item.type == "back") {
|
||||
this.$emit("goChildDir", "__back__");
|
||||
} else {
|
||||
if (
|
||||
//? 使用文本编辑器快捷打开文件
|
||||
["txt", "sql", "js", "css", "html", "log", "json"].indexOf(this.item.fileType) != -1
|
||||
) {
|
||||
this.openInEdit();
|
||||
} else if (["jpg", "jpeg", "png", "gif", "webp", "bmp"].indexOf(this.item.fileType) != -1) {
|
||||
let path = that.getPath();
|
||||
uni.previewImage({
|
||||
urls: [path],
|
||||
});
|
||||
} else {
|
||||
this.longpress();
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 使用文本编辑器打开
|
||||
*/
|
||||
openInEdit() {
|
||||
let that = this;
|
||||
let path = that.getPath();
|
||||
that.$emit("openEditFileDialog", {
|
||||
title: that.item.name,
|
||||
canSave: that.dirType != "PRIVATE_WWW",
|
||||
path,
|
||||
size: that.item.size,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 获取当前文件绝对路径
|
||||
*/
|
||||
getPath() {
|
||||
let that = this;
|
||||
let path = "";
|
||||
switch (that.dirType) {
|
||||
case "wx":
|
||||
path = wx.env.USER_DATA_PATH;
|
||||
break;
|
||||
case "PRIVATE_DOC":
|
||||
path = "_doc";
|
||||
break;
|
||||
case "PRIVATE_WWW":
|
||||
path = "_www";
|
||||
break;
|
||||
case "PUBLIC_DOCUMENTS":
|
||||
path = "_documents";
|
||||
break;
|
||||
case "PUBLIC_DOWNLOADS":
|
||||
path = "_downloads";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
that.dirList.map((x) => {
|
||||
path += "/" + x;
|
||||
});
|
||||
path = path + "/" + that.item.name;
|
||||
return path;
|
||||
},
|
||||
/**
|
||||
* 长按事件
|
||||
*/
|
||||
longpress() {
|
||||
let that = this;
|
||||
let path = that.getPath();
|
||||
|
||||
let menu = [
|
||||
{
|
||||
text: `复制绝对路径`,
|
||||
click() {
|
||||
// #ifdef APP-PLUS
|
||||
path = plus.io.convertLocalFileSystemURL(path);
|
||||
// #endif
|
||||
uni.setClipboardData({
|
||||
data: path,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
text: `删除`,
|
||||
click() {
|
||||
uni.showModal({
|
||||
title: "警告",
|
||||
content: "是否确认删除" + (that.item.type == "dir" ? "文件夹:" : "文件:") + that.item.name + "?",
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({
|
||||
title: "删除中",
|
||||
});
|
||||
|
||||
function delSuccess() {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "删除成功!",
|
||||
icon: "success",
|
||||
});
|
||||
that.$emit("getPage");
|
||||
}
|
||||
function delError() {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "删除失败",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
if (1) {
|
||||
let fs = wx.getFileSystemManager();
|
||||
|
||||
if (that.item.type == "file") {
|
||||
// ! 删除文件
|
||||
fs.unlink({
|
||||
filePath: path,
|
||||
success: delSuccess,
|
||||
fail: delError,
|
||||
});
|
||||
} else {
|
||||
// ! 删除文件夹
|
||||
fs.rmdir({
|
||||
dirPath: path,
|
||||
recursive: true,
|
||||
success: delSuccess,
|
||||
fail: delError,
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
// #endif
|
||||
|
||||
if (that.item.type == "file") {
|
||||
// ! 删除文件
|
||||
plus.io.resolveLocalFileSystemURL(
|
||||
path,
|
||||
(entry) => {
|
||||
// 可通过entry对象操作test.html文件
|
||||
entry.remove(delSuccess, delError);
|
||||
},
|
||||
delError
|
||||
);
|
||||
} else {
|
||||
// ! 删除文件夹
|
||||
appDelDir(path + "/")
|
||||
.then(delSuccess)
|
||||
.catch(delError);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
let isMp = false;
|
||||
// #ifdef MP-WEIXIN
|
||||
isMp = true;
|
||||
// #endif
|
||||
|
||||
if (!isMp || that.item.type != "dir") {
|
||||
menu.push({
|
||||
text: "重命名",
|
||||
click() {
|
||||
that.$emit("editDirName", {
|
||||
isDir: that.item.type == "dir",
|
||||
isEdit: true,
|
||||
name: that.item.name,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
if (that.item.type == "file") {
|
||||
menu.push({
|
||||
text: "调用外部程序打开此文件",
|
||||
click() {
|
||||
plus.runtime.openFile(path, null, (e) => {
|
||||
uni.showToast({
|
||||
title: "文档打开失败!" + e.message,
|
||||
icon: "none",
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
if (["doc", "xls", "ppt", "pdf", "docx", "xlsx", "pptx"].indexOf(that.item.fileType) != -1) {
|
||||
menu.unshift({
|
||||
text: "打开该文档",
|
||||
click() {
|
||||
let path = that.getPath();
|
||||
uni.openDocument({
|
||||
filePath: path,
|
||||
fail() {
|
||||
uni.showToast({
|
||||
title: "文档打开失败!",
|
||||
icon: "none",
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
|
||||
if (that.item.type == "file") {
|
||||
menu.unshift({
|
||||
text: `用文本编辑器打开`,
|
||||
click() {
|
||||
that.openInEdit();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
uni.showActionSheet({
|
||||
itemList: menu.map((x) => x.text),
|
||||
success({ tapIndex }) {
|
||||
menu[tapIndex].click();
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.fileItem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0rpx 20rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
width: 750rpx;
|
||||
min-height: 70rpx;
|
||||
&:active {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.icon {
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
border-radius: 10rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
.iconImg {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
}
|
||||
.fileInfo {
|
||||
margin-left: 10rpx;
|
||||
width: 650rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.fileName {
|
||||
width: 650rpx;
|
||||
lines: 1;
|
||||
overflow: hidden;
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
line-height: 28rpx;
|
||||
}
|
||||
.fileMeta {
|
||||
margin-top: 5rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 650rpx;
|
||||
.textItem {
|
||||
margin-right: 20rpx;
|
||||
font-size: 20rpx;
|
||||
line-height: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<view class="storageList">
|
||||
<objectAnalysis
|
||||
v-if="isLoaded"
|
||||
:data="data"
|
||||
:isOpenFirst="true"
|
||||
:width="710"
|
||||
/>
|
||||
<view
|
||||
v-else
|
||||
class="dataLoading"
|
||||
>
|
||||
<text class="status">加载中</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import objectAnalysis from "./objectAnalysis.vue";
|
||||
import getRuntimeInfo from "../libs/getRuntimeInfo";
|
||||
export default {
|
||||
components: {
|
||||
objectAnalysis,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 是否完成加载
|
||||
*/
|
||||
isLoaded: false,
|
||||
/**
|
||||
* 缓存里的数据
|
||||
*/
|
||||
data: {},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 加载数据
|
||||
*/
|
||||
async getData() {
|
||||
let that = this;
|
||||
that.isLoaded = false;
|
||||
let data = await getRuntimeInfo();
|
||||
setTimeout(() => {
|
||||
that.data = data;
|
||||
that.isLoaded = true;
|
||||
}, 500);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.storageList {
|
||||
padding: 20rpx;
|
||||
width: 750rpx;
|
||||
}
|
||||
.dataLoading {
|
||||
width: 750rpx;
|
||||
height: 400rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.status {
|
||||
font-size: 20rpx;
|
||||
color: #888;
|
||||
line-height: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,212 @@
|
||||
<template>
|
||||
<view class="jsRunnerItem">
|
||||
<view class="codeInput">
|
||||
<image
|
||||
src="@/devTools/page/static/fold.png"
|
||||
class="fold-left"
|
||||
/>
|
||||
<view class="codeView">
|
||||
<text class="codeText">{{ item.code }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="codeResult">
|
||||
<image
|
||||
src="@/devTools/page/static/fold.png"
|
||||
class="fold-right"
|
||||
/>
|
||||
<view class="codeResultView">
|
||||
<template v-if="item.isEnd">
|
||||
<template v-if="isObj(item.result)">
|
||||
<objectAnalysis
|
||||
:data="item.result"
|
||||
:showObjProto="true"
|
||||
:width="610"
|
||||
:canLongpress="false"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<view>
|
||||
<text
|
||||
class="context"
|
||||
:class="[getTypeClass(item.result)]"
|
||||
selectable
|
||||
>
|
||||
{{ getText(item.result) }}
|
||||
</text>
|
||||
</view>
|
||||
</template>
|
||||
</template>
|
||||
<text
|
||||
v-else
|
||||
class="loadingOutput"
|
||||
>
|
||||
...
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import objectAnalysis from "./objectAnalysis.vue";
|
||||
export default {
|
||||
components: {
|
||||
objectAnalysis,
|
||||
},
|
||||
props: {
|
||||
/**
|
||||
* 单行数据
|
||||
*/
|
||||
item: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
id: "",
|
||||
code: "",
|
||||
result: "",
|
||||
isEnd: false,
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 是否为对象类型
|
||||
*/
|
||||
isObj(data) {
|
||||
return typeof data == "object" && data != null && data != undefined;
|
||||
},
|
||||
/**
|
||||
* 获取对应的类型样式
|
||||
*/
|
||||
getTypeClass(obj) {
|
||||
try {
|
||||
let type = typeof obj;
|
||||
|
||||
if (type == "string") {
|
||||
if (obj.indexOf("at ") == 0) {
|
||||
return "t-line";
|
||||
} else if (obj === undefined || obj == "undefined") {
|
||||
return "t-undefined";
|
||||
} else if (obj === null || obj == "null") {
|
||||
return "t-null";
|
||||
} else if (obj == "true" || obj == "false") {
|
||||
return "t-boolean";
|
||||
} else if (Number.isFinite(Number(obj))) {
|
||||
return "t-number";
|
||||
}
|
||||
} else if (type == "function") {
|
||||
return "t-function";
|
||||
}
|
||||
return "t-" + type;
|
||||
} catch (error) {}
|
||||
return "t-string";
|
||||
},
|
||||
/**
|
||||
* 获取数据文字
|
||||
*/
|
||||
getText(data) {
|
||||
if (data === null) {
|
||||
return "null";
|
||||
}
|
||||
switch (typeof data) {
|
||||
case "string":
|
||||
// return data.replace(/\n/g, "");
|
||||
return data;
|
||||
case "boolean":
|
||||
return data ? "true" : "false";
|
||||
case "undefined":
|
||||
return "undefined";
|
||||
case "function":
|
||||
return data.toString();
|
||||
case "symbol":
|
||||
return "js:symbol";
|
||||
default:
|
||||
return data;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.jsRunnerItem:active {
|
||||
// background-color: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
.jsRunnerItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 750rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
.codeInput {
|
||||
width: 710rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 10rpx;
|
||||
.codeView {
|
||||
margin-left: 10rpx;
|
||||
width: 670rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.codeText {
|
||||
font-size: 20rpx;
|
||||
color: #777;
|
||||
line-height: 26rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.codeResult {
|
||||
width: 710rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.02);
|
||||
padding-top: 10rpx;
|
||||
.codeResultView {
|
||||
margin-left: 10rpx;
|
||||
width: 670rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.loadingOutput {
|
||||
font-size: 20rpx;
|
||||
color: rgba(0, 0, 0, 0.1);
|
||||
line-height: 26rpx;
|
||||
}
|
||||
.context {
|
||||
font-size: 20rpx;
|
||||
color: rgb(227, 54, 46);
|
||||
line-height: 26rpx;
|
||||
&.t-number {
|
||||
color: rgb(8, 66, 160);
|
||||
}
|
||||
&.t-boolean {
|
||||
color: rgb(133, 2, 255);
|
||||
}
|
||||
&.t-string {
|
||||
color: rgb(227, 54, 46);
|
||||
}
|
||||
&.t-undefined {
|
||||
color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
&.t-null {
|
||||
color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
&.t-line {
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
&.t-function {
|
||||
color: rgb(121, 38, 117);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.fold-left {
|
||||
transform: rotate(90deg);
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
.fold-right {
|
||||
transform: rotate(-90deg);
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<view
|
||||
class="logItem"
|
||||
@longpress.stop="logLongpress"
|
||||
>
|
||||
<view class="content">
|
||||
<view>
|
||||
<text class="text-xs">{{ item.m }}</text>
|
||||
</view>
|
||||
<view class="msgBar">
|
||||
<text class="time">{{ item.date }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="tools">
|
||||
<view
|
||||
class="copyBtn"
|
||||
@click="copyItem"
|
||||
>
|
||||
<image
|
||||
src="@/devTools/page/static/copy.png"
|
||||
class="copyIcon"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import devCache from "../../../core/libs/devCache";
|
||||
export default {
|
||||
props: {
|
||||
/**
|
||||
* logs单行数据
|
||||
*/
|
||||
item: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
date: "",
|
||||
m: "", //日志内容
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 复制
|
||||
*/
|
||||
copyItem() {
|
||||
uni.setClipboardData({
|
||||
data: this.item.m,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 长按事件
|
||||
*/
|
||||
logLongpress() {
|
||||
let that = this;
|
||||
|
||||
let menu = [
|
||||
{
|
||||
text: `复制日志信息`,
|
||||
click() {
|
||||
uni.setClipboardData({
|
||||
data: JSON.stringify(that.item),
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
text: `删除此记录`,
|
||||
click() {
|
||||
uni.$emit("devTools_delLog", that.item);
|
||||
let logs = devCache.get("logReport");
|
||||
if (!logs) logs = [];
|
||||
let i = logs.findIndex(
|
||||
(x) => x.m == that.item.m && x.t == that.item.t
|
||||
);
|
||||
if (i != -1) {
|
||||
logs.splice(i, 1);
|
||||
devCache.set("logReport", logs);
|
||||
}
|
||||
uni.showToast({
|
||||
title: "删除成功!",
|
||||
icon: "success",
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
uni.showActionSheet({
|
||||
itemList: menu.map((x) => x.text),
|
||||
success({ tapIndex }) {
|
||||
menu[tapIndex].click();
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.logItem:active {
|
||||
background-color: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
.logItem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
width: 750rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
.content {
|
||||
width: 610rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.context {
|
||||
font-size: 20rpx;
|
||||
color: #333;
|
||||
line-height: 24rpx;
|
||||
}
|
||||
.msgBar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
.time {
|
||||
font-size: 16rpx;
|
||||
color: #888;
|
||||
}
|
||||
.page {
|
||||
font-size: 16rpx;
|
||||
color: #888;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.copyBtn:active {
|
||||
background-color: rgba(103, 194, 58, 0.6);
|
||||
}
|
||||
.copyBtn {
|
||||
padding: 5rpx;
|
||||
border-radius: 999rpx;
|
||||
overflow: hidden;
|
||||
background-color: #67c23a;
|
||||
.copyIcon {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
}
|
||||
}
|
||||
.text-xs {
|
||||
font-size: 20rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,337 @@
|
||||
<template>
|
||||
<view
|
||||
class="networkItem"
|
||||
:class="['type-' + item.type]"
|
||||
@longpress.stop="networkLongpress"
|
||||
>
|
||||
<view class="content">
|
||||
<view class="head">
|
||||
<view
|
||||
class="method"
|
||||
:class="'type-' + item.method"
|
||||
>
|
||||
<text
|
||||
class="methodText"
|
||||
:class="'type-' + item.method"
|
||||
>
|
||||
{{ item.method }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="path">
|
||||
<text class="pageText">{{ getPath(item.url) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<objectAnalysis
|
||||
:data="getItem(item)"
|
||||
:width="710"
|
||||
:canLongpress="false"
|
||||
@onLongpress="networkLongpress"
|
||||
/>
|
||||
<view class="msgBar">
|
||||
<text
|
||||
class="data"
|
||||
style="min-width: 90rpx"
|
||||
>
|
||||
{{ item.date }}
|
||||
</text>
|
||||
<text
|
||||
class="time"
|
||||
:style="{
|
||||
width: '100rpx',
|
||||
color: getTimeColor,
|
||||
}"
|
||||
>
|
||||
{{ item.useTime }}s
|
||||
</text>
|
||||
<text
|
||||
class="status"
|
||||
:class="'s-' + item.type"
|
||||
style="width: 120rpx"
|
||||
>
|
||||
{{ getTypeName(item.type) }}
|
||||
</text>
|
||||
<text
|
||||
v-if="item.type == 1"
|
||||
class="time"
|
||||
:style="{
|
||||
color: getSizeColor,
|
||||
}"
|
||||
>
|
||||
{{ getByteSize }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import objectAnalysis from "./objectAnalysis.vue";
|
||||
export default {
|
||||
components: {
|
||||
objectAnalysis,
|
||||
},
|
||||
props: {
|
||||
/**
|
||||
* console单行数据
|
||||
*/
|
||||
item: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
id: 0, //请求id
|
||||
type: 0, // 0发起请求中 1请求成功 2请求失败
|
||||
date: "", //请求日期
|
||||
sendTime: 0, //发送请求的时间
|
||||
responseTime: 0, //响应时间
|
||||
useTime: 0, //请求总耗时
|
||||
|
||||
url: "", //请求地址
|
||||
header: "", //请求头
|
||||
method: "get", //请求方式
|
||||
data: "", //请求参数
|
||||
|
||||
responseBody: "", //响应主体
|
||||
responseHeader: "", //响应头
|
||||
responseStatus: "", //响应编码
|
||||
responseMsg: "", //响应报错信息
|
||||
responseBodySize: 0, //请求主体大小
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* 获取请求时间的颜色
|
||||
*/
|
||||
getTimeColor() {
|
||||
if (this.item.useTime == 0) {
|
||||
return "#eeeeee";
|
||||
} else if (this.item.useTime > 3) {
|
||||
return "#fa3534";
|
||||
} else if (this.item.useTime > 1) {
|
||||
return "#ff9900";
|
||||
} else {
|
||||
return "#909399";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 获取字节大小,b转kb mb
|
||||
*/
|
||||
getByteSize() {
|
||||
let size = Number(this.item.responseBodySize);
|
||||
if (null == size || size == "") return "0.00 KB";
|
||||
var unitArr = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
var index = 0;
|
||||
var srcsize = parseFloat(size);
|
||||
index = Math.floor(Math.log(srcsize) / Math.log(1024));
|
||||
size = srcsize / Math.pow(1024, index);
|
||||
size = size.toFixed(2); //保留的小数位数
|
||||
if (Number(this.item.responseBodySize) < 1024) {
|
||||
return (size / 1000).toFixed(2) + "KB";
|
||||
}
|
||||
return size + unitArr[index];
|
||||
},
|
||||
/**
|
||||
* 获取响应大小的颜色
|
||||
*/
|
||||
getSizeColor() {
|
||||
let size = this.item.responseBodySize;
|
||||
if (size == 0) {
|
||||
return "#fa3534";
|
||||
} else if (size > 256 * 1024) {
|
||||
return "#ff9900";
|
||||
} else if (size > 1024 * 1024) {
|
||||
return "#fa3534";
|
||||
} else {
|
||||
return "#909399";
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 通过url获取路径
|
||||
*/
|
||||
getPath(url) {
|
||||
if (!url) return "无";
|
||||
function getPathFromUrl(url) {
|
||||
const pathStart = url.indexOf("//") + 2;
|
||||
const pathEnd = url.indexOf("?", pathStart) >= 0 ? url.indexOf("?", pathStart) : url.length;
|
||||
return url.substring(url.indexOf("/", pathStart), pathEnd);
|
||||
}
|
||||
return getPathFromUrl(url);
|
||||
},
|
||||
/**
|
||||
* 获取请求类型名称
|
||||
*/
|
||||
getTypeName(type) {
|
||||
return ["请求中...", "请求完成", "请求失败"][Number(type)];
|
||||
},
|
||||
/**
|
||||
* 精简item
|
||||
*/
|
||||
getItem(item) {
|
||||
return {
|
||||
data: item.data,
|
||||
responseMsg: item.responseMsg,
|
||||
responseStatus: item.responseStatus,
|
||||
method: item.method,
|
||||
url: item.url,
|
||||
header: item.header,
|
||||
responseBody: item.responseBody,
|
||||
responseHeader: item.responseHeader,
|
||||
responseBodySize: this.getByteSize,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 长按事件
|
||||
*/
|
||||
networkLongpress() {
|
||||
let that = this;
|
||||
|
||||
let menu = [
|
||||
{
|
||||
text: `重发此请求`,
|
||||
click() {
|
||||
that.$emit("goSendRequest", that.item);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: `复制请求日志信息`,
|
||||
click() {
|
||||
uni.setClipboardData({
|
||||
data: JSON.stringify(that.item),
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
text: `在请求构建工具中打开`,
|
||||
click() {
|
||||
that.$emit("goOpenRequest", that.item);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: `删除此记录`,
|
||||
click() {
|
||||
uni.$emit("devTools_delNetworkItemById", that.item.id);
|
||||
uni.showToast({
|
||||
title: "删除成功!",
|
||||
icon: "success",
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
uni.showActionSheet({
|
||||
itemList: menu.map((x) => x.text),
|
||||
success({ tapIndex }) {
|
||||
menu[tapIndex].click();
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.networkItem:active {
|
||||
background-color: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
.networkItem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
width: 750rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
&.type-0 {
|
||||
background-color: rgb(255, 251, 229);
|
||||
}
|
||||
&.type-2 {
|
||||
background-color: rgb(255, 240, 240);
|
||||
}
|
||||
.content {
|
||||
width: 710rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.head {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-bottom: 5rpx;
|
||||
.method {
|
||||
background-color: #e2e2e2;
|
||||
color: #333;
|
||||
border-radius: 4rpx;
|
||||
padding: 4rpx 6rpx;
|
||||
border-radius: 10rpx;
|
||||
&.type-get {
|
||||
background-color: rgba(168, 25, 197, 0.1);
|
||||
}
|
||||
&.type-post {
|
||||
background-color: rgba(255, 217, 0, 0.1);
|
||||
}
|
||||
.methodText {
|
||||
color: #333;
|
||||
font-size: 22rpx;
|
||||
line-height: 22rpx;
|
||||
max-width: 650rpx;
|
||||
&.type-get {
|
||||
// color: #fff;
|
||||
}
|
||||
&.type-post {
|
||||
// color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.path {
|
||||
width: 620rpx;
|
||||
max-width: 620rpx;
|
||||
lines: 1;
|
||||
margin-left: 6rpx;
|
||||
overflow: hidden;
|
||||
/* #ifdef H5 */
|
||||
// 限制行数
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
/* #endif */
|
||||
.pageText {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
.context {
|
||||
font-size: 20rpx;
|
||||
color: #333;
|
||||
line-height: 24rpx;
|
||||
}
|
||||
.msgBar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-top: 5rpx;
|
||||
.data {
|
||||
font-size: 16rpx;
|
||||
color: #888;
|
||||
}
|
||||
.time {
|
||||
font-size: 16rpx;
|
||||
color: #333;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
.status {
|
||||
margin-left: 20rpx;
|
||||
font-size: 16rpx;
|
||||
&.s-0 {
|
||||
color: #fa3534;
|
||||
}
|
||||
&.s-1 {
|
||||
color: #909399;
|
||||
}
|
||||
&.s-2 {
|
||||
color: #ff9900;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,733 @@
|
||||
<template>
|
||||
<view
|
||||
class="objectAnalysis"
|
||||
:style="{
|
||||
width: width + 'rpx',
|
||||
}"
|
||||
>
|
||||
<view
|
||||
class="objectTitle"
|
||||
:class="canLongpress ? 'objectTitleActive' : ''"
|
||||
@click="titleClick"
|
||||
@longpress.stop="faLongpress"
|
||||
>
|
||||
<image class="foldItem" v-if="isOpen" src="@/devTools/page/static/fold.png" />
|
||||
<image class="foldItem" v-else src="@/devTools/page/static/unfold.png" />
|
||||
<text
|
||||
class="title"
|
||||
:style="{
|
||||
width: width - 50 + 'rpx',
|
||||
}"
|
||||
>
|
||||
{{ title }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="objectList"
|
||||
v-if="isOpen"
|
||||
:style="{
|
||||
width: width + 'rpx',
|
||||
}"
|
||||
>
|
||||
<view
|
||||
v-for="(item, index) in list"
|
||||
:key="item.i"
|
||||
class="listItem"
|
||||
:style="{
|
||||
marginLeft: item.l + 'rpx',
|
||||
}"
|
||||
@click="rowClick(item, index)"
|
||||
@longpress.stop="rowLongpress($event, item, index)"
|
||||
>
|
||||
<template v-if="item.t == 'array' || item.t == 'object'">
|
||||
<image class="foldItem" v-if="item.o" src="@/devTools/page/static/fold.png" />
|
||||
<image class="foldItem" v-else src="@/devTools/page/static/unfold.png" />
|
||||
</template>
|
||||
<view
|
||||
v-else
|
||||
class="emptyFold"
|
||||
></view>
|
||||
<text
|
||||
class="objKey"
|
||||
:class="'t-' + item.t"
|
||||
:style="{
|
||||
maxWidth: (width - item.l - 40) / 2 + 'rpx',
|
||||
}"
|
||||
>
|
||||
{{ item.k }}:
|
||||
</text>
|
||||
<text
|
||||
class="objValue"
|
||||
:class="'t-' + item.t"
|
||||
>
|
||||
{{ item.vt }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import jsonCompress from "../../../core/libs/jsonCompress";
|
||||
function getType(v) {
|
||||
return Object.prototype.toString.call(v).slice(8, -1).toLocaleLowerCase();
|
||||
}
|
||||
function randId() {
|
||||
return Math.ceil(Math.random() * 1000000000000000);
|
||||
}
|
||||
export default {
|
||||
props: {
|
||||
/**
|
||||
* 需要解析的对象数据
|
||||
*/
|
||||
data: "",
|
||||
/**
|
||||
* 组件宽度 rpx
|
||||
*/
|
||||
width: {
|
||||
type: Number,
|
||||
default: 610,
|
||||
},
|
||||
/**
|
||||
* 是否默认展开第一层
|
||||
*/
|
||||
isOpenFirst: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* 是否自定义长按的菜单
|
||||
*/
|
||||
isDiyMenu: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* 是否响应最外层的长按事件
|
||||
*/
|
||||
canLongpress: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
/**
|
||||
* 是否展示完整的对象类型
|
||||
*/
|
||||
showObjProto: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 对象类型 array object map unknown
|
||||
*/
|
||||
type: "unknown",
|
||||
/**
|
||||
* 对象标题
|
||||
*/
|
||||
title: "",
|
||||
/**
|
||||
* 是否为开启节点状态
|
||||
*/
|
||||
isOpen: false,
|
||||
/**
|
||||
* 渲染的列表
|
||||
*/
|
||||
list: [
|
||||
{
|
||||
t: "text", // 类型
|
||||
k: "键名", // key
|
||||
v: "名称", // value
|
||||
vt: "", //view层渲染的文字
|
||||
i: "s", //节点id
|
||||
p: "0", //父节点id
|
||||
o: true, //是否开启下级
|
||||
l: 0, // 距离左侧长度
|
||||
d: null, //原对象
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
let that = this;
|
||||
|
||||
try {
|
||||
let { title } = that.getObjType(this.data);
|
||||
that.list = [];
|
||||
that.title = title;
|
||||
if (that.isOpenFirst) {
|
||||
that.titleClick();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("objectAnalysis error", error);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 标题点击事件
|
||||
*/
|
||||
titleClick() {
|
||||
if (this.isOpen) {
|
||||
this.isOpen = false;
|
||||
} else {
|
||||
if (this.list.length == 0) {
|
||||
this.analysisData(this.data);
|
||||
}
|
||||
this.isOpen = true;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 解析渲染数组
|
||||
*/
|
||||
analysisData(data, pid = 0) {
|
||||
let list = [];
|
||||
let l = this.getParentNum(pid);
|
||||
|
||||
let keys = [];
|
||||
keys = Reflect.ownKeys(data);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let key = keys[i];
|
||||
let value = data[key];
|
||||
|
||||
if (key == "__proto__" || key == "__ob__") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let o = {
|
||||
t: typeof value,
|
||||
k: key,
|
||||
v: value,
|
||||
vt: "",
|
||||
i: randId(),
|
||||
p: pid,
|
||||
o: false,
|
||||
l: l * 5,
|
||||
d: value,
|
||||
};
|
||||
|
||||
try {
|
||||
let t = typeof value;
|
||||
if (t == "function") {
|
||||
try {
|
||||
o.vt = value.toString();
|
||||
} catch (error) {
|
||||
if (error && error.message) {
|
||||
o.vt = "[js:function]" + error.message;
|
||||
} else {
|
||||
o.vt = "[js:function]";
|
||||
}
|
||||
}
|
||||
o.v = o.vt;
|
||||
o.d = "";
|
||||
} else if (t == "object") {
|
||||
if (this.showObjProto) {
|
||||
let str = "unknown";
|
||||
if (value === null) {
|
||||
o.t = "null";
|
||||
str = "null";
|
||||
} else if (Array.isArray(value)) {
|
||||
o.t = "array";
|
||||
let l = 0;
|
||||
try {
|
||||
l = value.length;
|
||||
} catch (error) {}
|
||||
str = Object.prototype.toString.call(value).slice(8, -1) + (l > 0 ? ` (${l})[...]` : " (0)[]");
|
||||
} else {
|
||||
o.t = "object";
|
||||
let childList = [];
|
||||
try {
|
||||
childList = Reflect.ownKeys(value);
|
||||
} catch (error) {}
|
||||
str = Object.prototype.toString.call(value).slice(8, -1) + (childList.length == 0 ? " {}" : " {...}");
|
||||
}
|
||||
o.vt = str;
|
||||
} else {
|
||||
let type = getType(value);
|
||||
let title = "";
|
||||
try {
|
||||
title = JSON.stringify(value);
|
||||
if (title.length > 50) {
|
||||
title = title.slice(0, 50) + "...";
|
||||
}
|
||||
if (type == "array" && value.length > 0) {
|
||||
title = "(" + value.length + ")" + title;
|
||||
}
|
||||
} catch (error) {
|
||||
title = "对象解析失败:" + error;
|
||||
}
|
||||
o.t = type;
|
||||
o.vt = title;
|
||||
o.v = value;
|
||||
}
|
||||
} else if (t == "symbol") {
|
||||
o.t = "symbol";
|
||||
try {
|
||||
if (value.toString) {
|
||||
o.vt = value.toString();
|
||||
} else {
|
||||
o.vt = "[js:symbol]";
|
||||
}
|
||||
} catch (error) {
|
||||
let msg = "";
|
||||
if (error && error.message) {
|
||||
msg = error.message;
|
||||
} else {
|
||||
msg = "[js:symbol解析失败]";
|
||||
}
|
||||
o.vt = msg;
|
||||
}
|
||||
} else if (t == "string") {
|
||||
if (value.length > 200) {
|
||||
o.vt = `"` + value.slice(0, 200) + "..." + '"';
|
||||
} else {
|
||||
o.vt = `"${value}"`;
|
||||
}
|
||||
} else if (t == "number") {
|
||||
if (Number.isFinite(value)) {
|
||||
o.vt = value.toString();
|
||||
} else {
|
||||
o.vt = isNaN(value) ? "NaN" : "Infinity";
|
||||
}
|
||||
} else if (t == "boolean") {
|
||||
o.vt = value ? "true" : "false";
|
||||
} else if (t == "undefined") {
|
||||
o.vt = "undefined";
|
||||
} else {
|
||||
o.vt = "[js:unknown type]";
|
||||
}
|
||||
} catch (error) {
|
||||
let msg = "";
|
||||
if (error && error.message) {
|
||||
msg = error.message;
|
||||
} else {
|
||||
msg = "[js对象解析失败]";
|
||||
}
|
||||
o.vt = msg;
|
||||
}
|
||||
list.push(o);
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
this.list = list;
|
||||
} else {
|
||||
let faIndex = this.list.findIndex((x) => x.i == pid) + 1;
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
this.list.splice(faIndex, 0, list[i]);
|
||||
faIndex++;
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 获取节点的父类数量
|
||||
*/
|
||||
getParentNum(pid) {
|
||||
let that = this;
|
||||
let count = 0;
|
||||
if (pid == 0) {
|
||||
return count;
|
||||
} else {
|
||||
let p = Number(pid);
|
||||
while (true) {
|
||||
count = count + 1;
|
||||
let fa = that.list.find((x) => x.i == p);
|
||||
if (!fa || fa.i == 0) {
|
||||
break;
|
||||
} else {
|
||||
p = Number(fa.p);
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
},
|
||||
/**
|
||||
* 行对象点击事件
|
||||
*/
|
||||
rowClick(item, index) {
|
||||
let that = this;
|
||||
const nodeItem = that.list[index];
|
||||
if (item.t == "object" || item.t == "array") {
|
||||
if (item.o) {
|
||||
nodeItem.o = false;
|
||||
that.hideListByPid(item.i);
|
||||
} else {
|
||||
nodeItem.o = true;
|
||||
that.analysisData(nodeItem.d, item.i);
|
||||
}
|
||||
} else if (item.t == "string" && item.v.length > 100) {
|
||||
// 长文本点击时默认打开文本编辑器
|
||||
uni.$emit("devTools_showTextEditDialog", {
|
||||
title: item.k,
|
||||
canSave: false,
|
||||
isFileEdit: false,
|
||||
value: item.v,
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 根据父类id删除数组内元素
|
||||
*/
|
||||
hideListByPid(pid = 0) {
|
||||
let that = this;
|
||||
while (true) {
|
||||
let i = that.list.findIndex((x) => x.p == pid);
|
||||
if (i == -1) {
|
||||
break;
|
||||
}
|
||||
if (that.list[i].o) {
|
||||
that.hideListByPid(that.list[i].i);
|
||||
}
|
||||
that.list.splice(i, 1);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 长按事件
|
||||
*/
|
||||
rowLongpress(e, item, index) {
|
||||
// #ifdef APP-PLUS
|
||||
if (e && e.stopPropagation) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
// #endif
|
||||
let that = this;
|
||||
if (that.isDiyMenu) {
|
||||
that.$emit("diyMenu", { item, index });
|
||||
} else {
|
||||
let k = this.toString(item.k);
|
||||
if (k.length > 20) {
|
||||
k = k.slice(0, 20) + "...";
|
||||
}
|
||||
let v = this.toString(item.v);
|
||||
if (v.length > 20) {
|
||||
v = v.slice(0, 20) + "...";
|
||||
}
|
||||
uni.showActionSheet({
|
||||
itemList: [`复制键(${k})`, `复制值(${v})`],
|
||||
success({ tapIndex }) {
|
||||
if (tapIndex == 0) {
|
||||
uni.setClipboardData({
|
||||
data: that.toString(item.k),
|
||||
});
|
||||
} else {
|
||||
uni.setClipboardData({
|
||||
data: that.toString(item.v),
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 尝试转字符串
|
||||
*/
|
||||
toString(data) {
|
||||
try {
|
||||
if (data === undefined) return "undefined";
|
||||
if (data === null) return "null";
|
||||
if (typeof data == "boolean") return data ? "true" : "false";
|
||||
if (typeof data == "object") {
|
||||
return JSON.stringify(data);
|
||||
}
|
||||
return data.toString();
|
||||
} catch (error) {
|
||||
return "尝试解析失败!" + error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 获取列表
|
||||
*/
|
||||
getList() {
|
||||
return this.list;
|
||||
},
|
||||
/**
|
||||
* 获取父级key类别
|
||||
*/
|
||||
getFaKeyList(itemId) {
|
||||
let keyList = [];
|
||||
let item = this.list.find((x) => x.i == itemId);
|
||||
if (!item) return keyList;
|
||||
keyList = [item.k];
|
||||
if (item.p == 0) return keyList;
|
||||
|
||||
while (true) {
|
||||
item = this.list.find((x) => x.i == item.p);
|
||||
if (!item) break;
|
||||
keyList.unshift(item.k);
|
||||
if (item.p == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return keyList;
|
||||
},
|
||||
/**
|
||||
* 长按复制一整个对象
|
||||
*/
|
||||
faLongpress(e) {
|
||||
// #ifdef APP-PLUS
|
||||
if (e && e.stopPropagation) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
// #endif
|
||||
let that = this;
|
||||
if (that.canLongpress) {
|
||||
uni.setClipboardData({
|
||||
data: JSON.stringify(that.data),
|
||||
});
|
||||
} else {
|
||||
that.$emit("onLongpress");
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 获取对象单行数据
|
||||
*/
|
||||
getObjType(obj) {
|
||||
try {
|
||||
let title = "unknown";
|
||||
let type = typeof obj;
|
||||
let data = obj;
|
||||
switch (typeof data) {
|
||||
case "symbol":
|
||||
title = "[js:symbol]";
|
||||
try {
|
||||
if (data.toString) {
|
||||
title = data.toString();
|
||||
} else {
|
||||
title = "[js:symbol]";
|
||||
}
|
||||
} catch (error) {
|
||||
let msg = "";
|
||||
if (error && error.message) {
|
||||
msg = error.message;
|
||||
} else {
|
||||
msg = "[js:symbol解析失败]";
|
||||
}
|
||||
title = msg;
|
||||
}
|
||||
break;
|
||||
case "string":
|
||||
title = data;
|
||||
break;
|
||||
case "object":
|
||||
if (this.showObjProto) {
|
||||
try {
|
||||
let objType = Object.prototype.toString.call(data).slice(8, -1);
|
||||
title = {};
|
||||
let keys = Reflect.ownKeys(data);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let key = keys[i];
|
||||
if (key == "__proto__" || key == "__ob__") {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
let value = data[key];
|
||||
let t = typeof value;
|
||||
if (t == "function") {
|
||||
continue;
|
||||
}
|
||||
if (t == "object") {
|
||||
let str = "unknown";
|
||||
if (value === null) {
|
||||
str = "null";
|
||||
} else if (Array.isArray(value)) {
|
||||
str = Object.prototype.toString.call(value).slice(8, -1) + " [...]";
|
||||
} else {
|
||||
str = Object.prototype.toString.call(value).slice(8, -1) + " {...}";
|
||||
}
|
||||
title[key] = str;
|
||||
continue;
|
||||
}
|
||||
title[key] = data[key];
|
||||
} catch (error) {
|
||||
if (error && error.message) {
|
||||
title[key] = error.message;
|
||||
} else {
|
||||
title[key] = "[js对象解析失败]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let key = keys[i];
|
||||
try {
|
||||
let value = data[key];
|
||||
let t = typeof value;
|
||||
if (t == "function") {
|
||||
try {
|
||||
title[key] = value.toString();
|
||||
} catch (error) {
|
||||
if (error && error.message) {
|
||||
title[key] = "[js:function]" + error.message;
|
||||
} else {
|
||||
title[key] = "[js:function]";
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (error && error.message) {
|
||||
title[key] = error.message;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (title.toJSON) {
|
||||
title.toJSON = "[js:function]";
|
||||
}
|
||||
|
||||
if (objType == "Array") {
|
||||
title = objType + " " + jsonCompress.safeJsonStringify(title);
|
||||
} else {
|
||||
title = objType + " " + jsonCompress.safeJsonStringify(title);
|
||||
}
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
type = "array";
|
||||
} else {
|
||||
type = "object";
|
||||
}
|
||||
} catch (error) {
|
||||
let msg = "unknown";
|
||||
if (error && error.message) {
|
||||
msg = error.message;
|
||||
}
|
||||
title = "对象解析出错:" + msg;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
title = JSON.stringify(data);
|
||||
if (title.length > 50) {
|
||||
title = title.slice(0, 50) + "...";
|
||||
}
|
||||
} catch (error) {
|
||||
title = "对象解析失败:" + error;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case "function":
|
||||
try {
|
||||
title = data.toString();
|
||||
} catch (error) {
|
||||
title = "[js:function]";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
title = data;
|
||||
break;
|
||||
}
|
||||
return { title, type };
|
||||
} catch (error) {
|
||||
console.log("getObjType error", error);
|
||||
return {
|
||||
title: "unknown",
|
||||
type: "unknown",
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.objectAnalysis {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.objectTitle {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
.title {
|
||||
font-size: 20rpx;
|
||||
line-height: 20rpx;
|
||||
color: rgb(89, 74, 154);
|
||||
lines: 1;
|
||||
overflow: hidden;
|
||||
/* #ifdef H5 */
|
||||
// 限制行数
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
/* #endif */
|
||||
}
|
||||
}
|
||||
.objectTitleActive:active {
|
||||
background-color: rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
.objectList {
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
border-radius: 8rpx;
|
||||
/* #ifndef APP-PLUS */
|
||||
min-height: 50rpx;
|
||||
/* #endif */
|
||||
padding: 10rpx;
|
||||
.listItem:active {
|
||||
background-color: rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
.listItem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
.emptyFold {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
.objKey {
|
||||
font-size: 20rpx;
|
||||
line-height: 28rpx;
|
||||
color: rgb(121, 38, 117);
|
||||
lines: 1;
|
||||
overflow: hidden;
|
||||
/* #ifdef H5 */
|
||||
// 限制行数
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
/* #endif */
|
||||
}
|
||||
.objValue {
|
||||
line-height: 28rpx;
|
||||
margin-left: 5rpx;
|
||||
color: #333;
|
||||
font-size: 20rpx;
|
||||
lines: 1;
|
||||
overflow: hidden;
|
||||
/* #ifdef H5 */
|
||||
// 限制行数
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
/* #endif */
|
||||
&.t-number {
|
||||
color: rgb(8, 66, 160);
|
||||
}
|
||||
&.t-boolean {
|
||||
color: rgb(133, 2, 255);
|
||||
}
|
||||
&.t-string {
|
||||
color: rgb(227, 54, 46);
|
||||
}
|
||||
&.t-array {
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
&.t-object {
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
&.t-undefined {
|
||||
color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
&.t-null {
|
||||
color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.foldItem {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background-color: #eee;
|
||||
border-radius: 4rpx;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<view
|
||||
class="pageItem"
|
||||
@click.stop="showMenu"
|
||||
>
|
||||
<view class="content">
|
||||
<view>
|
||||
<text class="text-xs">页面路由:{{ item.route }}</text>
|
||||
</view>
|
||||
<view>
|
||||
<text class="text-xs">停留时长:{{ item.timeCount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
/**
|
||||
* logs单行数据
|
||||
*/
|
||||
item: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
route: "",
|
||||
timeCount: "",
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 展示菜单
|
||||
*/
|
||||
showMenu() {
|
||||
let that = this;
|
||||
|
||||
let r = String(that.item.route).substring(0, 10) + "...";
|
||||
|
||||
let menu = [
|
||||
{
|
||||
text: `复制路径(${r})`,
|
||||
click() {
|
||||
uni.setClipboardData({
|
||||
data: that.item.route,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
text: `复制时间(${that.item.timeCount})`,
|
||||
click() {
|
||||
uni.setClipboardData({
|
||||
data: that.item.timeCount,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
text: `跳转至此页面`,
|
||||
click() {
|
||||
uni.$emit("devTools_showRouteDialog", that.item.route);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
uni.showActionSheet({
|
||||
itemList: menu.map((x) => x.text),
|
||||
success({ tapIndex }) {
|
||||
menu[tapIndex].click();
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.pageItem:active {
|
||||
background-color: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
.pageItem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
width: 750rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
.content {
|
||||
width: 610rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.context {
|
||||
font-size: 20rpx;
|
||||
color: #333;
|
||||
line-height: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.text-xs {
|
||||
font-size: 20rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<view
|
||||
v-if="isShow"
|
||||
class="pagesList"
|
||||
>
|
||||
<!-- <objectAnalysis
|
||||
v-if="isLoaded"
|
||||
:data="pages"
|
||||
:isOpenFirst="true"
|
||||
:width="710"
|
||||
/> -->
|
||||
<template v-if="isLoaded">
|
||||
<view
|
||||
v-for="(item, index) in pages"
|
||||
:key="index"
|
||||
class="pageItem"
|
||||
@longpress.stop="longpress(item)"
|
||||
>
|
||||
<text
|
||||
v-if="pages.length == index + 1"
|
||||
class="t-red"
|
||||
>
|
||||
当前
|
||||
</text>
|
||||
|
||||
<view class="routeInfo">
|
||||
<text class="path">{{ item.route }}</text>
|
||||
<text class="options">{{ item.options }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<view
|
||||
v-else
|
||||
class="dataLoading"
|
||||
>
|
||||
<text class="status">加载中</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import objectAnalysis from "./objectAnalysis.vue";
|
||||
export default {
|
||||
components: {
|
||||
objectAnalysis,
|
||||
},
|
||||
props: {
|
||||
/**
|
||||
* 是否渲染
|
||||
*/
|
||||
isShow: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 是否完成加载
|
||||
*/
|
||||
isLoaded: false,
|
||||
/**
|
||||
* 页面路由数据
|
||||
*/
|
||||
pages: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 加载数据
|
||||
*/
|
||||
getData() {
|
||||
let that = this;
|
||||
that.isLoaded = false;
|
||||
|
||||
let pageList = getCurrentPages().map((x) => {
|
||||
let options = "";
|
||||
if (x.options) {
|
||||
Object.keys(x.options).map((key) => {
|
||||
options =
|
||||
options + (options == "" ? "" : "&") + key + "=" + x.options[key];
|
||||
});
|
||||
}
|
||||
return {
|
||||
route: x.route,
|
||||
options,
|
||||
};
|
||||
});
|
||||
pageList.pop();
|
||||
that.pages = pageList;
|
||||
|
||||
that.isLoaded = true;
|
||||
},
|
||||
/**
|
||||
* 长按事件
|
||||
*/
|
||||
longpress(item) {
|
||||
let that = this;
|
||||
|
||||
let menu = [
|
||||
{
|
||||
text: `复制路径`,
|
||||
click() {
|
||||
uni.setClipboardData({
|
||||
data: item.route,
|
||||
});
|
||||
},
|
||||
},
|
||||
...(item.options
|
||||
? [
|
||||
{
|
||||
text: `复制参数`,
|
||||
click() {
|
||||
uni.setClipboardData({
|
||||
data: item.options,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
text: `复制路径+参数`,
|
||||
click() {
|
||||
uni.setClipboardData({
|
||||
data: item.route + item.options ? "?" + item.options : "",
|
||||
});
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
|
||||
uni.showActionSheet({
|
||||
itemList: menu.map((x) => x.text),
|
||||
success({ tapIndex }) {
|
||||
menu[tapIndex].click();
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.pagesList {
|
||||
width: 750rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.pageItem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 15rpx 20rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
&:active {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.t-red {
|
||||
font-size: 20rpx;
|
||||
color: #fff;
|
||||
padding: 3rpx 8rpx;
|
||||
background-color: #ff2d55;
|
||||
border-radius: 10rpx;
|
||||
margin-right: 10rpx;
|
||||
height: 34rpx;
|
||||
}
|
||||
.routeInfo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 580rpx;
|
||||
.path {
|
||||
font-size: 26rpx;
|
||||
line-height: 30rpx;
|
||||
color: #333;
|
||||
width: 580rpx;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
white-space: normal;
|
||||
}
|
||||
.options {
|
||||
margin-top: 4rpx;
|
||||
font-size: 20rpx;
|
||||
line-height: 26rpx;
|
||||
color: #888;
|
||||
width: 580rpx;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.dataLoading {
|
||||
width: 750rpx;
|
||||
height: 100rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.status {
|
||||
font-size: 20rpx;
|
||||
color: #888;
|
||||
line-height: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<view
|
||||
class="routeItem"
|
||||
@click.stop="showMenu"
|
||||
>
|
||||
<view>
|
||||
<text class="routeText">{{ item.path }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 展示菜单
|
||||
*/
|
||||
showMenu() {
|
||||
let that = this;
|
||||
|
||||
let menuList = [];
|
||||
|
||||
let p = that.item.path.substr(0, 20);
|
||||
if (p.length == 20) {
|
||||
p = p + "...";
|
||||
}
|
||||
menuList.push(`复制路径(${p})`);
|
||||
|
||||
let isTabBar = false;
|
||||
if (that.item.meta && that.item.meta.isTabBar) {
|
||||
isTabBar = true;
|
||||
}
|
||||
if (!isTabBar) {
|
||||
menuList.push("跳转至此页面");
|
||||
}
|
||||
|
||||
uni.showActionSheet({
|
||||
itemList: menuList,
|
||||
success({ tapIndex }) {
|
||||
if (tapIndex == 0) {
|
||||
uni.setClipboardData({
|
||||
data: that.item.path,
|
||||
});
|
||||
} else {
|
||||
uni.$emit("devTools_showRouteDialog", that.item.path);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.routeItem {
|
||||
padding: 10rpx 20rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.routeText {
|
||||
width: 710rpx;
|
||||
font-size: 20rpx;
|
||||
line-height: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
.routeItem:active {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,723 @@
|
||||
<template>
|
||||
<view class="settingView">
|
||||
<view v-if="loading" class="loading">
|
||||
<text class="loadingText">加载中</text>
|
||||
</view>
|
||||
<template v-else>
|
||||
<subTitleBar :isOpen="exportIsShow" @click="exportIsShow = !exportIsShow" title="导出全部日志" />
|
||||
<template v-if="exportIsShow">
|
||||
<view class="delBtn" @click="exportJsonFile">
|
||||
<text class="delBtnText">导出日志文件(.json)</text>
|
||||
</view>
|
||||
</template>
|
||||
<view class="divisionLine"></view>
|
||||
|
||||
<subTitleBar :isOpen="cacheListIsShow" title="清空全部缓存" @click="cacheListIsShow = !cacheListIsShow" />
|
||||
<template v-if="cacheListIsShow">
|
||||
<view v-for="(item, index) in cacheSelectList" :key="index" @click.stop="doSelectCache(index)" class="checkboxItem">
|
||||
<checkbox :value="item.check ? '1' : '0'" :checked="item.check" color="#ff2d55" />
|
||||
<text
|
||||
class="name"
|
||||
:style="{
|
||||
color: item.count ? '#333' : '#888'
|
||||
}"
|
||||
>
|
||||
{{ item.name }}
|
||||
</text>
|
||||
|
||||
<text v-if="item.key == 'file'"></text>
|
||||
<text v-else-if="item.count" class="count"> ({{ item.count }}) </text>
|
||||
<text v-else class="empty"> (空) </text>
|
||||
</view>
|
||||
<view class="delBtn" @click="delCache">
|
||||
<text class="delBtnText">清空选中</text>
|
||||
</view>
|
||||
</template>
|
||||
<view class="divisionLine"></view>
|
||||
|
||||
<subTitleBar :isOpen="configIsShow" title="DevTools当前配置参数" @click="configIsShow = !configIsShow" />
|
||||
<view v-if="configIsShow" class="objectAnalysisView">
|
||||
<objectAnalysis :isOpenFirst="true" :data="config" :width="710" />
|
||||
</view>
|
||||
<view class="divisionLine"></view>
|
||||
|
||||
<subTitleBar :showArrow="false" title="关于" />
|
||||
|
||||
<view class="about">
|
||||
<view>
|
||||
<text class="row">Copyright©2024 福州重塑网络科技有限公司 前端团队</text>
|
||||
</view>
|
||||
<view @click="goUrl('https://dev.api0.cn')" style="display: flex; flex-direction: row">
|
||||
<text class="row">在线文档:</text>
|
||||
<text class="row" style="color: #ff2d55">https://dev.api0.cn</text>
|
||||
</view>
|
||||
<view>
|
||||
<text class="row">当前版本:v{{ config.version }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view style="height: 100rpx"></view>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import devCache from '../../../core/libs/devCache'
|
||||
import devOptions from '../../../core/libs/devOptions'
|
||||
import jsonCompress from '../../../core/libs/jsonCompress'
|
||||
import appDelDir from '../libs/appDelDir'
|
||||
import subTitleBar from '../ui/subTitleBar.vue'
|
||||
import objectAnalysis from './objectAnalysis.vue'
|
||||
import getRuntimeInfo from '../libs/getRuntimeInfo'
|
||||
export default {
|
||||
components: {
|
||||
subTitleBar,
|
||||
objectAnalysis
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 是否加载中
|
||||
*/
|
||||
loading: false,
|
||||
/**
|
||||
* 缓存列表是否展示
|
||||
*/
|
||||
cacheListIsShow: false,
|
||||
/**
|
||||
* 缓存列表
|
||||
*/
|
||||
cacheSelectList: [],
|
||||
/**
|
||||
* 配置文件是否显示
|
||||
*/
|
||||
configIsShow: false,
|
||||
/**
|
||||
* 当前配置
|
||||
*/
|
||||
config: devOptions.getOptions(),
|
||||
/**
|
||||
* 是否显示导出日志按钮
|
||||
*/
|
||||
exportIsShow: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 加载页面
|
||||
*/
|
||||
async getPage() {
|
||||
let that = this
|
||||
that.loading = true
|
||||
that.cacheSelectList = await that.countCache()
|
||||
that.loading = false
|
||||
},
|
||||
/**
|
||||
* 统计缓存信息
|
||||
*/
|
||||
countCache() {
|
||||
let that = this
|
||||
return new Promise(async (yes) => {
|
||||
let cacheSelectList = []
|
||||
|
||||
// dev 工具日志
|
||||
let keys = {
|
||||
errorReport: 'Error错误日志',
|
||||
console: 'Console打印日志',
|
||||
request: 'Request请求日志',
|
||||
logReport: 'Logs日志',
|
||||
uniBus: 'UniBus函数日志'
|
||||
}
|
||||
Object.keys(keys).map((key) => {
|
||||
let logs = devCache.get(key)
|
||||
cacheSelectList.push({
|
||||
name: keys[key],
|
||||
check: logs.length > 0,
|
||||
count: logs.length,
|
||||
key
|
||||
})
|
||||
})
|
||||
// #ifdef H5
|
||||
let indexDBList = await this.getIndexDBList()
|
||||
let cookieLength = document.cookie.split(';').length
|
||||
if (document.cookie == '') {
|
||||
cookieLength = 0
|
||||
}
|
||||
// #endif
|
||||
|
||||
cacheSelectList = cacheSelectList.concat([
|
||||
that.countStorageCache(),
|
||||
// #ifdef H5
|
||||
{
|
||||
key: 'sessionStorage',
|
||||
name: 'SessionStorage临时缓存',
|
||||
check: sessionStorage.length > 0,
|
||||
count: sessionStorage.length
|
||||
},
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
{
|
||||
key: 'file',
|
||||
name: 'FileSys本地文件(_doc)',
|
||||
check: false,
|
||||
count: '未知 // TODO'
|
||||
},
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
{
|
||||
name: 'FileSys本地文件(FileSystemManager)',
|
||||
check: false,
|
||||
key: 'file',
|
||||
count: '未知 // TODO'
|
||||
},
|
||||
// #endif
|
||||
{
|
||||
key: 'pageCount',
|
||||
name: 'Pages页面停留统计',
|
||||
check: devCache.get('pageCount').length > 0,
|
||||
count: devCache.get('pageCount').length
|
||||
},
|
||||
{
|
||||
key: 'dayOnline',
|
||||
name: 'Pages日活时间统计',
|
||||
check: devCache.get('dayOnline').length > 0,
|
||||
count: devCache.get('dayOnline').length
|
||||
},
|
||||
// #ifdef H5
|
||||
{
|
||||
key: 'cookie',
|
||||
name: 'Cookie',
|
||||
check: cookieLength > 0,
|
||||
count: cookieLength
|
||||
},
|
||||
{
|
||||
key: 'IndexDB',
|
||||
name: 'IndexDB',
|
||||
check: indexDBList.length > 0,
|
||||
count: indexDBList.length
|
||||
}
|
||||
// #endif
|
||||
])
|
||||
|
||||
yes(cacheSelectList)
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 统计本地缓存
|
||||
*/
|
||||
countStorageCache() {
|
||||
let n = 0
|
||||
// #ifdef APP-PLUS
|
||||
let keys = plus.storage.getAllKeys()
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]
|
||||
if (key.indexOf('devTools_') == 0) {
|
||||
// 忽略以 devTools_ 开头的key
|
||||
continue
|
||||
}
|
||||
n++
|
||||
}
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
let key = localStorage.key(i)
|
||||
if (key.indexOf('devTools_') == 0) {
|
||||
continue
|
||||
}
|
||||
n++
|
||||
}
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
let keyList = devCache.get('storage')
|
||||
if (!keyList) keyList = []
|
||||
for (let i = 0; i < keyList.length; i++) {
|
||||
const key = keyList[i]
|
||||
if (key.indexOf('devTools_') == 0) {
|
||||
continue
|
||||
}
|
||||
n++
|
||||
}
|
||||
// #endif
|
||||
return {
|
||||
key: 'localStorage',
|
||||
name: 'localStorage本地缓存',
|
||||
check: n > 0,
|
||||
count: n
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 获取indexDB列表
|
||||
*/
|
||||
getIndexDBList() {
|
||||
return new Promise((yes) => {
|
||||
try {
|
||||
indexedDB.databases().then((list) => {
|
||||
yes(list)
|
||||
})
|
||||
} catch (error) {
|
||||
console.log('getIndexDBList error', error)
|
||||
yes([])
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 选择清空的缓存项目
|
||||
*/
|
||||
doSelectCache(index) {
|
||||
this.cacheSelectList[index].check = !this.cacheSelectList[index].check
|
||||
},
|
||||
/**
|
||||
* 清空缓存
|
||||
*/
|
||||
delCache() {
|
||||
let that = this
|
||||
|
||||
let selectedKey = []
|
||||
that.cacheSelectList.map((item) => {
|
||||
if (item.check) {
|
||||
selectedKey.push(item.key)
|
||||
}
|
||||
})
|
||||
|
||||
let keyDelFun = {
|
||||
errorReport() {
|
||||
devCache.set('errorReport', [])
|
||||
},
|
||||
console() {
|
||||
uni.$emit('devTools_delConsoleAll')
|
||||
},
|
||||
request() {
|
||||
uni.$emit('devTools_delNetworkAll')
|
||||
},
|
||||
logReport() {
|
||||
devCache.set('logReport', [])
|
||||
},
|
||||
uniBus() {
|
||||
uni.$emit('devTools_delUniBusAll')
|
||||
},
|
||||
localStorage() {
|
||||
// #ifdef APP-PLUS
|
||||
let keys = plus.storage.getAllKeys()
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = String(keys[i])
|
||||
if (key.indexOf('devTools_') == 0) {
|
||||
continue
|
||||
}
|
||||
uni.removeStorageSync(key)
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
let key = String(localStorage.key(i))
|
||||
if (key.indexOf('devTools_') == 0) {
|
||||
continue
|
||||
}
|
||||
setTimeout(() => {
|
||||
localStorage.removeItem(key)
|
||||
}, i * 2 + 1)
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
let keyList = devCache.get('storage')
|
||||
if (!keyList) keyList = []
|
||||
for (let i = 0; i < keyList.length; i++) {
|
||||
const key = keyList[i]
|
||||
if (key.indexOf('devTools_') == 0) {
|
||||
continue
|
||||
}
|
||||
uni.removeStorageSync(key)
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
sessionStorage() {
|
||||
for (let i = 0; i < sessionStorage.length; i++) {
|
||||
let key = String(sessionStorage.key(i))
|
||||
if (key.indexOf('devTools_') == 0) {
|
||||
continue
|
||||
}
|
||||
sessionStorage.removeItem(key)
|
||||
}
|
||||
},
|
||||
file() {
|
||||
// #ifdef APP-PLUS
|
||||
appDelDir('_doc/')
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
let fs = wx.getFileSystemManager()
|
||||
fs.rmdir({
|
||||
dirPath: wx.env.USER_DATA_PATH + '/',
|
||||
recursive: true
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
pageCount() {
|
||||
devCache.set('pageCount', [])
|
||||
},
|
||||
dayOnline() {
|
||||
devCache.set('dayOnline', [])
|
||||
},
|
||||
cookie() {
|
||||
let keys = []
|
||||
document.cookie.split(';').forEach((cookieStr) => {
|
||||
const [name, value] = cookieStr.trim().split('=')
|
||||
keys.push(name)
|
||||
})
|
||||
keys.map((k) => {
|
||||
document.cookie = `${k}=;expires=` + new Date(new Date().getTime() + 200).toGMTString() + ';path=/'
|
||||
})
|
||||
},
|
||||
IndexDB() {
|
||||
indexedDB.databases().then((list) => {
|
||||
list.map((item) => {
|
||||
indexedDB.deleteDatabase(item.name)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedKey.length == 0) {
|
||||
return uni.showToast({
|
||||
title: '请先勾选需要清空的项目!',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
uni.showLoading({
|
||||
title: '清空中...',
|
||||
mask: true
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '清空成功!',
|
||||
icon: 'success'
|
||||
})
|
||||
that.getPage()
|
||||
}, 5100)
|
||||
|
||||
selectedKey.map((key) => {
|
||||
keyDelFun[key]()
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 导出日志文件到json
|
||||
*/
|
||||
async exportJsonFile() {
|
||||
let that = this
|
||||
|
||||
// #ifdef MP
|
||||
if (1) {
|
||||
uni.showToast({
|
||||
title: '小程序平台不支持导出日志,建议直接上传至服务器!',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
// #endif
|
||||
|
||||
uni.showLoading({
|
||||
title: '打包中...'
|
||||
})
|
||||
|
||||
try {
|
||||
let devOp = devOptions.getOptions()
|
||||
let waitExportObject = {
|
||||
exportOptions: {
|
||||
version: devOp.version,
|
||||
config: devOp,
|
||||
exportTime: new Date().getTime(),
|
||||
// #ifdef APP-PLUS
|
||||
platform: 'app',
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
platform: 'h5',
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
platform: 'mp',
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
platform: 'wx',
|
||||
// #endif
|
||||
// #ifdef MP-QQ
|
||||
platform: 'qq'
|
||||
// #endif
|
||||
},
|
||||
error: devCache.get('errorReport'),
|
||||
console: devCache.get('console'),
|
||||
network: devCache.get('request'),
|
||||
pageCount: devCache.get('pageCount'),
|
||||
dayOnline: devCache.get('dayOnline'),
|
||||
logs: devCache.get('logReport'), // ! 运行日志
|
||||
info: await getRuntimeInfo(), // ! 当前运行的系统信息
|
||||
uniBus: devCache.get('uniBus'),
|
||||
busCount: devCache.get('busCount'),
|
||||
storage: {},
|
||||
sessionStorage: {},
|
||||
cookie: {},
|
||||
...that.getCache()
|
||||
}
|
||||
|
||||
try {
|
||||
if (that.$store.state) {
|
||||
waitExportObject.vuex = that.$store.state
|
||||
}
|
||||
} catch (error) {}
|
||||
try {
|
||||
if (uni.Pinia) {
|
||||
waitExportObject.pinia = uni.Pinia.getActivePinia().state.value
|
||||
} else if (that.$pinia.state.value) {
|
||||
waitExportObject.pinia = that.$pinia.state.value
|
||||
}
|
||||
} catch (error) {}
|
||||
try {
|
||||
if (getApp().globalData) {
|
||||
waitExportObject.globalData = getApp().globalData
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
let data = jsonCompress.safeJsonStringify(waitExportObject)
|
||||
data = JSON.parse(data)
|
||||
data = JSON.stringify(data, null, 2)
|
||||
let t = new Date().getTime()
|
||||
let exportFileName = `export_devtools_log_${t}.json`
|
||||
|
||||
// #ifdef H5
|
||||
const blob = new Blob([data], { type: 'application/json' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.style = 'display: none'
|
||||
a.download = exportFileName
|
||||
a.href = url
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
uni.showToast({
|
||||
title: '导出成功!',
|
||||
icon: 'success'
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
plus.io.resolveLocalFileSystemURL(
|
||||
'_downloads/',
|
||||
(entry) => {
|
||||
entry.getFile(
|
||||
exportFileName,
|
||||
{
|
||||
create: true
|
||||
},
|
||||
(fileEntry) => {
|
||||
fileEntry.createWriter((writer) => {
|
||||
writer.onwrite = (e) => {
|
||||
uni.hideLoading()
|
||||
uni.showModal({
|
||||
title: '导出成功',
|
||||
content: '文件导出成功!已保存至公共下载路径,文件名称:' + exportFileName
|
||||
})
|
||||
}
|
||||
writer.onerror = () => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '日志导出失败!_写入文件失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
writer.write(data)
|
||||
})
|
||||
}
|
||||
)
|
||||
},
|
||||
(err) => {
|
||||
console.log('err', err)
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '文件保存失败!_打开目录失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
)
|
||||
// #endif
|
||||
|
||||
uni.hideLoading()
|
||||
} catch (error) {
|
||||
if (error && error.message) {
|
||||
console.log('导出失败!', error.message)
|
||||
} else {
|
||||
console.log('导出失败!', error)
|
||||
}
|
||||
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '导出失败!',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 获取缓存数据
|
||||
*/
|
||||
getCache() {
|
||||
let data = {
|
||||
storage: {},
|
||||
sessionStorage: {},
|
||||
cookie: {}
|
||||
}
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
let keys = plus.storage.getAllKeys()
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]
|
||||
if (key.indexOf('devTools_') == 0) {
|
||||
// 忽略以 devTools_ 开头的key
|
||||
continue
|
||||
}
|
||||
data.storage[key] = uni.getStorageSync(key)
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
let key = localStorage.key(i)
|
||||
if (key.indexOf('devTools_') == 0) {
|
||||
continue
|
||||
}
|
||||
let value = uni.getStorageSync(key)
|
||||
data.storage[key] = value
|
||||
}
|
||||
|
||||
for (let i = 0; i < sessionStorage.length; i++) {
|
||||
let key = sessionStorage.key(i)
|
||||
if (key.indexOf('devTools_') == 0) {
|
||||
continue
|
||||
}
|
||||
let value = sessionStorage.getItem(key)
|
||||
data.sessionStorage[key] = value
|
||||
}
|
||||
|
||||
document.cookie.split(';').forEach((cookieStr) => {
|
||||
const [name, value] = cookieStr.trim().split('=')
|
||||
data.cookie[name] = value
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
let keyList = devCache.get('storage')
|
||||
if (!keyList) keyList = []
|
||||
for (let i = 0; i < keyList.length; i++) {
|
||||
const key = keyList[i]
|
||||
if (key.indexOf('devTools_') == 0) {
|
||||
continue
|
||||
}
|
||||
let value = uni.getStorageSync(key)
|
||||
if (value) {
|
||||
data.storage[key] = value
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
return data
|
||||
},
|
||||
/**
|
||||
* 跳转指定URL
|
||||
*/
|
||||
goUrl(url) {
|
||||
// #ifdef H5
|
||||
window.open(url)
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
uni.setClipboardData({
|
||||
data: url
|
||||
})
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
plus.runtime.openURL(url)
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.settingView {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 750rpx;
|
||||
.loading {
|
||||
width: 750rpx;
|
||||
height: 300rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.loadingText {
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
}
|
||||
}
|
||||
.divisionLine {
|
||||
width: 750rpx;
|
||||
height: 1px;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.checkboxItem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 10rpx 20rpx;
|
||||
width: 750rpx;
|
||||
align-items: center;
|
||||
&:active {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.name {
|
||||
font-size: 24rpx;
|
||||
margin-left: 5rpx;
|
||||
}
|
||||
.count {
|
||||
font-size: 20rpx;
|
||||
margin-left: 10rpx;
|
||||
color: #ff2d55;
|
||||
}
|
||||
.empty {
|
||||
font-size: 20rpx;
|
||||
margin-left: 10rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
.delBtn {
|
||||
width: 710rpx;
|
||||
margin-left: 20rpx;
|
||||
border-radius: 20rpx;
|
||||
height: 70rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgb(255, 45, 85);
|
||||
margin-top: 20rpx;
|
||||
margin-bottom: 30rpx;
|
||||
&:active {
|
||||
background-color: rgba(255, 45, 85, 0.8);
|
||||
}
|
||||
.delBtnText {
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.objectAnalysisView {
|
||||
width: 710rpx;
|
||||
margin-left: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.about {
|
||||
width: 710rpx;
|
||||
margin-left: 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.row {
|
||||
margin-bottom: 10rpx;
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,319 @@
|
||||
<template>
|
||||
<view class="storageList">
|
||||
<objectAnalysis
|
||||
v-if="isLoaded && !isEmpty"
|
||||
:data="storageData"
|
||||
:isOpenFirst="true"
|
||||
:width="710"
|
||||
:isDiyMenu="true"
|
||||
@diyMenu="diyMenu"
|
||||
/>
|
||||
<view
|
||||
v-if="!isLoaded"
|
||||
class="dataLoading"
|
||||
>
|
||||
<text class="status">加载中</text>
|
||||
</view>
|
||||
<view
|
||||
v-if="isLoaded && isEmpty"
|
||||
class="dataLoading"
|
||||
>
|
||||
<text class="status">无缓存数据</text>
|
||||
</view>
|
||||
<view
|
||||
v-if="isLoaded && !isEmpty"
|
||||
class="moreTools"
|
||||
>
|
||||
<text class="tips">Tips:长按最外层key可复制、编辑或删除缓存</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
// #ifdef MP
|
||||
import devCache from "../../../core/libs/devCache";
|
||||
// #endif
|
||||
import objectAnalysis from "./objectAnalysis.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
objectAnalysis,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 是否完成加载
|
||||
*/
|
||||
isLoaded: false,
|
||||
/**
|
||||
* 缓存里的数据
|
||||
*/
|
||||
storageData: {},
|
||||
/**
|
||||
* 数据是否为空
|
||||
*/
|
||||
isEmpty: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 加载数据
|
||||
*/
|
||||
getData(storageType) {
|
||||
let that = this;
|
||||
that.isLoaded = false;
|
||||
|
||||
let data = {};
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
let keys = plus.storage.getAllKeys();
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
if (key.indexOf("devTools_") == 0) {
|
||||
// 忽略以 devTools_ 开头的key
|
||||
continue;
|
||||
}
|
||||
data[key] = uni.getStorageSync(key);
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
if (storageType == "localStorage") {
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
let key = localStorage.key(i);
|
||||
if (key.indexOf("devTools_") == 0) {
|
||||
continue;
|
||||
}
|
||||
let value = uni.getStorageSync(key);
|
||||
data[key] = value;
|
||||
}
|
||||
} else if (storageType == "sessionStorage") {
|
||||
for (let i = 0; i < sessionStorage.length; i++) {
|
||||
let key = sessionStorage.key(i);
|
||||
if (key.indexOf("devTools_") == 0) {
|
||||
continue;
|
||||
}
|
||||
let value = sessionStorage.getItem(key);
|
||||
data[key] = value;
|
||||
}
|
||||
} else if (storageType == "cookie") {
|
||||
document.cookie.split(";").forEach((cookieStr) => {
|
||||
const [name, value] = cookieStr.trim().split("=");
|
||||
data[name] = value;
|
||||
});
|
||||
}
|
||||
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
let keyList = devCache.get("storage");
|
||||
if (!keyList) keyList = [];
|
||||
for (let i = 0; i < keyList.length; i++) {
|
||||
const key = keyList[i];
|
||||
if (key.indexOf("devTools_") == 0) {
|
||||
continue;
|
||||
}
|
||||
let value = uni.getStorageSync(key);
|
||||
if (value) {
|
||||
data[key] = value;
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
setTimeout(() => {
|
||||
that.storageData = data;
|
||||
if (Object.keys(data).length == 0) {
|
||||
that.isEmpty = true;
|
||||
} else {
|
||||
that.isEmpty = false;
|
||||
}
|
||||
that.isLoaded = true;
|
||||
}, 500);
|
||||
},
|
||||
/**
|
||||
* 自定义长按事件
|
||||
*/
|
||||
diyMenu({ item, index }) {
|
||||
let that = this;
|
||||
|
||||
let menu = [
|
||||
{
|
||||
text: `复制键(key)`,
|
||||
click() {
|
||||
uni.setClipboardData({
|
||||
data: item.k,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
text: `复制值(value)`,
|
||||
click() {
|
||||
uni.setClipboardData({
|
||||
data: item.v,
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
if (item.p == 0) {
|
||||
//点击第一层 key
|
||||
menu.unshift({
|
||||
text: "删除该键",
|
||||
click() {
|
||||
// #ifdef H5
|
||||
if (that.storageType == "localStorage") {
|
||||
uni.removeStorageSync(item.k);
|
||||
} else if (that.storageType == "sessionStorage") {
|
||||
sessionStorage.removeItem(item.k);
|
||||
} else if (that.storageType == "cookie") {
|
||||
document.cookie = `${item.k}=;expires=` + new Date(new Date().getTime() + 200).toGMTString() + ";path=/";
|
||||
}
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
uni.removeStorageSync(item.k);
|
||||
// #endif
|
||||
uni.showToast({
|
||||
title: "删除成功!",
|
||||
icon: "success",
|
||||
});
|
||||
if (that.storageType == "cookie") {
|
||||
// cookie删除后需要等待一秒后生效
|
||||
setTimeout(() => {
|
||||
that.getData();
|
||||
}, 1500);
|
||||
} else {
|
||||
that.getData();
|
||||
}
|
||||
},
|
||||
});
|
||||
menu.unshift({
|
||||
text: "编辑值",
|
||||
click() {
|
||||
let key = item.k;
|
||||
let value = "";
|
||||
if (that.storageType == "sessionStorage") {
|
||||
value = sessionStorage.getItem(key);
|
||||
} else if (that.storageType == "cookie") {
|
||||
document.cookie.split(";").forEach((cookieStr) => {
|
||||
const [name, v] = cookieStr.trim().split("=");
|
||||
if (name == key) {
|
||||
value = v;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
value = uni.getStorageSync(key);
|
||||
}
|
||||
if (typeof value == "object") {
|
||||
value = JSON.stringify(value);
|
||||
} else if (value === false) {
|
||||
value = "false";
|
||||
} else if (value === true) {
|
||||
value = "true";
|
||||
} else if (!value) {
|
||||
value = "";
|
||||
}
|
||||
uni.$emit("devTools_showEditDialog", {
|
||||
title: `key:${key}`,
|
||||
value,
|
||||
});
|
||||
|
||||
uni.$on("devTools_editDialogClose", () => {
|
||||
uni.$off("devTools_editDialogSaveSuccess");
|
||||
uni.$off("devTools_editDialogClose");
|
||||
});
|
||||
|
||||
uni.$on("devTools_editDialogSaveSuccess", (val) => {
|
||||
uni.$off("devTools_editDialogSaveSuccess");
|
||||
uni.$off("devTools_editDialogClose");
|
||||
let oldValue = uni.getStorageSync(key);
|
||||
if (oldValue === false || oldValue === true) {
|
||||
if (val == "true" || val == "false") {
|
||||
val = val == "true";
|
||||
}
|
||||
}
|
||||
// #ifdef H5
|
||||
if (that.storageType == "localStorage") {
|
||||
uni.setStorageSync(key, val);
|
||||
} else if (that.storageType == "sessionStorage") {
|
||||
sessionStorage.setItem(key, val);
|
||||
} else if (that.storageType == "cookie") {
|
||||
key = encodeURIComponent(key);
|
||||
val = encodeURIComponent(val);
|
||||
let cookie =
|
||||
`${key}=${val}; path=/; expires=` + new Date(new Date().getTime() + 86400 * 1000 * 365).toGMTString();
|
||||
document.cookie = cookie;
|
||||
}
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
uni.setStorageSync(key, val);
|
||||
// #endif
|
||||
uni.showToast({
|
||||
icon: "success",
|
||||
title: "保存成功",
|
||||
});
|
||||
setTimeout(() => {
|
||||
that.getData();
|
||||
}, 300);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
uni.showActionSheet({
|
||||
itemList: menu.map((x) => x.text),
|
||||
success({ tapIndex }) {
|
||||
menu[tapIndex].click();
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.storageList {
|
||||
padding: 20rpx;
|
||||
width: 750rpx;
|
||||
}
|
||||
.dataLoading {
|
||||
width: 750rpx;
|
||||
height: 400rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.status {
|
||||
font-size: 20rpx;
|
||||
color: #888;
|
||||
line-height: 20rpx;
|
||||
}
|
||||
}
|
||||
.moreTools {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.tips {
|
||||
font-size: 20rpx;
|
||||
color: #888;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
.delBtn {
|
||||
all: initial;
|
||||
margin-top: 50rpx;
|
||||
border-radius: 8rpx;
|
||||
padding: 10rpx;
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 375rpx;
|
||||
|
||||
&:active {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.delBtnText {
|
||||
font-size: 20rpx;
|
||||
line-height: 20rpx;
|
||||
color: #f37b1d;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,304 @@
|
||||
<template>
|
||||
<view>
|
||||
<subTitleBar
|
||||
:isOpen="isShowSetting"
|
||||
@click="changeStatus('isShowSetting')"
|
||||
title="DevTools扩展配置项"
|
||||
/>
|
||||
<template v-if="isShowSetting">
|
||||
<!-- #ifdef APP-PLUS || H5 -->
|
||||
<view class="settingItem">
|
||||
<view class="settingHead">
|
||||
<text class="settingTitle">页面自动注入Eruda调试工具</text>
|
||||
<text class="settingSubtitle">(强大的节点选择等工具;重启APP后生效)</text>
|
||||
</view>
|
||||
<switch
|
||||
:checked="isInjectEruda"
|
||||
@change="switchChange($event, 'isInjectEruda')"
|
||||
color="#ff2d55"
|
||||
/>
|
||||
</view>
|
||||
<view class="settingItem">
|
||||
<view class="settingHead">
|
||||
<text class="settingTitle">页面自动注入vConsole调试工具</text>
|
||||
<text class="settingSubtitle">(腾讯开源的h5调试工具;重启APP后生效)</text>
|
||||
</view>
|
||||
<switch
|
||||
:checked="isInjectVConsole"
|
||||
@change="switchChange($event, 'isInjectVConsole')"
|
||||
color="#ff2d55"
|
||||
/>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view class="settingItem">
|
||||
<view class="settingHead">
|
||||
<text class="settingTitle">小程序VConsole开关</text>
|
||||
<text class="settingSubtitle">设置是否打开调试开关。此开关对正式版也能生效。</text>
|
||||
</view>
|
||||
<switch
|
||||
:checked="isOpenMpDevTag"
|
||||
@change="switchChange($event, 'isOpenMpDevTag')"
|
||||
color="#ff2d55"
|
||||
/>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
<subTitleBar
|
||||
:isOpen="isShowBtnPanel"
|
||||
@click="changeStatus('isShowBtnPanel')"
|
||||
title="常用工具"
|
||||
/>
|
||||
<view
|
||||
v-if="isShowBtnPanel"
|
||||
class="btnPanel"
|
||||
>
|
||||
<!-- #ifdef APP-PLUS || H5 -->
|
||||
<view
|
||||
class="btnItem btn-def"
|
||||
@click="restart"
|
||||
>
|
||||
<text class="btnText">重启APP</text>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
<view
|
||||
class="btnItem btn-def"
|
||||
@click="goPage"
|
||||
>
|
||||
<text class="btnText">跳转指定页面</text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="btnItem btn-def"
|
||||
@click="$emit('goOpenRequest')"
|
||||
>
|
||||
<text class="btnText">发起网络请求</text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="btnItem btn-def"
|
||||
@click="delLocalStorage"
|
||||
>
|
||||
<text class="btnText">清空localStorage缓存</text>
|
||||
</view>
|
||||
</view>
|
||||
<subTitleBar
|
||||
:isOpen="isShowDiyTools"
|
||||
@click="changeStatus('isShowDiyTools')"
|
||||
title="自定义Tools"
|
||||
/>
|
||||
<tools
|
||||
v-if="isShowDiyTools"
|
||||
ref="tools"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import tools from "../../../tools.vue";
|
||||
import subTitleBar from "../ui/subTitleBar.vue";
|
||||
export default {
|
||||
components: {
|
||||
tools,
|
||||
subTitleBar,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 是否显示系统工具栏
|
||||
*/
|
||||
isShowSetting: false,
|
||||
/**
|
||||
* 是否自动注入Eruda
|
||||
*/
|
||||
isInjectEruda: uni.getStorageSync("devTools_isInjectEruda") == "yes",
|
||||
/**
|
||||
* 是否自动注入vConsole
|
||||
*/
|
||||
isInjectVConsole: uni.getStorageSync("devTools_isInjectVConsole") == "yes",
|
||||
/**
|
||||
* 是否显示 用户自定义tools
|
||||
*/
|
||||
isShowDiyTools: true,
|
||||
/**
|
||||
* 是否打开小程序调试工具
|
||||
*/
|
||||
isOpenMpDevTag: uni.getStorageSync("devTools_isOpenMpDevTag") == "yes",
|
||||
/**
|
||||
* 常用工具栏开关
|
||||
*/
|
||||
isShowBtnPanel: true,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 更改选中状态
|
||||
*/
|
||||
changeStatus(key) {
|
||||
this[key] = !this[key];
|
||||
},
|
||||
/**
|
||||
* 开关选择器改变事件
|
||||
*/
|
||||
switchChange(e, key) {
|
||||
let status = e.detail.value;
|
||||
this[key] = status;
|
||||
uni.setStorageSync("devTools_" + key, status ? "yes" : "no");
|
||||
|
||||
if (key == "isOpenMpDevTag") {
|
||||
wx.setEnableDebug({
|
||||
enableDebug: status,
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 重启APP
|
||||
*/
|
||||
restart() {
|
||||
// #ifdef APP-PLUS
|
||||
plus.runtime.restart();
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
location.href = "/";
|
||||
// #endif
|
||||
},
|
||||
/**
|
||||
* 跳转指定页面
|
||||
*/
|
||||
goPage() {
|
||||
uni.$emit("devTools_showRouteDialog");
|
||||
},
|
||||
/**
|
||||
* 清空LocalStorage
|
||||
*/
|
||||
delLocalStorage() {
|
||||
uni.showModal({
|
||||
title: "操作确认",
|
||||
content: "是否确认清空LocalStorage缓存?",
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
// #ifdef APP-PLUS
|
||||
let keys = plus.storage.getAllKeys();
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = String(keys[i]);
|
||||
if (key.indexOf("devTools_") == 0) {
|
||||
continue;
|
||||
}
|
||||
uni.removeStorageSync(key);
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
let key = String(localStorage.key(i));
|
||||
if (key.indexOf("devTools_") == 0) {
|
||||
continue;
|
||||
}
|
||||
setTimeout(() => {
|
||||
localStorage.removeItem(key);
|
||||
}, i * 2 + 1);
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
let keyList = devCache.get("storage");
|
||||
if (!keyList) keyList = [];
|
||||
for (let i = 0; i < keyList.length; i++) {
|
||||
const key = keyList[i];
|
||||
if (key.indexOf("devTools_") == 0) {
|
||||
continue;
|
||||
}
|
||||
uni.removeStorageSync(key);
|
||||
}
|
||||
// #endif
|
||||
uni.showToast({
|
||||
icon: "success",
|
||||
title: "清空成功!",
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.padding-sm {
|
||||
padding: 20rpx;
|
||||
}
|
||||
.settingItem:active {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.settingItem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 750rpx;
|
||||
padding: 15rpx 0;
|
||||
.settingHead {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 20rpx;
|
||||
.settingTitle {
|
||||
font-size: 24rpx;
|
||||
line-height: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
.settingSubtitle {
|
||||
margin-top: 4rpx;
|
||||
font-size: 20rpx;
|
||||
line-height: 26rpx;
|
||||
color: #777;
|
||||
}
|
||||
}
|
||||
}
|
||||
.btnPanel {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
padding-left: 20rpx;
|
||||
padding-right: 20rpx;
|
||||
padding-top: 20rpx;
|
||||
|
||||
.btnItem {
|
||||
margin-right: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
min-width: 120rpx;
|
||||
height: 60rpx;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 15rpx;
|
||||
.btnText {
|
||||
font-size: 20rpx;
|
||||
line-height: 30rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
.btn-red:active {
|
||||
background-color: rgba(255, 45, 85, 0.5);
|
||||
}
|
||||
.btn-red {
|
||||
border: 1px solid rgba(255, 45, 85, 1);
|
||||
background-color: rgba(255, 45, 85, 1);
|
||||
.btnText {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.btn-def:active {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.btn-def {
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
.btnText {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<view class="storageList">
|
||||
<objectAnalysis
|
||||
v-if="isLoaded"
|
||||
:data="storageData"
|
||||
:isOpenFirst="true"
|
||||
:width="710"
|
||||
:isDiyMenu="true"
|
||||
@diyMenu="diyMenu"
|
||||
ref="objectAnalysis"
|
||||
/>
|
||||
<view v-else class="dataLoading">
|
||||
<text class="status">加载中</text>
|
||||
</view>
|
||||
|
||||
<text v-if="isLoaded" class="tipsText"> 长按非对象类型的数据可编辑 </text>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import objectAnalysis from './objectAnalysis.vue'
|
||||
export default {
|
||||
components: {
|
||||
objectAnalysis
|
||||
},
|
||||
props: {
|
||||
/**
|
||||
* 全局变量类型
|
||||
*/
|
||||
stateType: {
|
||||
type: String,
|
||||
default: 'vuex'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 是否完成加载
|
||||
*/
|
||||
isLoaded: false,
|
||||
/**
|
||||
* 缓存里的数据
|
||||
*/
|
||||
storageData: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 加载数据
|
||||
*/
|
||||
getData() {
|
||||
let that = this
|
||||
let data = {}
|
||||
if (that.stateType == 'vuex') {
|
||||
try {
|
||||
data = JSON.parse(JSON.stringify(that.$store.state))
|
||||
} catch (error) {}
|
||||
} else if (that.stateType == 'pinia') {
|
||||
try {
|
||||
if (uni.Pinia) {
|
||||
data = JSON.parse(JSON.stringify(uni.Pinia.getActivePinia().state.value))
|
||||
} else if (that.$pinia) {
|
||||
data = JSON.parse(JSON.stringify(that.$pinia.state.value))
|
||||
}
|
||||
} catch (error) {}
|
||||
} else if (that.stateType == 'globalData') {
|
||||
try {
|
||||
data = JSON.parse(JSON.stringify(getApp().globalData))
|
||||
} catch (error) {}
|
||||
}
|
||||
that.isLoaded = false
|
||||
setTimeout(() => {
|
||||
that.storageData = data
|
||||
that.isLoaded = true
|
||||
}, 500)
|
||||
},
|
||||
/**
|
||||
* 自定义长按事件
|
||||
*/
|
||||
diyMenu({ item, index }) {
|
||||
let that = this
|
||||
|
||||
let menu = [
|
||||
{
|
||||
text: `复制键(key)`,
|
||||
click() {
|
||||
uni.setClipboardData({
|
||||
data: that.toString(item.k)
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
text: `复制值(value)`,
|
||||
click() {
|
||||
uni.setClipboardData({
|
||||
data: that.toString(item.v)
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
if (typeof item.v != 'object') {
|
||||
menu.push({
|
||||
text: '编辑值',
|
||||
click() {
|
||||
let keyList = that.$refs.objectAnalysis.getFaKeyList(item.i)
|
||||
let title = ''
|
||||
if (keyList.length == 0) {
|
||||
title = 'key'
|
||||
} else {
|
||||
keyList.map((x) => {
|
||||
title = (title == '' ? '' : title + '.') + x
|
||||
})
|
||||
}
|
||||
|
||||
let isBool = typeof item.v == 'boolean'
|
||||
if (isBool) {
|
||||
item.v = item.v ? 'true' : 'false'
|
||||
}
|
||||
if (item.v === undefined || item.v === null) {
|
||||
item.v = ''
|
||||
}
|
||||
|
||||
uni.$emit('devTools_showEditDialog', {
|
||||
title,
|
||||
value: item.v
|
||||
})
|
||||
|
||||
uni.$on('devTools_editDialogClose', () => {
|
||||
uni.$off('devTools_editDialogSaveSuccess')
|
||||
uni.$off('devTools_editDialogClose')
|
||||
})
|
||||
|
||||
uni.$on('devTools_editDialogSaveSuccess', (val) => {
|
||||
uni.$off('devTools_editDialogSaveSuccess')
|
||||
uni.$off('devTools_editDialogClose')
|
||||
if (isBool && (val == 'true' || val == 'false')) {
|
||||
val = val == 'true'
|
||||
}
|
||||
let data
|
||||
if (that.stateType == 'vuex') {
|
||||
data = that.$store.state
|
||||
} else if (that.stateType == 'pinia') {
|
||||
if (uni.Pinia) {
|
||||
data = uni.Pinia.getActivePinia().state.value
|
||||
} else if (that.$pinia) {
|
||||
data = that.$pinia.state.value
|
||||
}
|
||||
} else if (that.stateType == 'globalData') {
|
||||
data = getApp().globalData
|
||||
}
|
||||
let lastKey = keyList.pop()
|
||||
keyList.map((x) => {
|
||||
data = data[x]
|
||||
})
|
||||
that.$set(data, lastKey, val)
|
||||
that.getData()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
uni.showActionSheet({
|
||||
itemList: menu.map((x) => x.text),
|
||||
success({ tapIndex }) {
|
||||
menu[tapIndex].click()
|
||||
}
|
||||
})
|
||||
},
|
||||
getFaKeyList() {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.storageList {
|
||||
padding: 20rpx;
|
||||
width: 750rpx;
|
||||
}
|
||||
.dataLoading {
|
||||
width: 750rpx;
|
||||
height: 400rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.status {
|
||||
font-size: 20rpx;
|
||||
color: #888;
|
||||
line-height: 20rpx;
|
||||
}
|
||||
}
|
||||
.tipsText {
|
||||
font-size: 20rpx;
|
||||
color: #8799a3;
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
</style>
|
||||
1838
uni_modules/UniDevTools/src/devTools/page/components/main.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
// #ifdef APP-PLUS
|
||||
const animation = weex.requireModule('animation')
|
||||
// #endif
|
||||
export default {
|
||||
methods: {
|
||||
/**
|
||||
* 显示面板
|
||||
*/
|
||||
panelShow() {
|
||||
let that = this;
|
||||
|
||||
let sys = uni.getSystemInfoSync();
|
||||
|
||||
animation.transition(
|
||||
that.$refs.mask,
|
||||
{
|
||||
styles: {
|
||||
opacity: 1,
|
||||
height: sys.windowHeight + 'px'
|
||||
},
|
||||
duration: 200, //ms
|
||||
timingFunction: 'linear',
|
||||
delay: 0 //ms
|
||||
}
|
||||
)
|
||||
|
||||
let height = Math.ceil(sys.windowHeight * 0.8);
|
||||
|
||||
animation.transition(
|
||||
that.$refs.panel,
|
||||
{
|
||||
styles: {
|
||||
opacity: 1,
|
||||
transform: `transform: translate(0px,${height}px)`
|
||||
},
|
||||
duration: 1, //ms
|
||||
timingFunction: 'linear',
|
||||
delay: 0 //ms
|
||||
},
|
||||
(res) => {
|
||||
|
||||
animation.transition(
|
||||
that.$refs.panel,
|
||||
{
|
||||
styles: {
|
||||
transform: `transform: translate(0px,0px)`
|
||||
},
|
||||
duration: 200, //ms
|
||||
timingFunction: 'linear',
|
||||
delay: 0 //ms
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
)
|
||||
},
|
||||
/**
|
||||
* 关闭面板
|
||||
*/
|
||||
panelHide() {
|
||||
let that = this;
|
||||
|
||||
animation.transition(
|
||||
that.$refs.mask,
|
||||
{
|
||||
styles: {
|
||||
opacity: 0,
|
||||
},
|
||||
duration: 200, //ms
|
||||
timingFunction: 'linear',
|
||||
delay: 0 //ms
|
||||
}
|
||||
)
|
||||
let height = uni.upx2px(1000);
|
||||
animation.transition(
|
||||
that.$refs.panel,
|
||||
{
|
||||
styles: {
|
||||
transform: `transform: translate(0px,${height}px)`
|
||||
},
|
||||
duration: 200, //ms
|
||||
timingFunction: 'linear',
|
||||
delay: 0 //ms
|
||||
},
|
||||
() => {
|
||||
uni.$emit("devTools_panelHideSuccess")
|
||||
}
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
windowInfo: getWindowInfo(),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* 小程序和H5的标题
|
||||
*/
|
||||
navbarStyle() {
|
||||
let windowInfo = getWindowInfo();
|
||||
return {
|
||||
statusBarHeightPx: windowInfo.system.statusBarHeight + 'px',
|
||||
navbarHeightPx: windowInfo.navbar.bodyHeightPx + 'px',
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
back() {
|
||||
uni.navigateBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前窗口数据
|
||||
*
|
||||
*/
|
||||
function getWindowInfo() {
|
||||
let systemInfo = uni.getSystemInfoSync();
|
||||
systemInfo.pixelRatio = Math.round(systemInfo.pixelRatio * 100) / 100;
|
||||
|
||||
let windowInfo = {
|
||||
system: systemInfo,
|
||||
capsule: {
|
||||
bottom: 78,
|
||||
height: 30,
|
||||
left: 283,
|
||||
right: 363,
|
||||
top: 48,
|
||||
width: 0,
|
||||
},
|
||||
navbar: {
|
||||
heightPx: uni.upx2px(100) + systemInfo.statusBarHeight,
|
||||
bodyHeightPx: uni.upx2px(100),
|
||||
bodyWidthPx: systemInfo.windowWidth,
|
||||
capsuleWidthPx: 0,
|
||||
capsuleRightPx: 0,
|
||||
},
|
||||
|
||||
height: systemInfo.windowHeight * (750 / systemInfo.windowWidth),
|
||||
width: 750,
|
||||
statusBarHeight: systemInfo.statusBarHeight * (750 / systemInfo.windowWidth),
|
||||
safeBottom: systemInfo.windowHeight - systemInfo.safeArea.bottom,
|
||||
safeBottomRpx: (systemInfo.windowHeight - systemInfo.safeArea.bottom) * (750 / systemInfo.windowWidth),
|
||||
/**
|
||||
* 原生端 底部导航栏高度
|
||||
*/
|
||||
footNavbarHeight: uni.upx2px(100) + (systemInfo.windowHeight - systemInfo.safeArea.bottom),
|
||||
}
|
||||
|
||||
// #ifdef MP-QQ || MP-WEIXIN
|
||||
let capsuleInfo = uni.getMenuButtonBoundingClientRect();
|
||||
windowInfo.capsule = capsuleInfo;
|
||||
let capsuleToStatusBarPx = capsuleInfo.top - systemInfo.statusBarHeight; //胶囊和状态栏之间的高度
|
||||
windowInfo.navbar.bodyHeightPx = capsuleInfo.height + (capsuleToStatusBarPx * 2);
|
||||
windowInfo.navbar.heightPx = windowInfo.navbar.bodyHeightPx + systemInfo.statusBarHeight;
|
||||
let capsuleWidthPx = (systemInfo.windowWidth - capsuleInfo.right) * 2 + capsuleInfo.width;
|
||||
windowInfo.navbar.bodyWidthPx = systemInfo.windowWidth - capsuleWidthPx;
|
||||
windowInfo.navbar.capsuleWidthPx = capsuleWidthPx;
|
||||
windowInfo.navbar.capsuleRightPx = capsuleWidthPx - (systemInfo.windowWidth - capsuleInfo.right);
|
||||
// #endif
|
||||
|
||||
return windowInfo;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<view
|
||||
class="btnTabs"
|
||||
v-if="list.length > 0"
|
||||
>
|
||||
<block v-for="(item, index) in list" :key="item.title">
|
||||
<view
|
||||
class="btnTabsItem"
|
||||
:style="{
|
||||
'background-color': '#f9f9f9',
|
||||
}"
|
||||
@click="$emit('indexChange', index)"
|
||||
>
|
||||
<text
|
||||
class="tabsText"
|
||||
:style="{
|
||||
color: index == value ? '#ff2d55' : '#333333',
|
||||
}"
|
||||
>
|
||||
{{ item.title }}
|
||||
</text>
|
||||
</view>
|
||||
<view
|
||||
v-if="index != list.length - 1"
|
||||
:key="index"
|
||||
class="splitLine"
|
||||
></view>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
/**
|
||||
* 按钮列表
|
||||
*/
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
/**
|
||||
* 当前选中的按钮索引
|
||||
*/
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.btnTabs {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-radius: 8rpx;
|
||||
overflow: hidden;
|
||||
height: 40rpx;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
.btnTabsItem {
|
||||
display: flex;
|
||||
height: 40rpx;
|
||||
padding: 0 8rpx;
|
||||
.tabsText {
|
||||
font-size: 20rpx;
|
||||
line-height: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
}
|
||||
.splitLine {
|
||||
width: 1px;
|
||||
height: 40rpx;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<view
|
||||
class="codeHisPicker"
|
||||
v-if="isShow"
|
||||
@click="close"
|
||||
:style="{
|
||||
height: height + 'px',
|
||||
}"
|
||||
>
|
||||
<view
|
||||
class="codeList"
|
||||
@click.stop
|
||||
>
|
||||
<view class="head">
|
||||
<view class="title">
|
||||
<text class="titleText">历史运行代码:</text>
|
||||
</view>
|
||||
<view class="subTitle">
|
||||
<text class="subTitleText">(保留100条运行记录)</text>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="codeScroll"
|
||||
>
|
||||
<view
|
||||
class="hisItem"
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
@click="selectedRow(item)"
|
||||
>
|
||||
<text class="hisItemCode">
|
||||
{{ item }}
|
||||
</text>
|
||||
</view>
|
||||
<view style="height: 100rpx"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 是否展示
|
||||
*/
|
||||
isShow: false,
|
||||
/**
|
||||
* 筛选的列表
|
||||
*/
|
||||
list: [],
|
||||
/**
|
||||
* 默认选中的索引
|
||||
*/
|
||||
index: 0,
|
||||
/**
|
||||
* 选中的回调事件
|
||||
*/
|
||||
callback: null,
|
||||
/**
|
||||
* 屏幕高度
|
||||
*/
|
||||
height: uni.getSystemInfoSync().screenHeight,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 展示弹窗
|
||||
*/
|
||||
show(list = []) {
|
||||
let that = this;
|
||||
that.index = 0;
|
||||
that.list = list;
|
||||
that.isShow = true;
|
||||
return new Promise((yes) => {
|
||||
that.callback = yes;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 选择改变事件
|
||||
*/
|
||||
pickerChange(e) {
|
||||
that.callback = "";
|
||||
console.log("e", e);
|
||||
},
|
||||
/**
|
||||
* 关闭弹窗
|
||||
*/
|
||||
close() {
|
||||
this.isShow = false;
|
||||
},
|
||||
/**
|
||||
* 选择单行代码
|
||||
*/
|
||||
selectedRow(row) {
|
||||
this.callback(row);
|
||||
this.close();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.codeHisPicker {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 750rpx;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
/* #ifndef APP-PLUS */
|
||||
backdrop-filter: blur(1px);
|
||||
/* #endif */
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
z-index: 999;
|
||||
.codeList {
|
||||
width: 750rpx;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
background-color: #fff;
|
||||
.head {
|
||||
padding: 20rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.title {
|
||||
.titleText {
|
||||
font-size: 24rpx;
|
||||
line-height: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
.subTitle {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
.subTitleText {
|
||||
font-size: 20rpx;
|
||||
line-height: 28rpx;
|
||||
color: #777;
|
||||
}
|
||||
}
|
||||
}
|
||||
.codeScroll {
|
||||
height: 750rpx;
|
||||
width: 750rpx;
|
||||
.hisItem {
|
||||
width: 750rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
.hisItemCode {
|
||||
font-size: 20rpx;
|
||||
color: #333;
|
||||
line-height: 26rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
/* #ifdef H5 */
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
/* #endif */
|
||||
lines: 3;
|
||||
}
|
||||
}
|
||||
.hisItem:active {
|
||||
background-color: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div><slot></slot></div>
|
||||
</template>
|
||||
<script>
|
||||
export default {};
|
||||
</script>
|
||||
<style></style>
|
||||
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<swiper
|
||||
:current="tabIndex"
|
||||
:style="{
|
||||
height: scrollHeight + 'px',
|
||||
}"
|
||||
@change="$emit('tabIndexChange', $event.detail.current)"
|
||||
>
|
||||
<swiper-item
|
||||
v-for="(item, index) in tabList"
|
||||
:key="index + 'tabList'"
|
||||
>
|
||||
<list
|
||||
:style="{
|
||||
height: scrollHeight + 'px',
|
||||
}"
|
||||
class="contentList"
|
||||
show-scrollbar
|
||||
:index="index"
|
||||
:id="`contentList_${index}`"
|
||||
:fixFreezing="true"
|
||||
ref="mob_list"
|
||||
>
|
||||
<refresh
|
||||
v-if="item.canRefreshing"
|
||||
class="refreshView"
|
||||
@refresh="$emit('refresh', index)"
|
||||
@pullingdown="$emit('pullingdown', { event: $event, index })"
|
||||
:display="item.isRefreshing ? 'show' : 'hide'"
|
||||
>
|
||||
<view class="content">
|
||||
<template v-if="item.refreshType == 'waitPullUp'">
|
||||
<text class="statusText">↓下拉刷新</text>
|
||||
</template>
|
||||
<template v-if="item.refreshType == 'waitRelease'">
|
||||
<text class="statusText">松手刷新</text>
|
||||
</template>
|
||||
<template v-if="item.refreshType == 'refreshing'">
|
||||
<text class="statusText">刷新中...</text>
|
||||
</template>
|
||||
<template v-if="item.refreshType == 'success'">
|
||||
<text class="statusText">刷新成功</text>
|
||||
</template>
|
||||
<template v-if="item.refreshType == 'error'">
|
||||
<text class="statusText">刷新失败</text>
|
||||
</template>
|
||||
</view>
|
||||
</refresh>
|
||||
<slot
|
||||
:item="item"
|
||||
:index="index"
|
||||
></slot>
|
||||
<cell ref="mob_list_end">
|
||||
<view></view>
|
||||
</cell>
|
||||
</list>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef APP-PLUS -->
|
||||
<scroll-view
|
||||
scroll-y
|
||||
:style="{
|
||||
height: scrollHeight + 'px',
|
||||
}"
|
||||
:scroll-top="scrollTop"
|
||||
>
|
||||
<slot
|
||||
:item="tabList[tabIndex]"
|
||||
:index="tabIndex"
|
||||
></slot>
|
||||
</scroll-view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
/**
|
||||
* 分类索引
|
||||
*/
|
||||
tabIndex: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
/**
|
||||
* tab列表
|
||||
*/
|
||||
tabList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
/**
|
||||
* 滚动高度
|
||||
*/
|
||||
scrollHeight: {
|
||||
type: Number,
|
||||
default: 1000,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 滚动位置
|
||||
*/
|
||||
scrollTop: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 滚动到列表底部
|
||||
*/
|
||||
scrollToBottom() {
|
||||
let that = this;
|
||||
// #ifdef APP-PLUS
|
||||
const dom = weex.requireModule("dom");
|
||||
dom.scrollToElement(that.$refs.mob_list_end[that.tabIndex]);
|
||||
// #endif
|
||||
// #ifndef APP-PLUS
|
||||
that.scrollTop = 999999999 + Math.random();
|
||||
// #endif
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.contentList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 750rpx;
|
||||
.cell {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
margin-top: 20rpx;
|
||||
width: 750rpx;
|
||||
}
|
||||
}
|
||||
.refreshView {
|
||||
background-color: #fff;
|
||||
width: 750rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
height: 100rpx;
|
||||
.content {
|
||||
height: 100rpx;
|
||||
width: 750rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
.statusText {
|
||||
color: #8799a3;
|
||||
font-size: 24rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<view
|
||||
class="subTitleBar"
|
||||
@click.stop="click"
|
||||
>
|
||||
<view class="left">
|
||||
<view class="titleLine"></view>
|
||||
<text class="titleText">{{ title }}</text>
|
||||
</view>
|
||||
<view
|
||||
v-if="showArrow"
|
||||
class="right"
|
||||
>
|
||||
<image
|
||||
v-if="isOpen"
|
||||
src="@/devTools/page/static/fold.png"
|
||||
class="arrow"
|
||||
/>
|
||||
<image
|
||||
v-else
|
||||
src="@/devTools/page/static/unfold.png"
|
||||
class="arrow"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
emits: ['click'],
|
||||
props: {
|
||||
/**
|
||||
* 标题名称
|
||||
*/
|
||||
title: {
|
||||
type: String,
|
||||
default: "标题",
|
||||
},
|
||||
/**
|
||||
* 是否显示右侧箭头
|
||||
*/
|
||||
showArrow: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
/**
|
||||
* 是否为开启状态
|
||||
*/
|
||||
isOpen: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
click(){
|
||||
this.$emit('click');
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.subTitleBar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 0;
|
||||
width: 750rpx;
|
||||
&:active {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-left: 20rpx;
|
||||
.titleLine {
|
||||
width: 4rpx;
|
||||
height: 24rpx;
|
||||
border-radius: 4px;
|
||||
background-color: #ff2d55;
|
||||
}
|
||||
.titleText {
|
||||
color: #333;
|
||||
margin-left: 10rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
margin-right: 20rpx;
|
||||
.arrow {
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
77
uni_modules/UniDevTools/src/devTools/page/index.nvue
Normal file
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<view class="nvue">
|
||||
<mainView ref="mainView" />
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import mainView from "./components/main.vue";
|
||||
export default {
|
||||
components: {
|
||||
mainView,
|
||||
},
|
||||
onLoad(options) {
|
||||
let that = this;
|
||||
that.$nextTick(() => {
|
||||
that.$refs.mainView.pageOnLoad(options);
|
||||
});
|
||||
},
|
||||
onBackPress(e) {
|
||||
if (e.from == "navigateBack") {
|
||||
return false;
|
||||
}
|
||||
this.$refs.mainView.hide();
|
||||
return true;
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.nvue {
|
||||
width: 750rpx;
|
||||
/* #ifndef APP-PLUS */
|
||||
height: 100vh;
|
||||
/* #endif */
|
||||
/* #ifdef APP-PLUS */
|
||||
flex: 1;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
/* #ifdef H5 */
|
||||
@media only screen and (pointer: fine) {
|
||||
.showScrollbars {
|
||||
::-webkit-scrollbar-thumb:horizontal {
|
||||
/*水平滚动条的样式*/
|
||||
width: 4px;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
-webkit-border-radius: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track-piece {
|
||||
background-color: #fff;
|
||||
/*滚动条的背景颜色*/
|
||||
-webkit-border-radius: 0;
|
||||
/*滚动条的圆角宽度*/
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
/*滚动条的宽度*/
|
||||
height: 5px;
|
||||
/*滚动条的高度*/
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:vertical {
|
||||
display: none;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
/*滚动条的hover样式*/
|
||||
height: 50px;
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
-webkit-border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
</style>
|
||||
BIN
uni_modules/UniDevTools/src/devTools/page/static/copy.png
Normal file
|
After Width: | Height: | Size: 772 B |
BIN
uni_modules/UniDevTools/src/devTools/page/static/delete.png
Normal file
|
After Width: | Height: | Size: 761 B |
BIN
uni_modules/UniDevTools/src/devTools/page/static/fileSys/AI.png
Normal file
|
After Width: | Height: | Size: 596 B |
BIN
uni_modules/UniDevTools/src/devTools/page/static/fileSys/DWG.png
Normal file
|
After Width: | Height: | Size: 841 B |
BIN
uni_modules/UniDevTools/src/devTools/page/static/fileSys/EXE.png
Normal file
|
After Width: | Height: | Size: 664 B |
BIN
uni_modules/UniDevTools/src/devTools/page/static/fileSys/GIF.png
Normal file
|
After Width: | Height: | Size: 595 B |
|
After Width: | Height: | Size: 616 B |
BIN
uni_modules/UniDevTools/src/devTools/page/static/fileSys/PSD.png
Normal file
|
After Width: | Height: | Size: 676 B |
BIN
uni_modules/UniDevTools/src/devTools/page/static/fileSys/RVT.png
Normal file
|
After Width: | Height: | Size: 693 B |
BIN
uni_modules/UniDevTools/src/devTools/page/static/fileSys/SKP.png
Normal file
|
After Width: | Height: | Size: 762 B |
BIN
uni_modules/UniDevTools/src/devTools/page/static/fileSys/SVG.png
Normal file
|
After Width: | Height: | Size: 877 B |
|
After Width: | Height: | Size: 637 B |
BIN
uni_modules/UniDevTools/src/devTools/page/static/fileSys/pdf.png
Normal file
|
After Width: | Height: | Size: 641 B |
|
After Width: | Height: | Size: 503 B |
|
After Width: | Height: | Size: 444 B |
|
After Width: | Height: | Size: 532 B |
BIN
uni_modules/UniDevTools/src/devTools/page/static/fileSys/txt.png
Normal file
|
After Width: | Height: | Size: 404 B |
|
After Width: | Height: | Size: 538 B |
|
After Width: | Height: | Size: 255 B |
|
After Width: | Height: | Size: 740 B |
|
After Width: | Height: | Size: 487 B |
|
After Width: | Height: | Size: 626 B |
BIN
uni_modules/UniDevTools/src/devTools/page/static/fold.png
Normal file
|
After Width: | Height: | Size: 543 B |
BIN
uni_modules/UniDevTools/src/devTools/page/static/refresh.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
uni_modules/UniDevTools/src/devTools/page/static/unfold.png
Normal file
|
After Width: | Height: | Size: 561 B |
25
uni_modules/UniDevTools/src/devTools/tools.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<view class="tools">
|
||||
<!-- 在这里可以开发自己的DIY工具 -->
|
||||
<text style="margin-top: 50rpx;color: grey;font-size: 24rpx;">Empty</text>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.tools {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 750rpx;
|
||||
}
|
||||
|
||||
</style>
|
||||
9
uni_modules/UniDevTools/src/env.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/// <reference types="vite/client" />
|
||||
declare module '*.vue' {
|
||||
import { DefineComponent } from 'vue'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
|
||||
|
||||
149
uni_modules/UniDevTools/src/hybrid/html/js/colorview.js
Normal file
@@ -0,0 +1,149 @@
|
||||
// 这是nvue版本的colorview组件专用
|
||||
var _tmColorView_w = 0
|
||||
var _tmColorView_h = 0
|
||||
var dpr = window.devicePixelRatio;
|
||||
window.tmColorView_showdiv = function(w,h){
|
||||
var ele = document.querySelector("#tmColorview");
|
||||
var canvasdom = ele.querySelector("canvas")
|
||||
ele.style.display = "block"
|
||||
ele.style.width = w+'px'
|
||||
ele.style.height = h+'px'
|
||||
canvasdom.width = w
|
||||
canvasdom.height = h
|
||||
canvasdom.style.width = w+'px'
|
||||
canvasdom.style.height = h+'px'
|
||||
_tmColorView_w = w;
|
||||
_tmColorView_h = h;
|
||||
document.all.addEventListener('touchmove', function(evt) {
|
||||
evt.preventDefault();
|
||||
});
|
||||
}
|
||||
window.tmColorView_getCanvas = function(){
|
||||
var ele = document.querySelector("#tmColorview");
|
||||
var canvasdom = ele.querySelector("canvas");
|
||||
var ctx = canvasdom.getContext("2d")
|
||||
return ctx
|
||||
}
|
||||
window.tmColorView_renderColorHu = function(){
|
||||
var ctx = tmColorView_getCanvas();
|
||||
var dy = _tmColorView_h/3;
|
||||
var barcolorWidth = 30
|
||||
var x = 0
|
||||
var gradient = ctx.createLinearGradient( barcolorWidth/2, 0, barcolorWidth/2,dy);
|
||||
gradient.addColorStop(0, 'rgba(255,0,0,1)');
|
||||
gradient.addColorStop(0.5, 'rgba(255,0,255,1)');
|
||||
gradient.addColorStop(1, 'rgba(0,0,255,1)');
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(x, 0, barcolorWidth, dy);
|
||||
if(ctx?.draw){
|
||||
ctx?.draw()
|
||||
}
|
||||
|
||||
|
||||
var gradient2 = ctx.createLinearGradient( barcolorWidth/2, dy, barcolorWidth/2, dy*2);
|
||||
gradient2.addColorStop(0, 'rgba(0,0,255,1)');
|
||||
gradient2.addColorStop(0.5, 'rgba(0,255,255,1)');
|
||||
gradient2.addColorStop(1, 'rgba(0,255,0,1)');
|
||||
ctx.fillStyle = gradient2;
|
||||
ctx.fillRect(x, dy, barcolorWidth, dy);
|
||||
if(ctx?.draw){
|
||||
ctx?.draw(true)
|
||||
}
|
||||
var gradient3 = ctx.createLinearGradient( barcolorWidth/2, dy*2, barcolorWidth/2,dy*3);
|
||||
gradient3.addColorStop(0, 'rgba(0,255,0,1)');
|
||||
gradient3.addColorStop(0.5, 'rgba(255,255,1,1)');
|
||||
gradient3.addColorStop(1, 'rgba(255,0,0,1)');
|
||||
ctx.fillStyle = gradient3;
|
||||
ctx.fillRect(x, dy*2,barcolorWidth, dy);
|
||||
// ctx.drawImage(colorimgUrl, 0, 0,_width.value,_height.value)
|
||||
if(ctx?.draw){
|
||||
ctx?.draw(true)
|
||||
}
|
||||
}
|
||||
function hslaToRgba(scolor) {
|
||||
var { h, s, l, a } = scolor;
|
||||
h = h / 360;
|
||||
s = s / 100;
|
||||
l = l / 100;
|
||||
var rgb = [];
|
||||
|
||||
if (s == 0) {
|
||||
rgb = [Math.round(l * 255), Math.round(l * 255), Math.round(l * 255)];
|
||||
} else {
|
||||
var q = l >= 0.5 ? (l + s - l * s) : (l * (1 + s));
|
||||
var p = 2 * l - q;
|
||||
var tr = rgb[0] = h + 1 / 3;
|
||||
var tg = rgb[1] = h;
|
||||
var tb = rgb[2] = h - 1 / 3;
|
||||
for (var i = 0; i < rgb.length; i++) {
|
||||
var tc = rgb[i];
|
||||
if (tc < 0) {
|
||||
tc = tc + 1;
|
||||
} else if (tc > 1) {
|
||||
tc = tc - 1;
|
||||
}
|
||||
switch (true) {
|
||||
case (tc < (1 / 6)):
|
||||
tc = p + (q - p) * 6 * tc;
|
||||
break;
|
||||
case ((1 / 6) <= tc && tc < 0.5):
|
||||
tc = q;
|
||||
break;
|
||||
case (0.5 <= tc && tc < (2 / 3)):
|
||||
tc = p + (q - p) * (4 - 6 * tc);
|
||||
break;
|
||||
default:
|
||||
tc = p;
|
||||
break;
|
||||
}
|
||||
rgb[i] = Math.round(tc * 255);
|
||||
}
|
||||
}
|
||||
|
||||
return { r: rgb[0], g: rgb[1], b: rgb[2], a: a };
|
||||
}
|
||||
function rgbaToCss(sColor){
|
||||
return `rgba(${sColor.r},${sColor.g},${sColor.b},${sColor.a})`;
|
||||
}
|
||||
window.tmColorView_renderRectFill = function(H){
|
||||
if(!H) H=0;
|
||||
H = Number(H)
|
||||
let x = 40;
|
||||
let dy = 2;
|
||||
let w = _tmColorView_w;
|
||||
|
||||
var ctx = tmColorView_getCanvas();
|
||||
for(let i=0;i<100;i++){
|
||||
let gradient = ctx.createLinearGradient( x, i, w,i);
|
||||
gradient.addColorStop(0, rgbaToCss(hslaToRgba({h:H,s:0,l:100-i,a:1})));
|
||||
gradient.addColorStop(1, rgbaToCss(hslaToRgba({h:H,s:100,l:50-i/2,a:1})));
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(x, (i+1)*dy, w, (i+1)*dy);
|
||||
if(ctx?.draw){
|
||||
ctx?.draw(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.tmColorView_getColor = function(x,y,active){
|
||||
var ctx = tmColorView_getCanvas();
|
||||
let arg = ctx.getImageData(Number(x),Number(y),1,1);
|
||||
uni.postMessage({
|
||||
data: {
|
||||
action: 'tmColorView_getColor',
|
||||
tmColor:JSON.stringify({
|
||||
r:arg.data["0"],
|
||||
g:arg.data["1"],
|
||||
b:arg.data["2"],
|
||||
a:1
|
||||
}),
|
||||
tmColorActive:active
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
45
uni_modules/UniDevTools/src/hybrid/html/js/echarts.min.js
vendored
Normal file
256
uni_modules/UniDevTools/src/hybrid/html/js/handler.js
Normal file
@@ -0,0 +1,256 @@
|
||||
|
||||
// 等待初始化完毕
|
||||
document.addEventListener('UniAppJSBridgeReady', () => {
|
||||
|
||||
document.body.onclick = () =>
|
||||
uni.postMessage({
|
||||
data: {
|
||||
action: 'onClick'
|
||||
}
|
||||
})
|
||||
uni.postMessage({
|
||||
data: {
|
||||
action: 'onJSBridgeReady'
|
||||
}
|
||||
})
|
||||
})
|
||||
let options
|
||||
let medias = []
|
||||
|
||||
/**
|
||||
* @description 获取标签的所有属性
|
||||
* @param {Element} ele
|
||||
*/
|
||||
function getAttrs (ele) {
|
||||
const attrs = Object.create(null)
|
||||
for (let i = ele.attributes.length; i--;) {
|
||||
attrs[ele.attributes[i].name] = ele.attributes[i].value
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 图片加载出错
|
||||
*/
|
||||
function onImgError () {
|
||||
if (options[1]) {
|
||||
this.src = options[1]
|
||||
this.onerror = null
|
||||
}
|
||||
// 取消监听点击
|
||||
this.onclick = null
|
||||
this.ontouchstart = null
|
||||
uni.postMessage({
|
||||
data: {
|
||||
action: 'onError',
|
||||
source: 'img',
|
||||
attrs: getAttrs(this)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 检查是否所有图片加载完毕
|
||||
*/
|
||||
function checkReady () {
|
||||
window.unloadimgs -= 1
|
||||
if (window.unloadimgs === 0) {
|
||||
// 所有图片加载完毕
|
||||
uni.postMessage({
|
||||
data: {
|
||||
action: 'onReady'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 创建 dom 结构
|
||||
* @param {object[]} nodes 节点数组
|
||||
* @param {Element} parent 父节点
|
||||
* @param {string} namespace 命名空间
|
||||
*/
|
||||
function createDom (nodes, parent, namespace) {
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i]
|
||||
let ele
|
||||
if (!node.type || node.type === 'node') {
|
||||
let name = node.name
|
||||
// svg 需要设置 namespace
|
||||
if (name === 'svg') {
|
||||
namespace = 'http://www.w3.org/2000/svg'
|
||||
}
|
||||
if (name === 'html' || name === 'body') {
|
||||
name = 'div'
|
||||
}
|
||||
// 创建标签
|
||||
if (!namespace) {
|
||||
ele = document.createElement(name)
|
||||
} else {
|
||||
ele = document.createElementNS(namespace, name)
|
||||
}
|
||||
// 设置属性
|
||||
for (const item in node.attrs) {
|
||||
ele.setAttribute(item, node.attrs[item])
|
||||
}
|
||||
// 递归创建子节点
|
||||
if (node.children) {
|
||||
createDom(node.children, ele, namespace)
|
||||
}
|
||||
|
||||
// 处理图片
|
||||
if (name === 'img') {
|
||||
window.unloadimgs += 1
|
||||
ele.onload = checkReady
|
||||
ele.onerror = checkReady
|
||||
if (!ele.src && ele.getAttribute('data-src')) {
|
||||
ele.src = ele.getAttribute('data-src')
|
||||
}
|
||||
if (!node.attrs.ignore) {
|
||||
// 监听图片点击事件
|
||||
ele.onclick = function (e) {
|
||||
e.stopPropagation()
|
||||
uni.postMessage({
|
||||
data: {
|
||||
action: 'onImgTap',
|
||||
attrs: getAttrs(this)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
if (options[2]) {
|
||||
const image = new Image()
|
||||
image.src = ele.src
|
||||
ele.src = options[2]
|
||||
image.onload = function () {
|
||||
ele.src = this.src
|
||||
}
|
||||
image.onerror = function () {
|
||||
ele.onerror()
|
||||
}
|
||||
}
|
||||
ele.onerror = onImgError
|
||||
} else if (name === 'a') {
|
||||
// 处理链接
|
||||
ele.addEventListener('click', function (e) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault() // 阻止默认跳转
|
||||
const href = this.getAttribute('href')
|
||||
let offset
|
||||
if (href && href[0] === '#') {
|
||||
offset = (document.getElementById(href.substr(1)) || {}).offsetTop
|
||||
}
|
||||
uni.postMessage({
|
||||
data: {
|
||||
action: 'onLinkTap',
|
||||
attrs: getAttrs(this),
|
||||
offset
|
||||
}
|
||||
})
|
||||
}, true)
|
||||
} else if (name === 'video' || name === 'audio') {
|
||||
// 处理音视频
|
||||
medias.push(ele)
|
||||
if (!node.attrs.autoplay && !node.attrs.controls) {
|
||||
ele.setAttribute('controls', 'true')
|
||||
}
|
||||
ele.onplay = function () {
|
||||
uni.postMessage({
|
||||
data: {
|
||||
action: 'onPlay'
|
||||
}
|
||||
})
|
||||
if (options[3]) {
|
||||
for (let i = 0; i < medias.length; i++) {
|
||||
if (medias[i] !== this) {
|
||||
medias[i].pause()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ele.onerror = function () {
|
||||
uni.postMessage({
|
||||
data: {
|
||||
action: 'onError',
|
||||
source: name,
|
||||
attrs: getAttrs(this)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if (name === 'table' && options[4] && !ele.style.cssText.includes('inline')) {
|
||||
// 处理表格
|
||||
const div = document.createElement('div')
|
||||
div.style.overflow = 'auto'
|
||||
div.appendChild(ele)
|
||||
ele = div
|
||||
} else if (name === 'svg') {
|
||||
namespace = undefined
|
||||
}
|
||||
} else {
|
||||
ele = document.createTextNode(node.text.replace(/&/g, '&'))
|
||||
}
|
||||
parent.appendChild(ele)
|
||||
}
|
||||
}
|
||||
|
||||
// 设置 html 内容
|
||||
window.setContent = function (nodes, opts, append) {
|
||||
const ele = document.getElementById('content')
|
||||
document.body.style.overflow = "scroll"
|
||||
// 容器样式
|
||||
if (opts[0]) {
|
||||
document.body.style.cssText = opts[0]
|
||||
}
|
||||
|
||||
|
||||
// 长按复制
|
||||
if (!opts[5]) {
|
||||
ele.style.userSelect = 'none'
|
||||
}
|
||||
|
||||
if (!append) {
|
||||
ele.innerHTML = '' // 不追加则先清空
|
||||
medias = []
|
||||
}
|
||||
|
||||
options = opts
|
||||
window.unloadimgs = 0
|
||||
const fragment = document.createDocumentFragment()
|
||||
createDom(nodes, fragment)
|
||||
ele.appendChild(fragment)
|
||||
|
||||
// 触发事件
|
||||
let height = ele.scrollHeight
|
||||
uni.postMessage({
|
||||
data: {
|
||||
action: 'onLoad',
|
||||
height
|
||||
}
|
||||
})
|
||||
if (!window.unloadimgs) {
|
||||
uni.postMessage({
|
||||
data: {
|
||||
action: 'onReady',
|
||||
height
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
clearInterval(window.timer)
|
||||
window.timer = setInterval(() => {
|
||||
if (ele.scrollHeight !== height) {
|
||||
height = ele.scrollHeight
|
||||
uni.postMessage({
|
||||
data: {
|
||||
action: 'onHeightChange',
|
||||
height: height
|
||||
}
|
||||
})
|
||||
}
|
||||
}, 350)
|
||||
}
|
||||
|
||||
// 回收计时器
|
||||
window.onunload = function () {
|
||||
clearInterval(window.timer)
|
||||
}
|
||||
55
uni_modules/UniDevTools/src/hybrid/html/js/tmechart.js
Normal file
@@ -0,0 +1,55 @@
|
||||
var chartDom = null;
|
||||
window.mychart = null;
|
||||
window.echart_createDom = function (w, h) {
|
||||
w = Number(w);
|
||||
h = Number(h);
|
||||
chartDom = document.createElement("div");
|
||||
chartDom.style.width = w + 'px';
|
||||
chartDom.style.height = h + 'px';
|
||||
chartDom.style.display = 'block';
|
||||
document.body.appendChild(chartDom);
|
||||
document.all.addEventListener('touchmove', function(evt) {
|
||||
evt.preventDefault();
|
||||
});
|
||||
|
||||
return chartDom;
|
||||
}
|
||||
window.echart_createChart = function (opts) {
|
||||
if (!opts) {
|
||||
opts = {}
|
||||
}
|
||||
window.mychart = echarts.init(chartDom, undefined, opts)
|
||||
return window.mychart;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将传入echart配置中的函数从字符类型还原为函数类型
|
||||
* @param {*} opt
|
||||
*/
|
||||
function recoverEchartCBFn(opt) {
|
||||
for (let key in opt) {
|
||||
if (opt.hasOwnProperty(key)) {
|
||||
const curr = opt[key]
|
||||
if (typeof curr === 'string' && curr.includes('echartCbFn')) {
|
||||
const fnObj = JSON.parse(curr);
|
||||
tempFn = new Function(`return (${fnObj.fnString})`)();
|
||||
opt[key] = function(...params) {
|
||||
try {
|
||||
return tempFn(...params)
|
||||
} catch (error) {
|
||||
return error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof curr === "object" && curr !== null) {
|
||||
recoverEchartCBFn(curr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.echart_setOption= function (opts,ops) {
|
||||
recoverEchartCBFn(opts)
|
||||
window.mychart.setOption(opts,ops)
|
||||
}
|
||||