|
|
@@ -133,6 +133,40 @@
|
|
|
flex-wrap: wrap;
|
|
|
justify-content: flex-end;
|
|
|
}
|
|
|
+ .attachment-log-panel {
|
|
|
+ margin-top: 24px;
|
|
|
+ padding-top: 20px;
|
|
|
+ border-top: 1px solid #e8e8e8;
|
|
|
+ }
|
|
|
+ .attachment-log-panel.collapsed .attachment-log-body {
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
+ .attachment-log-toolbar {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ }
|
|
|
+ .attachment-log-toolbar .attachment-ua-label {
|
|
|
+ margin-bottom: 0;
|
|
|
+ flex: 1 1 auto;
|
|
|
+ }
|
|
|
+ .attachment-log-body {
|
|
|
+ margin: 0;
|
|
|
+ padding: 12px 14px;
|
|
|
+ background: #1e1e1e;
|
|
|
+ color: #d4d4d4;
|
|
|
+ border-radius: 8px;
|
|
|
+ font-size: 0.7em;
|
|
|
+ line-height: 1.4;
|
|
|
+ white-space: pre-wrap;
|
|
|
+ word-break: break-word;
|
|
|
+ font-family: ui-monospace, Consolas, monospace;
|
|
|
+ max-height: 240px;
|
|
|
+ overflow-y: auto;
|
|
|
+ -webkit-overflow-scrolling: touch;
|
|
|
+ }
|
|
|
</style>
|
|
|
{% endblock %}
|
|
|
|
|
|
@@ -172,6 +206,16 @@
|
|
|
<div class="attachment-ua-label">浏览器 User-Agent</div>
|
|
|
<pre id="userAgentText" class="attachment-ua-text"></pre>
|
|
|
</div>
|
|
|
+
|
|
|
+ <div class="attachment-log-panel" id="screenLogPanel" aria-label="运行日志">
|
|
|
+ <div class="attachment-log-toolbar">
|
|
|
+ <span class="attachment-ua-label">运行日志(手机端无开发者工具时可在此查看)</span>
|
|
|
+ <button type="button" class="btn btn-info" id="btnLogToggle" style="padding:8px 14px;font-size:0.85em;">收起</button>
|
|
|
+ <button type="button" class="btn btn-warning" id="btnLogClear" style="padding:8px 14px;font-size:0.85em;">清空</button>
|
|
|
+ <button type="button" class="btn btn-primary" id="btnLogCopy" style="padding:8px 14px;font-size:0.85em;">复制全部</button>
|
|
|
+ </div>
|
|
|
+ <pre id="screenLog" class="attachment-log-body" role="log"></pre>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
<div id="faultModal" class="attachment-fault-modal" role="dialog" aria-modal="true" aria-labelledby="faultModalTitle" aria-hidden="true">
|
|
|
@@ -188,6 +232,75 @@
|
|
|
{% endblock %}
|
|
|
|
|
|
{% block scripts %}
|
|
|
+<script>
|
|
|
+(function () {
|
|
|
+ 'use strict';
|
|
|
+ var MAX_LINES = 200;
|
|
|
+ var buffer = [];
|
|
|
+ function fmtArg(a) {
|
|
|
+ if (a instanceof Error) {
|
|
|
+ return a.message + (a.stack ? '\n' + a.stack : '');
|
|
|
+ }
|
|
|
+ if (typeof a === 'object' && a !== null) {
|
|
|
+ try {
|
|
|
+ return JSON.stringify(a);
|
|
|
+ } catch (e) {
|
|
|
+ return String(a);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return String(a);
|
|
|
+ }
|
|
|
+ function flushScreenLog() {
|
|
|
+ var el = document.getElementById('screenLog');
|
|
|
+ if (!el) return;
|
|
|
+ el.textContent = buffer.join('\n');
|
|
|
+ el.scrollTop = el.scrollHeight;
|
|
|
+ }
|
|
|
+ function appendScreenLog(level, args) {
|
|
|
+ var t = new Date().toISOString().slice(11, 23);
|
|
|
+ var msg = [].slice.call(args).map(fmtArg).join(' ');
|
|
|
+ var line = '[' + t + '] ' + level + ' ' + msg;
|
|
|
+ buffer.push(line);
|
|
|
+ if (buffer.length > MAX_LINES) {
|
|
|
+ buffer.shift();
|
|
|
+ }
|
|
|
+ flushScreenLog();
|
|
|
+ }
|
|
|
+ function patchConsole() {
|
|
|
+ if (typeof console === 'undefined') return;
|
|
|
+ ['log', 'warn', 'error', 'info', 'debug'].forEach(function (name) {
|
|
|
+ var orig = console[name];
|
|
|
+ if (typeof orig !== 'function') return;
|
|
|
+ console[name] = function () {
|
|
|
+ try {
|
|
|
+ orig.apply(console, arguments);
|
|
|
+ } catch (e) { /* ignore */ }
|
|
|
+ appendScreenLog(name.toUpperCase(), arguments);
|
|
|
+ };
|
|
|
+ });
|
|
|
+ }
|
|
|
+ window.__attachmentLogAppend = function (level, str) {
|
|
|
+ appendScreenLog(level || 'LOG', [str]);
|
|
|
+ };
|
|
|
+ window.__attachmentLogClear = function () {
|
|
|
+ buffer = [];
|
|
|
+ flushScreenLog();
|
|
|
+ };
|
|
|
+ window.__attachmentLogGetText = function () {
|
|
|
+ return buffer.join('\n');
|
|
|
+ };
|
|
|
+ patchConsole();
|
|
|
+ window.addEventListener('error', function (ev) {
|
|
|
+ appendScreenLog('WINDOW', [
|
|
|
+ (ev.message || '') + ' @' + (ev.filename || '') + ':' + (ev.lineno || 0) + ':' + (ev.colno || 0)
|
|
|
+ ]);
|
|
|
+ });
|
|
|
+ window.addEventListener('unhandledrejection', function (ev) {
|
|
|
+ var r = ev.reason;
|
|
|
+ appendScreenLog('REJECT', [r instanceof Error ? r : String(r)]);
|
|
|
+ });
|
|
|
+})();
|
|
|
+</script>
|
|
|
<script src="{{ url_for('main.serve_templates_js', filename='im-sdk.js') }}"></script>
|
|
|
<script>
|
|
|
(function () {
|
|
|
@@ -265,6 +378,46 @@
|
|
|
}
|
|
|
});
|
|
|
|
|
|
+ var screenLogPanel = document.getElementById('screenLogPanel');
|
|
|
+ var btnLogToggle = document.getElementById('btnLogToggle');
|
|
|
+ var btnLogClear = document.getElementById('btnLogClear');
|
|
|
+ var btnLogCopy = document.getElementById('btnLogCopy');
|
|
|
+ if (btnLogToggle && screenLogPanel) {
|
|
|
+ btnLogToggle.addEventListener('click', function () {
|
|
|
+ var collapsed = screenLogPanel.classList.toggle('collapsed');
|
|
|
+ btnLogToggle.textContent = collapsed ? '展开' : '收起';
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (btnLogClear && window.__attachmentLogClear) {
|
|
|
+ btnLogClear.addEventListener('click', function () {
|
|
|
+ window.__attachmentLogClear();
|
|
|
+ if (window.__attachmentLogAppend) {
|
|
|
+ window.__attachmentLogAppend('LOG', '日志已清空');
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (btnLogCopy) {
|
|
|
+ btnLogCopy.addEventListener('click', function () {
|
|
|
+ var text = window.__attachmentLogGetText ? window.__attachmentLogGetText() : '';
|
|
|
+ if (!text) {
|
|
|
+ showMessage('暂无日志', 'error');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
|
+ navigator.clipboard.writeText(text).then(function () {
|
|
|
+ showMessage('日志已复制');
|
|
|
+ }).catch(function (e) {
|
|
|
+ showFaultModal('复制日志失败', e);
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ showFaultModal('无法复制', '当前环境不支持 clipboard');
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (window.__attachmentLogAppend) {
|
|
|
+ window.__attachmentLogAppend('LOG', '运行日志已启用(含 im-sdk 与页面内 console)');
|
|
|
+ }
|
|
|
+
|
|
|
function useUniNative() {
|
|
|
return window.imSDK && typeof imSDK.isUniAppIm === 'function' && imSDK.isUniAppIm();
|
|
|
}
|