747 lines
20 KiB
Vue
Raw Normal View History

<template>
<view class="settingView">
<view v-if="loading" class="loading">
<text class="loadingText">加载中</text>
</view>
<template v-else>
<subTitleBar
:isOpen="exportIsShow"
@click="exportIsShow = !exportIsShow"
title="导出全部日志"
/>
<template v-if="exportIsShow">
<view class="delBtn" @click="exportJsonFile">
<text class="delBtnText">导出日志文件(.json)</text>
</view>
</template>
<view class="divisionLine"></view>
<subTitleBar
:isOpen="cacheListIsShow"
title="清空全部缓存"
@click="cacheListIsShow = !cacheListIsShow"
/>
<template v-if="cacheListIsShow">
<view
v-for="(item, index) in cacheSelectList"
:key="index"
@click.stop="doSelectCache(index)"
class="checkboxItem"
>
<checkbox :value="item.check ? '1' : '0'" :checked="item.check" color="#ff2d55" />
<text
class="name"
:style="{
color: item.count ? '#333' : '#888',
}"
>
{{ item.name }}
</text>
<text v-if="item.key == 'file'"></text>
<text v-else-if="item.count" class="count">({{ item.count }})</text>
<text v-else class="empty">()</text>
</view>
<view class="delBtn" @click="delCache">
<text class="delBtnText">清空选中</text>
</view>
</template>
<view class="divisionLine"></view>
<subTitleBar
:isOpen="configIsShow"
title="DevTools当前配置参数"
@click="configIsShow = !configIsShow"
/>
<view v-if="configIsShow" class="objectAnalysisView">
<objectAnalysis :isOpenFirst="true" :data="config" :width="710" />
</view>
<view class="divisionLine"></view>
<subTitleBar :showArrow="false" title="关于" />
<view class="about">
<view>
<text class="row">Copyright©2024 福州重塑网络科技有限公司 前端团队</text>
</view>
<view @click="goUrl('https://dev.api0.cn')" style="display: flex; flex-direction: row">
<text class="row">在线文档</text>
<text class="row" style="color: #ff2d55">https://dev.api0.cn</text>
</view>
<view>
<text class="row">当前版本v{{ config.version }}</text>
</view>
</view>
<view style="height: 100rpx"></view>
</template>
</view>
</template>
<script>
import devCache from '../../../core/libs/devCache'
import devOptions from '../../../core/libs/devOptions'
import jsonCompress from '../../../core/libs/jsonCompress'
import appDelDir from '../libs/appDelDir'
import subTitleBar from '../ui/subTitleBar.vue'
import objectAnalysis from './objectAnalysis.vue'
import getRuntimeInfo from '../libs/getRuntimeInfo'
export default {
components: {
subTitleBar,
objectAnalysis,
},
data() {
return {
/**
* 是否加载中
*/
loading: false,
/**
* 缓存列表是否展示
*/
cacheListIsShow: false,
/**
* 缓存列表
*/
cacheSelectList: [],
/**
* 配置文件是否显示
*/
configIsShow: false,
/**
* 当前配置
*/
config: devOptions.getOptions(),
/**
* 是否显示导出日志按钮
*/
exportIsShow: false,
}
},
methods: {
/**
* 加载页面
*/
async getPage() {
let that = this
that.loading = true
that.cacheSelectList = await that.countCache()
that.loading = false
},
/**
* 统计缓存信息
*/
countCache() {
let that = this
return new Promise(async (yes) => {
let cacheSelectList = []
// dev 工具日志
let keys = {
errorReport: 'Error错误日志',
console: 'Console打印日志',
request: 'Request请求日志',
logReport: 'Logs日志',
uniBus: 'UniBus函数日志',
}
Object.keys(keys).map((key) => {
let logs = devCache.get(key)
cacheSelectList.push({
name: keys[key],
check: logs.length > 0,
count: logs.length,
key,
})
})
// #ifdef H5
let indexDBList = await this.getIndexDBList()
let cookieLength = document.cookie.split(';').length
if (document.cookie == '') {
cookieLength = 0
}
// #endif
cacheSelectList = cacheSelectList.concat([
that.countStorageCache(),
// #ifdef H5
{
key: 'sessionStorage',
name: 'SessionStorage临时缓存',
check: sessionStorage.length > 0,
count: sessionStorage.length,
},
// #endif
// #ifdef APP-PLUS
{
key: 'file',
name: 'FileSys本地文件(_doc)',
check: false,
count: '未知 // TODO',
},
// #endif
// #ifdef MP-WEIXIN
{
name: 'FileSys本地文件(FileSystemManager)',
check: false,
key: 'file',
count: '未知 // TODO',
},
// #endif
{
key: 'pageCount',
name: 'Pages页面停留统计',
check: devCache.get('pageCount').length > 0,
count: devCache.get('pageCount').length,
},
{
key: 'dayOnline',
name: 'Pages日活时间统计',
check: devCache.get('dayOnline').length > 0,
count: devCache.get('dayOnline').length,
},
// #ifdef H5
{
key: 'cookie',
name: 'Cookie',
check: cookieLength > 0,
count: cookieLength,
},
{
key: 'IndexDB',
name: 'IndexDB',
check: indexDBList.length > 0,
count: indexDBList.length,
},
// #endif
])
yes(cacheSelectList)
})
},
/**
* 统计本地缓存
*/
countStorageCache() {
let n = 0
// #ifdef APP-PLUS
let keys = plus.storage.getAllKeys()
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
if (key.indexOf('devTools_') == 0) {
// 忽略以 devTools_ 开头的key
continue
}
n++
}
// #endif
// #ifdef H5
for (let i = 0; i < localStorage.length; i++) {
let key = localStorage.key(i)
if (key.indexOf('devTools_') == 0) {
continue
}
n++
}
// #endif
// #ifdef MP
let keyList = devCache.get('storage')
if (!keyList) keyList = []
for (let i = 0; i < keyList.length; i++) {
const key = keyList[i]
if (key.indexOf('devTools_') == 0) {
continue
}
n++
}
// #endif
return {
key: 'localStorage',
name: 'localStorage本地缓存',
check: n > 0,
count: n,
}
},
/**
* 获取indexDB列表
*/
getIndexDBList() {
return new Promise((yes) => {
try {
indexedDB.databases().then((list) => {
yes(list)
})
} catch (error) {
console.log('getIndexDBList error', error)
yes([])
}
})
},
/**
* 选择清空的缓存项目
*/
doSelectCache(index) {
this.cacheSelectList[index].check = !this.cacheSelectList[index].check
},
/**
* 清空缓存
*/
delCache() {
let that = this
let selectedKey = []
that.cacheSelectList.map((item) => {
if (item.check) {
selectedKey.push(item.key)
}
})
let keyDelFun = {
errorReport() {
devCache.set('errorReport', [])
},
console() {
uni.$emit('devTools_delConsoleAll')
},
request() {
uni.$emit('devTools_delNetworkAll')
},
logReport() {
devCache.set('logReport', [])
},
uniBus() {
uni.$emit('devTools_delUniBusAll')
},
localStorage() {
// #ifdef APP-PLUS
let keys = plus.storage.getAllKeys()
for (let i = 0; i < keys.length; i++) {
const key = String(keys[i])
if (key.indexOf('devTools_') == 0) {
continue
}
uni.removeStorageSync(key)
}
// #endif
// #ifdef H5
for (let i = 0; i < localStorage.length; i++) {
let key = String(localStorage.key(i))
if (key.indexOf('devTools_') == 0) {
continue
}
setTimeout(
() => {
localStorage.removeItem(key)
},
i * 2 + 1,
)
}
// #endif
// #ifdef MP
let keyList = devCache.get('storage')
if (!keyList) keyList = []
for (let i = 0; i < keyList.length; i++) {
const key = keyList[i]
if (key.indexOf('devTools_') == 0) {
continue
}
uni.removeStorageSync(key)
}
// #endif
},
sessionStorage() {
for (let i = 0; i < sessionStorage.length; i++) {
let key = String(sessionStorage.key(i))
if (key.indexOf('devTools_') == 0) {
continue
}
sessionStorage.removeItem(key)
}
},
file() {
// #ifdef APP-PLUS
appDelDir('_doc/')
// #endif
// #ifdef MP-WEIXIN
let fs = wx.getFileSystemManager()
fs.rmdir({
dirPath: wx.env.USER_DATA_PATH + '/',
recursive: true,
})
// #endif
},
pageCount() {
devCache.set('pageCount', [])
},
dayOnline() {
devCache.set('dayOnline', [])
},
cookie() {
let keys = []
document.cookie.split(';').forEach((cookieStr) => {
const [name, value] = cookieStr.trim().split('=')
keys.push(name)
})
keys.map((k) => {
document.cookie =
`${k}=;expires=` + new Date(new Date().getTime() + 200).toGMTString() + ';path=/'
})
},
IndexDB() {
indexedDB.databases().then((list) => {
list.map((item) => {
indexedDB.deleteDatabase(item.name)
})
})
},
}
if (selectedKey.length == 0) {
return uni.showToast({
title: '请先勾选需要清空的项目!',
icon: 'none',
})
}
uni.showLoading({
title: '清空中...',
mask: true,
})
setTimeout(() => {
uni.hideLoading()
uni.showToast({
title: '清空成功!',
icon: 'success',
})
that.getPage()
}, 5100)
selectedKey.map((key) => {
keyDelFun[key]()
})
},
/**
* 导出日志文件到json
*/
async exportJsonFile() {
let that = this
// #ifdef MP
if (1) {
uni.showToast({
title: '小程序平台不支持导出日志,建议直接上传至服务器!',
icon: 'none',
})
return
}
// #endif
uni.showLoading({
title: '打包中...',
})
try {
let devOp = devOptions.getOptions()
let waitExportObject = {
exportOptions: {
version: devOp.version,
config: devOp,
exportTime: new Date().getTime(),
// #ifdef APP-PLUS
platform: 'app',
// #endif
// #ifdef H5
platform: 'h5',
// #endif
// #ifdef MP
platform: 'mp',
// #endif
// #ifdef MP-WEIXIN
platform: 'wx',
// #endif
// #ifdef MP-QQ
platform: 'qq',
// #endif
},
error: devCache.get('errorReport'),
console: devCache.get('console'),
network: devCache.get('request'),
pageCount: devCache.get('pageCount'),
dayOnline: devCache.get('dayOnline'),
logs: devCache.get('logReport'), // ! 运行日志
info: await getRuntimeInfo(), // ! 当前运行的系统信息
uniBus: devCache.get('uniBus'),
busCount: devCache.get('busCount'),
pageRouteMap: devCache.get('pageRouteMap'),
pageRouteKeyMap: devCache.get('pageRouteKeyMap'),
storage: {},
sessionStorage: {},
cookie: {},
...that.getCache(),
}
try {
if (that.$store.state) {
waitExportObject.vuex = that.$store.state
}
} catch (error) {}
try {
if (uni.Pinia) {
waitExportObject.pinia = uni.Pinia.getActivePinia().state.value
} else if (that.$pinia.state.value) {
waitExportObject.pinia = that.$pinia.state.value
}
} catch (error) {}
try {
if (getApp().globalData) {
waitExportObject.globalData = getApp().globalData
}
} catch (error) {}
let data = jsonCompress.safeJsonStringify(waitExportObject)
data = JSON.parse(data)
data = JSON.stringify(data, null, 2)
let t = new Date().getTime()
let exportFileName = `export_devtools_log_${t}.json`
// #ifdef H5
const blob = new Blob([data], { type: 'application/json' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.style = 'display: none'
a.download = exportFileName
a.href = url
document.body.appendChild(a)
a.click()
uni.showToast({
title: '导出成功!',
icon: 'success',
})
// #endif
// #ifdef APP-PLUS
plus.io.resolveLocalFileSystemURL(
'_downloads/',
(entry) => {
entry.getFile(
exportFileName,
{
create: true,
},
(fileEntry) => {
fileEntry.createWriter((writer) => {
writer.onwrite = (e) => {
uni.hideLoading()
uni.showModal({
title: '导出成功',
content: '文件导出成功!已保存至公共下载路径,文件名称:' + exportFileName,
})
}
writer.onerror = () => {
uni.hideLoading()
uni.showToast({
title: '日志导出失败_写入文件失败',
icon: 'none',
})
}
writer.write(data)
})
},
)
},
(err) => {
console.log('err', err)
uni.hideLoading()
uni.showToast({
title: '文件保存失败_打开目录失败',
icon: 'none',
})
},
)
// #endif
uni.hideLoading()
} catch (error) {
if (error && error.message) {
console.log('导出失败!', error.message)
} else {
console.log('导出失败!', error)
}
uni.hideLoading()
uni.showToast({
title: '导出失败!',
icon: 'error',
})
}
},
/**
* 获取缓存数据
*/
getCache() {
let data = {
storage: {},
sessionStorage: {},
cookie: {},
}
// #ifdef APP-PLUS
let keys = plus.storage.getAllKeys()
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
if (key.indexOf('devTools_') == 0) {
// 忽略以 devTools_ 开头的key
continue
}
data.storage[key] = uni.getStorageSync(key)
}
// #endif
// #ifdef H5
for (let i = 0; i < localStorage.length; i++) {
let key = localStorage.key(i)
if (key.indexOf('devTools_') == 0) {
continue
}
let value = uni.getStorageSync(key)
data.storage[key] = value
}
for (let i = 0; i < sessionStorage.length; i++) {
let key = sessionStorage.key(i)
if (key.indexOf('devTools_') == 0) {
continue
}
let value = sessionStorage.getItem(key)
data.sessionStorage[key] = value
}
document.cookie.split(';').forEach((cookieStr) => {
const [name, value] = cookieStr.trim().split('=')
data.cookie[name] = value
})
// #endif
// #ifdef MP
let keyList = devCache.get('storage')
if (!keyList) keyList = []
for (let i = 0; i < keyList.length; i++) {
const key = keyList[i]
if (key.indexOf('devTools_') == 0) {
continue
}
let value = uni.getStorageSync(key)
if (value) {
data.storage[key] = value
}
}
// #endif
return data
},
/**
* 跳转指定URL
*/
goUrl(url) {
// #ifdef H5
window.open(url)
// #endif
// #ifdef MP
uni.setClipboardData({
data: url,
})
// #endif
// #ifdef APP-PLUS
plus.runtime.openURL(url)
// #endif
},
},
}
</script>
<style lang="scss" scoped>
.settingView {
display: flex;
flex-direction: column;
width: 750rpx;
.loading {
width: 750rpx;
height: 300rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
.loadingText {
font-size: 24rpx;
color: #888;
}
}
.divisionLine {
width: 750rpx;
height: 1px;
background-color: rgba(0, 0, 0, 0.1);
}
.checkboxItem {
display: flex;
flex-direction: row;
padding: 10rpx 20rpx;
width: 750rpx;
align-items: center;
&:active {
background-color: rgba(0, 0, 0, 0.05);
}
.name {
font-size: 24rpx;
margin-left: 5rpx;
}
.count {
font-size: 20rpx;
margin-left: 10rpx;
color: #ff2d55;
}
.empty {
font-size: 20rpx;
margin-left: 10rpx;
color: #999;
}
}
.delBtn {
width: 710rpx;
margin-left: 20rpx;
border-radius: 20rpx;
height: 70rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
background-color: rgb(255, 45, 85);
margin-top: 20rpx;
margin-bottom: 30rpx;
&:active {
background-color: rgba(255, 45, 85, 0.8);
}
.delBtnText {
font-size: 24rpx;
color: #fff;
}
}
}
.objectAnalysisView {
width: 710rpx;
margin-left: 20rpx;
margin-bottom: 20rpx;
}
.about {
width: 710rpx;
margin-left: 20rpx;
display: flex;
flex-direction: column;
.row {
margin-bottom: 10rpx;
font-size: 24rpx;
color: #888;
}
}
</style>