2025-06-15 16:36:08 +08:00

747 lines
20 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="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>