fl-web-component 1.3.6 → 1.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- @charset "UTF-8";#fl-model[data-v-2d99390c]{width:100%;height:100%;cursor:pointer}[data-v-2d99390c] .tips-label{width:60px;color:#000;font:12px Helvetica;margin-top:-3em;padding:5px;text-align:center;vertical-align:middle;background-color:khaki}[data-v-2d99390c] .measure-label{max-width:100px;margin-top:-1em;border:10px;border-radius:5px;padding:3px 10px;cursor:pointer;color:#009bea;background-color:#f4f4f4;box-shadow:0 1px 3px 1px rgba(0,0,0,.25)}[data-v-2d99390c] .circle-tag{width:10px;height:10px;margin-top:5px;border-radius:50%;background-color:#ff5000}[data-v-2d99390c] .measure-label-font{word-break:break-all}[data-v-2d99390c] .mark-label-img{padding-top:5px;width:20px;height:20px}#konva-container[data-v-32dc6e8c]{z-index:3;width:100%;height:100%;cursor:pointer;overflow:hidden}span[data-v-f547d5c6]{font-weight:bolder}.text[data-v-f547d5c6]{margin-top:20px}.line[data-v-f547d5c6]{border-bottom:1px solid #dcdfe6;margin:20px 0}.center[data-v-f547d5c6]{display:flex;flex-direction:column;align-items:center}.center .cen span[data-v-f547d5c6],.center .top span[data-v-f547d5c6]{color:"#53a8ff";display:inline-block;width:30px;height:30px;text-align:center;line-height:30px;border:1px solid;padding:5px;margin-bottom:10px;background-color:#e9f3ff}.center .cen span[data-v-f547d5c6]{margin:10px}.button[data-v-f547d5c6]{display:flex;justify-content:end;margin-top:20px}@font-face{font-family:iconfont;src:url(//at.alicdn.com/t/font_3226805_qqvo3ag3r8.woff2?t=1646635700216) format("woff2"),url(//at.alicdn.com/t/font_3226805_qqvo3ag3r8.woff?t=1646635700216) format("woff"),url(//at.alicdn.com/t/font_3226805_qqvo3ag3r8.ttf?t=1646635700216) format("truetype")}.iconfont[data-v-f547d5c6]{font-family:iconfont!important;font-size:50px;font-style:normal;color:"#53a8ff";-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-shubiao[data-v-f547d5c6]:before{content:""}#svg-tigger[data-v-0ec35ee4]{cursor:pointer;height:100%;width:100%}
1
+ @charset "UTF-8";#fl-model[data-v-67cfa148]{width:100%;height:100%;cursor:pointer}[data-v-67cfa148] .tips-label{width:60px;color:#000;font:12px Helvetica;margin-top:-3em;padding:5px;text-align:center;vertical-align:middle;background-color:khaki}[data-v-67cfa148] .measure-label{max-width:100px;margin-top:-1em;border:10px;border-radius:5px;padding:3px 10px;cursor:pointer;color:#009bea;background-color:#f4f4f4;box-shadow:0 1px 3px 1px rgba(0,0,0,.25)}[data-v-67cfa148] .circle-tag{width:10px;height:10px;margin-top:5px;border-radius:50%;background-color:#ff5000}[data-v-67cfa148] .measure-label-font{word-break:break-all}[data-v-67cfa148] .mark-label-img{padding-top:5px;width:20px;height:20px}#konva-container[data-v-2fa9e609]{z-index:3;width:100%;height:100%;cursor:pointer;overflow:hidden}span[data-v-f547d5c6]{font-weight:bolder}.text[data-v-f547d5c6]{margin-top:20px}.line[data-v-f547d5c6]{border-bottom:1px solid #dcdfe6;margin:20px 0}.center[data-v-f547d5c6]{display:flex;flex-direction:column;align-items:center}.center .cen span[data-v-f547d5c6],.center .top span[data-v-f547d5c6]{color:"#53a8ff";display:inline-block;width:30px;height:30px;text-align:center;line-height:30px;border:1px solid;padding:5px;margin-bottom:10px;background-color:#e9f3ff}.center .cen span[data-v-f547d5c6]{margin:10px}.button[data-v-f547d5c6]{display:flex;justify-content:end;margin-top:20px}@font-face{font-family:iconfont;src:url(//at.alicdn.com/t/font_3226805_qqvo3ag3r8.woff2?t=1646635700216) format("woff2"),url(//at.alicdn.com/t/font_3226805_qqvo3ag3r8.woff?t=1646635700216) format("woff"),url(//at.alicdn.com/t/font_3226805_qqvo3ag3r8.ttf?t=1646635700216) format("truetype")}.iconfont[data-v-f547d5c6]{font-family:iconfont!important;font-size:50px;font-style:normal;color:"#53a8ff";-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-shubiao[data-v-f547d5c6]:before{content:""}#svg-tigger[data-v-0ec35ee4]{cursor:pointer;height:100%;width:100%}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fl-web-component",
3
- "version": "1.3.6",
3
+ "version": "1.3.8",
4
4
  "scripts": {
5
5
  "tip1": "仅调试本组件不涉及业务组件,请执行dev",
6
6
  "dev": "vue-cli-service serve",
@@ -11,8 +11,7 @@
11
11
  "watch": "vue-cli-service build --watch --mode production --target lib --name fl-web-component --formats commonjs ./src/main.js",
12
12
  "build": "npm run lint && vue-cli-service build --target lib --name fl-web-component --formats commonjs ./src/main.js",
13
13
  "build:test": "vue-cli-service build --target lib --name fl-web-component --formats commonjs ./src/main.js",
14
- "registry:npm": "npm config set registry https://registry.npmjs.org/",
15
- "publish:base": "npm run build && npm run registry:npm && npm publish",
14
+ "publish:base": "npm run build && npm publish --registry https://registry.npmjs.org/",
16
15
  "==": "=============================================================",
17
16
  "tip3": "发版注意: 【修改bug执行publish:fix】【新增功能执行publish:feat】【新特性执行publish:perf】",
18
17
  "publish:fix": "npm version patch && npm run publish:base",
@@ -103,7 +103,7 @@ var activedSvgPan = {
103
103
  var newScale = direction > 0 ? oldScale * scaleBy : oldScale / scaleBy;
104
104
  this.scaleRatio = Math.ceil(newScale);
105
105
  // 判断比例尺百分比
106
- if (this.scaleRatio <= 1 || this.scaleRatio >= 100) return;
106
+ if (this.scaleRatio <1 || this.scaleRatio >= 100) return;
107
107
  // 设置放大缩小多少倍
108
108
  konvaStage.scale({ x: newScale, y: newScale });
109
109
  var newPos = {
@@ -279,7 +279,6 @@
279
279
  });
280
280
  // cameraControls.fitToSphere(scene, true); // TODO 待处理,先用 setModelCenter 进行定位
281
281
  this.setModelCenter(modelGroup);
282
- console.log(modelGroup);
283
282
  this.$emit('modelLoaded');
284
283
  // cameraControls.saveState();
285
284
  }
