diff --git a/pages.config.ts b/pages.config.ts
index 547d0da..383d5fc 100644
--- a/pages.config.ts
+++ b/pages.config.ts
@@ -48,4 +48,26 @@ export default defineUniPages({
},
],
},
+ subPackages: [
+ {
+ root: 'devTools/page',
+ pages: [
+ {
+ path: 'index',
+ style: {
+ navigationStyle: 'custom',
+ // #ifdef APP-PLUS
+ softinputMode: 'adjustResize',
+ // backgroundColor: 'transparent',
+ animationDuration: 1,
+ animationType: 'none',
+ popGesture: 'none',
+ bounce: 'none',
+ titleNView: false,
+ // #endif
+ },
+ },
+ ],
+ },
+ ],
})
diff --git a/src/devTools/READEME.MD b/src/devTools/READEME.MD
new file mode 100644
index 0000000..a0a48e1
--- /dev/null
+++ b/src/devTools/READEME.MD
@@ -0,0 +1,9 @@
+# UniDevTools - 调试工具
+
+支持 Vue2+Vue3 的跨平台调试工具
+
+> 文档&安装教程 https://dev.api0.cn/
+
+当前版本:v3.8
+
+更新日期:2025.5.5
diff --git a/src/devTools/config.js b/src/devTools/config.js
new file mode 100644
index 0000000..77ab4dd
--- /dev/null
+++ b/src/devTools/config.js
@@ -0,0 +1,69 @@
+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
diff --git a/src/devTools/core/components/mpDevBubble.vue b/src/devTools/core/components/mpDevBubble.vue
new file mode 100644
index 0000000..593db89
--- /dev/null
+++ b/src/devTools/core/components/mpDevBubble.vue
@@ -0,0 +1,156 @@
+
+
+
+ {{ options.bubble.text }}
+
+
+
+
+
diff --git a/src/devTools/core/libs/createH5Bubble.js b/src/devTools/core/libs/createH5Bubble.js
new file mode 100644
index 0000000..aeb6523
--- /dev/null
+++ b/src/devTools/core/libs/createH5Bubble.js
@@ -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
diff --git a/src/devTools/core/libs/devCache.js b/src/devTools/core/libs/devCache.js
new file mode 100644
index 0000000..d735d57
--- /dev/null
+++ b/src/devTools/core/libs/devCache.js
@@ -0,0 +1,131 @@
+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,
+ )
+ },
+}
diff --git a/src/devTools/core/libs/devOptions.js b/src/devTools/core/libs/devOptions.js
new file mode 100644
index 0000000..d8efb72
--- /dev/null
+++ b/src/devTools/core/libs/devOptions.js
@@ -0,0 +1,184 @@
+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.81,
+ 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 {}
+ }
+}
diff --git a/src/devTools/core/libs/drawView.js b/src/devTools/core/libs/drawView.js
new file mode 100644
index 0000000..96f5491
--- /dev/null
+++ b/src/devTools/core/libs/drawView.js
@@ -0,0 +1,118 @@
+/**
+ * 绘制调试工具
+ */
+
+/**
+ * 入口文件
+ */
+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
diff --git a/src/devTools/core/libs/errorReport.js b/src/devTools/core/libs/errorReport.js
new file mode 100644
index 0000000..c94c6cc
--- /dev/null
+++ b/src/devTools/core/libs/errorReport.js
@@ -0,0 +1,60 @@
+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
diff --git a/src/devTools/core/libs/jsonCompress.js b/src/devTools/core/libs/jsonCompress.js
new file mode 100644
index 0000000..f93efcb
--- /dev/null
+++ b/src/devTools/core/libs/jsonCompress.js
@@ -0,0 +1,333 @@
+/**
+ * json压缩工具
+ */
+export default {
+ /**
+ * 压缩js对象成json字符串,并控制json字节大小,多余部分裁剪
+ */
+ compressObject(obj = {}, maxSize = 1024 * 10.24) {
+ try {
+ if (obj === undefined || obj === null) return obj
+ if (typeof obj == 'string') {
+ return this.truncateStrBySize(obj, maxSize)
+ }
+ if (typeof obj == 'number') {
+ return obj
+ }
+
+ let t = new Date().getTime()
+
+ const type = typeof obj
+
+ if (type === 'symbol') {
+ obj = 'Symbol->' + obj.toString()
+ } else if (type === 'bigint') {
+ obj = 'bigint->' + obj.toString()
+ } else if (typeof Error != 'undefined' && obj instanceof Error) {
+ obj = `Error->${obj.name}\n${obj.message}\n${obj.stack}`
+ } else if (typeof Date != 'undefined' && obj instanceof Date) {
+ obj = 'Date->' + obj.toISOString()
+ } else if (typeof obj == 'function') {
+ obj = 'Function->' + obj.toString()
+ } else if (typeof RegExp != 'undefined' && obj instanceof RegExp) {
+ obj = 'RegExp->' + obj.toString()
+ } else if (typeof Map != 'undefined' && obj instanceof Map) {
+ obj = `Map->(${obj.size}) { ${Array.from(obj.entries())
+ .map(([key, value]) => `${convertToString(key)} => ${convertToString(value)}`)
+ .join(', ')} }`
+ } else if (typeof Set != 'undefined' && obj instanceof Set) {
+ obj = `Set->(${obj.size}) { ${Array.from(obj.values())
+ .map((value) => convertToString(value))
+ .join(', ')} }`
+ } else if (typeof Blob != 'undefined' && obj instanceof Blob) {
+ obj = `Blob->{ size: ${obj.size}, type: ${obj.type} }`
+ } else if (typeof File != 'undefined' && obj instanceof File) {
+ obj = `File->{ name: "${obj.name}", size: ${obj.size}, type: ${obj.type}, lastModified: ${new Date(obj.lastModified).toISOString()} }`
+ } else if (typeof URL != 'undefined' && obj instanceof URL) {
+ obj = `URL->{ href: "${obj.href}", protocol: "${obj.protocol}", host: "${obj.host}", pathname: "${obj.pathname}", search: "${obj.search}", hash: "${obj.hash}" }`
+ } else if (typeof FormData != 'undefined' && obj instanceof FormData) {
+ const entries = []
+ obj.forEach((key, item) => {
+ entries.push(key)
+ })
+ obj = `FormData->{ ${entries.join(', ')} }`
+ } else if (typeof Location != 'undefined' && obj instanceof Location) {
+ obj = `Location->{ href: "${obj.href}", protocol: "${obj.protocol}", host: "${obj.host}", pathname: "${obj.pathname}", search: "${obj.search}", hash: "${obj.hash}" }`
+ } else if (typeof Document != 'undefined' && obj instanceof Document) {
+ obj = `Document->{ title: "${obj.title}", URL: "${obj.URL}" }`
+ } else if (typeof Window !== 'undefined' && obj instanceof Window) {
+ obj = `Window->{ location: ${this.compressObject(obj.location)}, document: ${this.compressObject(obj.document)} }`
+ } else if (typeof Element != 'undefined' && obj instanceof Element) {
+ obj = `Element->{ tagName: "${obj.tagName}", id: "${obj.id}", class: "${obj.className}" }`
+ } else if (typeof HTMLCanvasElement != 'undefined' && obj instanceof HTMLCanvasElement) {
+ obj = `Canvas->{ width: ${obj.width}, height: ${obj.height} }`
+ } else if (typeof HTMLAudioElement != 'undefined' && obj instanceof HTMLAudioElement) {
+ obj = `Audio->{ src: "${obj.src}", duration: ${obj.duration} }`
+ } else if (typeof HTMLVideoElement != 'undefined' && obj instanceof HTMLVideoElement) {
+ obj = `Video->{ src: "${obj.src}", width: ${obj.videoWidth}, height: ${obj.videoHeight}, duration: ${obj.duration} }`
+ } else if (typeof Storage != 'undefined' && obj instanceof Storage) {
+ obj = `Storage->{ length: ${obj.length} }`
+ } else if (
+ typeof Worker != 'undefined' &&
+ typeof ServiceWorker != 'undefined' &&
+ typeof SharedWorker != 'undefined' &&
+ (obj instanceof Worker || obj instanceof ServiceWorker || obj instanceof SharedWorker)
+ ) {
+ obj = `Worker->${obj.constructor.name} { scriptURL: "${obj.scriptURL}" }`
+ } else if (typeof WebSocket != 'undefined' && obj instanceof WebSocket) {
+ obj = `WebSocket->{ url: "${obj.url}", readyState: ${obj.readyState} }`
+ } else if (typeof XMLHttpRequest != 'undefined' && obj instanceof XMLHttpRequest) {
+ obj = `XMLHttpRequest->{ readyState: ${obj.readyState}, status: ${obj.status} }`
+ } else if (typeof EventSource != 'undefined' && obj instanceof EventSource) {
+ obj = `EventSource->{ url: "${obj.url}", readyState: ${obj.readyState} }`
+ } else if (typeof MediaStream != 'undefined' && obj instanceof MediaStream) {
+ obj = `MediaStream->{ id: "${obj.id}", active: ${obj.active} }`
+ } else if (typeof RTCPeerConnection != 'undefined' && obj instanceof RTCPeerConnection) {
+ obj = `RTCPeerConnection->{ connectionState: "${obj.connectionState}" }`
+ } else if (typeof AudioContext != 'undefined' && obj instanceof AudioContext) {
+ obj = `AudioContext->{ state: "${obj.state}" }`
+ } else if (typeof Element != 'undefined' && obj instanceof Element) {
+ obj = `Element->{ tagName: "${obj.tagName}", id: "${obj.id}", class: "${obj.className}" }`
+ } else if (typeof HTMLCanvasElement != 'undefined' && obj instanceof HTMLCanvasElement) {
+ obj = `Canvas->{ width: ${obj.width}, height: ${obj.height} }`
+ } else if (typeof HTMLAudioElement != 'undefined' && obj instanceof HTMLAudioElement) {
+ obj = `Audio->{ src: "${obj.src}", duration: ${obj.duration} }`
+ } else if (typeof HTMLVideoElement != 'undefined' && obj instanceof HTMLVideoElement) {
+ obj = `Video->{ src: "${obj.src}", width: ${obj.videoWidth}, height: ${obj.videoHeight}, duration: ${obj.duration} }`
+ } else if (typeof Geolocation != 'undefined' && obj instanceof Geolocation) {
+ obj = `Geolocation->{ }`
+ } else if (typeof Performance != 'undefined' && obj instanceof Performance) {
+ obj = `Performance->{ now: ${obj.now()} }`
+ } else if (typeof Event != 'undefined' && obj instanceof Event) {
+ obj = `Event->{ type: "${obj.type}", target: "${obj.target}" }`
+ }
+
+ 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 ''
+ }
+ },
+}
diff --git a/src/devTools/core/libs/logReport.js b/src/devTools/core/libs/logReport.js
new file mode 100644
index 0000000..7fa0b6f
--- /dev/null
+++ b/src/devTools/core/libs/logReport.js
@@ -0,0 +1,63 @@
+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
diff --git a/src/devTools/core/libs/pageLinkList.js b/src/devTools/core/libs/pageLinkList.js
new file mode 100644
index 0000000..696ea97
--- /dev/null
+++ b/src/devTools/core/libs/pageLinkList.js
@@ -0,0 +1,124 @@
+import devCache from './devCache'
+
+export default {
+ pageRouteMap: [],
+ pageRouteKeyMap: {},
+ /**
+ * 安装路径分析插件
+ */
+ install() {
+ let allRoutes = this.getAllRoutes()
+
+ let pageRouteKeyMap = devCache.get('pageRouteKeyMap')
+ if (!pageRouteKeyMap || typeof pageRouteKeyMap != 'object') {
+ pageRouteKeyMap = {}
+ }
+
+ let lastNo = 0
+ Object.keys(pageRouteKeyMap).forEach((key) => {
+ let item = Number(pageRouteKeyMap[key])
+ if (item > lastNo) {
+ lastNo = item
+ }
+ })
+
+ allRoutes.forEach((item) => {
+ if (!pageRouteKeyMap[item.path]) {
+ pageRouteKeyMap[item.path] = lastNo + 1
+ lastNo = lastNo + 1
+ }
+ })
+
+ devCache.set('pageRouteKeyMap', pageRouteKeyMap)
+ this.pageRouteKeyMap = pageRouteKeyMap
+
+ let pageRouteMap = devCache.get('pageRouteMap')
+ if (!pageRouteMap || typeof pageRouteMap != 'object') {
+ pageRouteMap = {}
+ }
+
+ Object.keys(pageRouteMap).forEach((key) => {
+ try {
+ let n = Number(pageRouteMap[key])
+ if (!Number.isInteger(n) || n < 0) {
+ pageRouteMap[key] = 1
+ }
+ } catch (error) {}
+ })
+ this.pageRouteMap = pageRouteMap
+
+ this.saveData()
+ },
+ /**
+ * 获取APP注册的所有路由
+ * @returns {{path: string}[]} 返回路由列表
+ */
+ getAllRoutes() {
+ let pages = []
+ // #ifdef H5 || APP-PLUS
+ try {
+ __uniRoutes.map((item) => {
+ let path = item.alias ? item.alias : item.path
+ pages.push({ path })
+ })
+ } catch (error) {
+ pages = []
+ }
+ // #endif
+ // #ifdef MP-WEIXIN
+ try {
+ let wxPages = __wxConfig.pages
+ wxPages.map((item) => {
+ pages.push({
+ path: '/' + item,
+ })
+ })
+ } catch (error) {
+ pages = []
+ }
+ // #endif
+ return pages
+ },
+ /**
+ * 写入路由列表
+ */
+ pushPageRouteMap(list = []) {
+ if (!list || list.length == 0) {
+ list = getCurrentPages()
+ }
+ let key = ''
+ list.forEach((item) => {
+ let path = item.route.indexOf('/') == 0 ? item.route : '/' + item.route
+ let keyItem = this.pageRouteKeyMap[path]
+ if (!keyItem) {
+ keyItem = path
+ }
+ if (key == '') {
+ key = keyItem + ''
+ } else {
+ key = key + ',' + keyItem
+ }
+ })
+
+ if (this.pageRouteMap[key]) {
+ this.pageRouteMap[key] = this.pageRouteMap[key] + 1
+ } else {
+ this.pageRouteMap[key] = 1
+ }
+ },
+ /**
+ * 保存路由缓存
+ */
+ saveData() {
+ let that = this
+ setTimeout(
+ () => {
+ devCache.set('pageRouteMap', that.pageRouteMap)
+ setTimeout(() => {
+ that.saveData()
+ }, 500)
+ },
+ Math.round(Math.random() * 3 * 1000) + 2000,
+ )
+ },
+}
diff --git a/src/devTools/core/libs/pageStatistics.js b/src/devTools/core/libs/pageStatistics.js
new file mode 100644
index 0000000..ffe59ce
--- /dev/null
+++ b/src/devTools/core/libs/pageStatistics.js
@@ -0,0 +1,71 @@
+/**
+ * !页面统计:访问次数、停留时长
+ */
+
+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
diff --git a/src/devTools/core/libs/timeFormat.js b/src/devTools/core/libs/timeFormat.js
new file mode 100644
index 0000000..261db5c
--- /dev/null
+++ b/src/devTools/core/libs/timeFormat.js
@@ -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)
+ }
+}
diff --git a/src/devTools/core/proxy/console.js b/src/devTools/core/proxy/console.js
new file mode 100644
index 0000000..cae443f
--- /dev/null
+++ b/src/devTools/core/proxy/console.js
@@ -0,0 +1,413 @@
+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)
+ }
+ },
+}
diff --git a/src/devTools/core/proxy/index.js b/src/devTools/core/proxy/index.js
new file mode 100644
index 0000000..bef54e2
--- /dev/null
+++ b/src/devTools/core/proxy/index.js
@@ -0,0 +1,33 @@
+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)
+ }
+}
diff --git a/src/devTools/core/proxy/request.js b/src/devTools/core/proxy/request.js
new file mode 100644
index 0000000..5dde9f3
--- /dev/null
+++ b/src/devTools/core/proxy/request.js
@@ -0,0 +1,246 @@
+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) {
+ return new Promise(async (yes, err) => {
+ //! 延迟请求返回,模拟弱网环境
+ let speedLimit = uni.getStorageSync('devtools_uniResLimitType')
+ if (speedLimit) {
+ let delayDuration = {
+ '2g': [30, 60],
+ '3g-': [10, 30],
+ '3g': [3, 10],
+ '4g': [0.5, 3],
+ }
+ let sleepParam = delayDuration[speedLimit]
+ if (sleepParam) {
+ let sleepTime = rNum(sleepParam[0], sleepParam[1])
+ await sleep(sleepTime * 1000)
+ response.errMsg = response.errMsg + ` | [devtools模拟弱网延迟:${sleepTime}s]`
+ }
+ }
+
+ // ! 随机响应失败概率
+ let resTimeout = uni.getStorageSync('devtools_uniResTimeout')
+ let isFail = false
+ if (resTimeout && resTimeout > 1) {
+ let targetPro = Number(resTimeout)
+ let randPro = rNum(0, 100)
+ if (randPro < targetPro) {
+ // 命中失败
+ response.statusCode = rNum(400, 600) //生成随机400 ~ 600之间的状态码
+ response.errMsg =
+ response.errMsg +
+ ` | [devtools随机超时报错,当前命中的概率阶层为:${targetPro}%,生成的随机数为:${randPro}]`
+ response.data = '[devTools]模拟请求失败结果!'
+ isFail = true
+ }
+ }
+
+ // ! 记录响应内容
+ 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)
+ }
+
+ if (isFail) {
+ err(response.data)
+ } else {
+ yes(response)
+ }
+ })
+ },
+ 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 ''
+ }
+ },
+}
+
+/**
+ * 随机生成n~m的数,支持两位小数
+ */
+function rNum(n, m) {
+ return Number((Math.random() * (m - n) + n).toFixed(2))
+}
+
+/**
+ * 休眠指定时长
+ */
+function sleep(t) {
+ return new Promise((y) => {
+ setTimeout(y, t)
+ })
+}
diff --git a/src/devTools/core/proxy/storage.js b/src/devTools/core/proxy/storage.js
new file mode 100644
index 0000000..99934cc
--- /dev/null
+++ b/src/devTools/core/proxy/storage.js
@@ -0,0 +1,96 @@
+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)
+ }
+ },
+}
diff --git a/src/devTools/core/proxy/uniBus.js b/src/devTools/core/proxy/uniBus.js
new file mode 100644
index 0000000..12b5f45
--- /dev/null
+++ b/src/devTools/core/proxy/uniBus.js
@@ -0,0 +1,160 @@
+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)
+ },
+}
diff --git a/src/devTools/core/proxy/uniListen.js b/src/devTools/core/proxy/uniListen.js
new file mode 100644
index 0000000..03348e0
--- /dev/null
+++ b/src/devTools/core/proxy/uniListen.js
@@ -0,0 +1,195 @@
+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)
+ }
+ })
+ },
+}
diff --git a/src/devTools/core/proxy/vueMixin.js b/src/devTools/core/proxy/vueMixin.js
new file mode 100644
index 0000000..3c69718
--- /dev/null
+++ b/src/devTools/core/proxy/vueMixin.js
@@ -0,0 +1,160 @@
+import devOptions from '../libs/devOptions'
+import logReport from '../libs/logReport'
+import pageLinkList from '../libs/pageLinkList'
+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)
+ }
+ }
+
+ pageLinkList.pushPageRouteMap(pages)
+ } 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)
+ }
+ },
+}
diff --git a/src/devTools/index.js b/src/devTools/index.js
new file mode 100644
index 0000000..76d2f5c
--- /dev/null
+++ b/src/devTools/index.js
@@ -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'
+import pageLinkList from './core/libs/pageLinkList'
+
+/**
+ * @type {Vue}
+ */
+let that
+
+const devTools = {
+ options: null,
+ /**
+ * 挂载安装APP页面
+ */
+ 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)
+ })
+
+ // ! 页面路由列表
+ pageLinkList.install()
+ } 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
diff --git a/src/devTools/page/components/bottomTools.vue b/src/devTools/page/components/bottomTools.vue
new file mode 100644
index 0000000..10028f7
--- /dev/null
+++ b/src/devTools/page/components/bottomTools.vue
@@ -0,0 +1,914 @@
+
+
+
+
+
+ 清空 x
+
+
+
+
+
+
+
+ 清空 x
+
+
+
+
+
+
+
+ 清空 x
+
+
+
+
+
+
+
+
+
+
+
+ 清空停留统计
+
+
+ 清空日活统计
+
+
+ 跳转页面
+
+
+
+
+
+
+ 清空 x
+
+
+
+
+
+
+ 清空 x
+
+
+
+
+
+ 新增数据+
+
+
+
+
+
+
+ 清空 x
+
+
+
+
+
+
+
+
+
+
+ {{ options.fileSysDirType }}
+
+ /
+
+ /
+
+ {{ item }}
+
+
+
+
+
+ 清空 x
+
+
+
+
+
+
+ 新建文件(夹)
+
+
+
+
+
+
+
+
+
+ 运行环境:
+
+
+
+
+ {{ item }}
+
+
+
+
+
+
+ 清空记录
+
+
+ 历史代码
+
+
+
+
+
+
+ run
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/dialog/addStorage.vue b/src/devTools/page/components/dialog/addStorage.vue
new file mode 100644
index 0000000..423fb49
--- /dev/null
+++ b/src/devTools/page/components/dialog/addStorage.vue
@@ -0,0 +1,185 @@
+
+
+
+
+ 新增缓存
+
+
+
+
+ 取消
+
+
+ 提交
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/dialog/createDir.vue b/src/devTools/page/components/dialog/createDir.vue
new file mode 100644
index 0000000..7b5876d
--- /dev/null
+++ b/src/devTools/page/components/dialog/createDir.vue
@@ -0,0 +1,356 @@
+
+
+
+
+ {{ title }}
+
+
+
+ 取消
+
+
+ 提交
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/dialog/dayOnlinePageList.vue b/src/devTools/page/components/dialog/dayOnlinePageList.vue
new file mode 100644
index 0000000..c502a00
--- /dev/null
+++ b/src/devTools/page/components/dialog/dayOnlinePageList.vue
@@ -0,0 +1,202 @@
+
+
+
+
+
+
+ {{ item.date + ' ' + item.timeCount }}
+
+
+
+
+
+
+
+ 页面:{{ row.r }}
+ 活跃:{{ row.timeCount }}
+
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/dialog/editDialog.vue b/src/devTools/page/components/dialog/editDialog.vue
new file mode 100644
index 0000000..261ff51
--- /dev/null
+++ b/src/devTools/page/components/dialog/editDialog.vue
@@ -0,0 +1,178 @@
+
+
+
+
+
+ {{ title }}
+
+
+
+
+ 取消
+
+
+ 提交
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/dialog/routeDialog.vue b/src/devTools/page/components/dialog/routeDialog.vue
new file mode 100644
index 0000000..476260d
--- /dev/null
+++ b/src/devTools/page/components/dialog/routeDialog.vue
@@ -0,0 +1,169 @@
+
+
+
+
+
+ 跳转至指定页面
+
+
+
+
+ 取消
+
+
+ 跳转
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/dialog/sendRequest.vue b/src/devTools/page/components/dialog/sendRequest.vue
new file mode 100644
index 0000000..eb5c602
--- /dev/null
+++ b/src/devTools/page/components/dialog/sendRequest.vue
@@ -0,0 +1,443 @@
+
+
+
+
+
+
+ 请求构建工具
+
+
+
+
+
+
+
+
+
+ x
+
+
+
+
+
+
+ {{ request.method }}
+
+
+
+
+
+
+
+
+ x
+
+
+
+
+
+ x
+
+
+
+
+ 发送中[{{ send.t }}ms]
+ (点击可取消)
+
+ 发送
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/dialog/textFileEditDialog.vue b/src/devTools/page/components/dialog/textFileEditDialog.vue
new file mode 100644
index 0000000..7ac3d7d
--- /dev/null
+++ b/src/devTools/page/components/dialog/textFileEditDialog.vue
@@ -0,0 +1,361 @@
+
+
+
+
+
+
+ {{ title }}
+
+
+
+
+
+
+
+
+ 保存
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/libs/appDelDir.js b/src/devTools/page/components/libs/appDelDir.js
new file mode 100644
index 0000000..dd02678
--- /dev/null
+++ b/src/devTools/page/components/libs/appDelDir.js
@@ -0,0 +1,77 @@
+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}
+ */
+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,
+ )
+ })
+}
diff --git a/src/devTools/page/components/libs/dirReader.js b/src/devTools/page/components/libs/dirReader.js
new file mode 100644
index 0000000..1241218
--- /dev/null
+++ b/src/devTools/page/components/libs/dirReader.js
@@ -0,0 +1,216 @@
+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 ''
+ }
+}
diff --git a/src/devTools/page/components/libs/fileSize.js b/src/devTools/page/components/libs/fileSize.js
new file mode 100644
index 0000000..87e5550
--- /dev/null
+++ b/src/devTools/page/components/libs/fileSize.js
@@ -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]
+ },
+}
diff --git a/src/devTools/page/components/libs/getRuntimeInfo.js b/src/devTools/page/components/libs/getRuntimeInfo.js
new file mode 100644
index 0000000..60bebfd
--- /dev/null
+++ b/src/devTools/page/components/libs/getRuntimeInfo.js
@@ -0,0 +1,132 @@
+/**
+ * 获取当前运行时环境信息
+ */
+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 获取失败!' })
+ },
+ })
+ })
+}
diff --git a/src/devTools/page/components/listItem/consoleItem.vue b/src/devTools/page/components/listItem/consoleItem.vue
new file mode 100644
index 0000000..de11ca1
--- /dev/null
+++ b/src/devTools/page/components/listItem/consoleItem.vue
@@ -0,0 +1,271 @@
+
+
+
+
+
+
+
+
+
+
+ {{ getText(row) }}
+
+
+
+
+
+
+ {{ item.date }}
+
+ {{ item.type }}
+
+ {{ item.page }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/listItem/dayOnlineItem.vue b/src/devTools/page/components/listItem/dayOnlineItem.vue
new file mode 100644
index 0000000..ff47c6a
--- /dev/null
+++ b/src/devTools/page/components/listItem/dayOnlineItem.vue
@@ -0,0 +1,89 @@
+
+
+
+ {{ item.date }}
+ {{ item.timeCount }}
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/listItem/errorItem.vue b/src/devTools/page/components/listItem/errorItem.vue
new file mode 100644
index 0000000..e2146d7
--- /dev/null
+++ b/src/devTools/page/components/listItem/errorItem.vue
@@ -0,0 +1,265 @@
+
+
+
+
+
+
+
+
+
+ {{ getText(row) }}
+
+
+
+
+
+ {{ item.date }}
+
+ {{ getType(item.type) }}
+
+ {{ item.p }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/listItem/fileSysItem.vue b/src/devTools/page/components/listItem/fileSysItem.vue
new file mode 100644
index 0000000..13053f6
--- /dev/null
+++ b/src/devTools/page/components/listItem/fileSysItem.vue
@@ -0,0 +1,380 @@
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+ {{ item.date }}
+
+ {{ item.sizeText }}
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/listItem/infoList.vue b/src/devTools/page/components/listItem/infoList.vue
new file mode 100644
index 0000000..cb6b93d
--- /dev/null
+++ b/src/devTools/page/components/listItem/infoList.vue
@@ -0,0 +1,62 @@
+
+
+
+
+ 加载中
+
+
+
+
+
diff --git a/src/devTools/page/components/listItem/jsRunnerItem.vue b/src/devTools/page/components/listItem/jsRunnerItem.vue
new file mode 100644
index 0000000..8c7ebf4
--- /dev/null
+++ b/src/devTools/page/components/listItem/jsRunnerItem.vue
@@ -0,0 +1,197 @@
+
+
+
+
+
+ {{ item.code }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ getText(item.result) }}
+
+
+
+
+ ...
+
+
+
+
+
+
diff --git a/src/devTools/page/components/listItem/logItem.vue b/src/devTools/page/components/listItem/logItem.vue
new file mode 100644
index 0000000..e952952
--- /dev/null
+++ b/src/devTools/page/components/listItem/logItem.vue
@@ -0,0 +1,140 @@
+
+
+
+
+ {{ item.m }}
+
+
+ {{ item.date }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/listItem/networkItem.vue b/src/devTools/page/components/listItem/networkItem.vue
new file mode 100644
index 0000000..05aa235
--- /dev/null
+++ b/src/devTools/page/components/listItem/networkItem.vue
@@ -0,0 +1,330 @@
+
+
+
+
+
+
+ {{ item.method }}
+
+
+
+ {{ getPath(item.url) }}
+
+
+
+
+
+ {{ item.date }}
+
+
+ {{ item.useTime }}s
+
+
+ {{ getTypeName(item.type) }}
+
+
+ {{ getByteSize }}
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/listItem/objectAnalysis.vue b/src/devTools/page/components/listItem/objectAnalysis.vue
new file mode 100644
index 0000000..425f599
--- /dev/null
+++ b/src/devTools/page/components/listItem/objectAnalysis.vue
@@ -0,0 +1,731 @@
+
+
+
+
+
+
+ {{ title }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.k }}:
+
+
+ {{ item.vt }}
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/listItem/pageItem.vue b/src/devTools/page/components/listItem/pageItem.vue
new file mode 100644
index 0000000..3b15a84
--- /dev/null
+++ b/src/devTools/page/components/listItem/pageItem.vue
@@ -0,0 +1,99 @@
+
+
+
+
+ 页面路由:{{ item.route }}
+
+
+ 停留时长:{{ item.timeCount }}
+
+
+
+
+
+
diff --git a/src/devTools/page/components/listItem/pages.vue b/src/devTools/page/components/listItem/pages.vue
new file mode 100644
index 0000000..73e81c4
--- /dev/null
+++ b/src/devTools/page/components/listItem/pages.vue
@@ -0,0 +1,194 @@
+
+
+
+
+
+ 当前
+
+
+ {{ item.route }}
+ {{ item.options }}
+
+
+
+
+ 加载中
+
+
+
+
+
diff --git a/src/devTools/page/components/listItem/routeItem.vue b/src/devTools/page/components/listItem/routeItem.vue
new file mode 100644
index 0000000..b9d6a1c
--- /dev/null
+++ b/src/devTools/page/components/listItem/routeItem.vue
@@ -0,0 +1,74 @@
+
+
+
+ {{ item.path }}
+
+
+
+
+
diff --git a/src/devTools/page/components/listItem/setting.vue b/src/devTools/page/components/listItem/setting.vue
new file mode 100644
index 0000000..30ff775
--- /dev/null
+++ b/src/devTools/page/components/listItem/setting.vue
@@ -0,0 +1,746 @@
+
+
+
+ 加载中
+
+
+
+
+
+ 导出日志文件(.json)
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+ ({{ item.count }})
+ (空)
+
+
+ 清空选中
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Copyright©2024 福州重塑网络科技有限公司 前端团队
+
+
+ 在线文档:
+ https://dev.api0.cn
+
+
+ 当前版本:v{{ config.version }}
+
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/listItem/storageList.vue b/src/devTools/page/components/listItem/storageList.vue
new file mode 100644
index 0000000..ab28514
--- /dev/null
+++ b/src/devTools/page/components/listItem/storageList.vue
@@ -0,0 +1,313 @@
+
+
+
+
+ 加载中
+
+
+ 无缓存数据
+
+
+ Tips:长按最外层key可复制、编辑或删除缓存
+
+
+
+
+
diff --git a/src/devTools/page/components/listItem/tools.vue b/src/devTools/page/components/listItem/tools.vue
new file mode 100644
index 0000000..d6499a1
--- /dev/null
+++ b/src/devTools/page/components/listItem/tools.vue
@@ -0,0 +1,292 @@
+
+
+
+
+
+
+
+ 页面自动注入Eruda调试工具
+ (强大的节点选择等工具;重启APP后生效)
+
+
+
+
+
+ 页面自动注入vConsole调试工具
+ (腾讯开源的h5调试工具;重启APP后生效)
+
+
+
+
+
+
+
+ 小程序VConsole开关
+ 设置是否打开调试开关。此开关对正式版也能生效。
+
+
+
+
+
+
+
+
+
+ 重启APP
+
+
+
+
+ 跳转指定页面
+
+
+
+ 发起网络请求
+
+
+
+ 清空localStorage缓存
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/listItem/vuexList.vue b/src/devTools/page/components/listItem/vuexList.vue
new file mode 100644
index 0000000..b21c779
--- /dev/null
+++ b/src/devTools/page/components/listItem/vuexList.vue
@@ -0,0 +1,196 @@
+
+
+
+
+ 加载中
+
+
+ 长按非对象类型的数据可编辑
+
+
+
+
diff --git a/src/devTools/page/components/main.vue b/src/devTools/page/components/main.vue
new file mode 100644
index 0000000..bbd9edb
--- /dev/null
+++ b/src/devTools/page/components/main.vue
@@ -0,0 +1,1777 @@
+
+
+
+
+
+
+
+ 返回
+
+
+
+ {{ tabList[tabIndex].isRefreshing ? '刷新中' : '刷新' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+ ×
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+
+ |
+
+
+
+ |
+
+
+ |
+
+
+
+ |
+
+
+
+ |
+
+
+ |
+
+
+
+ |
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+ name: {{ _item.e }}
+
+
+ on: {{ _item.on }}
+
+
+ emit: {{ _item.emit }}
+
+
+ once: {{ _item.once }}
+
+
+ off: {{ _item.off }}
+
+
+
+ |
+
+
+
+ |
+
+
+ |
+
+
+
+ {{ _item.e }}
+ {{ _item.date }}
+
+ |
+
+
+
+ |
+
+
+
+
+ $refs.textFileEditDialog.show($event)"
+ />
+ |
+
+
+ 空空如也
+
+ |
+
+
+
+
+ 该平台无FileSys文件管理
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+ |
+
+
+ 动态执行JS代码
+
+ |
+
+
+
+
+ 该平台不支持该功能
+
+ |
+
+
+
+
+
+
+
+ 展示全部数据
+
+
+ |
+
+
+
+ 空空如也
+
+ |
+
+
+
+ 加载中
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/mixins/animationControl.js b/src/devTools/page/components/mixins/animationControl.js
new file mode 100644
index 0000000..a7b32b2
--- /dev/null
+++ b/src/devTools/page/components/mixins/animationControl.js
@@ -0,0 +1,80 @@
+// #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')
+ },
+ )
+ },
+ },
+}
diff --git a/src/devTools/page/components/mixins/mp.js b/src/devTools/page/components/mixins/mp.js
new file mode 100644
index 0000000..8bfc827
--- /dev/null
+++ b/src/devTools/page/components/mixins/mp.js
@@ -0,0 +1,77 @@
+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
+}
diff --git a/src/devTools/page/components/ui/btnTabs.vue b/src/devTools/page/components/ui/btnTabs.vue
new file mode 100644
index 0000000..4221a50
--- /dev/null
+++ b/src/devTools/page/components/ui/btnTabs.vue
@@ -0,0 +1,68 @@
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/ui/codeHisPicker.vue b/src/devTools/page/components/ui/codeHisPicker.vue
new file mode 100644
index 0000000..f963a7c
--- /dev/null
+++ b/src/devTools/page/components/ui/codeHisPicker.vue
@@ -0,0 +1,161 @@
+
+
+
+
+
+ 历史运行代码:
+
+
+ (保留100条运行记录)
+
+
+
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/ui/h5Cell.vue b/src/devTools/page/components/ui/h5Cell.vue
new file mode 100644
index 0000000..841e39d
--- /dev/null
+++ b/src/devTools/page/components/ui/h5Cell.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/src/devTools/page/components/ui/menuBtn.vue b/src/devTools/page/components/ui/menuBtn.vue
new file mode 100644
index 0000000..5f342ff
--- /dev/null
+++ b/src/devTools/page/components/ui/menuBtn.vue
@@ -0,0 +1,218 @@
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/ui/mobileSwiperScroll.vue b/src/devTools/page/components/ui/mobileSwiperScroll.vue
new file mode 100644
index 0000000..d580290
--- /dev/null
+++ b/src/devTools/page/components/ui/mobileSwiperScroll.vue
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+
+
+
+ ↓下拉刷新
+
+
+ 松手刷新
+
+
+ 刷新中...
+
+
+ 刷新成功
+
+
+ 刷新失败
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/ui/requestSpeedLimit.vue b/src/devTools/page/components/ui/requestSpeedLimit.vue
new file mode 100644
index 0000000..41b079e
--- /dev/null
+++ b/src/devTools/page/components/ui/requestSpeedLimit.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/ui/requestTimeoutMock.vue b/src/devTools/page/components/ui/requestTimeoutMock.vue
new file mode 100644
index 0000000..6298d18
--- /dev/null
+++ b/src/devTools/page/components/ui/requestTimeoutMock.vue
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
diff --git a/src/devTools/page/components/ui/subTitleBar.vue b/src/devTools/page/components/ui/subTitleBar.vue
new file mode 100644
index 0000000..2ccd7d2
--- /dev/null
+++ b/src/devTools/page/components/ui/subTitleBar.vue
@@ -0,0 +1,86 @@
+
+
+
+
+ {{ title }}
+
+
+
+
+
+
+
+
+
diff --git a/src/devTools/page/index.nvue b/src/devTools/page/index.nvue
new file mode 100644
index 0000000..9d929bd
--- /dev/null
+++ b/src/devTools/page/index.nvue
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
diff --git a/src/devTools/page/static/copy.png b/src/devTools/page/static/copy.png
new file mode 100644
index 0000000..f38fbec
Binary files /dev/null and b/src/devTools/page/static/copy.png differ
diff --git a/src/devTools/page/static/delete.png b/src/devTools/page/static/delete.png
new file mode 100644
index 0000000..35d2a82
Binary files /dev/null and b/src/devTools/page/static/delete.png differ
diff --git a/src/devTools/page/static/fileSys/AI.png b/src/devTools/page/static/fileSys/AI.png
new file mode 100644
index 0000000..bc2dfda
Binary files /dev/null and b/src/devTools/page/static/fileSys/AI.png differ
diff --git a/src/devTools/page/static/fileSys/DWG.png b/src/devTools/page/static/fileSys/DWG.png
new file mode 100644
index 0000000..af62a2b
Binary files /dev/null and b/src/devTools/page/static/fileSys/DWG.png differ
diff --git a/src/devTools/page/static/fileSys/EXE.png b/src/devTools/page/static/fileSys/EXE.png
new file mode 100644
index 0000000..8d503e3
Binary files /dev/null and b/src/devTools/page/static/fileSys/EXE.png differ
diff --git a/src/devTools/page/static/fileSys/GIF.png b/src/devTools/page/static/fileSys/GIF.png
new file mode 100644
index 0000000..1355aa4
Binary files /dev/null and b/src/devTools/page/static/fileSys/GIF.png differ
diff --git a/src/devTools/page/static/fileSys/HTML.png b/src/devTools/page/static/fileSys/HTML.png
new file mode 100644
index 0000000..94b5066
Binary files /dev/null and b/src/devTools/page/static/fileSys/HTML.png differ
diff --git a/src/devTools/page/static/fileSys/PSD.png b/src/devTools/page/static/fileSys/PSD.png
new file mode 100644
index 0000000..d49ac0b
Binary files /dev/null and b/src/devTools/page/static/fileSys/PSD.png differ
diff --git a/src/devTools/page/static/fileSys/RVT.png b/src/devTools/page/static/fileSys/RVT.png
new file mode 100644
index 0000000..e42c8c2
Binary files /dev/null and b/src/devTools/page/static/fileSys/RVT.png differ
diff --git a/src/devTools/page/static/fileSys/SKP.png b/src/devTools/page/static/fileSys/SKP.png
new file mode 100644
index 0000000..f010651
Binary files /dev/null and b/src/devTools/page/static/fileSys/SKP.png differ
diff --git a/src/devTools/page/static/fileSys/SVG.png b/src/devTools/page/static/fileSys/SVG.png
new file mode 100644
index 0000000..20ca5b6
Binary files /dev/null and b/src/devTools/page/static/fileSys/SVG.png differ
diff --git a/src/devTools/page/static/fileSys/excel.png b/src/devTools/page/static/fileSys/excel.png
new file mode 100644
index 0000000..b32df14
Binary files /dev/null and b/src/devTools/page/static/fileSys/excel.png differ
diff --git a/src/devTools/page/static/fileSys/pdf.png b/src/devTools/page/static/fileSys/pdf.png
new file mode 100644
index 0000000..666c350
Binary files /dev/null and b/src/devTools/page/static/fileSys/pdf.png differ
diff --git a/src/devTools/page/static/fileSys/pptl.png b/src/devTools/page/static/fileSys/pptl.png
new file mode 100644
index 0000000..be7d58c
Binary files /dev/null and b/src/devTools/page/static/fileSys/pptl.png differ
diff --git a/src/devTools/page/static/fileSys/shipin.png b/src/devTools/page/static/fileSys/shipin.png
new file mode 100644
index 0000000..484661e
Binary files /dev/null and b/src/devTools/page/static/fileSys/shipin.png differ
diff --git a/src/devTools/page/static/fileSys/tupian.png b/src/devTools/page/static/fileSys/tupian.png
new file mode 100644
index 0000000..8acd7b6
Binary files /dev/null and b/src/devTools/page/static/fileSys/tupian.png differ
diff --git a/src/devTools/page/static/fileSys/txt.png b/src/devTools/page/static/fileSys/txt.png
new file mode 100644
index 0000000..93851bf
Binary files /dev/null and b/src/devTools/page/static/fileSys/txt.png differ
diff --git a/src/devTools/page/static/fileSys/weizhiwenjian.png b/src/devTools/page/static/fileSys/weizhiwenjian.png
new file mode 100644
index 0000000..de4fc3d
Binary files /dev/null and b/src/devTools/page/static/fileSys/weizhiwenjian.png differ
diff --git a/src/devTools/page/static/fileSys/wenjianjia.png b/src/devTools/page/static/fileSys/wenjianjia.png
new file mode 100644
index 0000000..406019d
Binary files /dev/null and b/src/devTools/page/static/fileSys/wenjianjia.png differ
diff --git a/src/devTools/page/static/fileSys/word.png b/src/devTools/page/static/fileSys/word.png
new file mode 100644
index 0000000..76ca19e
Binary files /dev/null and b/src/devTools/page/static/fileSys/word.png differ
diff --git a/src/devTools/page/static/fileSys/yasuo.png b/src/devTools/page/static/fileSys/yasuo.png
new file mode 100644
index 0000000..61f7193
Binary files /dev/null and b/src/devTools/page/static/fileSys/yasuo.png differ
diff --git a/src/devTools/page/static/fileSys/yinpin.png b/src/devTools/page/static/fileSys/yinpin.png
new file mode 100644
index 0000000..26cc0d0
Binary files /dev/null and b/src/devTools/page/static/fileSys/yinpin.png differ
diff --git a/src/devTools/page/static/fold.png b/src/devTools/page/static/fold.png
new file mode 100644
index 0000000..b11adb6
Binary files /dev/null and b/src/devTools/page/static/fold.png differ
diff --git a/src/devTools/page/static/menu.png b/src/devTools/page/static/menu.png
new file mode 100644
index 0000000..df91bf8
Binary files /dev/null and b/src/devTools/page/static/menu.png differ
diff --git a/src/devTools/page/static/refresh.png b/src/devTools/page/static/refresh.png
new file mode 100644
index 0000000..e9117de
Binary files /dev/null and b/src/devTools/page/static/refresh.png differ
diff --git a/src/devTools/page/static/unfold.png b/src/devTools/page/static/unfold.png
new file mode 100644
index 0000000..07f0929
Binary files /dev/null and b/src/devTools/page/static/unfold.png differ
diff --git a/src/devTools/tools.vue b/src/devTools/tools.vue
new file mode 100644
index 0000000..f30cb69
--- /dev/null
+++ b/src/devTools/tools.vue
@@ -0,0 +1,22 @@
+
+
+
+ Empty
+
+
+
+
diff --git a/src/main.ts b/src/main.ts
index 0064344..58e6060 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -7,6 +7,11 @@ import App from './App.vue'
import { prototypeInterceptor, requestInterceptor, routeInterceptor } from './interceptors'
import store from './store'
+import devTools from './devTools/index.js'
+import devToolsConfig from './devTools/config.js'
+import mpDevBubble from './devTools/core/components/mpDevBubble.vue'
+import devToolsVueMixin from './devTools/core/proxy/vueMixin.js'
+
export function createApp() {
const app = createSSRApp(App)
app.use(store)
@@ -14,6 +19,14 @@ export function createApp() {
app.use(requestInterceptor)
app.use(prototypeInterceptor)
app.use(VueQueryPlugin)
+ //混入DevTools生命周期监听
+ app.mixin(devToolsVueMixin)
+
+ //挂载Devtools
+ app.use(devTools, devToolsConfig)
+
+ //注册小程序端专用的拖动浮标组件
+ app.component('mpDevBubble', mpDevBubble)
return {
app,
diff --git a/src/pages.json b/src/pages.json
index d8c43f9..238f5e9 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -44,6 +44,26 @@
}
]
},
+ "subPackages": [
+ {
+ "root": "devTools/page",
+ "pages": [
+ {
+ "path": "index",
+ "style": {
+ "navigationStyle": "custom",
+ "softinputMode": "adjustResize",
+ "animationDuration": 1,
+ "animationType": "none",
+ "popGesture": "none",
+ "bounce": "none",
+ "titleNView": false
+ }
+ }
+ ]
+ }
+ ],
+ "__esModule": true,
"pages": [
{
"path": "pages/index/index",
@@ -97,6 +117,5 @@
"navigationBarTitleText": "修改密码"
}
}
- ],
- "subPackages": []
+ ]
}
\ No newline at end of file
diff --git a/src/pages/about/about.vue b/src/pages/about/about.vue
index 30e39e4..d446846 100644
--- a/src/pages/about/about.vue
+++ b/src/pages/about/about.vue
@@ -9,6 +9,7 @@
+
关于