setting.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783
  1. <template>
  2. <view class="settingView">
  3. <view
  4. v-if="loading"
  5. class="loading"
  6. >
  7. <text class="loadingText">加载中</text>
  8. </view>
  9. <template v-else>
  10. <subTitleBar
  11. :isOpen="exportIsShow"
  12. @click="exportIsShow = !exportIsShow"
  13. title="导出全部日志"
  14. />
  15. <template v-if="exportIsShow">
  16. <view
  17. class="delBtn"
  18. @click="exportJsonFile"
  19. >
  20. <text class="delBtnText">导出日志文件(.json)</text>
  21. </view>
  22. </template>
  23. <view class="divisionLine"></view>
  24. <subTitleBar
  25. :isOpen="cacheListIsShow"
  26. title="清空全部缓存"
  27. @click="cacheListIsShow = !cacheListIsShow"
  28. />
  29. <template v-if="cacheListIsShow">
  30. <view
  31. v-for="(item, index) in cacheSelectList"
  32. :key="index"
  33. @click.stop="doSelectCache(index)"
  34. class="checkboxItem"
  35. >
  36. <checkbox
  37. :value="item.check ? '1' : '0'"
  38. :checked="item.check"
  39. color="#ff2d55"
  40. />
  41. <text
  42. class="name"
  43. :style="{
  44. color: item.count ? '#333' : '#888',
  45. }"
  46. >
  47. {{ item.name }}
  48. </text>
  49. <text v-if="item.key == 'file'"></text>
  50. <text
  51. v-else-if="item.count"
  52. class="count"
  53. >
  54. ({{ item.count }})
  55. </text>
  56. <text
  57. v-else
  58. class="empty"
  59. >
  60. (空)
  61. </text>
  62. </view>
  63. <view
  64. class="delBtn"
  65. @click="delCache"
  66. >
  67. <text class="delBtnText">清空选中</text>
  68. </view>
  69. </template>
  70. <view class="divisionLine"></view>
  71. <subTitleBar
  72. :isOpen="configIsShow"
  73. title="DevTools当前配置参数"
  74. @click="configIsShow = !configIsShow"
  75. />
  76. <view
  77. v-if="configIsShow"
  78. class="objectAnalysisView"
  79. >
  80. <objectAnalysis
  81. :isOpenFirst="true"
  82. :data="config"
  83. :width="710"
  84. />
  85. </view>
  86. <view class="divisionLine"></view>
  87. <subTitleBar
  88. :showArrow="false"
  89. title="关于"
  90. />
  91. <view class="about">
  92. <view>
  93. <text class="row">Copyright©2024 福州重塑网络科技有限公司 前端团队</text>
  94. </view>
  95. <view
  96. @click="goUrl('https://dev.api0.cn')"
  97. style="display: flex; flex-direction: row"
  98. >
  99. <text class="row">在线文档:</text>
  100. <text
  101. class="row"
  102. style="color: #ff2d55"
  103. >https://dev.api0.cn</text
  104. >
  105. </view>
  106. <view>
  107. <text class="row">当前版本:v{{ config.version }}</text>
  108. </view>
  109. </view>
  110. <view style="height: 100rpx"></view>
  111. </template>
  112. </view>
  113. </template>
  114. <script>
  115. import devCache from "../../../core/libs/devCache";
  116. import devOptions from "../../../core/libs/devOptions";
  117. import jsonCompress from "../../../core/libs/jsonCompress";
  118. import appDelDir from "../libs/appDelDir";
  119. import subTitleBar from "../ui/subTitleBar.vue";
  120. import objectAnalysis from "./objectAnalysis.vue";
  121. import getRuntimeInfo from "../libs/getRuntimeInfo";
  122. export default {
  123. components: {
  124. subTitleBar,
  125. objectAnalysis,
  126. },
  127. data() {
  128. return {
  129. /**
  130. * 是否加载中
  131. */
  132. loading: false,
  133. /**
  134. * 缓存列表是否展示
  135. */
  136. cacheListIsShow: false,
  137. /**
  138. * 缓存列表
  139. */
  140. cacheSelectList: [],
  141. /**
  142. * 配置文件是否显示
  143. */
  144. configIsShow: false,
  145. /**
  146. * 当前配置
  147. */
  148. config: devOptions.getOptions(),
  149. /**
  150. * 是否显示导出日志按钮
  151. */
  152. exportIsShow: false,
  153. };
  154. },
  155. methods: {
  156. /**
  157. * 加载页面
  158. */
  159. async getPage() {
  160. let that = this;
  161. that.loading = true;
  162. that.cacheSelectList = await that.countCache();
  163. that.loading = false;
  164. },
  165. /**
  166. * 统计缓存信息
  167. */
  168. countCache() {
  169. let that = this;
  170. return new Promise(async (yes) => {
  171. let cacheSelectList = [];
  172. // dev 工具日志
  173. let keys = {
  174. errorReport: "Error错误日志",
  175. console: "Console打印日志",
  176. request: "Request请求日志",
  177. logReport: "Logs日志",
  178. uniBus: "UniBus函数日志",
  179. };
  180. Object.keys(keys).map((key) => {
  181. let logs = devCache.get(key);
  182. cacheSelectList.push({
  183. name: keys[key],
  184. check: logs.length > 0,
  185. count: logs.length,
  186. key,
  187. });
  188. });
  189. // #ifdef H5
  190. let indexDBList = await this.getIndexDBList();
  191. let cookieLength = document.cookie.split(";").length;
  192. if (document.cookie == "") {
  193. cookieLength = 0;
  194. }
  195. // #endif
  196. cacheSelectList = cacheSelectList.concat([
  197. that.countStorageCache(),
  198. // #ifdef H5
  199. {
  200. key: "sessionStorage",
  201. name: "SessionStorage临时缓存",
  202. check: sessionStorage.length > 0,
  203. count: sessionStorage.length,
  204. },
  205. // #endif
  206. // #ifdef APP-PLUS
  207. {
  208. key: "file",
  209. name: "FileSys本地文件(_doc)",
  210. check: false,
  211. count: "未知 // TODO",
  212. },
  213. // #endif
  214. // #ifdef MP-WEIXIN
  215. {
  216. name: "FileSys本地文件(FileSystemManager)",
  217. check: false,
  218. key: "file",
  219. count: "未知 // TODO",
  220. },
  221. // #endif
  222. {
  223. key: "pageCount",
  224. name: "Pages页面停留统计",
  225. check: devCache.get("pageCount").length > 0,
  226. count: devCache.get("pageCount").length,
  227. },
  228. {
  229. key: "dayOnline",
  230. name: "Pages日活时间统计",
  231. check: devCache.get("dayOnline").length > 0,
  232. count: devCache.get("dayOnline").length,
  233. },
  234. // #ifdef H5
  235. {
  236. key: "cookie",
  237. name: "Cookie",
  238. check: cookieLength > 0,
  239. count: cookieLength,
  240. },
  241. {
  242. key: "IndexDB",
  243. name: "IndexDB",
  244. check: indexDBList.length > 0,
  245. count: indexDBList.length,
  246. },
  247. // #endif
  248. ]);
  249. yes(cacheSelectList);
  250. });
  251. },
  252. /**
  253. * 统计本地缓存
  254. */
  255. countStorageCache() {
  256. let n = 0;
  257. // #ifdef APP-PLUS
  258. let keys = plus.storage.getAllKeys();
  259. for (let i = 0; i < keys.length; i++) {
  260. const key = keys[i];
  261. if (key.indexOf("devTools_") == 0) {
  262. // 忽略以 devTools_ 开头的key
  263. continue;
  264. }
  265. n++;
  266. }
  267. // #endif
  268. // #ifdef H5
  269. for (let i = 0; i < localStorage.length; i++) {
  270. let key = localStorage.key(i);
  271. if (key.indexOf("devTools_") == 0) {
  272. continue;
  273. }
  274. n++;
  275. }
  276. // #endif
  277. // #ifdef MP
  278. let keyList = devCache.get("storage");
  279. if (!keyList) keyList = [];
  280. for (let i = 0; i < keyList.length; i++) {
  281. const key = keyList[i];
  282. if (key.indexOf("devTools_") == 0) {
  283. continue;
  284. }
  285. n++;
  286. }
  287. // #endif
  288. return {
  289. key: "localStorage",
  290. name: "localStorage本地缓存",
  291. check: n > 0,
  292. count: n,
  293. };
  294. },
  295. /**
  296. * 获取indexDB列表
  297. */
  298. getIndexDBList() {
  299. return new Promise((yes) => {
  300. try {
  301. indexedDB.databases().then((list) => {
  302. yes(list);
  303. });
  304. } catch (error) {
  305. console.log("getIndexDBList error", error);
  306. yes([]);
  307. }
  308. });
  309. },
  310. /**
  311. * 选择清空的缓存项目
  312. */
  313. doSelectCache(index) {
  314. this.cacheSelectList[index].check = !this.cacheSelectList[index].check;
  315. },
  316. /**
  317. * 清空缓存
  318. */
  319. delCache() {
  320. let that = this;
  321. let selectedKey = [];
  322. that.cacheSelectList.map((item) => {
  323. if (item.check) {
  324. selectedKey.push(item.key);
  325. }
  326. });
  327. let keyDelFun = {
  328. errorReport() {
  329. devCache.set("errorReport", []);
  330. },
  331. console() {
  332. uni.$emit("devTools_delConsoleAll");
  333. },
  334. request() {
  335. uni.$emit("devTools_delNetworkAll");
  336. },
  337. logReport() {
  338. devCache.set("logReport", []);
  339. },
  340. uniBus() {
  341. uni.$emit("devTools_delUniBusAll");
  342. },
  343. localStorage() {
  344. // #ifdef APP-PLUS
  345. let keys = plus.storage.getAllKeys();
  346. for (let i = 0; i < keys.length; i++) {
  347. const key = String(keys[i]);
  348. if (key.indexOf("devTools_") == 0) {
  349. continue;
  350. }
  351. uni.removeStorageSync(key);
  352. }
  353. // #endif
  354. // #ifdef H5
  355. for (let i = 0; i < localStorage.length; i++) {
  356. let key = String(localStorage.key(i));
  357. if (key.indexOf("devTools_") == 0) {
  358. continue;
  359. }
  360. setTimeout(() => {
  361. localStorage.removeItem(key);
  362. }, i * 2 + 1);
  363. }
  364. // #endif
  365. // #ifdef MP
  366. let keyList = devCache.get("storage");
  367. if (!keyList) keyList = [];
  368. for (let i = 0; i < keyList.length; i++) {
  369. const key = keyList[i];
  370. if (key.indexOf("devTools_") == 0) {
  371. continue;
  372. }
  373. uni.removeStorageSync(key);
  374. }
  375. // #endif
  376. },
  377. sessionStorage() {
  378. for (let i = 0; i < sessionStorage.length; i++) {
  379. let key = String(sessionStorage.key(i));
  380. if (key.indexOf("devTools_") == 0) {
  381. continue;
  382. }
  383. sessionStorage.removeItem(key);
  384. }
  385. },
  386. file() {
  387. // #ifdef APP-PLUS
  388. appDelDir("_doc/");
  389. // #endif
  390. // #ifdef MP-WEIXIN
  391. let fs = wx.getFileSystemManager();
  392. fs.rmdir({
  393. dirPath: wx.env.USER_DATA_PATH + "/",
  394. recursive: true,
  395. });
  396. // #endif
  397. },
  398. pageCount() {
  399. devCache.set("pageCount", []);
  400. },
  401. dayOnline() {
  402. devCache.set("dayOnline", []);
  403. },
  404. cookie() {
  405. let keys = [];
  406. document.cookie.split(";").forEach((cookieStr) => {
  407. const [name, value] = cookieStr.trim().split("=");
  408. keys.push(name);
  409. });
  410. keys.map((k) => {
  411. document.cookie = `${k}=;expires=` + new Date(new Date().getTime() + 200).toGMTString() + ";path=/";
  412. });
  413. },
  414. IndexDB() {
  415. indexedDB.databases().then((list) => {
  416. list.map((item) => {
  417. indexedDB.deleteDatabase(item.name);
  418. });
  419. });
  420. },
  421. };
  422. if (selectedKey.length == 0) {
  423. return uni.showToast({
  424. title: "请先勾选需要清空的项目!",
  425. icon: "none",
  426. });
  427. }
  428. uni.showLoading({
  429. title: "清空中...",
  430. mask: true,
  431. });
  432. setTimeout(() => {
  433. uni.hideLoading();
  434. uni.showToast({
  435. title: "清空成功!",
  436. icon: "success",
  437. });
  438. that.getPage();
  439. }, 5100);
  440. selectedKey.map((key) => {
  441. keyDelFun[key]();
  442. });
  443. },
  444. /**
  445. * 导出日志文件到json
  446. */
  447. async exportJsonFile() {
  448. let that = this;
  449. // #ifdef MP
  450. if (1) {
  451. uni.showToast({
  452. title: "小程序平台不支持导出日志,建议直接上传至服务器!",
  453. icon: "none",
  454. });
  455. return;
  456. }
  457. // #endif
  458. uni.showLoading({
  459. title: "打包中...",
  460. });
  461. try {
  462. let devOp = devOptions.getOptions();
  463. let waitExportObject = {
  464. exportOptions: {
  465. version: devOp.version,
  466. config: devOp,
  467. exportTime: new Date().getTime(),
  468. // #ifdef APP-PLUS
  469. platform: "app",
  470. // #endif
  471. // #ifdef H5
  472. platform: "h5",
  473. // #endif
  474. // #ifdef MP
  475. platform: "mp",
  476. // #endif
  477. // #ifdef MP-WEIXIN
  478. platform: "wx",
  479. // #endif
  480. // #ifdef MP-QQ
  481. platform: "qq",
  482. // #endif
  483. },
  484. error: devCache.get("errorReport"),
  485. console: devCache.get("console"),
  486. network: devCache.get("request"),
  487. pageCount: devCache.get("pageCount"),
  488. dayOnline: devCache.get("dayOnline"),
  489. logs: devCache.get("logReport"), // ! 运行日志
  490. info: await getRuntimeInfo(), // ! 当前运行的系统信息
  491. uniBus: devCache.get("uniBus"),
  492. busCount: devCache.get("busCount"),
  493. pageRouteMap: devCache.get("pageRouteMap"),
  494. pageRouteKeyMap: devCache.get("pageRouteKeyMap"),
  495. storage: {},
  496. sessionStorage: {},
  497. cookie: {},
  498. ...that.getCache(),
  499. };
  500. try {
  501. if (that.$store.state) {
  502. waitExportObject.vuex = that.$store.state;
  503. }
  504. } catch (error) {}
  505. try {
  506. if (uni.Pinia) {
  507. waitExportObject.pinia = uni.Pinia.getActivePinia().state.value;
  508. } else if (that.$pinia.state.value) {
  509. waitExportObject.pinia = that.$pinia.state.value;
  510. }
  511. } catch (error) {}
  512. try {
  513. if (getApp().globalData) {
  514. waitExportObject.globalData = getApp().globalData;
  515. }
  516. } catch (error) {}
  517. let data = jsonCompress.safeJsonStringify(waitExportObject);
  518. data = JSON.parse(data);
  519. data = JSON.stringify(data, null, 2);
  520. let t = new Date().getTime();
  521. let exportFileName = `export_devtools_log_${t}.json`;
  522. // #ifdef H5
  523. const blob = new Blob([data], { type: "application/json" });
  524. const url = URL.createObjectURL(blob);
  525. const a = document.createElement("a");
  526. a.style = "display: none";
  527. a.download = exportFileName;
  528. a.href = url;
  529. document.body.appendChild(a);
  530. a.click();
  531. uni.showToast({
  532. title: "导出成功!",
  533. icon: "success",
  534. });
  535. // #endif
  536. // #ifdef APP-PLUS
  537. plus.io.resolveLocalFileSystemURL(
  538. "_downloads/",
  539. (entry) => {
  540. entry.getFile(
  541. exportFileName,
  542. {
  543. create: true,
  544. },
  545. (fileEntry) => {
  546. fileEntry.createWriter((writer) => {
  547. writer.onwrite = (e) => {
  548. uni.hideLoading();
  549. uni.showModal({
  550. title: "导出成功",
  551. content: "文件导出成功!已保存至公共下载路径,文件名称:" + exportFileName,
  552. });
  553. };
  554. writer.onerror = () => {
  555. uni.hideLoading();
  556. uni.showToast({
  557. title: "日志导出失败!_写入文件失败",
  558. icon: "none",
  559. });
  560. };
  561. writer.write(data);
  562. });
  563. }
  564. );
  565. },
  566. (err) => {
  567. console.log("err", err);
  568. uni.hideLoading();
  569. uni.showToast({
  570. title: "文件保存失败!_打开目录失败",
  571. icon: "none",
  572. });
  573. }
  574. );
  575. // #endif
  576. uni.hideLoading();
  577. } catch (error) {
  578. if (error && error.message) {
  579. console.log("导出失败!", error.message);
  580. } else {
  581. console.log("导出失败!", error);
  582. }
  583. uni.hideLoading();
  584. uni.showToast({
  585. title: "导出失败!",
  586. icon: "error",
  587. });
  588. }
  589. },
  590. /**
  591. * 获取缓存数据
  592. */
  593. getCache() {
  594. let data = {
  595. storage: {},
  596. sessionStorage: {},
  597. cookie: {},
  598. };
  599. // #ifdef APP-PLUS
  600. let keys = plus.storage.getAllKeys();
  601. for (let i = 0; i < keys.length; i++) {
  602. const key = keys[i];
  603. if (key.indexOf("devTools_") == 0) {
  604. // 忽略以 devTools_ 开头的key
  605. continue;
  606. }
  607. data.storage[key] = uni.getStorageSync(key);
  608. }
  609. // #endif
  610. // #ifdef H5
  611. for (let i = 0; i < localStorage.length; i++) {
  612. let key = localStorage.key(i);
  613. if (key.indexOf("devTools_") == 0) {
  614. continue;
  615. }
  616. let value = uni.getStorageSync(key);
  617. data.storage[key] = value;
  618. }
  619. for (let i = 0; i < sessionStorage.length; i++) {
  620. let key = sessionStorage.key(i);
  621. if (key.indexOf("devTools_") == 0) {
  622. continue;
  623. }
  624. let value = sessionStorage.getItem(key);
  625. data.sessionStorage[key] = value;
  626. }
  627. document.cookie.split(";").forEach((cookieStr) => {
  628. const [name, value] = cookieStr.trim().split("=");
  629. data.cookie[name] = value;
  630. });
  631. // #endif
  632. // #ifdef MP
  633. let keyList = devCache.get("storage");
  634. if (!keyList) keyList = [];
  635. for (let i = 0; i < keyList.length; i++) {
  636. const key = keyList[i];
  637. if (key.indexOf("devTools_") == 0) {
  638. continue;
  639. }
  640. let value = uni.getStorageSync(key);
  641. if (value) {
  642. data.storage[key] = value;
  643. }
  644. }
  645. // #endif
  646. return data;
  647. },
  648. /**
  649. * 跳转指定URL
  650. */
  651. goUrl(url) {
  652. // #ifdef H5
  653. window.open(url);
  654. // #endif
  655. // #ifdef MP
  656. uni.setClipboardData({
  657. data: url,
  658. });
  659. // #endif
  660. // #ifdef APP-PLUS
  661. plus.runtime.openURL(url);
  662. // #endif
  663. },
  664. },
  665. };
  666. </script>
  667. <style lang="scss" scoped>
  668. .settingView {
  669. display: flex;
  670. flex-direction: column;
  671. width: 750rpx;
  672. .loading {
  673. width: 750rpx;
  674. height: 300rpx;
  675. display: flex;
  676. flex-direction: row;
  677. align-items: center;
  678. justify-content: center;
  679. .loadingText {
  680. font-size: 24rpx;
  681. color: #888;
  682. }
  683. }
  684. .divisionLine {
  685. width: 750rpx;
  686. height: 1px;
  687. background-color: rgba(0, 0, 0, 0.1);
  688. }
  689. .checkboxItem {
  690. display: flex;
  691. flex-direction: row;
  692. padding: 10rpx 20rpx;
  693. width: 750rpx;
  694. align-items: center;
  695. &:active {
  696. background-color: rgba(0, 0, 0, 0.05);
  697. }
  698. .name {
  699. font-size: 24rpx;
  700. margin-left: 5rpx;
  701. }
  702. .count {
  703. font-size: 20rpx;
  704. margin-left: 10rpx;
  705. color: #ff2d55;
  706. }
  707. .empty {
  708. font-size: 20rpx;
  709. margin-left: 10rpx;
  710. color: #999;
  711. }
  712. }
  713. .delBtn {
  714. width: 710rpx;
  715. margin-left: 20rpx;
  716. border-radius: 20rpx;
  717. height: 70rpx;
  718. display: flex;
  719. flex-direction: row;
  720. align-items: center;
  721. justify-content: center;
  722. background-color: rgb(255, 45, 85);
  723. margin-top: 20rpx;
  724. margin-bottom: 30rpx;
  725. &:active {
  726. background-color: rgba(255, 45, 85, 0.8);
  727. }
  728. .delBtnText {
  729. font-size: 24rpx;
  730. color: #fff;
  731. }
  732. }
  733. }
  734. .objectAnalysisView {
  735. width: 710rpx;
  736. margin-left: 20rpx;
  737. margin-bottom: 20rpx;
  738. }
  739. .about {
  740. width: 710rpx;
  741. margin-left: 20rpx;
  742. display: flex;
  743. flex-direction: column;
  744. .row {
  745. margin-bottom: 10rpx;
  746. font-size: 24rpx;
  747. color: #888;
  748. }
  749. }
  750. </style>