@@ -390,7 +389,15 @@
390
389
  },
391
390
  setModelCenter(mesh) {
392
391
  const box3 = new this.THREE.Box3();
393
- box3.setFromObject(mesh);
392
+ mesh.traverseVisible(function (object) {
393
+ // 3. 只处理有几何体的网格 (Mesh)
394
+ if (object.isMesh) {
395
+ // 4. 使用 expandByObject 扩展包围盒
396
+ // 这个方法会计算 object 的世界坐标包围盒,并将其合并到 correctBoundingBox 中
397
+ box3.expandByObject(object);
398
+ }
399
+ });
400
+ // box3.setFromObject(mesh);
394
401
  const center = new this.THREE.Vector3();
395
402
  box3.getCenter(center);
396
403
  const size = box3.getSize(new this.THREE.Vector3());
package/src/main.js CHANGED
@@ -4,6 +4,7 @@ import FLPerControl from '../packages/components/com-graphics/per-control.vue';
4
4
  import FlSvg from '../packages/components/com-graphics/pid.vue';
5
5
  import * as THREE from 'three';
6
6
  import pkg from '../package.json';
7
+ import qtdevtools from './utils/mini-devtool'
7
8
 
8
9
  const components = [FlModel, Fl2dcanvas, FLPerControl, FlSvg];
9
10
 
@@ -17,6 +18,7 @@ const install = Vue => {
17
18
  // 支持浏览器环境直接引入
18
19
  if (typeof window !== 'undefined') {
19
20
  window.FLVersion = pkg.version;
21
+ window.FLDevTools = qtdevtools;
20
22
  }
21
23
 
22
24
  export default {
@@ -392,34 +392,34 @@ function draw3Dmodel(geom, instanceName, instanceCount, nColor, nOpacity) {
392
392
  geometry.setAttribute('opacity', new THREE.InstancedBufferAttribute(opacities, 1));
393
393
 
394
394
  // 自定义着色器逻辑
395
- if (!customMaterial) {
396
- material.onBeforeCompile = shader => {
397
- // 添加顶点着色器输入
398
- shader.vertexShader = `
399
- in float opacity; // 实例透明度属性
400
- out float vAlpha;
401
- ${shader.vertexShader}
402
- `.replace(
403
- '#include <begin_vertex>',
404
- `
405
- #include <begin_vertex>
406
- vAlpha = opacity; // 传递透明度到片段着色器
407
- `
408
- );
409
-
410
- // 修改片段着色器
411
- shader.fragmentShader = `
412
- in float vAlpha;
413
- ${shader.fragmentShader}
414
- `.replace(
415
- '#include <alphatest_fragment>',
416
- `
417
- #include <alphatest_fragment>
418
- diffuseColor.a *= vAlpha; // 应用实例透明度
419
- `
420
- );
421
- };
422
- }
395
+ // if (!customMaterial) {
396
+ // material.onBeforeCompile = shader => {
397
+ // // 添加顶点着色器输入
398
+ // shader.vertexShader = `
399
+ // in float opacity; // 实例透明度属性
400
+ // out float vAlpha;
401
+ // ${shader.vertexShader}
402
+ // `.replace(
403
+ // '#include <begin_vertex>',
404
+ // `
405
+ // #include <begin_vertex>
406
+ // vAlpha = opacity; // 传递透明度到片段着色器
407
+ // `
408
+ // );
409
+
410
+ // // 修改片段着色器
411
+ // shader.fragmentShader = `
412
+ // in float vAlpha;
413
+ // ${shader.fragmentShader}
414
+ // `.replace(
415
+ // '#include <alphatest_fragment>',
416
+ // `
417
+ // #include <alphatest_fragment>
418
+ // diffuseColor.a *= vAlpha; // 应用实例透明度
419
+ // `
420
+ // );
421
+ // };
422
+ // }
423
423
  mesh = new THREE.InstancedMesh(geometry, material, instanceCount);
424
424
 
425
425
  const { visible } = prop;
@@ -0,0 +1,309 @@
1
+ // ========= Error & Console Panel (非阻塞右下角显示,包含 console.log 等) =========
2
+ export default function installErrorConsolePanel() {
3
+ // ========== 可配置项 ==========
4
+ const MAX_ENTRIES = 400;
5
+ const PANEL_ID = 'js-error-panel';
6
+ const COLLAPSE_LENGTH = 1200; // 超过多少字符时折叠显示
7
+ const CAPTURE_CONSOLE = true; // 是否捕获 console.log/info/warn/debug(保留原行为)
8
+ // ================================
9
+
10
+ // 安全 stringify(处理循环引用)
11
+ function getCircularReplacer() {
12
+ const seen = new WeakSet();
13
+ return (key, value) => {
14
+ if (typeof value === "object" && value !== null) {
15
+ if (seen.has(value)) return "[Circular]";
16
+ seen.add(value);
17
+ }
18
+ return value;
19
+ };
20
+ }
21
+ function safeStringify(x) {
22
+ try {
23
+ if (typeof x === "string") return x;
24
+ if (x instanceof Error) return x.stack || x.message || String(x);
25
+ return JSON.stringify(x, getCircularReplacer(), 2);
26
+ } catch (e) {
27
+ try { return String(x); } catch (e2) { return "[unstringifiable]"; }
28
+ }
29
+ }
30
+
31
+ // ========== Panel DOM 创建 ==========
32
+ function createPanel() {
33
+ if (document.getElementById(PANEL_ID)) return document.getElementById(PANEL_ID);
34
+
35
+ const panel = document.createElement('div');
36
+ panel.id = PANEL_ID;
37
+ Object.assign(panel.style, {
38
+ position: 'fixed',
39
+ right: '12px',
40
+ bottom: '12px',
41
+ width: '460px',
42
+ maxHeight: '45vh',
43
+ boxSizing: 'border-box',
44
+ overflow: 'hidden',
45
+ zIndex: 2147483647,
46
+ fontFamily: 'Menlo,Consolas,monospace',
47
+ fontSize: '12px',
48
+ color: '#fff',
49
+ borderRadius: '8px',
50
+ boxShadow: '0 6px 18px rgba(0,0,0,0.55)',
51
+ background: 'linear-gradient(180deg, rgba(0,0,0,0.86), rgba(24,24,24,0.86))',
52
+ display: 'flex',
53
+ flexDirection: 'column',
54
+ gap: '6px',
55
+ padding: '8px'
56
+ });
57
+
58
+ // header
59
+ const header = document.createElement('div');
60
+ Object.assign(header.style, { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '8px' });
61
+ header.innerHTML = `<strong style="font-size:13px">JS Logs / Errors</strong><span style="opacity:.7;font-size:11px">(非阻塞)</span>`;
62
+
63
+ const btns = document.createElement('div');
64
+
65
+ const clearBtn = document.createElement('button');
66
+ clearBtn.textContent = '清空';
67
+ Object.assign(clearBtn.style, { marginLeft: '6px', cursor: 'pointer', fontSize: '12px' });
68
+ clearBtn.onclick = () => { entriesContainer.innerHTML = ''; entryCount = 0; updateStats(); };
69
+
70
+ const toggleBtn = document.createElement('button');
71
+ toggleBtn.textContent = '隐藏';
72
+ Object.assign(toggleBtn.style, { cursor: 'pointer', fontSize: '12px' });
73
+ toggleBtn.onclick = () => {
74
+ if (entriesContainer.style.display === 'none') { entriesContainer.style.display = ''; toggleBtn.textContent = '隐藏'; }
75
+ else { entriesContainer.style.display = 'none'; toggleBtn.textContent = '显示'; }
76
+ };
77
+
78
+ const statsSpan = document.createElement('span');
79
+ statsSpan.style.opacity = '.8';
80
+ statsSpan.style.fontSize = '11px';
81
+ statsSpan.textContent = '0 条';
82
+
83
+ btns.appendChild(statsSpan);
84
+ btns.appendChild(toggleBtn);
85
+ btns.appendChild(clearBtn);
86
+ header.appendChild(btns);
87
+
88
+ // entries container (scrollable)
89
+ const entriesContainer = document.createElement('div');
90
+ Object.assign(entriesContainer.style, {
91
+ overflowY: 'auto',
92
+ padding: '6px',
93
+ borderRadius: '6px',
94
+ background: 'rgba(0,0,0,0.32)',
95
+ maxHeight: 'calc(45vh - 48px)',
96
+ boxSizing: 'border-box'
97
+ });
98
+
99
+ panel.appendChild(header);
100
+ panel.appendChild(entriesContainer);
101
+ document.body.appendChild(panel);
102
+
103
+ // helper to update stats
104
+ function updateStats() {
105
+ statsSpan.textContent = `${entryCount} 条`;
106
+ }
107
+
108
+ panel._entriesContainer = entriesContainer;
109
+ panel._statsUpdater = updateStats;
110
+ return panel;
111
+ }
112
+
113
+ // ========== 添加条目 ==========
114
+ let entryCount = 0;
115
+ function addEntry({ type = 'log', message = '', time = new Date().toLocaleString(), meta = null }) {
116
+ const panel = createPanel();
117
+ const entriesContainer = panel._entriesContainer;
118
+ const updateStats = panel._statsUpdater;
119
+
120
+ // 限制数量
121
+ if (entryCount >= MAX_ENTRIES) {
122
+ const first = entriesContainer.firstChild;
123
+ if (first) entriesContainer.removeChild(first);
124
+ entryCount--;
125
+ }
126
+
127
+ const entry = document.createElement('div');
128
+ Object.assign(entry.style, {
129
+ padding: '6px',
130
+ marginBottom: '6px',
131
+ borderRadius: '6px',
132
+ background: 'rgba(255,255,255,0.03)',
133
+ });
134
+
135
+ const header = document.createElement('div');
136
+ header.style.display = 'flex';
137
+ header.style.justifyContent = 'space-between';
138
+ header.style.alignItems = 'center';
139
+ header.style.gap = '8px';
140
+
141
+ const left = document.createElement('div');
142
+ // 彩色 type 标签
143
+ const colorMap = {
144
+ error: '#ff6b6b',
145
+ 'window.onerror': '#ff6b6b',
146
+ resource: '#ff9966',
147
+ 'unhandledrejection': '#ff8c66',
148
+ 'console.error': '#ff6b6b',
149
+ 'console.warn': '#ffd966',
150
+ 'console.info': '#66bfff',
151
+ 'console.log': '#bfbfbf',
152
+ 'console.debug': '#a0a0a0',
153
+ 'Vue.error (2)': '#ff6b6b',
154
+ 'Vue.error (3)': '#ff6b6b'
155
+ };
156
+ const tagColor = colorMap[type] || '#bfbfbf';
157
+ left.innerHTML = `<span style="font-weight:700;color:${tagColor}">${type}</span> <span style="opacity:.7;font-size:11px"> ${time}</span>`;
158
+
159
+ const right = document.createElement('div');
160
+ right.style.display = 'flex';
161
+ right.style.gap = '6px';
162
+
163
+ const copyBtn = document.createElement('button');
164
+ copyBtn.textContent = '复制';
165
+ Object.assign(copyBtn.style, { cursor: 'pointer', fontSize: '11px' });
166
+
167
+ const expandBtn = document.createElement('button');
168
+ expandBtn.textContent = '展开';
169
+ Object.assign(expandBtn.style, { cursor: 'pointer', fontSize: '11px' });
170
+
171
+ right.appendChild(copyBtn);
172
+ right.appendChild(expandBtn);
173
+ header.appendChild(left);
174
+ header.appendChild(right);
175
+
176
+ const content = document.createElement('pre');
177
+ Object.assign(content.style, {
178
+ whiteSpace: 'pre-wrap',
179
+ wordBreak: 'break-word',
180
+ margin: '6px 0 0 0',
181
+ maxHeight: '240px',
182
+ overflow: 'auto',
183
+ fontSize: '12px',
184
+ lineHeight: '1.3',
185
+ display: 'block'
186
+ });
187
+
188
+ // 合成文本(message + meta)
189
+ let txt = typeof message === 'string' ? message : safeStringify(message);
190
+ if (meta) {
191
+ try {
192
+ const metaStr = safeStringify(meta);
193
+ txt = `${txt}\n\n[meta]\n${metaStr}`;
194
+ } catch (e) {}
195
+ }
196
+ const fullText = txt;
197
+
198
+ if (txt.length > COLLAPSE_LENGTH) {
199
+ content.textContent = txt.slice(0, COLLAPSE_LENGTH) + '\n\n...(已折叠,点击展开查看全部)';
200
+ expandBtn.onclick = () => { content.textContent = fullText; expandBtn.style.display = 'none'; entriesContainer.scrollTop = entriesContainer.scrollHeight; };
201
+ } else {
202
+ content.textContent = txt;
203
+ expandBtn.style.display = 'none';
204
+ }
205
+
206
+ copyBtn.onclick = () => {
207
+ try { navigator.clipboard.writeText(fullText); } catch (e) { try { prompt('复制文本:Ctrl+C, Enter', fullText); } catch (ee) {} }
208
+ };
209
+
210
+ entry.appendChild(header);
211
+ entry.appendChild(content);
212
+ entriesContainer.appendChild(entry);
213
+ entryCount++;
214
+ updateStats();
215
+
216
+ // 自动滚到底部
217
+ entriesContainer.scrollTop = entriesContainer.scrollHeight;
218
+ }
219
+
220
+ // 报告包装
221
+ function report(obj) {
222
+ try {
223
+ addEntry({ type: obj.type || 'log', message: obj.message || safeStringify(obj), time: new Date().toLocaleString(), meta: obj.meta || null });
224
+ } catch (e) {
225
+ console.warn('Error panel report failed', e);
226
+ }
227
+ }
228
+
229
+ // ========== 捕获逻辑 ==========
230
+
231
+ // 传统同步错误
232
+ window.onerror = function(message, source, lineno, colno, error) {
233
+ const msg = `${message}\n at ${source}:${lineno}:${colno}\n${error && error.stack ? error.stack : ''}`;
234
+ report({ type: 'window.onerror', message: msg });
235
+ return false;
236
+ };
237
+
238
+ // 资源加载错误(图片/script/css 等)
239
+ window.addEventListener('error', function(event) {
240
+ const target = event.target || event.srcElement;
241
+ if (target && (target.src || target.href)) {
242
+ const tag = target.tagName;
243
+ const url = target.src || target.href;
244
+ report({ type: 'resource', message: `Failed to load resource: <${tag}> ${url}` });
245
+ }
246
+ }, true);
247
+
248
+ // 未处理的 Promise 拒绝
249
+ window.addEventListener('unhandledrejection', function(event) {
250
+ report({ type: 'unhandledrejection', message: safeStringify(event.reason) });
251
+ });
252
+
253
+ // 捕获 console.*(可选)
254
+ if (CAPTURE_CONSOLE && typeof console !== 'undefined') {
255
+ const methods = ['log', 'info', 'warn', 'error', 'debug'];
256
+ methods.forEach((m) => {
257
+ if (!(m in console)) return;
258
+ const orig = console[m].bind(console);
259
+ console[m] = function(...args) {
260
+ try {
261
+ // 生成调用栈(方便定位是谁调用了 console)
262
+ let stack = null;
263
+ try {
264
+ const err = new Error();
265
+ if (err.stack) {
266
+ // 移除前两行(Error + 本函数)
267
+ stack = err.stack.split('\n').slice(2).join('\n');
268
+ }
269
+ } catch (e) { stack = null; }
270
+
271
+ const payload = {
272
+ type: `console.${m}`,
273
+ message: args.map(a => safeStringify(a)).join(' '),
274
+ meta: stack
275
+ };
276
+ report(payload);
277
+ } catch (e) {
278
+ // noop
279
+ }
280
+ // 保持原有行为
281
+ try { orig(...args); } catch (e) {}
282
+ };
283
+ });
284
+ }
285
+
286
+ // Vue 2 支持(若使用全局 Vue)
287
+ if (typeof Vue !== 'undefined' && Vue && Vue.config) {
288
+ try {
289
+ Vue.config.errorHandler = function(err, vm, info) {
290
+ report({ type: 'Vue.error (2)', message: `${info}\n${err && err.stack ? err.stack : safeStringify(err)}` });
291
+ };
292
+ } catch (e) { /* ignore */ }
293
+ }
294
+
295
+ // Vue 3 注册函数(在 main.js 创建 app 后调用)
296
+ window.registerVue3ErrorHandler = function(app) {
297
+ if (!app || !app.config) return;
298
+ app.config.errorHandler = (err, instance, info) => {
299
+ report({ type: 'Vue.error (3)', message: `${info}\n${err && err.stack ? err.stack : safeStringify(err)}` });
300
+ };
301
+ };
302
+
303
+ // 立即创建 panel(或可改为按需)
304
+ if (document.readyState === 'loading') {
305
+ document.addEventListener('DOMContentLoaded', createPanel);
306
+ } else createPanel();
307
+
308
+ }
309
+ // ========= End Panel =========