Files
my-h5/uni_modules/UniDevTools/src/devTools/page/components/main.vue
2025-09-26 14:50:30 +08:00

1839 lines
53 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="devTools">
<!-- #ifndef APP-PLUS -->
<view class="navbar">
<view
class="navbarBody"
:style="{
marginTop: navbarStyle.statusBarHeightPx,
height: navbarStyle.navbarHeightPx,
}"
>
<view
class="left"
@click="back"
>
<image src="@/devTools/page/static/fold.png" />
<text class="margin-left-sm">返回</text>
</view>
<view
class="right"
:style="{ 'margin-right': windowInfo.navbar.capsuleRightPx + 'px' }"
v-if="tabList[tabIndex].canRefreshing"
@click="getPage(true)"
>
<text :class="tabList[tabIndex].isRefreshing ? 'refreshing' : ''">
{{ tabList[tabIndex].isRefreshing ? "刷新中" : "刷新" }}
</text>
<image src="@/devTools/page/static/refresh.png" />
</view>
</view>
</view>
<!-- #endif -->
<view
ref="mask"
class="mask"
@click="$devTools.hide()"
></view>
<view
@click.stop
ref="panel"
class="panel"
:style="{
height: panelHeight + 'px',
}"
>
<view class="head">
<scroll-view
:scroll-x="true"
class="tabList showScrollbars"
:class="isDialog ? '' : 'tab-h5'"
:show-scrollbar="false"
>
<view style="display: flex; flex-direction: row">
<view
v-for="(item, index) in tabList"
:key="index"
class="tabItem"
@click="tabIndex = index"
>
<text
class="tabText"
:class="index == tabIndex ? 'active' : ''"
>
{{ item.title }}
</text>
<view
class="tabLine"
v-if="index == tabIndex"
></view>
</view>
</view>
</scroll-view>
<view
v-if="isDialog"
class="close"
@click="$devTools.hide()"
>
<view class="line"></view>
<text class="icon">×</text>
</view>
</view>
<mobileSwiperScroll
:scrollHeight="listHeight"
:tabIndex="tabIndex"
:tabList="tabList"
@refresh="onrefresh"
@pullingdown="onpullingdown"
@tabIndexChange="tabIndex = $event"
ref="mobileSwiperScroll"
>
<template #default="{ item, index }">
<template v-if="item.title == 'Tools'">
<cell>
<tools
ref="tools"
@goOpenRequest="$refs.sendRequest.show()"
/>
</cell>
</template>
<template v-if="item.title == 'Error'">
<cell
v-for="(_item, _index) in item.data"
:key="_index"
>
<errorItem
:item="_item"
:key="_index"
/>
</cell>
</template>
<template v-if="item.title == 'Console'">
<cell
v-for="(_item, _index) in item.data"
:key="_index"
>
<consoleItem
:item="_item"
:key="_index"
/>
</cell>
</template>
<template v-if="item.title == 'Network'">
<cell
v-for="(_item, _index) in item.data"
:key="_index"
>
<networkItem
:item="_item"
:key="_index"
@goSendRequest="$refs.sendRequest.show($event, true)"
@goOpenRequest="$refs.sendRequest.show($event)"
/>
</cell>
</template>
<template v-if="item.title == 'Storage'">
<cell>
<storageList ref="storageList" />
</cell>
</template>
<template v-if="item.title == 'Pages'">
<cell>
<subTitleBar
:isOpen="item.isShowPages"
title="当前页面栈"
@click="pagesShowChange('isShowPages')"
/>
</cell>
<cell>
<pages
ref="pages"
:isShow="item.isShowPages"
/>
</cell>
<cell>
<view class="cellDivisionLine"></view>
</cell>
<cell>
<subTitleBar
:isOpen="item.isShowRouteList"
title="全部路由"
@click="pagesShowChange('isShowRouteList')"
/>
</cell>
<template v-if="item.isShowRouteList">
<cell
v-for="_item in item.routeList"
:key="_item.path"
>
<routeItem :item="_item" />
</cell>
</template>
<cell>
<view class="cellDivisionLine"></view>
</cell>
<cell>
<subTitleBar
:isOpen="item.isShowData"
title="页面停留统计"
@click="pagesShowChange('isShowData')"
/>
</cell>
<template v-if="item.isShowData">
<cell
v-for="(_item, _index) in item.data"
:key="_item.route"
>
<pageItem
:item="_item"
:key="_index"
/>
</cell>
</template>
<cell>
<view class="cellDivisionLine"></view>
</cell>
<cell>
<subTitleBar
:isOpen="item.dayOnlineShow"
title="日活跃时间统计"
@click="pagesShowChange('dayOnlineShow')"
/>
</cell>
<template v-if="item.dayOnlineShow">
<cell
v-for="(_item, _index) in item.dayOnlineList"
:key="_index"
>
<dayOnlineItem
@click="showOnlineDetailsDialog(_item)"
:item="_item"
:key="_index"
/>
</cell>
</template>
<cell>
<view class="cellDivisionLine"></view>
</cell>
</template>
<template v-if="item.title == 'Vuex'">
<cell>
<vuexList
ref="vuexList"
:stateType="tabList[tabList.findIndex((x) => x.title == 'Vuex')].stateType"
/>
</cell>
</template>
<template v-if="item.title == 'Logs'">
<cell
v-for="(_item, _index) in item.data"
:key="_index"
>
<logItem
:item="_item"
:key="_index"
/>
</cell>
</template>
<template v-if="item.title == 'Info'">
<cell>
<infoList ref="infoList" />
</cell>
</template>
<template v-if="item.title == 'UniBus'">
<cell>
<subTitleBar
:isOpen="item.isShowCount"
title="事件统计"
@click="pagesShowChange('isShowCount')"
/>
</cell>
<template v-if="item.isShowCount">
<cell
v-for="_item in item.countList"
:key="_item.e"
>
<view
class="eventItem"
@longpress.stop="busCountMenu(_item)"
>
<text class="eventName">name: {{ _item.e }}</text>
<view class="eventCount">
<text
v-if="item.filterType == '' || item.filterType == 'on'"
class="eventCountItem"
>
on: {{ _item.on }}
</text>
<text
v-if="item.filterType == '' || item.filterType == 'emit'"
class="eventCountItem"
>
emit: {{ _item.emit }}
</text>
<text
v-if="item.filterType == '' || item.filterType == 'once'"
class="eventCountItem"
>
once: {{ _item.once }}
</text>
<text
v-if="item.filterType == '' || item.filterType == 'off'"
class="eventCountItem"
>
off: {{ _item.off }}
</text>
</view>
</view>
</cell>
</template>
<cell>
<view class="cellDivisionLine"></view>
</cell>
<cell>
<subTitleBar
:isOpen="item.isShowLog"
title="执行记录"
@click="pagesShowChange('isShowLog')"
/>
</cell>
<template v-if="item.isShowLog">
<cell
v-for="(_item, _index) in item.logList"
:key="_index"
>
<view
class="eventLogItem"
@longpress.stop="busLogMenu(_item)"
>
<text class="logText">{{ _item.e }}</text>
<text class="logTime">{{ _item.date }}</text>
</view>
</cell>
</template>
<cell>
<view class="cellDivisionLine"></view>
</cell>
</template>
<template v-if="item.title == 'FileSys'">
<!-- #ifdef APP-PLUS || MP-WEIXIN -->
<cell
v-for="(_item, _index) in item.data"
:key="_index"
>
<fileSysItem
:item="_item"
:key="_index"
:dirType="item.dirType"
:dirList="item.dirList"
@goChildDir="goChildDir"
@getPage="getPage"
@editDirName="editDirName"
@openEditFileDialog="($event) => $refs.textFileEditDialog.show($event)"
/>
</cell>
<cell v-if="item.data.length == 0 || (item.data.length == 1 && item.data[0].type == 'back')">
<view class="devEmpty">
<text class="devEmptyText">空空如也</text>
</view>
</cell>
<!-- #endif -->
<!-- #ifndef APP-PLUS || MP-WEIXIN -->
<cell>
<view class="listCenterView">
<text class="listCenterText">该平台无FileSys文件管理</text>
</view>
</cell>
<!-- #endif -->
</template>
<template v-if="item.title == 'Setting'">
<cell>
<settingView ref="settingView" />
</cell>
</template>
<template v-if="item.title == 'JsRunner'">
<!-- #ifdef APP-PLUS || H5 -->
<cell
v-for="(_item, _index) in item.data"
:key="_index"
>
<jsRunnerItem
:item="_item"
:key="_index"
/>
</cell>
<cell v-if="item.data.length == 0">
<view class="devEmpty">
<text class="devEmptyText">动态执行JS代码</text>
</view>
</cell>
<!-- #endif -->
<!-- #ifndef APP-PLUS || H5 -->
<cell>
<view class="listCenterView">
<text class="listCenterText">该平台不支持该功能</text>
</view>
</cell>
<!-- #endif -->
</template>
<template v-if="item.isLongList">
<cell v-if="!item.isLoadAll && item.data.length == 30">
<view
class="loadAll"
@click="showListAll"
>
<image
class="loadAllFold"
src="@/devTools/page/static/unfold.png"
/>
<text class="loadAllText">展示全部数据</text>
<image
class="loadAllFold"
src="@/devTools/page/static/unfold.png"
/>
</view>
</cell>
<cell v-if="item.type == 'success' && item.data.length == 0">
<view class="devEmpty">
<text class="devEmptyText">空空如也</text>
</view>
</cell>
<cell v-if="(item.type == 'loading' || item.type == 'loading') && item.data.length == 0">
<view class="devEmpty">
<text class="devEmptyText">加载中</text>
</view>
</cell>
</template>
<cell>
<view style="height: 300rpx"></view>
</cell>
</template>
</mobileSwiperScroll>
</view>
<bottomTools
:tabIndex="tabIndex"
:tabTitle="tabTitle"
:options="bottomToolsOptions"
:isShow="isShowBottomTools"
:stateType="tabList[tabList.findIndex((x) => x.title == 'Vuex')].stateType"
@filterTypeChange="onFilterTypeChange"
@goChildDir="goChildDir"
@changeFileDirType="changeFileDirType"
@changeStorageType="changeStorageType"
@editDirName="editDirName"
@getPage="getPage"
@runJs="runJs"
@emptyCodeHis="emptyCodeHis"
@changeStateType="changeStateType"
/>
<editDialog ref="editDialog" />
<dayOnlinePageList ref="dayOnlinePageList" />
<sendRequest ref="sendRequest" />
<textFileEditDialog
ref="textFileEditDialog"
@getPage="getPage"
/>
<createDir
ref="createDir"
:dirList="tabList[11].dirList"
:dirType="tabList[11].dirType"
@getPage="getPage"
/>
<addStorage
:storageType="tabList[5].storageType"
@getPage="getPage"
/>
<routeDialog />
</view>
</template>
<script>
import init from "../../index";
import consoleItem from "./listItem/consoleItem.vue";
import infoList from "./listItem/infoList.vue";
import storageList from "./listItem/storageList.vue";
import tools from "./listItem/tools.vue";
import vuexList from "./listItem/vuexList.vue";
import animationControl from "./mixins/animationControl";
import { timeFormat, timeFromNow } from "../../core/libs/timeFormat";
import devCache from "../../core/libs/devCache";
import networkItem from "./listItem/networkItem.vue";
import pages from "./listItem/pages.vue";
import logItem from "./listItem/logItem.vue";
import pageItem from "./listItem/pageItem.vue";
import errorItem from "./listItem/errorItem.vue";
import mobileSwiperScroll from "./ui/mobileSwiperScroll.vue";
import subTitleBar from "./ui/subTitleBar.vue";
import dayOnlineItem from "./listItem/dayOnlineItem.vue";
import dayOnlinePageList from "./dialog/dayOnlinePageList.vue";
import sendRequest from "./dialog/sendRequest.vue";
import bottomTools from "./bottomTools.vue";
import dirReader from "./libs/dirReader";
import fileSysItem from "./listItem/fileSysItem.vue";
import fileSize from "./libs/fileSize";
import textFileEditDialog from "./dialog/textFileEditDialog.vue";
import createDir from "./dialog/createDir.vue";
import editDialog from "./dialog/editDialog.vue";
import addStorage from "./dialog/addStorage.vue";
import settingView from "./listItem/setting.vue";
import mp from "./mixins/mp";
import jsRunnerItem from "./listItem/jsRunnerItem.vue";
import routeItem from "./listItem/routeItem.vue";
import routeDialog from "./dialog/routeDialog.vue";
// #ifndef APP-PLUS
import h5Cell from "./ui/h5Cell.vue";
// #endif
/**
* 时间戳转时间文字
*/
function getTime(t) {
let c = "0s";
if (t < 60) {
c = t + "秒";
} else if (t < 60 * 60) {
c = Math.ceil(t / 60) + "分钟";
} else {
c = Math.ceil(t / 60 / 60) + "小时" + Math.ceil((t % (60 * 60)) / 60) + "分钟";
}
return c;
}
export default {
mixins: [animationControl, mp],
components: {
mobileSwiperScroll,
consoleItem,
storageList,
vuexList,
tools,
infoList,
networkItem,
pages,
logItem,
pageItem,
errorItem,
subTitleBar,
dayOnlinePageList,
dayOnlineItem,
sendRequest,
bottomTools,
fileSysItem,
textFileEditDialog,
createDir,
editDialog,
addStorage,
settingView,
jsRunnerItem,
routeItem,
routeDialog,
// #ifndef APP-PLUS
cell: h5Cell,
// #endif
},
data() {
return {
/**
* 是否处于弹窗状态
*/
isDialog: true,
tabLoading: true,
tabIndex: 0,
tabList: [
{
title: "Tools", //标题
hasLoad: false,
},
{
title: "Error", //标题
canRefreshing: true, //是否允许下拉刷新
isRefreshing: false, //下拉刷新状态
refreshType: "waitPullUp", //刷新状态 'waitPullUp','waitRelease','refreshing'
type: "init", //列表状态
data: [], //数据条数
hasLoad: false,
isLongList: true, // 是否长列表
isLoadAll: false, //是否加载全部
filterType: "", //过滤类型
isShowBottomTools: true, //显示底部工具栏
},
{
title: "Console", //标题
canRefreshing: true, //是否允许下拉刷新
isRefreshing: false, //下拉刷新状态
refreshType: "waitPullUp", //刷新状态 'waitPullUp','waitRelease','refreshing'
type: "init", //列表状态
data: [], //数据条数
hasLoad: false,
isLongList: true, // 是否长列表
isLoadAll: false, //是否加载全部
filterType: "", //过滤类型
isShowBottomTools: true, //显示底部工具栏
},
{
title: "Network", //标题
canRefreshing: true, //是否允许下拉刷新
isRefreshing: false, //下拉刷新状态
refreshType: "waitPullUp", //刷新状态 'waitPullUp','waitRelease','refreshing'
type: "init", //列表状态
data: [], //数据条数
hasLoad: false,
isLongList: true, // 是否长列表
isLoadAll: false, //是否加载全部
filterType: "", //过滤类型
isShowBottomTools: true, //显示底部工具栏
},
{
title: "JsRunner", //标题
canRefreshing: false, //是否允许下拉刷新
isRefreshing: false, //下拉刷新状态
refreshType: "waitPullUp", //刷新状态 'waitPullUp','waitRelease','refreshing'
type: "init", //列表状态
data: [], //数据条数
hasLoad: false,
// #ifdef APP-PLUS || H5
isShowBottomTools: true, //显示底部工具栏
// #endif
},
{
title: "Storage", //标题
canRefreshing: true, //是否允许下拉刷新
isRefreshing: false, //下拉刷新状态
refreshType: "waitPullUp", //刷新状态 'waitPullUp','waitRelease','refreshing'
hasLoad: false,
storageType: "localStorage", //缓存类型默认为localStorage
isShowBottomTools: true, //显示底部工具栏
},
{
title: "Pages", //标题
canRefreshing: true, //是否允许下拉刷新
isRefreshing: false, //下拉刷新状态
refreshType: "waitPullUp", //刷新状态 'waitPullUp','waitRelease','refreshing'
hasLoad: false,
isShowPages: true, //是否展示页面堆栈列表
data: [],
isShowData: false, //是否展示页面全部统计数据
dayOnlineList: [], //日浏览量日志
dayOnlineShow: false,
isShowBottomTools: true, //显示底部工具栏
isShowRouteList: false, // 是否显示路由列表
routeList: [], //全部路由列表
},
{
title: "Vuex", //标题
canRefreshing: true, //是否允许下拉刷新
isRefreshing: false, //下拉刷新状态
refreshType: "waitPullUp", //刷新状态 'waitPullUp','waitRelease','refreshing'
hasLoad: false,
stateType: "vuex", //全局变量类型 vuex pinia globalData
isShowBottomTools: true, //显示底部工具栏
},
{
title: "Logs", //标题
canRefreshing: true, //是否允许下拉刷新
isRefreshing: false, //下拉刷新状态
refreshType: "waitPullUp", //刷新状态 'waitPullUp','waitRelease','refreshing'
type: "init", //列表状态
data: [], //数据条数
page: 0, //当前页码
hasLoad: false,
isLoadAll: false, //是否加载全部
isLongList: true, // 是否长列表
isShowBottomTools: true, //显示底部工具栏
},
{
title: "Info", //标题
canRefreshing: true, //是否允许下拉刷新
isRefreshing: false, //下拉刷新状态
refreshType: "waitPullUp", //刷新状态 'waitPullUp','waitRelease','refreshing'
hasLoad: false,
},
{
title: "UniBus", //标题
canRefreshing: true, //是否允许下拉刷新
isRefreshing: false, //下拉刷新状态
refreshType: "waitPullUp", //刷新状态 'waitPullUp','waitRelease','refreshing'
type: "init", //列表状态
isShowCount: true,
countList: [],
isShowLog: false,
logList: [],
filterType: "", //过滤类型
hasLoad: false,
isShowBottomTools: true, //显示底部工具栏
},
{
title: "FileSys", //标题
canRefreshing: true, //是否允许下拉刷新
isRefreshing: false, //下拉刷新状态
refreshType: "waitPullUp", //刷新状态 'waitPullUp','waitRelease','refreshing'
type: "init", //列表状态
// #ifdef APP-PLUS
dirType: "PRIVATE_DOC", //文件类型 wx, PRIVATE_WWW, PRIVATE_DOC PUBLIC_DOCUMENTS PUBLIC_DOWNLOADS
// #endif
// #ifdef MP
dirType: "wx", //文件类型 wx, PRIVATE_WWW, PRIVATE_DOC
// #endif
data: [], //文件列表
dirList: [], //目录列表
hasLoad: false,
isShowBottomTools: true, //显示底部工具栏
},
{
title: "Setting", //标题
canRefreshing: true, //是否允许下拉刷新
isRefreshing: false, //下拉刷新状态
refreshType: "waitPullUp", //刷新状态 'waitPullUp','waitRelease','refreshing'
type: "init", //列表状态
data: [], //数据条数
page: 0, //当前页码
hasLoad: false,
},
],
};
},
watch: {
/**
* 监听tab改变事件
*/
tabIndex(index) {
if (!this.tabList[index].hasLoad) {
this.getPage();
}
},
},
computed: {
/**
* 挂载dev对象
*/
$devTools() {
return init;
},
/**
* 面板高度
*/
panelHeight() {
// #ifndef APP-PLUS
if (1) return this.windowInfo.system.windowHeight - this.windowInfo.navbar.heightPx;
// #endif
return Math.ceil(this.windowInfo.system.windowHeight * 0.8);
},
/**
* 滚动列表的高度
*/
listHeight() {
let sys = uni.getSystemInfoSync();
// #ifdef H5
if (1) return sys.windowHeight - 50 - uni.upx2px(80) - 1;
// #endif
// #ifdef MP-WEIXIN
if (1) return sys.windowHeight - this.windowInfo.navbar.heightPx - uni.upx2px(80) - 1;
// #endif
return this.panelHeight - uni.upx2px(80) - 1;
},
/**
* 当前tab栏的标题
*/
tabTitle() {
return this.tabList[this.tabIndex].title;
},
/**
* 获取底部栏需要的参数
*/
bottomToolsOptions() {
let that = this;
const getTabItemByTitle = (title) => {
let i = that.tabList.findIndex((x) => x.title == title);
return that.tabList[i];
};
return {
errorFilterType: getTabItemByTitle("Error").filterType,
consoleFilterType: getTabItemByTitle("Console").filterType,
networkFilterType: getTabItemByTitle("Network").filterType,
busFilterType: getTabItemByTitle("UniBus").filterType,
fileSysDirList: getTabItemByTitle("FileSys").dirList,
fileSysDirType: getTabItemByTitle("FileSys").dirType,
storageType: getTabItemByTitle("Storage").storageType,
codeHisLength: getTabItemByTitle("JsRunner").data.length,
};
},
/**
* 是否显示底部工具栏
*/
isShowBottomTools() {
// [1, 2, 3, 4, 5, 7, 9, 10].indexOf(tabIndex) != -1
let item = this.tabList[this.tabIndex];
return item.isShowBottomTools === true;
},
},
mounted() {
let that = this;
this.getPage();
// #ifndef APP-PLUS
this.isDialog = false;
// #endif
// ! 刷新当前列表
uni.$on("devTools_listGetPage", () => {
that.getPage();
});
// ! 删除指定请求记录
uni.$on("devTools_delNetworkItemById", (id) => {
let tabIndex = that.tabList.findIndex((x) => x.title == "Network");
let i = that.tabList[tabIndex].data.findIndex((x) => x.id == id);
if (i != -1) {
that.tabList[tabIndex].data.splice(i, 1);
}
});
// ! 删除日志
uni.$on("devTools_delLog", (item) => {
let tabIndex = that.tabList.findIndex((x) => x.title == "Logs");
let i = that.tabList[tabIndex].data.findIndex((x) => x.date == item.date && x.t == item.t);
if (i != -1) {
that.tabList[tabIndex].data.splice(i, 1);
}
});
// ! 删除错误记录
uni.$on("devTools_delError", (item) => {
let tabIndex = that.tabList.findIndex((x) => x.title == "Error");
let i = that.tabList[tabIndex].data.findIndex(
(x) => x.type == item.type && x.t == item.t && x.m == item.m && x.tr == item.tr && x.p == item.p
);
if (i != -1) {
that.tabList[tabIndex].data.splice(i, 1);
}
});
// ! 删除打印日志
uni.$on("devTools_delConsoleItem", (item) => {
let tabIndex = that.tabList.findIndex((x) => x.title == "Console");
let i = that.tabList[tabIndex].data.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.tabList[tabIndex].data.splice(i, 1);
}
});
// !初始化Vuex自动推断全局变量类型
let vIndex = that.tabList.findIndex((x) => x.title == "Vuex");
let vType = "globalData";
if (that.$store) {
vType = "vuex";
} else if (uni.Pinia || that.$pinia) {
vType = "pinia";
}
// console.log("vType", vType);
that.tabList[vIndex].stateType = vType;
},
methods: {
/**
* 页面加载事件
*/
pageOnLoad(options) {
let that = this;
that.$nextTick(() => {
// #ifdef APP-PLUS
that.panelShow();
// #endif
});
uni.$once("devTools_closeDevToolsPanel", () => {
that.panelHide();
});
},
/**
* 加载列表
*/
async getPage() {
let that = this;
const index = Number(that.tabIndex);
let item = that.tabList[index];
if (item.isRefreshing) return;
await that.awaitNextTick();
if (item.title == "Storage") {
that.refreshStorage();
} else if (item.title == "Vuex") {
that.refreshVuex();
} else if (item.title == "Info") {
that.refreshInfo();
} else if (item.title == "Pages") {
that.getPages();
} else if (item.title == "Console") {
that.getConsole();
} else if (item.title == "Network") {
that.getNetwork();
} else if (item.title == "Logs") {
that.getLogs();
} else if (item.title == "Error") {
that.getError();
} else if (item.title == "UniBus") {
that.getUniBus();
} else if (item.title == "FileSys") {
that.getDirList();
} else if (item.title == "Setting") {
let tabIndex = that.tabList.findIndex((x) => x.title == "Setting");
that.tabList[tabIndex].isRefreshing = true;
that.tabList[tabIndex].type = "loading";
that.$refs.settingView.getPage();
setTimeout(() => {
that.tabList[tabIndex].isRefreshing = false;
that.tabList[tabIndex].refreshType = "success";
that.tabList[tabIndex].type = "success";
setTimeout(() => {
that.tabList[tabIndex].refreshType = "waitRelease";
}, 1000);
}, 500);
}
that.tabList[index].hasLoad = true;
},
/**
* 等待渲染完成
*/
awaitNextTick() {
let that = this;
return new Promise((yes) => {
// #ifdef APP-PLUS
setTimeout(() => {
yes();
}, 200);
// #endif
// #ifndef APP-PLUS
that.$nextTick(() => {
setTimeout(() => {
yes();
}, 200);
});
// #endif
});
},
/**
* 隐藏弹窗
*/
hide() {
init.hide();
},
/**
* 下拉刷新事件
*/
onrefresh() {
if (!this.tabList[this.tabIndex].canRefreshing || this.tabList[this.tabIndex].type == "loading") return false;
this.getPage(true);
},
/**
* 下拉过程触发
*/
onpullingdown(args) {
let e = args.event;
if (this.tabList[this.tabIndex].refreshType == "refreshing") return false;
if (e.pullingDistance > e.viewHeight) {
this.tabList[this.tabIndex].refreshType = "waitRelease";
} else {
this.tabList[this.tabIndex].refreshType = "waitPullUp";
}
},
/**
* 刷新缓存数据
*/
refreshStorage() {
let that = this;
let tabIndex = that.tabList.findIndex((x) => x.title == "Storage");
that.tabList[tabIndex].isRefreshing = true;
that.tabList[tabIndex].refreshType = "refreshing";
that.$refs.storageList.getData(that.tabList[tabIndex].storageType);
setTimeout(() => {
that.tabList[tabIndex].isRefreshing = false;
that.tabList[tabIndex].refreshType = "success";
setTimeout(() => {
that.tabList[tabIndex].refreshType = "waitRelease";
}, 1000);
}, 500);
},
/**
* 刷新vuex数据
*/
refreshVuex() {
let that = this;
let tabIndex = that.tabList.findIndex((x) => x.title == "Vuex");
that.tabList[tabIndex].isRefreshing = true;
that.tabList[tabIndex].refreshType = "refreshing";
that.$refs.vuexList.getData();
setTimeout(() => {
that.tabList[tabIndex].isRefreshing = false;
that.tabList[tabIndex].refreshType = "success";
setTimeout(() => {
that.tabList[tabIndex].refreshType = "waitRelease";
}, 1000);
}, 500);
},
/**
* 刷新pages
*/
getPages() {
let that = this;
let tabIndex = that.tabList.findIndex((x) => x.title == "Pages");
that.tabList[tabIndex].isRefreshing = true;
that.tabList[tabIndex].refreshType = "refreshing";
that.tabList[tabIndex].data = [];
that.$refs.pages.getData();
let data = devCache.get("pageCount");
if (!data) data = [];
data = data.map((x) => {
x.timeCount = getTime(x.activeTimeCount);
return x;
});
let onlineList = devCache.get("dayOnline");
if (!onlineList) onlineList = [];
onlineList = onlineList.map((x) => {
x.timeCount = getTime(x.activeTimeCount);
x.page = x.page.map((v) => {
v.timeCount = getTime(v.t);
return v;
});
return x;
});
let pages = [];
// #ifdef H5 || APP-PLUS
try {
pages = __uniRoutes.map((item) => {
if (item.alias) {
item.path = item.alias;
}
return item;
});
} catch (error) {
pages = [];
}
// #endif
// #ifdef MP-WEIXIN
try {
let wxPages = __wxConfig.pages;
pages = wxPages.map((item) => {
return {
path: "/" + item,
};
});
} catch (error) {
pages = [];
}
// #endif
setTimeout(() => {
that.tabList[tabIndex].data = data;
that.tabList[tabIndex].routeList = pages;
that.tabList[tabIndex].dayOnlineList = onlineList;
that.tabList[tabIndex].refreshType = "success";
that.tabList[tabIndex].isRefreshing = false;
setTimeout(() => {
that.tabList[tabIndex].refreshType = "waitRelease";
}, 1000);
}, 500);
},
/**
* 改变pages列表展示状态
*/
pagesShowChange(key) {
this.tabList[this.tabIndex][key] = !this.tabList[this.tabIndex][key];
},
/**
* 打开在线统计详情弹窗
*/
showOnlineDetailsDialog(item) {
this.$refs.dayOnlinePageList.show(item);
},
/**
* 刷新info
*/
refreshInfo() {
let that = this;
let tabIndex = that.tabList.findIndex((x) => x.title == "Info");
that.tabList[tabIndex].isRefreshing = true;
that.tabList[tabIndex].isRefreshing = true;
that.tabList[tabIndex].refreshType = "refreshing";
that.$refs.infoList.getData();
setTimeout(() => {
that.tabList[tabIndex].isRefreshing = false;
that.tabList[tabIndex].refreshType = "success";
setTimeout(() => {
that.tabList[tabIndex].refreshType = "waitRelease";
}, 1000);
}, 500);
},
/**
* 加载数据
*/
getConsole() {
let that = this;
let tabIndex = that.tabList.findIndex((x) => x.title == "Console");
that.tabList[tabIndex].isRefreshing = true;
that.tabList[tabIndex].type = "loading";
let data = devCache.get("console");
if (!data) data = [];
data = data.map((item) => {
// item.date = timeFormat(item.time);
item.date = timeFromNow(item.time);
return item;
});
let filterType = that.tabList[tabIndex].filterType;
if (filterType != "") {
data = data.filter((x) => x.type == filterType);
}
if (data.length > 30 && that.tabList[tabIndex].isLongList && !that.tabList[tabIndex].isLoadAll) {
data.splice(30, data.length - 1);
}
that.tabList[tabIndex].data = data;
setTimeout(() => {
that.tabList[tabIndex].refreshType = "success";
that.tabList[tabIndex].isRefreshing = false;
that.tabList[tabIndex].type = "success";
setTimeout(() => {
that.tabList[tabIndex].refreshType = "waitRelease";
}, 1000);
}, 500);
},
/**
* 加载请求数据
*/
getNetwork() {
let that = this;
let tabIndex = that.tabList.findIndex((x) => x.title == "Network");
that.tabList[tabIndex].isRefreshing = true;
that.tabList[tabIndex].type = "loading";
let data = devCache.get("request");
if (!data) data = [];
data = data.map((item) => {
item.date = timeFromNow(item.sendTime);
return item;
});
let filterType = that.tabList[tabIndex].filterType;
if (filterType != "") {
if (filterType == "请求失败") {
data = data.filter((x) => x.type == 2 || (x.type == 1 && x.responseStatus != 200));
} else if (filterType == "其他") {
data = data.filter((x) => x.method != "get" && x.method != "post");
} else if (filterType == "10s+") {
data = data.filter((x) => x.type != 0 && x.useTime > 10);
} else if (filterType == "500KB+") {
data = data.filter((x) => x.type == 1 && x.responseBodySize > 1024 * 500);
} else {
data = data.filter((x) => x.method == filterType);
}
}
if (data.length > 30 && that.tabList[tabIndex].isLongList && !that.tabList[tabIndex].isLoadAll) {
data.splice(30, data.length - 1);
}
that.tabList[tabIndex].data = data;
setTimeout(() => {
that.tabList[tabIndex].isRefreshing = false;
that.tabList[tabIndex].refreshType = "success";
that.tabList[tabIndex].type = "success";
setTimeout(() => {
that.tabList[tabIndex].refreshType = "waitRelease";
}, 1000);
}, 500);
},
/**
* 获取日志
*/
getLogs() {
let that = this;
let tabIndex = that.tabList.findIndex((x) => x.title == "Logs");
that.tabList[tabIndex].isRefreshing = true;
that.tabList[tabIndex].type = "loading";
let data = devCache.get("logReport");
if (!data) data = [];
data = data.map((x) => {
x.date = timeFromNow(x.t);
return x;
});
if (data.length > 30 && that.tabList[tabIndex].isLongList && !that.tabList[tabIndex].isLoadAll) {
data.splice(30, data.length - 1);
}
that.tabList[tabIndex].data = data;
setTimeout(() => {
that.tabList[tabIndex].isRefreshing = false;
that.tabList[tabIndex].refreshType = "success";
that.tabList[tabIndex].type = "success";
setTimeout(() => {
that.tabList[tabIndex].refreshType = "waitRelease";
}, 1000);
}, 500);
},
/**
* 获取错误列表
*/
getError() {
let that = this;
let tabIndex = that.tabList.findIndex((x) => x.title == "Error");
that.tabList[tabIndex].isRefreshing = true;
that.tabList[tabIndex].type = "loading";
let data = devCache.get("errorReport");
if (!data) data = [];
data = data.map((x) => {
x.date = timeFromNow(x.t);
return x;
});
let filterType = that.tabList[tabIndex].filterType;
if (filterType != "") {
data = data.filter((x) => x.type == filterType);
}
if (data.length > 30 && that.tabList[tabIndex].isLongList && !that.tabList[tabIndex].isLoadAll) {
data.splice(30, data.length - 1);
}
that.tabList[tabIndex].data = data;
setTimeout(() => {
that.tabList[tabIndex].isRefreshing = false;
that.tabList[tabIndex].refreshType = "success";
that.tabList[tabIndex].type = "success";
setTimeout(() => {
that.tabList[tabIndex].refreshType = "waitRelease";
}, 1000);
}, 500);
},
/**
* 加载全部列表
*/
showListAll() {
let that = this;
that.tabList[that.tabIndex].isLoadAll = true;
that.tabList[that.tabIndex].data = [];
that.tabList[that.tabIndex].type = "loading";
that.getPage();
},
/**
* 加载eventBus日志列表
*/
getUniBus() {
let that = this;
let tabIndex = that.tabList.findIndex((x) => x.title == "UniBus");
that.tabList[tabIndex].isRefreshing = true;
that.tabList[tabIndex].refreshType = "refreshing";
let logList = devCache.get("uniBus");
if (!logList) logList = [];
let busCount = devCache.get("busCount");
if (!busCount) busCount = [];
logList = logList.map((x) => {
x.date = timeFormat(x.t);
return x;
});
let filterType = that.tabList[tabIndex].filterType;
if (filterType != "") {
logList = logList.filter((x) => x.e.indexOf(`${filterType}>`) == 0);
}
setTimeout(() => {
that.tabList[tabIndex].logList = logList;
that.tabList[tabIndex].countList = busCount;
that.tabList[tabIndex].refreshType = "success";
that.tabList[tabIndex].isRefreshing = false;
setTimeout(() => {
that.tabList[tabIndex].refreshType = "waitRelease";
}, 1000);
}, 500);
},
/**
* 长按BUS事件
*/
busCountMenu(item) {
let that = this;
let menu = [
{
text: `复制名称`,
click() {
uni.setClipboardData({
data: item.e,
});
},
},
{
text: `复制完整日志`,
click() {
uni.setClipboardData({
data: JSON.stringify(item),
});
},
},
];
uni.showActionSheet({
itemList: menu.map((x) => x.text),
success({ tapIndex }) {
menu[tapIndex].click();
},
});
},
/**
* 长按BUS log事件
*/
busLogMenu(item) {
let that = this;
let menu = [
{
text: `复制名称`,
click() {
uni.setClipboardData({
data: item.e,
});
},
},
{
text: `复制完整日志`,
click() {
uni.setClipboardData({
data: JSON.stringify(item),
});
},
},
];
uni.showActionSheet({
itemList: menu.map((x) => x.text),
success({ tapIndex }) {
menu[tapIndex].click();
},
});
},
/**
* 列表过滤类型改变事件
*/
onFilterTypeChange(type) {
this.tabList[this.tabIndex].filterType = type;
this.getPage();
},
/**
* 加载文件列表
*/
async getDirList() {
let that = this;
let tabIndex = that.tabList.findIndex((x) => x.title == "FileSys");
// #ifndef APP-PLUS || MP-WEIXIN
if (1) return;
// #endif
that.tabList[tabIndex].isRefreshing = true;
that.tabList[tabIndex].type = "loading";
let path = "";
let data = [];
// #ifdef APP-PLUS
if (that.tabList[tabIndex].dirType == "PRIVATE_DOC") {
path = "_doc";
} else if (that.tabList[tabIndex].dirType == "PRIVATE_WWW") {
path = "_www";
} else if (that.tabList[tabIndex].dirType == "PUBLIC_DOCUMENTS") {
path = "_documents";
} else if (that.tabList[tabIndex].dirType == "PUBLIC_DOWNLOADS") {
path = "_downloads";
}
if (that.tabList[tabIndex].dirList) {
that.tabList[tabIndex].dirList.forEach((item) => {
path = path + "/" + item;
});
}
data = await dirReader.getDirFileList(path);
// #endif
// #ifdef MP-WEIXIN
let fs = wx.getFileSystemManager();
path = wx.env.USER_DATA_PATH;
if (that.tabList[tabIndex].dirList) {
that.tabList[tabIndex].dirList.forEach((item) => {
path = path + "/" + item;
});
}
let fileList = [];
let dirList = [];
let list = fs.readdirSync(path + "/");
for (let i = 0; i < list.length; i++) {
let name = list[i];
let stats = fs.statSync(path + "/" + name);
let row = {
type: stats.isFile() ? "file" : "dir",
size: stats.size,
time: stats.lastModifiedTime,
name,
fileType: name.split(".").pop(),
};
if (stats.isFile()) {
fileList.push(row);
} else {
dirList.push(row);
}
}
data = [...dirList, ...fileList];
// #endif
data = data.map((x) => {
x.date = timeFormat(x.time, "yyyy-mm-dd hh:MM");
x.sizeText = fileSize.getByteSize(x.size);
x.icon = x.type == "dir" ? "/devTools/page/static/fileSys/wenjianjia.png" : dirReader.getFileIcon(x.fileType);
return x;
});
if (that.tabList[tabIndex].dirList.length > 0) {
data.unshift({
type: "back",
name: "返回上一级",
});
}
that.tabList[tabIndex].data = data;
setTimeout(() => {
that.tabList[tabIndex].isRefreshing = false;
that.tabList[tabIndex].refreshType = "success";
that.tabList[tabIndex].type = "success";
setTimeout(() => {
that.tabList[tabIndex].refreshType = "waitRelease";
}, 1000);
}, 500);
},
/**
* 打开文件夹
*/
goChildDir(dirName) {
let that = this;
let tabIndex = that.tabList.findIndex((x) => x.title == "FileSys");
if (typeof dirName == "string" && dirName.indexOf("_goIndex_") == 0) {
let i = Number(dirName.replace("_goIndex_", ""));
that.tabList[tabIndex].dirList.splice(i, 99999);
} else if (dirName == "__back__") {
if (that.tabList[tabIndex].dirList.length > 0) {
that.tabList[tabIndex].dirList.splice(that.tabList[tabIndex].dirList.length - 1, 1);
}
} else {
that.tabList[tabIndex].dirList.push(dirName);
}
that.getPage();
},
/**
* 更改文件管理器地址
*/
changeFileDirType(type) {
let that = this;
let tabIndex = that.tabList.findIndex((x) => x.title == "FileSys");
this.tabList[tabIndex].dirType = type;
this.tabList[tabIndex].dirList = [];
this.tabList[tabIndex].data = [];
this.getPage();
},
/**
* 编辑文件夹或创建文件夹
*/
editDirName(options) {
this.$refs.createDir.show(options);
},
/**
* 更改缓存类型
*/
changeStorageType(type) {
let that = this;
let tabIndex = that.tabList.findIndex((x) => x.title == "Storage");
this.tabList[tabIndex].storageType = type;
this.getPage();
},
/**
* 运行js代码
*/
runJs({ code, type }) {
let that = this;
let tabIndex = that.tabList.findIndex((x) => x.title == "JsRunner");
let id = new Date().getTime() + "_" + Math.random();
this.tabList[tabIndex].data.push({
id,
code,
result: "",
isEnd: false,
});
let logIndex = this.tabList[tabIndex].data.findIndex((x) => x.id == id);
let context;
try {
// #ifdef H5
context = window;
// #endif
// #ifdef APP-PLUS
context = globalThis;
// #endif
} catch (error) {}
let result = undefined;
uni.hideKeyboard();
if (type == "h5" || type == "nvue") {
try {
let fun = ("ev" + "__混淆__" + "al").replace("__混淆__", "");
if (context && context[fun]) {
result = context[fun](code);
} else {
result = "当前环境不支持运行js!";
}
} catch (error) {
if (error && error.message) {
result = error.message;
}
}
} else {
setTimeout(() => {
try {
uni.$once("devTools_jsRunnerCallback", (result) => {
console.log("result", result);
this.$set(this.tabList[this.tabIndex].data[logIndex], "result", result);
this.$set(this.tabList[this.tabIndex].data[logIndex], "isEnd", true);
this.$refs.mobileSwiperScroll.scrollToBottom();
});
uni.$emit("devTools_jsRunner", code);
} catch (error) {}
}, 1);
}
let his = devCache.get("codeRunHis");
if (!his) his = [];
let i = his.indexOf(code);
if (i > -1) {
his.splice(i, 1);
}
his.unshift(code);
if (his.length > 100) {
his.splice(100, 9999999);
}
devCache.set("codeRunHis", his);
this.$set(this.tabList[this.tabIndex].data[logIndex], "result", result);
this.$set(this.tabList[this.tabIndex].data[logIndex], "isEnd", true);
this.$refs.mobileSwiperScroll.scrollToBottom();
},
/**
* 清空js运行记录
*/
emptyCodeHis() {
let that = this;
let tabIndex = that.tabList.findIndex((x) => x.title == "JsRunner");
uni.showToast({
title: "清空成功!",
icon: "success",
});
that.tabList[tabIndex].data = [];
},
/**
* Vuex变量类型改变事件
*/
changeStateType(type) {
let that = this;
let tabIndex = that.tabList.findIndex((x) => x.title == "Vuex");
this.tabList[tabIndex].stateType = type;
this.getPage();
},
},
};
</script>
<style lang="scss" scoped>
view,
text {
box-sizing: border-box;
}
.devTools {
/* #ifdef APP-PLUS */
flex: 1;
/* #endif */
/* #ifndef APP-PLUS */
height: 100vh;
/* #endif */
width: 750rpx;
position: relative;
.mask {
position: fixed;
z-index: 1;
width: 750rpx;
flex: 1;
/* #ifndef APP-PLUS */
height: 100vh;
/* #endif */
background-color: rgba(0, 0, 0, 0.3);
opacity: 0;
}
.panel {
position: fixed;
z-index: 2;
bottom: 0px;
left: 0px;
background-color: #ffffff;
display: flex;
flex-direction: column;
width: 750rpx;
color: #333;
/* #ifdef APP-PLUS */
border-radius: 20rpx 20rpx 0 0;
/* #endif */
.head {
width: 750rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
height: 80rpx;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
.tabList {
height: 80rpx;
width: 670rpx;
display: flex;
flex-direction: row;
white-space: nowrap;
&.tab-h5 {
width: 750rpx !important;
}
.uni-scroll-view-content {
display: flex !important;
flex-direction: row !important;
}
.tabItem {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
height: 80rpx;
padding: 0 20rpx;
min-width: 90rpx;
position: relative;
.tabText {
font-size: 28rpx;
color: #888;
line-height: 80rpx;
}
.tabText.active {
font-size: 28rpx;
color: #ff2d55;
font-weight: bold;
}
.tabLine {
position: absolute;
bottom: -1px;
height: 2px;
width: 100rpx;
background-color: #ff2d55;
}
}
}
.close {
height: 80rpx;
width: 80rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
position: relative;
.icon {
font-size: 40rpx;
line-height: 40rpx;
text-align: center;
color: #ff2d55;
}
.line {
position: absolute;
left: 0;
height: 40rpx;
width: 1px;
background-color: rgba(0, 0, 0, 0.05);
}
}
}
}
}
.cellDivisionLine {
height: 1px;
width: 750rpx;
background-color: rgba(0, 0, 0, 0.1);
}
.navbar {
position: fixed;
background-color: #fff;
width: 750rpx;
z-index: 2;
border-bottom: 1px solid rgba(0, 0, 0, 0.03);
.navbarBody {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
color: #333;
width: 710rpx;
margin-left: 20rpx;
.left {
display: flex;
flex-direction: row;
align-items: center;
font-size: 26rpx;
line-height: 26rpx;
image {
transform: rotate(-90deg);
width: 30rpx;
height: 30rpx;
}
}
.right {
display: flex;
flex-direction: row;
align-items: center;
font-size: 26rpx;
line-height: 26rpx;
color: #ff2d55;
image {
width: 30rpx;
height: 30rpx;
margin-left: 5rpx;
}
.refreshing {
color: #aaa !important;
}
}
}
}
.navbarEmpty {
position: fixed;
}
.loadAll:active {
background-color: rgba(0, 0, 0, 0.03);
}
.loadAll {
width: 750rpx;
height: 80rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
.loadAllFold {
width: 30rpx;
height: 30rpx;
}
.loadAllText {
font-size: 26rpx;
color: #333;
margin: 0 10rpx;
}
}
.cellDivisionLine {
height: 1px;
width: 750rpx;
background-color: rgba(0, 0, 0, 0.1);
}
.cellDivisionLine {
height: 1px;
width: 750rpx;
background-color: rgba(0, 0, 0, 0.1);
}
.devEmpty {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
height: 200rpx;
width: 750rpx;
.devEmptyText {
font-size: 26rpx;
color: #888;
line-height: 26rpx;
}
}
.eventItem {
display: flex;
flex-direction: column;
width: 750rpx;
padding: 10rpx 20rpx;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
&:active {
background-color: rgba(0, 0, 0, 0.05);
}
.eventName {
font-size: 20rpx;
color: #333;
}
.eventCount {
margin-top: 4rpx;
display: flex;
flex-direction: row;
justify-content: space-between;
.eventCountItem {
width: 170rpx;
max-width: 170rpx;
font-size: 20rpx;
color: #333;
}
}
}
.eventLogItem {
display: flex;
flex-direction: column;
width: 750rpx;
padding: 10rpx 20rpx;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
&:active {
background-color: rgba(0, 0, 0, 0.05);
}
.logText {
font-size: 20rpx;
color: #333;
}
.logTime {
font-size: 20rpx;
color: #999;
}
}
.listCenterView {
width: 750rpx;
min-height: 200rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
.listCenterText {
font-size: 24rpx;
color: #999;
}
}
</style>