labull-framework 0.1.0
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.
- package/LICENSE +28 -0
- package/README.md +82 -0
- package/dist/auth.d.ts +1 -0
- package/dist/auth.js +17 -0
- package/dist/chunk-_plugin-vue_export-helper-CHgC5LLL.js +9 -0
- package/dist/chunk-index-BWpDbB19.js +129 -0
- package/dist/chunk-index-_jasBCim.js +526 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +30 -0
- package/dist/magic-block.d.ts +1 -0
- package/dist/magic-block.js +1580 -0
- package/dist/style.css +1 -0
- package/dist/theme.d.ts +1 -0
- package/dist/theme.js +4 -0
- package/dist/types/auth-real.d.ts +60 -0
- package/dist/types/auth.d.ts +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/magic-block-MagicBlockConsole.vue.d.ts +2 -0
- package/dist/types/magic-block-MagicBlockHost.vue.d.ts +21 -0
- package/dist/types/magic-block-bridge.d.ts +22 -0
- package/dist/types/magic-block-devkit.d.ts +15 -0
- package/dist/types/magic-block-instances.d.ts +34 -0
- package/dist/types/magic-block-localfs.d.ts +126 -0
- package/dist/types/magic-block-manifest.d.ts +64 -0
- package/dist/types/magic-block-preview.d.ts +11 -0
- package/dist/types/magic-block-real.d.ts +7 -0
- package/dist/types/magic-block-render.d.ts +12 -0
- package/dist/types/magic-block-theme.d.ts +33 -0
- package/dist/types/magic-block.d.ts +1 -0
- package/dist/types/theme.d.ts +3 -0
- package/dist/types/ui-AppSearch.vue.d.ts +2 -0
- package/dist/types/ui-ProductShell.vue.d.ts +31 -0
- package/dist/types/ui-ProductSide.vue.d.ts +14 -0
- package/dist/types/ui-ProductTabs.vue.d.ts +2 -0
- package/dist/types/ui-UserMenu.vue.d.ts +2 -0
- package/dist/types/ui-gate.d.ts +9 -0
- package/dist/types/ui-real.d.ts +16 -0
- package/dist/types/ui-tabs.d.ts +31 -0
- package/dist/types/ui-types.d.ts +20 -0
- package/dist/types/ui.d.ts +1 -0
- package/dist/ui.d.ts +1 -0
- package/dist/ui.js +15 -0
- package/package.json +67 -0
|
@@ -0,0 +1,1580 @@
|
|
|
1
|
+
import { defineComponent as We, ref as w, computed as T, watch as fe, nextTick as Et, onMounted as Ze, onActivated as Ct, onBeforeUnmount as Xe, openBlock as g, createElementBlock as v, normalizeStyle as Qe, normalizeClass as Y, unref as me, createElementVNode as p, toDisplayString as b, createCommentVNode as x, withModifiers as Bt, Fragment as j, withDirectives as Tt, vModelText as At, renderList as oe, createStaticVNode as Ot, createTextVNode as ce } from "vue";
|
|
2
|
+
import { useRoute as et, useRouter as tt } from "vue-router";
|
|
3
|
+
import { A as Mt, b as W, c as Me, u as nt } from "./chunk-index-BWpDbB19.js";
|
|
4
|
+
import { _ as ot } from "./chunk-_plugin-vue_export-helper-CHgC5LLL.js";
|
|
5
|
+
const G = "__mb", st = "call", Pe = "reply", at = "host-ready", it = "height", Pt = "html, body { overflow: hidden !important; }", Rt = `
|
|
6
|
+
(function () {
|
|
7
|
+
var seq = 0, waiting = {}, queue = [], ready = false, last = -1;
|
|
8
|
+
function call(method, args) {
|
|
9
|
+
return new Promise(function (resolve, reject) {
|
|
10
|
+
function run() {
|
|
11
|
+
var id = ++seq;
|
|
12
|
+
waiting[id] = { resolve: resolve, reject: reject };
|
|
13
|
+
parent.postMessage({ '${G}': '${st}', id: id, method: method, args: args }, '*');
|
|
14
|
+
}
|
|
15
|
+
if (ready) run(); else queue.push(run);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
window.addEventListener('message', function (event) {
|
|
19
|
+
var data = event.data, pending;
|
|
20
|
+
if (!data || data['${G}'] === undefined) return;
|
|
21
|
+
if (data['${G}'] === '${at}') { ready = true; queue.splice(0).forEach(function (run) { run(); }); return; }
|
|
22
|
+
if (data['${G}'] !== '${Pe}') return;
|
|
23
|
+
if (!(pending = waiting[data.id])) return;
|
|
24
|
+
delete waiting[data.id];
|
|
25
|
+
if (data.ok) pending.resolve(data.value); else pending.reject(new Error(data.error || '调用失败'));
|
|
26
|
+
});
|
|
27
|
+
window.magicBlock = {
|
|
28
|
+
getMe: function () { return call('getMe', []); },
|
|
29
|
+
navigateTab: function (path) { return call('navigateTab', [path]); }
|
|
30
|
+
};
|
|
31
|
+
function make(prefix) {
|
|
32
|
+
return new Proxy(function () {}, {
|
|
33
|
+
get: function (_, name) { return make(prefix ? prefix + '.' + name : name); },
|
|
34
|
+
apply: function (_, __, args) {
|
|
35
|
+
var params = args[0] === undefined || args[0] === null ? {} : args[0];
|
|
36
|
+
if (typeof params !== 'object' || Array.isArray(params)) return Promise.reject(new Error('magicBackendApi.' + prefix + '(params) 的参数要么是一个对象(拿不准就传 {}),要么是一个文件(File / Blob)'));
|
|
37
|
+
return call('backend', [prefix, params]);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
window.magicBackendApi = make('');
|
|
42
|
+
if (typeof document === 'undefined') return;
|
|
43
|
+
function report() {
|
|
44
|
+
var body = document.body;
|
|
45
|
+
if (!body) return;
|
|
46
|
+
var max = -1, style = window.getComputedStyle(body), bodyTop = body.getBoundingClientRect().top;
|
|
47
|
+
var gap = (parseFloat(style.marginTop) || 0) + (parseFloat(style.marginBottom) || 0);
|
|
48
|
+
try {
|
|
49
|
+
for (var i = 0; i < body.children.length; i += 1) {
|
|
50
|
+
var el = body.children[i], cs = window.getComputedStyle(el), rect = el.getBoundingClientRect();
|
|
51
|
+
if (cs.display === 'none' || cs.visibility === 'hidden' || cs.position === 'fixed' || rect.height <= 0) continue;
|
|
52
|
+
if (rect.bottom > max) max = rect.bottom;
|
|
53
|
+
}
|
|
54
|
+
} catch (error) { return; }
|
|
55
|
+
var height = max < 0 ? 0 : Math.ceil(max - bodyTop + gap); /* 一条正常流内容都没有 ⇒ 报 0(宿主按还没量到处理) */
|
|
56
|
+
/* 🔴 +1 是"取整的余量":几何上算出来的内容高度常常差那么零点几像素(子像素排版),
|
|
57
|
+
* 宿主照着它设高度时就会**差一像素**——那一像素正是一个竖滚动条的开关。 */
|
|
58
|
+
if (height > 0) height += 1;
|
|
59
|
+
if (height === last) return; /* 一样高就不再发:否则「宿主改高度 → 触发观察 → 又发一次」会转个不停 */
|
|
60
|
+
last = height;
|
|
61
|
+
parent.postMessage({ '${G}': '${it}', height: height }, '*');
|
|
62
|
+
}
|
|
63
|
+
function start() {
|
|
64
|
+
report();
|
|
65
|
+
if (window.ResizeObserver && document.body) new ResizeObserver(report).observe(document.body);
|
|
66
|
+
}
|
|
67
|
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start); else start();
|
|
68
|
+
setInterval(report, 500);
|
|
69
|
+
})();
|
|
70
|
+
`, jt = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/;
|
|
71
|
+
function lt(t) {
|
|
72
|
+
const e = typeof window > "u" ? "http://localhost" : window.location.href;
|
|
73
|
+
return new URL(`${Mt}${t}`, e);
|
|
74
|
+
}
|
|
75
|
+
const Ke = (() => {
|
|
76
|
+
try {
|
|
77
|
+
return lt("").origin;
|
|
78
|
+
} catch {
|
|
79
|
+
return typeof window > "u" ? "" : window.location.origin;
|
|
80
|
+
}
|
|
81
|
+
})();
|
|
82
|
+
async function Nt(t, e, n) {
|
|
83
|
+
if (!jt.test(e)) throw new Error(`路由名不合法:${e}(要写成 类名.方法名,例如 Customer.create)`);
|
|
84
|
+
const s = lt(`/bx/${t}/${e}`);
|
|
85
|
+
if (s.origin !== Ke) throw new Error(`后端地址必须是本应用的(${Ke}):${s.origin}`);
|
|
86
|
+
const l = n instanceof Blob ? n : null, r = await fetch(s.toString(), {
|
|
87
|
+
method: "POST",
|
|
88
|
+
credentials: "include",
|
|
89
|
+
// 带上 HttpOnly 会话 cookie;authHeaders('POST') 给 CSRF 头(缺了 403)
|
|
90
|
+
headers: l ? { "Content-Type": l.type || "application/octet-stream", ...Me("POST") } : { "Content-Type": "application/json", ...Me("POST") },
|
|
91
|
+
body: l ?? JSON.stringify(n ?? {})
|
|
92
|
+
}), c = await r.text().catch(() => "");
|
|
93
|
+
if (r.ok) {
|
|
94
|
+
if (!c) return null;
|
|
95
|
+
try {
|
|
96
|
+
return JSON.parse(c);
|
|
97
|
+
} catch {
|
|
98
|
+
return c;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
let u = c;
|
|
102
|
+
try {
|
|
103
|
+
const d = JSON.parse(c);
|
|
104
|
+
typeof d?.message == "string" && (u = d.message);
|
|
105
|
+
} catch {
|
|
106
|
+
u = c;
|
|
107
|
+
}
|
|
108
|
+
throw new Error(u ? `${e} 调用失败:${u.slice(0, 300)}` : `${e} 调用失败(${r.status})`);
|
|
109
|
+
}
|
|
110
|
+
function It(t, e) {
|
|
111
|
+
const n = (c) => {
|
|
112
|
+
try {
|
|
113
|
+
t.contentWindow?.postMessage(c, "*");
|
|
114
|
+
} catch (u) {
|
|
115
|
+
console.warn("[magic-block] 有一条宿主消息没能发给块(协议继续走):", u);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
async function s(c, u) {
|
|
119
|
+
if (c === "getMe") return await W("/auth/me");
|
|
120
|
+
if (c === "navigateTab") {
|
|
121
|
+
const d = typeof u[0] == "string" ? u[0].trim() : "";
|
|
122
|
+
if (!d || !e.onNavigateTab) throw new Error(`跳不了 ${d || "(空路径)"}:这个页面没有把宿主接到路由上`);
|
|
123
|
+
return await e.onNavigateTab(d);
|
|
124
|
+
}
|
|
125
|
+
if (c === "backend") {
|
|
126
|
+
const d = typeof u[0] == "string" ? u[0].trim() : "", f = u[1];
|
|
127
|
+
if (!d) throw new Error("后端调用的路由名是空的:要写成 magicBackendApi.类名.方法名(参数)");
|
|
128
|
+
if (!f || typeof f != "object" || Array.isArray(f)) throw new Error(`magicBackendApi.${d}(params) 的参数要么是一个对象,要么是一个文件(File / Blob)`);
|
|
129
|
+
return await Nt(e.uuid, d, f);
|
|
130
|
+
}
|
|
131
|
+
throw new Error(`不认识的方法:${String(c)}(这块桥只有 getMe / navigateTab / backend 三个)`);
|
|
132
|
+
}
|
|
133
|
+
const l = (c) => {
|
|
134
|
+
if (c.source !== t.contentWindow) return;
|
|
135
|
+
const u = c.data;
|
|
136
|
+
if (!u) return;
|
|
137
|
+
if (u[G] === it) {
|
|
138
|
+
const f = typeof u.height == "number" ? u.height : NaN;
|
|
139
|
+
Number.isFinite(f) && f > 0 && e.onHeight?.(f);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (u[G] !== st) return;
|
|
143
|
+
const d = u.id;
|
|
144
|
+
(async () => {
|
|
145
|
+
try {
|
|
146
|
+
n({ [G]: Pe, id: d, ok: !0, value: await s(String(u.method), u.args ?? []) });
|
|
147
|
+
} catch (f) {
|
|
148
|
+
n({ [G]: Pe, id: d, ok: !1, error: f instanceof Error ? f.message : String(f) });
|
|
149
|
+
}
|
|
150
|
+
})();
|
|
151
|
+
};
|
|
152
|
+
window.addEventListener("message", l);
|
|
153
|
+
const r = () => n({ [G]: at });
|
|
154
|
+
return r(), t.addEventListener("load", r), () => {
|
|
155
|
+
window.removeEventListener("message", l), t.removeEventListener("load", r);
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const rt = "index.open.html", Lt = "backend_open.py", zt = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
|
|
159
|
+
function Ue(t, e) {
|
|
160
|
+
if (zt.test(t)) return;
|
|
161
|
+
const n = (t.split(/[?#]/)[0] ?? "").replace(/^\.\//, "").replace(/^\/+/, "");
|
|
162
|
+
if (n in e) return n;
|
|
163
|
+
const s = n.split("/").pop() ?? "";
|
|
164
|
+
return s ? Object.keys(e).find((l) => (l.split("/").pop() ?? "") === s) : void 0;
|
|
165
|
+
}
|
|
166
|
+
const ct = "\0mb:";
|
|
167
|
+
function Dt(t) {
|
|
168
|
+
return `${ct}${t}\0`;
|
|
169
|
+
}
|
|
170
|
+
const Ft = /<script\b([^>]*?)\bsrc\s*=\s*(["'])(.*?)\2([^>]*)>\s*<\/script\s*>/gi, Ht = /<link\b([^>]*?)\bhref\s*=\s*(["'])(.*?)\2([^>]*?)\/?>/gi;
|
|
171
|
+
function Kt(t, e) {
|
|
172
|
+
const n = [], s = (c) => (n.push(c), Dt(n.length - 1));
|
|
173
|
+
return { html: t.replace(Ft, (c, u, d, f, _) => {
|
|
174
|
+
const y = Ue(f, e);
|
|
175
|
+
return y === void 0 ? c : s(`<script${u}${_}>
|
|
176
|
+
${ut(e[y] ?? "")}
|
|
177
|
+
<\/script>`);
|
|
178
|
+
}).replace(Ht, (c, u, d, f, _) => {
|
|
179
|
+
if (!/\brel\s*=\s*(?:"|')?stylesheet/i.test(`${u}${_}`)) return c;
|
|
180
|
+
const y = Ue(f, e);
|
|
181
|
+
return y === void 0 ? c : s(`<style>
|
|
182
|
+
${e[y]}
|
|
183
|
+
</style>`);
|
|
184
|
+
}), slots: n };
|
|
185
|
+
}
|
|
186
|
+
function ut(t) {
|
|
187
|
+
return t.replace(/<\/(script)/gi, "<\\/$1");
|
|
188
|
+
}
|
|
189
|
+
function dt(t, e) {
|
|
190
|
+
const n = /<\/head\s*>/i.exec(t);
|
|
191
|
+
return n ? `${t.slice(0, n.index)}${e}${t.slice(n.index)}` : `${e}${t}`;
|
|
192
|
+
}
|
|
193
|
+
function Ut(t, e) {
|
|
194
|
+
return e ? dt(t, `<script>
|
|
195
|
+
${ut(e)}
|
|
196
|
+
<\/script>
|
|
197
|
+
`) : t;
|
|
198
|
+
}
|
|
199
|
+
function qe(t, e) {
|
|
200
|
+
return e ? dt(t, `<style data-magic-block="host">
|
|
201
|
+
${e}
|
|
202
|
+
</style>
|
|
203
|
+
`) : t;
|
|
204
|
+
}
|
|
205
|
+
function qt(t, e = "") {
|
|
206
|
+
const n = typeof e == "string" ? e : e.bootstrap ?? "", s = typeof e == "string" ? "" : e.css ?? "", l = typeof e == "string" ? "" : e.themeCss ?? "", r = t[rt];
|
|
207
|
+
if (r === void 0) return "";
|
|
208
|
+
const { html: c, slots: u } = Kt(r, t), d = Ut(qe(qe(c, l), s), n), f = d.split(ct);
|
|
209
|
+
if (f.length === 1) return d;
|
|
210
|
+
let _ = f[0] ?? "";
|
|
211
|
+
for (let y = 1; y < f.length; y += 1) {
|
|
212
|
+
const S = f[y] ?? "", C = S.indexOf("\0"), N = Number(S.slice(0, C));
|
|
213
|
+
_ += (u[N] ?? "") + S.slice(C + 1);
|
|
214
|
+
}
|
|
215
|
+
return _;
|
|
216
|
+
}
|
|
217
|
+
const he = /* @__PURE__ */ new Map();
|
|
218
|
+
let ue = null;
|
|
219
|
+
const Re = /* @__PURE__ */ new Set();
|
|
220
|
+
function Gt(t) {
|
|
221
|
+
return Re.add(t), () => {
|
|
222
|
+
Re.delete(t);
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function Jt() {
|
|
226
|
+
he.clear(), ue = null;
|
|
227
|
+
for (const t of Re) t();
|
|
228
|
+
}
|
|
229
|
+
async function Vt(t) {
|
|
230
|
+
const e = [...new Set(t)].filter(Boolean);
|
|
231
|
+
if (e.some((n) => !he.has(n))) {
|
|
232
|
+
ue && await ue.catch(() => {
|
|
233
|
+
});
|
|
234
|
+
const n = e.filter((s) => !he.has(s));
|
|
235
|
+
n.length > 0 && (ue = W(`/magic-blocks/render?ids=${n.map(encodeURIComponent).join(",")}`).then((s) => {
|
|
236
|
+
for (const l of s) l?.id && he.set(l.id, l);
|
|
237
|
+
return s;
|
|
238
|
+
}).catch(() => []).finally(() => {
|
|
239
|
+
ue = null;
|
|
240
|
+
}), await ue.catch(() => {
|
|
241
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
return e.map((n) => he.get(n)).filter((n) => !!n);
|
|
244
|
+
}
|
|
245
|
+
const de = "labull.css", je = "CONTROLS.lock.html";
|
|
246
|
+
function Yt(t, e) {
|
|
247
|
+
const n = String(e || "");
|
|
248
|
+
if (/@font-face|@keyframes/i.test(n)) return !0;
|
|
249
|
+
const s = String(t || "");
|
|
250
|
+
return /(^|[\s,>+~])(\.p-|\.pi(?![a-z])|\.pi-|\.labull-|:root|html(?![a-z-])|body(?![a-z-])|\*|:where\()/.test(s) ? !0 : /--(?:sp|surface|primary|text-color|content|highlight|focus-ring|p-)/.test(n);
|
|
251
|
+
}
|
|
252
|
+
function pt(t, e, n) {
|
|
253
|
+
for (let s = 0; s < t.length; s += 1) {
|
|
254
|
+
const l = t[s], r = String(l.cssText || ""), c = String(l.selectorText || "");
|
|
255
|
+
if (l.cssRules && l.cssRules.length > 0) {
|
|
256
|
+
const u = [];
|
|
257
|
+
if (pt(l.cssRules, u, n), u.length === 0) continue;
|
|
258
|
+
const d = r.slice(0, r.indexOf("{")).trim();
|
|
259
|
+
e.push(`${d} {
|
|
260
|
+
${u.map((f) => " " + f).join(`
|
|
261
|
+
`)}
|
|
262
|
+
}`);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
Yt(c, r) && (e.push(r), n.n += 1);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function ft() {
|
|
269
|
+
const t = [], e = [], n = { n: 0 };
|
|
270
|
+
let s = 0;
|
|
271
|
+
const l = typeof document > "u" ? [] : Array.from(document.styleSheets);
|
|
272
|
+
for (const c of l) {
|
|
273
|
+
const u = c.href || "(内联 <style>)";
|
|
274
|
+
let d = null;
|
|
275
|
+
try {
|
|
276
|
+
d = c.cssRules;
|
|
277
|
+
} catch {
|
|
278
|
+
e.push(u);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
if (!d || d.length === 0) continue;
|
|
282
|
+
const f = [];
|
|
283
|
+
pt(d, f, n), f.length !== 0 && (s += 1, t.push(`/* ── ${u} ── */`, ...f));
|
|
284
|
+
}
|
|
285
|
+
return { css: [
|
|
286
|
+
"/* 生成物:每次导出都会重写,别手改。",
|
|
287
|
+
" *",
|
|
288
|
+
" * 这是这个应用此刻真正在用的 **labull 主题 + 控件样式**(token / PrimeVue 控件 / 结构类 / 图标)。",
|
|
289
|
+
` * 收自 ${s} 张样式表、${n.n} 条规则。`,
|
|
290
|
+
e.length > 0 ? ` * ⚠️ 有 ${e.length} 张样式表读不到规则(跨源,浏览器不让读):${e.join(" / ")} —— 需要的话手动补一份。` : " * (没有读不到的样式表:收全了。)",
|
|
291
|
+
" *",
|
|
292
|
+
" * 怎么用(块里):**什么都不用加** —— 宿主会把这一份注入每个块的 iframe(`MagicBlockHost` 的 `theme` prop),",
|
|
293
|
+
' * 所以直接写类名就行:`<button class="p-button">` / `<input class="p-inputtext">` / `<span class="p-tag">`。',
|
|
294
|
+
" * 这一份放在开发包里是给你**查 / 对照 / 抄**用的(`" + je + "` 里的每一块 markup 都能直接用)。",
|
|
295
|
+
" * ⚠️ 别把它当块包文件塞进块里:它 500 KB 上下,而且宿主已经注入了一份(重复只会更慢)。",
|
|
296
|
+
" * ⚠️ 块里**只有 CSS,没有 JS 组件**:下拉 / 日期 / 弹窗那些要装了 PrimeVue 才有行为,",
|
|
297
|
+
" * 这里能用的是类名(按钮、输入框、标签、面板、表格、图标)。",
|
|
298
|
+
" */",
|
|
299
|
+
""
|
|
300
|
+
].join(`
|
|
301
|
+
`) + t.join(`
|
|
302
|
+
`) + `
|
|
303
|
+
`, sheets: s, skipped: e, rules: n.n };
|
|
304
|
+
}
|
|
305
|
+
function Wt() {
|
|
306
|
+
const n = [
|
|
307
|
+
["按钮", '<button class="p-button" type="button">主要按钮</button>'],
|
|
308
|
+
["次要按钮", '<button class="p-button p-button-outlined" type="button">次要按钮</button>'],
|
|
309
|
+
["纯文字按钮", '<button class="p-button p-button-text" type="button">纯文字</button>'],
|
|
310
|
+
["小按钮 + 图标", '<button class="p-button p-button-sm" type="button"><i class="pi pi-refresh"></i> 刷新</button>'],
|
|
311
|
+
["危险按钮", '<button class="p-button p-button-danger" type="button">删除</button>'],
|
|
312
|
+
["输入框", '<input class="p-inputtext p-component" placeholder="请输入" />'],
|
|
313
|
+
["只读 + 等宽", '<input class="p-inputtext p-component" readonly value="只读的值" style="font-family:ui-monospace,monospace" />'],
|
|
314
|
+
["勾选框", '<label style="display:flex;align-items:center;gap:.5rem"><span class="p-checkbox p-component"><input class="p-checkbox-input" type="checkbox" checked /><span class="p-checkbox-box"></span></span>勾选我</label>'],
|
|
315
|
+
["标签", '<span class="p-tag">默认</span> <span class="p-tag p-tag-success">成功</span> <span class="p-tag p-tag-warning">警告</span> <span class="p-tag p-tag-danger">危险</span>'],
|
|
316
|
+
["图标", '<i class="pi pi-search"></i> <i class="pi pi-user"></i> <i class="pi pi-list"></i> <i class="pi pi-download"></i> <i class="pi pi-cog"></i>(类名表见 PrimeIcons)'],
|
|
317
|
+
["原生控件(不需要组件 JS)", '<input type="file" /> <select><option>下拉</option></select> <input type="checkbox" checked /> <progress value="60" max="100"></progress>'],
|
|
318
|
+
["弹窗(原生 <code><dialog></code> + <code>showModal()</code>)", `<button class="p-button p-button-sm" type="button" onclick="document.getElementById('demoDialog').showModal()">打开弹窗</button><dialog id="demoDialog"><h3 style="margin:0 0 .5rem">弹窗标题</h3><p style="margin:0 0 .75rem">弹窗正文。遮罩与长相来自主题,块里不必自己画一层浮层。</p><form method="dialog"><button class="p-button p-button-sm">关闭</button></form></dialog>`],
|
|
319
|
+
['"选择文件"另一种写法(文案更干净)', '<label class="p-button p-button-sm p-button-outlined" for="demo-file"><i class="pi pi-folder-open"></i> 选择照片</label><input id="demo-file" class="visually-hidden" type="file" />'],
|
|
320
|
+
["面板 + 标题", '<div class="labull-panel"><h2 class="labull-subject-title"><i class="pi pi-list"></i> 面板标题</h2><p>面板正文。</p></div>'],
|
|
321
|
+
["表格", "<table><thead><tr><th>列 A</th><th>列 B</th></tr></thead><tbody><tr><td>值</td><td>值</td></tr></tbody></table>"]
|
|
322
|
+
].map(
|
|
323
|
+
([s, l]) => ` <section>
|
|
324
|
+
<h3>${s}</h3>
|
|
325
|
+
<div class="demo">${l}</div>
|
|
326
|
+
<pre>${l.replace(/&/g, "&").replace(/</g, "<")}</pre>
|
|
327
|
+
</section>`
|
|
328
|
+
).join(`
|
|
329
|
+
`);
|
|
330
|
+
return `<!doctype html>
|
|
331
|
+
<!-- 生成物:每次导出都会重写,别手改。
|
|
332
|
+
控件清单:这里每一行都是**照着抄就能用**的 markup(样式来自同目录的 labull.css)。
|
|
333
|
+
🔴 只有 CSS:下拉 / 日期 / 弹窗那些要装 PrimeVue 才有行为,所以不列。 -->
|
|
334
|
+
<html lang="zh">
|
|
335
|
+
<head>
|
|
336
|
+
<meta charset="utf-8">
|
|
337
|
+
<title>labull 控件清单</title>
|
|
338
|
+
<link rel="stylesheet" href="${de}">
|
|
339
|
+
<style>
|
|
340
|
+
body { margin: 0; padding: 16px; }
|
|
341
|
+
h1 { margin: 0 0 4px; font-size: 18px; }
|
|
342
|
+
p.lead { margin: 0 0 16px; color: var(--text-color-secondary, #6b7280); }
|
|
343
|
+
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
|
|
344
|
+
section { margin-bottom: 18px; padding-bottom: 12px; border-bottom: 1px solid var(--surface-border, #e5e7eb); }
|
|
345
|
+
h3 { margin: 0 0 8px; font-size: 14px; }
|
|
346
|
+
.demo { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 6px; }
|
|
347
|
+
pre { margin: 0; padding: 8px; overflow: auto; background: var(--surface-ground, #f9fafb); border: 1px solid var(--surface-border, #e5e7eb); border-radius: 6px; font-size: 12px; }
|
|
348
|
+
code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
|
|
349
|
+
</style>
|
|
350
|
+
</head>
|
|
351
|
+
<body>
|
|
352
|
+
<h1>labull 控件清单</h1>
|
|
353
|
+
<p class="lead">
|
|
354
|
+
这些类名来自 <code>${de}</code>(与应用同源同版本)。块里别自己写一套配色 / 按钮:
|
|
355
|
+
把这一行加进 <code>index.open.html</code> 的 <code><head></code>,
|
|
356
|
+
下面每一块 markup 都能直接用:
|
|
357
|
+
<code><link rel="stylesheet" href="${de}"></code>
|
|
358
|
+
<br />
|
|
359
|
+
⚠️ 这里**只有 CSS,没有 JS 组件**:下拉 / 日期 / 弹窗那些要装了 PrimeVue 才有行为(块里没有构建步骤)。
|
|
360
|
+
</p>
|
|
361
|
+
${n}
|
|
362
|
+
</body>
|
|
363
|
+
</html>
|
|
364
|
+
`;
|
|
365
|
+
}
|
|
366
|
+
const se = /* @__PURE__ */ new Map(), Ne = /* @__PURE__ */ new Set(), Zt = 4e3;
|
|
367
|
+
let Ie = "", ve = null;
|
|
368
|
+
function Ge() {
|
|
369
|
+
for (const t of Ne) t();
|
|
370
|
+
}
|
|
371
|
+
function Xt(t) {
|
|
372
|
+
return Ne.add(t), () => {
|
|
373
|
+
Ne.delete(t);
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
function ye() {
|
|
377
|
+
return Ie;
|
|
378
|
+
}
|
|
379
|
+
function Qt(t, e) {
|
|
380
|
+
const n = String(t || "").trim(), s = String(e || "").trim();
|
|
381
|
+
if (!n || !s) return () => {
|
|
382
|
+
};
|
|
383
|
+
let l = se.get(n);
|
|
384
|
+
return l || (l = /* @__PURE__ */ new Set(), se.set(n, l)), l.add(s), () => {
|
|
385
|
+
const r = se.get(n);
|
|
386
|
+
r && (r.delete(s), r.size === 0 && se.delete(n));
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
function Le(t, e) {
|
|
390
|
+
const n = se.get(String(t || ""));
|
|
391
|
+
if (!n) return null;
|
|
392
|
+
for (const s of n)
|
|
393
|
+
if (s !== e) return s;
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
function en(t) {
|
|
397
|
+
return [...se.get(String(t || "")) ?? []];
|
|
398
|
+
}
|
|
399
|
+
function tn(t, e) {
|
|
400
|
+
const n = se.get(String(t || ""));
|
|
401
|
+
return !n || n.size === 0 || n.has(e) ? null : Le(t, e);
|
|
402
|
+
}
|
|
403
|
+
function mt(t, e = Zt) {
|
|
404
|
+
Ie = String(t || ""), ve !== null && clearTimeout(ve), ve = setTimeout(() => {
|
|
405
|
+
ve = null, Ie = "", Ge();
|
|
406
|
+
}, Math.max(1, e)), Ge();
|
|
407
|
+
}
|
|
408
|
+
const nn = ["data-magic-block", "data-magic-module"], on = ["srcdoc", "title", "scrolling"], sn = { class: "mb__state" }, an = { class: "mb__brief" }, ln = {
|
|
409
|
+
key: 0,
|
|
410
|
+
class: "mb__reason"
|
|
411
|
+
}, rn = {
|
|
412
|
+
key: 2,
|
|
413
|
+
class: "mb__ring",
|
|
414
|
+
"aria-hidden": "true"
|
|
415
|
+
}, cn = ["title", "aria-label"], un = ["title", "aria-label"], dn = ["title", "aria-label"], pn = ["title", "aria-label"], fn = /* @__PURE__ */ We({
|
|
416
|
+
__name: "MagicBlockHost",
|
|
417
|
+
props: {
|
|
418
|
+
uuid: {},
|
|
419
|
+
name: { default: "" },
|
|
420
|
+
module: { default: "" },
|
|
421
|
+
width: {},
|
|
422
|
+
height: {},
|
|
423
|
+
flexible: { type: Boolean, default: !0 },
|
|
424
|
+
fullSize: { type: Boolean, default: !1 },
|
|
425
|
+
responsive: { type: Boolean, default: !0 },
|
|
426
|
+
theme: { type: Boolean, default: !0 }
|
|
427
|
+
},
|
|
428
|
+
setup(t) {
|
|
429
|
+
const e = t, n = et(), s = tt(), { isStaff: l } = nt(), r = w(null), c = w(null), u = w(null), d = w(!1), f = w(0), _ = w(0);
|
|
430
|
+
let y = null, S = null, C = null, N = null, Z = null, A = null;
|
|
431
|
+
const B = (m) => typeof m == "number" ? `${m}rem` : m ?? "", K = T(() => e.name.trim() || e.uuid), I = T(() => u.value?.state ?? ""), L = T(() => I.value === "invalid" ? "校验不通过" : I.value === "draft" ? "未开发" : K.value), ae = T(() => (u.value?.brief ?? "").trim() || (u.value?.name ?? "").trim() || K.value), J = T(() => (u.value?.reason ?? "").trim()), ee = T(() => e.flexible && !e.fullSize && f.value > 0), M = T(() => {
|
|
432
|
+
const m = B(e.height);
|
|
433
|
+
if (!m || typeof document > "u") return 0;
|
|
434
|
+
const k = document.createElement("div");
|
|
435
|
+
k.style.cssText = `position:absolute;visibility:hidden;pointer-events:none;height:${m}`, (r.value ?? document.body).appendChild(k);
|
|
436
|
+
const D = parseFloat(getComputedStyle(k).height) || 0;
|
|
437
|
+
return k.remove(), D;
|
|
438
|
+
}), E = T(() => Math.max(_.value, f.value, M.value, 1)), we = T(() => {
|
|
439
|
+
const m = { "--mb-width": B(e.width) || (e.responsive ? "100%" : "auto"), "--mb-height": B(e.height) || "12rem" };
|
|
440
|
+
return e.fullSize ? { ...m, height: `${E.value}px`, flexShrink: "0" } : ee.value ? { ...m, height: `${f.value}px`, flexGrow: "0" } : m;
|
|
441
|
+
}), z = T(() => {
|
|
442
|
+
const m = {};
|
|
443
|
+
for (const [k, D] of Object.entries(u.value?.files ?? {})) k !== Lt && (m[k] = D);
|
|
444
|
+
return m;
|
|
445
|
+
}), V = T(() => I.value === "ok" && typeof z.value[rt] == "string");
|
|
446
|
+
let U = null;
|
|
447
|
+
function pe() {
|
|
448
|
+
if (!e.theme) return "";
|
|
449
|
+
if (U !== null || (U = "", typeof document > "u")) return U;
|
|
450
|
+
try {
|
|
451
|
+
U = ft().css;
|
|
452
|
+
} catch {
|
|
453
|
+
U = "";
|
|
454
|
+
}
|
|
455
|
+
return U;
|
|
456
|
+
}
|
|
457
|
+
const te = T(() => {
|
|
458
|
+
if (!V.value) return "";
|
|
459
|
+
const m = { bootstrap: Rt };
|
|
460
|
+
e.fullSize && (m.css = Pt);
|
|
461
|
+
const k = pe();
|
|
462
|
+
return k && (m.themeCss = k), qt(z.value, m);
|
|
463
|
+
});
|
|
464
|
+
async function $e(m) {
|
|
465
|
+
const k = s.getRoutes().map((D) => D.path);
|
|
466
|
+
if (!k.includes(m)) throw new Error(`这个应用里没有页面 ${m}。能跳的页面只有:${k.length > 0 ? k.join(" / ") : "(还没有配路由)"}`);
|
|
467
|
+
return await s.push(m), s.currentRoute.value.fullPath || m;
|
|
468
|
+
}
|
|
469
|
+
function xe() {
|
|
470
|
+
C?.(), C = null;
|
|
471
|
+
const m = c.value;
|
|
472
|
+
m && (C = It(m, { uuid: e.uuid, onHeight: (k) => {
|
|
473
|
+
f.value = k;
|
|
474
|
+
}, onNavigateTab: $e }));
|
|
475
|
+
}
|
|
476
|
+
async function ie() {
|
|
477
|
+
const m = e.uuid, k = await Vt([m]);
|
|
478
|
+
m === e.uuid && (u.value = k.find((D) => D.id === m) ?? null);
|
|
479
|
+
}
|
|
480
|
+
function Se() {
|
|
481
|
+
window.dispatchEvent(new CustomEvent("magic-block:open-console", { detail: { uuid: e.uuid } }));
|
|
482
|
+
}
|
|
483
|
+
function le(m = !1) {
|
|
484
|
+
m && Z?.(), Z = Qt(e.uuid, n.path);
|
|
485
|
+
}
|
|
486
|
+
const q = T(() => {
|
|
487
|
+
if (!l.value) return "";
|
|
488
|
+
const m = Le(e.uuid, n.path);
|
|
489
|
+
return m ? `管理员:跳到还挂着这个块的页面 ${m}(点一下这一块会亮红框)` : "管理员:这个块只在本页挂着 —— 点一下会亮几秒红框";
|
|
490
|
+
});
|
|
491
|
+
function H() {
|
|
492
|
+
if (!l.value) return;
|
|
493
|
+
const m = Le(e.uuid, n.path);
|
|
494
|
+
mt(e.uuid), m && s.push(m);
|
|
495
|
+
}
|
|
496
|
+
function ge(m) {
|
|
497
|
+
let k = m.parentElement;
|
|
498
|
+
for (; k; ) {
|
|
499
|
+
if (getComputedStyle(k).overflowY !== "visible" && k.clientHeight > 0) return k;
|
|
500
|
+
k = k.parentElement;
|
|
501
|
+
}
|
|
502
|
+
return document.documentElement;
|
|
503
|
+
}
|
|
504
|
+
function re() {
|
|
505
|
+
const m = r.value;
|
|
506
|
+
if (!m) return 0;
|
|
507
|
+
const k = ge(m), D = k === document.documentElement ? window.innerHeight : k.clientHeight, Ee = k === document.documentElement ? window.innerHeight : k.getBoundingClientRect().top + k.clientHeight;
|
|
508
|
+
return Math.max(0, Math.floor(Math.max(Ee - m.getBoundingClientRect().top, D)));
|
|
509
|
+
}
|
|
510
|
+
function X() {
|
|
511
|
+
if (!e.fullSize || !r.value) return;
|
|
512
|
+
const m = re();
|
|
513
|
+
m > 0 && Math.abs(m - _.value) > 1 && (_.value = m);
|
|
514
|
+
}
|
|
515
|
+
function R() {
|
|
516
|
+
if (!e.fullSize || !r.value) {
|
|
517
|
+
S?.disconnect(), S = null, y = null;
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
const m = ge(r.value);
|
|
521
|
+
y !== m && (y = m, S?.disconnect(), S = null), X(), !(S || typeof ResizeObserver > "u") && (S = new ResizeObserver(() => X()), S.observe(m));
|
|
522
|
+
}
|
|
523
|
+
return fe(c, () => xe()), fe(te, () => {
|
|
524
|
+
f.value = 0, _.value = 0, R();
|
|
525
|
+
}, { flush: "post" }), fe(() => [e.fullSize, e.height, e.uuid], () => {
|
|
526
|
+
Et().then(() => R());
|
|
527
|
+
}), fe(d, (m) => {
|
|
528
|
+
if (!m) return;
|
|
529
|
+
const k = r.value;
|
|
530
|
+
!k || !k.isConnected || k.offsetParent === null || k.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
531
|
+
}), fe(() => e.uuid, () => {
|
|
532
|
+
f.value = 0, u.value = null, le(!0), d.value = ye() === e.uuid, ie();
|
|
533
|
+
}), Ze(() => {
|
|
534
|
+
N = Gt(() => void ie()), A = Xt(() => {
|
|
535
|
+
d.value = ye() === e.uuid;
|
|
536
|
+
}), d.value = ye() === e.uuid, le(), R(), ie();
|
|
537
|
+
}), Ct(() => {
|
|
538
|
+
le(!0), d.value = ye() === e.uuid, R();
|
|
539
|
+
}), Xe(() => {
|
|
540
|
+
S?.disconnect(), S = null, N?.(), N = null, A?.(), A = null, Z?.(), Z = null, C?.(), C = null;
|
|
541
|
+
}), (m, k) => (g(), v("div", {
|
|
542
|
+
ref_key: "el",
|
|
543
|
+
ref: r,
|
|
544
|
+
class: Y(["mb", { "mb--responsive": t.responsive, "mb--full": t.fullSize, "mb--admin": me(l), "mb--lit": me(l) && d.value }]),
|
|
545
|
+
style: Qe(we.value),
|
|
546
|
+
"data-magic-block": t.uuid,
|
|
547
|
+
"data-magic-module": t.module
|
|
548
|
+
}, [
|
|
549
|
+
V.value ? (g(), v("iframe", {
|
|
550
|
+
key: 0,
|
|
551
|
+
ref_key: "frame",
|
|
552
|
+
ref: c,
|
|
553
|
+
class: "mb__frame",
|
|
554
|
+
srcdoc: te.value,
|
|
555
|
+
title: K.value,
|
|
556
|
+
scrolling: t.fullSize ? "no" : void 0,
|
|
557
|
+
sandbox: "allow-scripts allow-forms allow-modals allow-popups allow-downloads"
|
|
558
|
+
}, null, 8, on)) : (g(), v("div", {
|
|
559
|
+
key: 1,
|
|
560
|
+
class: Y(["mb__empty", { "mb__empty--off": I.value === "invalid", "mb__empty--draft": I.value === "draft" }]),
|
|
561
|
+
onClick: H
|
|
562
|
+
}, [
|
|
563
|
+
p("p", sn, b(L.value), 1),
|
|
564
|
+
p("p", an, b(ae.value), 1),
|
|
565
|
+
I.value === "invalid" && J.value ? (g(), v("p", ln, b(J.value), 1)) : x("", !0),
|
|
566
|
+
p("button", {
|
|
567
|
+
class: "mb__go",
|
|
568
|
+
type: "button",
|
|
569
|
+
onClick: Bt(Se, ["stop"])
|
|
570
|
+
}, "去控制台")
|
|
571
|
+
], 2)),
|
|
572
|
+
me(l) ? (g(), v("span", rn)) : x("", !0),
|
|
573
|
+
me(l) && V.value ? (g(), v(j, { key: 3 }, [
|
|
574
|
+
p("button", {
|
|
575
|
+
class: "mb__grab mb__grab--top",
|
|
576
|
+
type: "button",
|
|
577
|
+
title: q.value,
|
|
578
|
+
"aria-label": q.value,
|
|
579
|
+
onClick: H
|
|
580
|
+
}, null, 8, cn),
|
|
581
|
+
p("button", {
|
|
582
|
+
class: "mb__grab mb__grab--right",
|
|
583
|
+
type: "button",
|
|
584
|
+
title: q.value,
|
|
585
|
+
"aria-label": q.value,
|
|
586
|
+
onClick: H
|
|
587
|
+
}, null, 8, un),
|
|
588
|
+
p("button", {
|
|
589
|
+
class: "mb__grab mb__grab--bottom",
|
|
590
|
+
type: "button",
|
|
591
|
+
title: q.value,
|
|
592
|
+
"aria-label": q.value,
|
|
593
|
+
onClick: H
|
|
594
|
+
}, null, 8, dn),
|
|
595
|
+
p("button", {
|
|
596
|
+
class: "mb__grab mb__grab--left",
|
|
597
|
+
type: "button",
|
|
598
|
+
title: q.value,
|
|
599
|
+
"aria-label": q.value,
|
|
600
|
+
onClick: H
|
|
601
|
+
}, null, 8, pn)
|
|
602
|
+
], 64)) : x("", !0)
|
|
603
|
+
], 14, nn));
|
|
604
|
+
}
|
|
605
|
+
}), ts = /* @__PURE__ */ ot(fn, [["__scopeId", "data-v-6905a063"]]), Be = "index.open.html", Te = "magic-block.open.yaml", _e = ["id", "module", "name", "brief"], ze = 40, mn = /^(?![A-Za-z]:)(?!\/)[A-Za-z0-9._\-/]+$/, hn = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, bn = /^[a-z][a-z0-9]*$/, gn = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
606
|
+
function vn(t) {
|
|
607
|
+
return t.length > 0 && t.length <= ze && !/[\u0000-\u001f\u007f]/.test(t);
|
|
608
|
+
}
|
|
609
|
+
const yn = {
|
|
610
|
+
id: "块的 uuid,必须与页面里挂载它时写死的那个 uuid 逐字一致",
|
|
611
|
+
module: "模块名(小写字母与数字,必须以字母开头),例如 home",
|
|
612
|
+
name: `可读名:可以用中文,不超过 ${ze} 个字符`,
|
|
613
|
+
brief: "一句话说明这个块解决什么问题"
|
|
614
|
+
};
|
|
615
|
+
function Je(t) {
|
|
616
|
+
const e = Object.keys(t);
|
|
617
|
+
return e.length > 0 ? `现在包里有:${e.join(" / ")}` : "现在这个包是空的";
|
|
618
|
+
}
|
|
619
|
+
function _n(t) {
|
|
620
|
+
let e = "";
|
|
621
|
+
for (let n = 0; n < t.length; n += 1) {
|
|
622
|
+
const s = t[n];
|
|
623
|
+
if (e)
|
|
624
|
+
s === e && (e = "");
|
|
625
|
+
else if (s === '"' || s === "'") e = s;
|
|
626
|
+
else if (s === "#") return t.slice(0, n);
|
|
627
|
+
}
|
|
628
|
+
return t;
|
|
629
|
+
}
|
|
630
|
+
function kn(t) {
|
|
631
|
+
let e = "";
|
|
632
|
+
for (let n = 0; n < t.length; n += 1) {
|
|
633
|
+
const s = t[n];
|
|
634
|
+
if (e)
|
|
635
|
+
s === e && (e = "");
|
|
636
|
+
else if (s === '"' || s === "'") e = s;
|
|
637
|
+
else if (s === ":") return n;
|
|
638
|
+
}
|
|
639
|
+
return -1;
|
|
640
|
+
}
|
|
641
|
+
function wn(t) {
|
|
642
|
+
return !t || t[0] !== '"' && t[0] !== "'" ? t : t.length >= 2 && t[t.length - 1] === t[0] ? t.slice(1, -1).trim() : t;
|
|
643
|
+
}
|
|
644
|
+
function $n(t) {
|
|
645
|
+
const e = [], n = {}, s = {};
|
|
646
|
+
t.split(/\r?\n/).forEach((l, r) => {
|
|
647
|
+
const c = r + 1, u = _n(l).trim();
|
|
648
|
+
if (!u) return;
|
|
649
|
+
if (u.startsWith("-")) {
|
|
650
|
+
e.push(`第 ${c} 行是列表写法(${u}):清单只认「键: 值」,四个键各占一行 —— 例如 name: 客户档案`);
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
const d = kn(u);
|
|
654
|
+
if (d < 0) {
|
|
655
|
+
e.push(`第 ${c} 行不是「键: 值」(${u}):清单只认「键: 值」与 # 注释 —— 要么删掉这一行,要么照 name: 客户档案 写`);
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
const f = u.slice(0, d).trim();
|
|
659
|
+
if (!gn.test(f)) {
|
|
660
|
+
e.push(`第 ${c} 行的键名不合法(${f}):键名只许字母或下划线开头,认得的键只有 ${_e.join(" / ")}`);
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
if (!_e.includes(f)) {
|
|
664
|
+
e.push(`第 ${c} 行的键不认识(${f}):清单只有 ${_e.join(" / ")} 这四个键`);
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
if (f in s) {
|
|
668
|
+
e.push(`第 ${c} 行重复写了 ${f}(第 ${s[f]} 行已经写过):一个键只许出现一次`);
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
s[f] = c, n[f] = wn(u.slice(d + 1).trim());
|
|
672
|
+
});
|
|
673
|
+
for (const l of _e) {
|
|
674
|
+
const r = n[l] ?? "";
|
|
675
|
+
if (r === "") {
|
|
676
|
+
e.push(`清单里缺少 ${l}(或者是空的):${yn[l]}`);
|
|
677
|
+
continue;
|
|
678
|
+
}
|
|
679
|
+
l === "id" && !hn.test(r) ? e.push(`清单里的 id 不是 uuid:${r} —— 要 8-4-4-4-12 那种十六进制`) : l === "module" && !bn.test(r) ? e.push(
|
|
680
|
+
`清单里的 module 不合法:${r} —— 它同时是 Django 的 app label,所以只许小写字母与数字、必须以字母开头(不能有横线与下划线),例如 home / orders`
|
|
681
|
+
) : l === "name" && !vn(r) && e.push(`清单里的 name 不合法:${r} —— 不能超过 ${ze} 个字符、不能含控制字符(中文可以)`);
|
|
682
|
+
}
|
|
683
|
+
return [n, e];
|
|
684
|
+
}
|
|
685
|
+
function xn(t) {
|
|
686
|
+
return mn.test(t) && !t.split("/").includes("..");
|
|
687
|
+
}
|
|
688
|
+
function Sn(t) {
|
|
689
|
+
const e = [], n = t[Be];
|
|
690
|
+
n === void 0 ? e.push(`包里缺入口:必须有 ${Be}(${Je(t)})`) : n.trim() || e.push(`${Be} 不能是空文件`);
|
|
691
|
+
const s = t[Te];
|
|
692
|
+
let l = {};
|
|
693
|
+
if (s === void 0)
|
|
694
|
+
e.push(`包里缺清单:必须有 ${Te}(${Je(t)})`);
|
|
695
|
+
else if (!s.trim())
|
|
696
|
+
e.push(`${Te} 不能是空文件`);
|
|
697
|
+
else {
|
|
698
|
+
const [r, c] = $n(s);
|
|
699
|
+
l = r, e.push(...c);
|
|
700
|
+
}
|
|
701
|
+
for (const r of Object.keys(t))
|
|
702
|
+
xn(r) || e.push(`文件名不合法:${r}(只许字母数字与 . _ - /,且不许 .. 与绝对路径)`);
|
|
703
|
+
return { manifest: l, issues: e };
|
|
704
|
+
}
|
|
705
|
+
function En(t) {
|
|
706
|
+
const e = [];
|
|
707
|
+
if (t.length === 0)
|
|
708
|
+
return e.push("一个块都没勾:请在「读回改动」的结果里勾上要保存的块"), { selected: 0, issues: e };
|
|
709
|
+
for (const n of t) {
|
|
710
|
+
const s = n.name?.trim() || "(没有名字的块)";
|
|
711
|
+
for (const l of Sn(n.files ?? {}).issues) e.push(`${s}:${l}`);
|
|
712
|
+
}
|
|
713
|
+
return { selected: t.length, issues: e };
|
|
714
|
+
}
|
|
715
|
+
const Ve = "pages.lock.ts";
|
|
716
|
+
function Cn(t, e) {
|
|
717
|
+
const n = String(t || "").split(/[^A-Za-z0-9]+/).filter(Boolean);
|
|
718
|
+
if (n.length === 0) return `Page${e + 1}`;
|
|
719
|
+
const s = n.map((l) => l[0].toUpperCase() + l.slice(1)).join("");
|
|
720
|
+
return /^[0-9]/.test(s) ? `P${s}` : s;
|
|
721
|
+
}
|
|
722
|
+
function Bn(t, e) {
|
|
723
|
+
if (!e.has(t))
|
|
724
|
+
return e.add(t), t;
|
|
725
|
+
let n = 2;
|
|
726
|
+
for (; e.has(`${t}${n}`); ) n += 1;
|
|
727
|
+
const s = `${t}${n}`;
|
|
728
|
+
return e.add(s), s;
|
|
729
|
+
}
|
|
730
|
+
function Tn(t) {
|
|
731
|
+
const e = (t ?? []).filter((r) => r && typeof r.path == "string" && r.path.trim() !== ""), n = /* @__PURE__ */ new Set(), s = [];
|
|
732
|
+
e.forEach((r, c) => {
|
|
733
|
+
const u = r.group?.trim() || "(没写 meta.group)";
|
|
734
|
+
let d = s.find((f) => f.title === u);
|
|
735
|
+
d || (d = { title: u, rows: [] }, s.push(d)), d.rows.push({ key: Bn(Cn(r.path, c), n), row: r });
|
|
736
|
+
});
|
|
737
|
+
const l = [
|
|
738
|
+
"/* 生成物:**每次导出都会重写**,别手改 —— 改了下次导出就没了。",
|
|
739
|
+
" *",
|
|
740
|
+
" * 这个文件回答「块的前端(index.open.html 那一侧)里我能跳哪些页面、能调什么」。",
|
|
741
|
+
" *",
|
|
742
|
+
" * ① 跳页面:`magicBlock.navigateTab(Page.Orders)` —— 值就是路径,直接写字符串也可以。",
|
|
743
|
+
" * ② 当前用户:`await magicBlock.getMe()`(认人只认它,**别从请求参数里取**)。",
|
|
744
|
+
" * ③ 调这个块自己的后端:`await magicBackendApi.类名.方法名({...})`。",
|
|
745
|
+
" * 有哪些路由、每个方法收什么参数,见**这个块文件夹里的 `api.open.d.ts`**(照它的",
|
|
746
|
+
" * backend_open.py 生成):路由会被自动注册,前端只要按名字调,不必去读一个 .py。",
|
|
747
|
+
" *",
|
|
748
|
+
" * 🔴 `Page` 是 **const 对象**而不是 TS 的 `enum`:navigateTab 收的是路径本身,而 enum 的值是数字。",
|
|
749
|
+
" * 🔴 路由表**只在前端**:后端不知道页面,所以这份名单是前端按自己的 `router.getRoutes()` 算的。",
|
|
750
|
+
" * 🔴 `Page` 只是给你查路径用的**常量表**:块运行在不透明源的沙箱 iframe 里,那里没有这个对象 ——",
|
|
751
|
+
' * 真要用就把路径写进自己的代码(`navigateTab("/orders")` 与 `navigateTab(Page.Orders)` 等价)。',
|
|
752
|
+
" */",
|
|
753
|
+
""
|
|
754
|
+
];
|
|
755
|
+
if (s.length === 0)
|
|
756
|
+
l.push("/* ⚠️ 这次导出没拿到路由表(这个应用没装 vue-router,或者一条页面路由都没有)。 */", "export const Page = {} as const", "");
|
|
757
|
+
else {
|
|
758
|
+
l.push("export const Page = {");
|
|
759
|
+
for (const r of s) {
|
|
760
|
+
l.push(` // ── ${r.title} ──`);
|
|
761
|
+
for (const c of r.rows)
|
|
762
|
+
l.push(` /** ${c.row.title?.trim() || c.row.path}(${c.row.path}) */`), l.push(` ${c.key}: '${c.row.path}',`);
|
|
763
|
+
}
|
|
764
|
+
l.push("} as const", "");
|
|
765
|
+
}
|
|
766
|
+
return l.push(
|
|
767
|
+
"/** `Page` 里那些路径的联合类型(只想收页面路径的参数可以写它)。 */",
|
|
768
|
+
"export type PagePath = (typeof Page)[keyof typeof Page]",
|
|
769
|
+
"",
|
|
770
|
+
"declare global {",
|
|
771
|
+
" /** 宿主注入的桥(**运行期就有**):块里能用的东西只有下面这两个成员。 */",
|
|
772
|
+
" interface MagicBlockUser {",
|
|
773
|
+
" id: string",
|
|
774
|
+
" /** 登录用户名(OIDC 的 sub 落在这一列)。 */",
|
|
775
|
+
" username: string",
|
|
776
|
+
" /** 工号(没有就是空串)。 */",
|
|
777
|
+
" work_id: string",
|
|
778
|
+
" /** 显示名(拿不到就是空串 —— 不编造)。 */",
|
|
779
|
+
" name: string",
|
|
780
|
+
" /** 是不是管理员(Django 的 is_staff)。 */",
|
|
781
|
+
" is_staff: boolean",
|
|
782
|
+
" /** 所在组的名字。 */",
|
|
783
|
+
" groups: string[]",
|
|
784
|
+
" /** 直接权限 + 组权限的 codename(`app_label.codename`)。 */",
|
|
785
|
+
" permissions: string[]",
|
|
786
|
+
" }",
|
|
787
|
+
" const magicBlock: {",
|
|
788
|
+
" getMe(): Promise<MagicBlockUser>",
|
|
789
|
+
" /** 跳到某个页面(只认 `Page` 里那些路径;不在路由表里会被拒绝并列出能跳的)。 */",
|
|
790
|
+
" navigateTab(path: PagePath | string): Promise<string>",
|
|
791
|
+
" }",
|
|
792
|
+
" /** 一条后端路由的调用形状(参数就是请求正文;失败是 rejected Promise,报文是后端的中文原话)。 */",
|
|
793
|
+
" interface MagicBackendRoute {",
|
|
794
|
+
" (body?: unknown): Promise<unknown>",
|
|
795
|
+
" }",
|
|
796
|
+
" /** 调这个块自己的后端:`magicBackendApi.类名.方法名({...})`。精确到方法名的补全见各块的 api.open.d.ts。 */",
|
|
797
|
+
" type MagicBackendApi = MagicBackendRoute & { [name: string]: MagicBackendApi }",
|
|
798
|
+
" const magicBackendApi: MagicBackendApi",
|
|
799
|
+
"}",
|
|
800
|
+
"",
|
|
801
|
+
"export {}",
|
|
802
|
+
""
|
|
803
|
+
), l.join(`
|
|
804
|
+
`);
|
|
805
|
+
}
|
|
806
|
+
function ht() {
|
|
807
|
+
return typeof window < "u" && typeof window.showDirectoryPicker == "function";
|
|
808
|
+
}
|
|
809
|
+
const An = "这个浏览器没法直接写你本机的文件夹(少了 File System Access API):请用 Chrome / Edge,并且从 https 或 http://localhost 打开这个页面(内网 http 会更少 API)。";
|
|
810
|
+
async function Ae() {
|
|
811
|
+
if (!ht()) throw new Error(An);
|
|
812
|
+
try {
|
|
813
|
+
const t = await window.showDirectoryPicker({ mode: "readwrite", id: "labull-magic-block" });
|
|
814
|
+
return { name: t.name, handle: t };
|
|
815
|
+
} catch (t) {
|
|
816
|
+
const e = t instanceof Error ? t.name : "";
|
|
817
|
+
throw e === "AbortError" ? new Error("取消选目录了:想导出就再点一次「选择文件夹」") : e === "SecurityError" ? new Error(`选不了这个目录:浏览器不让(${ke(t)})—— 换一个目录,或确认页面是 https / localhost`) : new Error(`选目录失败:${ke(t)}`);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
function ke(t) {
|
|
821
|
+
return (t instanceof Error ? t.message.trim() : String(t)) || "没有说原因";
|
|
822
|
+
}
|
|
823
|
+
function On(t) {
|
|
824
|
+
const e = String(t ?? "").split("/");
|
|
825
|
+
for (const n of e)
|
|
826
|
+
if (!n || n === "." || n === ".." || n.includes("\\") || /^[A-Za-z]:/.test(n))
|
|
827
|
+
throw new Error(`路径不合法:${t}(要相对路径,不许 .. / 盘符 / 反斜杠)`);
|
|
828
|
+
return e;
|
|
829
|
+
}
|
|
830
|
+
const Mn = 8;
|
|
831
|
+
function Pn(t) {
|
|
832
|
+
const e = /* @__PURE__ */ new Map();
|
|
833
|
+
return (n) => {
|
|
834
|
+
const s = n.join("\0"), l = e.get(s);
|
|
835
|
+
if (l) return l;
|
|
836
|
+
let r = Promise.resolve(t);
|
|
837
|
+
for (let c = 0; c < n.length; c += 1) {
|
|
838
|
+
const u = n[c], d = n.slice(0, c + 1).join("\0"), f = e.get(d);
|
|
839
|
+
r = f ?? r.then((_) => _.getDirectoryHandle(u, { create: !0 })), f || e.set(d, r);
|
|
840
|
+
}
|
|
841
|
+
return r;
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
async function Rn(t, e, n) {
|
|
845
|
+
const s = Object.entries(e ?? {}).map(([_, y]) => ({ path: _, parts: On(_), body: String(y ?? "") })), l = s.length, r = [], c = Pn(t.handle);
|
|
846
|
+
let u = 0, d = 0;
|
|
847
|
+
n?.(0, l);
|
|
848
|
+
const f = async () => {
|
|
849
|
+
for (; ; ) {
|
|
850
|
+
const _ = u;
|
|
851
|
+
u += 1;
|
|
852
|
+
const y = s[_];
|
|
853
|
+
if (!y) return;
|
|
854
|
+
try {
|
|
855
|
+
const N = await (await (await c(y.parts.slice(0, -1))).getFileHandle(y.parts[y.parts.length - 1], { create: !0 })).createWritable();
|
|
856
|
+
try {
|
|
857
|
+
await N.write(y.body);
|
|
858
|
+
} finally {
|
|
859
|
+
await N.close();
|
|
860
|
+
}
|
|
861
|
+
d += 1;
|
|
862
|
+
} catch (S) {
|
|
863
|
+
r.push({ path: y.path, message: ke(S) });
|
|
864
|
+
}
|
|
865
|
+
n?.(d + r.length, l);
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
return await Promise.all(Array.from({ length: Math.max(1, Math.min(Mn, l)) }, () => f())), r.sort((_, y) => _.path < y.path ? -1 : _.path > y.path ? 1 : 0), { written: d, failed: r, total: l };
|
|
869
|
+
}
|
|
870
|
+
async function jn(t, e) {
|
|
871
|
+
const n = { items: [], topCount: 0, files: 0, dirs: 0 }, s = async (l, r) => {
|
|
872
|
+
for (const c of await be(l)) {
|
|
873
|
+
if (c.kind === "file") {
|
|
874
|
+
n.files += 1;
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
n.dirs += 1, !(r >= e.maxDepth) && (e.skipDirs.includes(c.name) || await s(c, r + 1));
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
for (const l of await be(t.handle))
|
|
881
|
+
if (n.items.push({ name: l.name, dir: l.kind === "directory" }), l.kind === "file") n.files += 1;
|
|
882
|
+
else {
|
|
883
|
+
if (n.dirs += 1, e.skipDirs.includes(l.name) || e.maxDepth <= 0) continue;
|
|
884
|
+
await s(l, 1);
|
|
885
|
+
}
|
|
886
|
+
return n.topCount = n.items.length, n;
|
|
887
|
+
}
|
|
888
|
+
async function Nn(t) {
|
|
889
|
+
const e = [];
|
|
890
|
+
for (const s of await be(t.handle)) e.push(s.name);
|
|
891
|
+
let n = 0;
|
|
892
|
+
for (const s of e)
|
|
893
|
+
await t.handle.removeEntry(s, { recursive: !0 }), n += 1;
|
|
894
|
+
return n;
|
|
895
|
+
}
|
|
896
|
+
function In(t, e) {
|
|
897
|
+
if (t.name === e.blocksDir)
|
|
898
|
+
throw new Error(
|
|
899
|
+
`你选中的是「${e.blocksDir}」这一层(它就是放代码的那一层):导出会铺成 ${e.blocksDir}/${e.blocksDir}/…。请选它**上面**那一层 —— 也就是开发目录本身。`
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
async function be(t) {
|
|
903
|
+
const e = [];
|
|
904
|
+
for await (const n of t.values()) e.push(n);
|
|
905
|
+
return e.sort((n, s) => n.name < s.name ? -1 : n.name > s.name ? 1 : 0), e;
|
|
906
|
+
}
|
|
907
|
+
function Ln(t) {
|
|
908
|
+
return t.charCodeAt(0) === 65279 ? t.slice(1) : t;
|
|
909
|
+
}
|
|
910
|
+
function zn(t) {
|
|
911
|
+
const e = t.lastIndexOf(".");
|
|
912
|
+
return e <= 0 ? "" : t.slice(e).toLowerCase();
|
|
913
|
+
}
|
|
914
|
+
async function bt(t, e, n, s) {
|
|
915
|
+
const l = {};
|
|
916
|
+
for (const r of await be(t)) {
|
|
917
|
+
const c = e ? `${e}/${r.name}` : r.name;
|
|
918
|
+
if (r.kind === "directory") {
|
|
919
|
+
if (n.skipDirs.includes(r.name)) continue;
|
|
920
|
+
const u = r;
|
|
921
|
+
for (const [d, f] of Object.entries(await bt(u, c, n, s)))
|
|
922
|
+
l[`${r.name}/${d}`] = f;
|
|
923
|
+
continue;
|
|
924
|
+
}
|
|
925
|
+
if (!n.platformFiles.includes(r.name)) {
|
|
926
|
+
if (!n.textExt.includes(zn(r.name))) {
|
|
927
|
+
s.failures.push({
|
|
928
|
+
kind: "file",
|
|
929
|
+
where: c,
|
|
930
|
+
message: `不是文本文件(认得的扩展名:${n.textExt.join(" ")}),已跳过`
|
|
931
|
+
});
|
|
932
|
+
continue;
|
|
933
|
+
}
|
|
934
|
+
try {
|
|
935
|
+
const u = await r.getFile();
|
|
936
|
+
if (u.size > n.maxFileBytes) {
|
|
937
|
+
s.failures.push({
|
|
938
|
+
kind: "file",
|
|
939
|
+
where: c,
|
|
940
|
+
message: `文件 ${Math.round(u.size / 1024)} KB 超过上限 ${Math.floor(n.maxFileBytes / 1024)} KB,已跳过`
|
|
941
|
+
});
|
|
942
|
+
continue;
|
|
943
|
+
}
|
|
944
|
+
l[r.name] = Ln(await u.text());
|
|
945
|
+
} catch (u) {
|
|
946
|
+
s.failures.push({ kind: "file", where: c, message: `读不了这个文件:${ke(u)}` });
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
return l;
|
|
951
|
+
}
|
|
952
|
+
async function gt(t, e, n, s, l) {
|
|
953
|
+
const r = await be(t), c = r.filter((d) => d.kind === "directory"), u = r.filter((d) => d.kind === "file");
|
|
954
|
+
if (c.length === 0 || u.some((d) => d.name === s.entry)) {
|
|
955
|
+
l.folders.push({ where: e.join("/"), files: await bt(t, e.join("/"), s, l) });
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
for (const d of u)
|
|
959
|
+
e.length === 0 && s.rootSkipFiles.includes(d.name.toLowerCase()) || l.failures.push({
|
|
960
|
+
kind: "file",
|
|
961
|
+
where: [...e, d.name].join("/"),
|
|
962
|
+
message: `这一层直接放着一份文件,但它自己不是块文件夹、也没有 ${s.entry},文件没有归属`
|
|
963
|
+
});
|
|
964
|
+
for (const d of c)
|
|
965
|
+
if (!s.skipDirs.includes(d.name)) {
|
|
966
|
+
if (n >= s.maxDepth) {
|
|
967
|
+
l.truncated = !0, l.failures.push({
|
|
968
|
+
kind: "dir",
|
|
969
|
+
where: [...e, d.name].join("/"),
|
|
970
|
+
message: `超过最大层数 ${s.maxDepth} 层还没见到 ${s.entry},没有继续往下找`
|
|
971
|
+
});
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
await gt(d, [...e, d.name], n + 1, s, l);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
function Ye(t) {
|
|
978
|
+
return t >= 1024 * 1024 ? `${(t / 1024 / 1024).toFixed(1)} MB` : `${Math.max(1, Math.round(t / 1024))} KB`;
|
|
979
|
+
}
|
|
980
|
+
async function Dn(t, e) {
|
|
981
|
+
const n = { root: t.name, folders: [], failures: [], truncated: !1 };
|
|
982
|
+
await gt(t.handle, [], 0, e, n);
|
|
983
|
+
const s = n.folders.reduce(
|
|
984
|
+
(l, r) => l + Object.values(r.files).reduce((c, u) => c + u.length, 0),
|
|
985
|
+
0
|
|
986
|
+
);
|
|
987
|
+
if (s > e.maxBodyBytes)
|
|
988
|
+
throw new Error(
|
|
989
|
+
`这个目录读到的正文有 ${Ye(s)},超过一次读回的上限 ${Ye(e.maxBodyBytes)}:请先只留要改的那几个块,或者把大文件挪走。`
|
|
990
|
+
);
|
|
991
|
+
return n;
|
|
992
|
+
}
|
|
993
|
+
const Fn = {
|
|
994
|
+
key: 0,
|
|
995
|
+
class: "mbc"
|
|
996
|
+
}, Hn = { class: "mbc__head" }, Kn = ["disabled"], Un = {
|
|
997
|
+
key: 0,
|
|
998
|
+
class: "mbc__error"
|
|
999
|
+
}, qn = {
|
|
1000
|
+
key: 1,
|
|
1001
|
+
class: "mbc__notice"
|
|
1002
|
+
}, Gn = { class: "mbc__panel" }, Jn = { class: "mbc__title" }, Vn = { class: "mbc__list" }, Yn = ["title", "onClick"], Wn = { class: "mbc__module" }, Zn = { class: "mbc__name" }, Xn = {
|
|
1003
|
+
key: 0,
|
|
1004
|
+
class: "mbc__where"
|
|
1005
|
+
}, Qn = {
|
|
1006
|
+
key: 0,
|
|
1007
|
+
class: "mbc__detail"
|
|
1008
|
+
}, eo = {
|
|
1009
|
+
key: 0,
|
|
1010
|
+
class: "mbc__hint"
|
|
1011
|
+
}, to = { class: "mbc__meta" }, no = { class: "mbc__mono" }, oo = { class: "mbc__actions" }, so = ["disabled", "onClick"], ao = ["disabled", "onClick"], io = ["disabled", "onClick"], lo = { class: "mbc__panel" }, ro = { class: "mbc__field" }, co = ["value"], uo = ["disabled"], po = ["disabled"], fo = {
|
|
1012
|
+
key: 0,
|
|
1013
|
+
class: "mbc__hint"
|
|
1014
|
+
}, mo = ["aria-valuenow"], ho = {
|
|
1015
|
+
key: 3,
|
|
1016
|
+
class: "mbc__confirm"
|
|
1017
|
+
}, bo = { class: "mbc__confirm-title" }, go = { class: "mbc__confirm-body" }, vo = {
|
|
1018
|
+
key: 0,
|
|
1019
|
+
class: "mbc__mono"
|
|
1020
|
+
}, yo = { class: "mbc__actions" }, _o = ["disabled"], ko = ["disabled"], wo = ["disabled"], $o = { class: "mbc__panel" }, xo = { class: "mbc__field" }, So = ["value"], Eo = ["disabled"], Co = ["disabled"], Bo = {
|
|
1021
|
+
key: 0,
|
|
1022
|
+
class: "mbc__hint"
|
|
1023
|
+
}, To = {
|
|
1024
|
+
key: 1,
|
|
1025
|
+
class: "mbc__hint"
|
|
1026
|
+
}, Ao = {
|
|
1027
|
+
key: 0,
|
|
1028
|
+
class: "mbc__scan"
|
|
1029
|
+
}, Oo = { class: "mbc__pick" }, Mo = ["disabled", "checked", "onChange"], Po = { class: "mbc__name" }, Ro = { class: "mbc__module" }, jo = { class: "mbc__hint" }, No = { class: "mbc__mono" }, Io = {
|
|
1030
|
+
key: 0,
|
|
1031
|
+
class: "mbc__problem"
|
|
1032
|
+
}, Lo = { class: "mbc__hint mbc__mono" }, zo = {
|
|
1033
|
+
key: 1,
|
|
1034
|
+
class: "mbc__hint"
|
|
1035
|
+
}, Do = {
|
|
1036
|
+
key: 2,
|
|
1037
|
+
class: "mbc__problem"
|
|
1038
|
+
}, Fo = {
|
|
1039
|
+
key: 3,
|
|
1040
|
+
class: "mbc__failures"
|
|
1041
|
+
}, Ho = { class: "mbc__field mbc__field--end" }, Ko = ["disabled"], Uo = {
|
|
1042
|
+
key: 2,
|
|
1043
|
+
class: "mbc__panel"
|
|
1044
|
+
}, qo = { class: "mbc__title" }, Go = {
|
|
1045
|
+
key: 0,
|
|
1046
|
+
class: "mbc__failures"
|
|
1047
|
+
}, Jo = {
|
|
1048
|
+
key: 1,
|
|
1049
|
+
class: "mbc__failures"
|
|
1050
|
+
}, Vo = { key: 0 }, Yo = {
|
|
1051
|
+
key: 1,
|
|
1052
|
+
class: "mbc__failures mbc__failures--nested"
|
|
1053
|
+
}, Oe = 2, Wo = /* @__PURE__ */ We({
|
|
1054
|
+
__name: "MagicBlockConsole",
|
|
1055
|
+
setup(t) {
|
|
1056
|
+
const e = { modified: "有改动", unchanged: "没改动", missing: "目录里没有" }, { isStaff: n } = nt(), s = tt(), l = et(), r = w(!1), c = w(""), u = w([]), d = w(""), f = w(""), _ = w(""), y = w(!1), S = w(!1), C = w(!1), N = w(!1), Z = w(!1), A = w(null), B = w(null), K = w(null), I = w(""), L = w(null), ae = w({}), J = w([]), ee = w([]), M = w(null), E = w({ active: !1, percent: 0, stage: "", failed: !1 }), we = T(() => ({
|
|
1057
|
+
width: `${Math.max(0, Math.min(100, E.value.percent))}%`,
|
|
1058
|
+
background: E.value.failed ? "var(--sp-color-danger)" : "var(--sp-color-primary, var(--primary-color))"
|
|
1059
|
+
}));
|
|
1060
|
+
function z(i, o) {
|
|
1061
|
+
E.value = { active: !0, percent: Math.max(E.value.percent, i), stage: o, failed: !1 };
|
|
1062
|
+
try {
|
|
1063
|
+
const a = window.localStorage.getItem("mb:progress") ?? "[]", h = JSON.parse(a);
|
|
1064
|
+
h.push({ p: E.value.percent, s: o, t: Math.round(performance.now()) }), window.localStorage.setItem("mb:progress", JSON.stringify(h));
|
|
1065
|
+
} catch {
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
function V(i, o) {
|
|
1069
|
+
z(i ? 100 : E.value.percent, o), E.value = { active: !i, percent: i ? 100 : E.value.percent, stage: o, failed: !i };
|
|
1070
|
+
}
|
|
1071
|
+
const U = (i) => (i.disabledReason ?? "").trim(), pe = (i) => (i.issues ?? []).map((o) => String(o?.message ?? "")).filter(Boolean), te = (i) => en(i.id);
|
|
1072
|
+
function $e(i) {
|
|
1073
|
+
d.value = d.value === i.id ? "" : i.id;
|
|
1074
|
+
const o = l?.path ?? "", a = tn(i.id, o);
|
|
1075
|
+
mt(i.id);
|
|
1076
|
+
const h = i.name || i.id;
|
|
1077
|
+
if (a) {
|
|
1078
|
+
_.value = `「${h}」不在这一页:正在跳到 ${a}`, s?.push?.(a);
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
const $ = te(i);
|
|
1082
|
+
_.value = $.length === 0 ? `「${h}」这次会话里还没在页面上露过面:先打开挂着它的那一页,再点一次` : `「${h}」就在这一页(${$.join(" / ")}):已经把它标红`;
|
|
1083
|
+
}
|
|
1084
|
+
function xe(i) {
|
|
1085
|
+
const o = te(i), a = o.length === 0 ? "点一下:这次会话里它还没在页面上露过面" : `点一下:把「${i.name || i.id}」标红并跳到 ${o.join(" / ")}`, h = U(i);
|
|
1086
|
+
return h ? `${h}
|
|
1087
|
+
${a}` : a;
|
|
1088
|
+
}
|
|
1089
|
+
const ie = T(() => {
|
|
1090
|
+
const i = c.value.trim().toLowerCase();
|
|
1091
|
+
return i ? u.value.filter((o) => `${o.module} ${o.name} ${o.brief}`.toLowerCase().includes(i)) : u.value;
|
|
1092
|
+
}), Se = T(() => (L.value?.blocks ?? []).filter((i) => i.canApply)), le = T(() => Se.value.filter((i) => ae.value[i.id] === !0));
|
|
1093
|
+
function q() {
|
|
1094
|
+
const i = s?.getRoutes?.() ?? [], o = /* @__PURE__ */ new Set(), a = [];
|
|
1095
|
+
for (const h of i) {
|
|
1096
|
+
const $ = typeof h?.path == "string" ? h.path.trim() : "";
|
|
1097
|
+
if (!$ || o.has($) || $.includes(":") || $.includes("*") || h.redirect || !h.components?.default || h.meta?.tab === !1) continue;
|
|
1098
|
+
o.add($);
|
|
1099
|
+
const F = h.meta?.title ?? h.meta?.sub, O = h.meta?.group;
|
|
1100
|
+
a.push({
|
|
1101
|
+
path: $,
|
|
1102
|
+
title: typeof F == "string" && F.trim() ? F.trim() : $,
|
|
1103
|
+
group: typeof O == "string" ? O.trim() : ""
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
return a;
|
|
1107
|
+
}
|
|
1108
|
+
function H(i) {
|
|
1109
|
+
return (i instanceof Error ? i.message.trim() : String(i)) || "请求没有到达后端(网络或登录态的问题),请重试";
|
|
1110
|
+
}
|
|
1111
|
+
function ge(i) {
|
|
1112
|
+
const o = i?.issues;
|
|
1113
|
+
return Array.isArray(o) ? o.map((a) => typeof a == "string" ? a : String(a?.message ?? "")).filter(Boolean) : [];
|
|
1114
|
+
}
|
|
1115
|
+
async function re(i, o) {
|
|
1116
|
+
const a = JSON.stringify(o);
|
|
1117
|
+
try {
|
|
1118
|
+
return await W(i, { method: "POST", body: a });
|
|
1119
|
+
} catch (h) {
|
|
1120
|
+
const $ = H(h), F = await fetch(`http://localhost:3000${i}`, {
|
|
1121
|
+
method: "POST",
|
|
1122
|
+
credentials: "include",
|
|
1123
|
+
headers: { "Content-Type": "application/json", ...Me("POST", !0) },
|
|
1124
|
+
body: a
|
|
1125
|
+
}).then((Q) => Q.json()).catch(() => null), O = ge(F);
|
|
1126
|
+
throw O.length === 0 ? new Error($) : new Error(`${String(F?.message ?? $)}
|
|
1127
|
+
- ${O.join(`
|
|
1128
|
+
- `)}`);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
async function X() {
|
|
1132
|
+
try {
|
|
1133
|
+
const i = await W("/magic-blocks/index");
|
|
1134
|
+
u.value = i.blocks ?? [];
|
|
1135
|
+
} catch (i) {
|
|
1136
|
+
f.value = H(i);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
async function R(i, o) {
|
|
1140
|
+
o.value = !0, f.value = "";
|
|
1141
|
+
try {
|
|
1142
|
+
await i(), Jt();
|
|
1143
|
+
} catch (a) {
|
|
1144
|
+
f.value = H(a);
|
|
1145
|
+
} finally {
|
|
1146
|
+
o.value = !1;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
const m = (i) => R(async () => {
|
|
1150
|
+
S.value = !0;
|
|
1151
|
+
try {
|
|
1152
|
+
await W("/magic-blocks/check", { method: "POST", body: JSON.stringify(i ? { blocks: [i] } : {}) }), _.value = i ? "这一块重新体检完了" : "全部块重新体检完了", await X();
|
|
1153
|
+
} finally {
|
|
1154
|
+
S.value = !1;
|
|
1155
|
+
}
|
|
1156
|
+
}, y), k = (i) => R(async () => {
|
|
1157
|
+
const o = await Ae();
|
|
1158
|
+
i === "export" ? (A.value = o, B.value || (B.value = o)) : (B.value = o, A.value || (A.value = o)), _.value = i === "export" ? `导出目录:${o.name}${B.value === o ? "(读回目录也填成它了)" : ""}` : `读回目录:${o.name}`;
|
|
1159
|
+
}, y);
|
|
1160
|
+
async function D() {
|
|
1161
|
+
if (K.value) return K.value;
|
|
1162
|
+
const i = await W("/magic-blocks/rules");
|
|
1163
|
+
return K.value = i.rules, i.rules;
|
|
1164
|
+
}
|
|
1165
|
+
const Ee = () => R(async () => {
|
|
1166
|
+
if (!ht()) throw new Error("这个浏览器没法直接写你本机的文件夹:请用 Chrome / Edge,并从 https 或 localhost 打开");
|
|
1167
|
+
C.value = !0, _.value = "", I.value = "", f.value = "", E.value = { active: !0, percent: 0, stage: "准备…", failed: !1 };
|
|
1168
|
+
try {
|
|
1169
|
+
const i = A.value ?? await Ae();
|
|
1170
|
+
A.value = i, B.value || (B.value = i);
|
|
1171
|
+
const o = await re("/magic-blocks/export", {});
|
|
1172
|
+
K.value = o.rules, In(i, o.rules);
|
|
1173
|
+
const a = await jn(i, o.rules);
|
|
1174
|
+
if (a.topCount > 0) {
|
|
1175
|
+
z(5, "等你确认要不要先清空"), M.value = { target: i, inventory: a };
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
await Ce(i, o, !1);
|
|
1179
|
+
} catch (i) {
|
|
1180
|
+
throw V(!1, "导出失败"), i;
|
|
1181
|
+
} finally {
|
|
1182
|
+
C.value = !1;
|
|
1183
|
+
}
|
|
1184
|
+
}, y);
|
|
1185
|
+
async function Ce(i, o, a) {
|
|
1186
|
+
try {
|
|
1187
|
+
z(20, "收主题与控件清单…");
|
|
1188
|
+
const h = { ...o.files, [Ve]: Tn(q()) };
|
|
1189
|
+
let $ = "";
|
|
1190
|
+
await new Promise((P) => {
|
|
1191
|
+
setTimeout(P, 0);
|
|
1192
|
+
});
|
|
1193
|
+
try {
|
|
1194
|
+
const P = ft();
|
|
1195
|
+
h[de] = P.css, h[je] = Wt(), $ = P.skipped.length > 0 ? `;⚠️ 有 ${P.skipped.length} 张样式表读不到规则(跨源):${P.skipped.join(" / ")}` : "";
|
|
1196
|
+
} catch (P) {
|
|
1197
|
+
$ = `;主题 CSS 没收成(${H(P)}):${de} 没写,其余照旧`;
|
|
1198
|
+
}
|
|
1199
|
+
let F = "";
|
|
1200
|
+
a && (z(35, "清空目录…"), F = `,先清掉了 ${await Nn(i)} 项`);
|
|
1201
|
+
const O = Object.keys(h).length, Q = a ? 45 : 30;
|
|
1202
|
+
z(Q, `写文件 0/${O}…`);
|
|
1203
|
+
const ne = await Rn(i, h, (P, He) => {
|
|
1204
|
+
z(Q + Math.round(P / Math.max(1, He) * (95 - Q)), `写文件 ${P}/${He}…`);
|
|
1205
|
+
});
|
|
1206
|
+
z(98, "收尾…");
|
|
1207
|
+
const St = ne.failed.length > 0 ? `;⚠️ ${ne.failed.length} 个文件没写成:${ne.failed.slice(0, 3).map((P) => `${P.path}(${P.message})`).join("、")}` + (ne.failed.length > 3 ? ` 等 ${ne.failed.length} 条` : "") : "";
|
|
1208
|
+
I.value = `铺到「${i.name}」:${o.blocks} 个块、${ne.written} 个文件(含 ${Ve} / ${de} / ${je})${F}${St}${$}`, _.value = ne.failed.length > 0 ? "导出完了,但有文件没写成(见下面那句)" : "导出完了:用你自己的编辑器改,改完回这里「读回改动」", V(!0, "完成");
|
|
1209
|
+
} catch (h) {
|
|
1210
|
+
throw V(!1, "导出中途失败(已经铺进去的文件不会自己删掉)"), h;
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
const vt = () => R(async () => {
|
|
1214
|
+
const i = M.value;
|
|
1215
|
+
if (M.value = null, !!i) {
|
|
1216
|
+
C.value = !0;
|
|
1217
|
+
try {
|
|
1218
|
+
z(15, "重新取一份开发包…");
|
|
1219
|
+
const o = await re("/magic-blocks/export", {});
|
|
1220
|
+
K.value = o.rules, await Ce(i.target, o, !0);
|
|
1221
|
+
} catch (o) {
|
|
1222
|
+
throw V(!1, "导出失败"), o;
|
|
1223
|
+
} finally {
|
|
1224
|
+
C.value = !1;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
}, y), yt = () => R(async () => {
|
|
1228
|
+
const i = M.value;
|
|
1229
|
+
if (M.value = null, !!i) {
|
|
1230
|
+
C.value = !0;
|
|
1231
|
+
try {
|
|
1232
|
+
z(15, "重新取一份开发包…");
|
|
1233
|
+
const o = await re("/magic-blocks/export", {});
|
|
1234
|
+
K.value = o.rules, await Ce(i.target, o, !1);
|
|
1235
|
+
} catch (o) {
|
|
1236
|
+
throw V(!1, "导出失败"), o;
|
|
1237
|
+
} finally {
|
|
1238
|
+
C.value = !1;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
}, y);
|
|
1242
|
+
function _t() {
|
|
1243
|
+
M.value = null, E.value = { active: !1, percent: 0, stage: "", failed: !1 }, _.value = "这次没有导出(目录里的东西一个字节都没动)", I.value = "";
|
|
1244
|
+
}
|
|
1245
|
+
const De = () => R(async () => {
|
|
1246
|
+
const i = B.value ?? await Ae();
|
|
1247
|
+
B.value = i, A.value || (A.value = i), N.value = !0, _.value = "", J.value = [], ee.value = [];
|
|
1248
|
+
try {
|
|
1249
|
+
const o = await D().catch((O) => {
|
|
1250
|
+
throw new Error(`取不到扫描规则(GET /magic-blocks/rules):${H(O)}`);
|
|
1251
|
+
}), a = await Dn(i, o), h = await W("/magic-blocks/read", {
|
|
1252
|
+
method: "POST",
|
|
1253
|
+
body: JSON.stringify({ ...a, includeMissing: !1 })
|
|
1254
|
+
});
|
|
1255
|
+
L.value = h;
|
|
1256
|
+
const $ = {};
|
|
1257
|
+
for (const O of h.blocks ?? []) $[O.id] = O.status === "modified" && O.canApply;
|
|
1258
|
+
ae.value = $;
|
|
1259
|
+
const F = (h.blocks ?? []).reduce((O, Q) => ({ ...O, [Q.status]: (O[Q.status] ?? 0) + 1 }), {});
|
|
1260
|
+
_.value = `扫到 ${h.folders} 个文件夹、${h.files} 个文件:有改动 ${F.modified ?? 0} / 没改动 ${F.unchanged ?? 0}` + (h.complete ? "" : "(层数到了上限,没走完,可能还有块没扫到)");
|
|
1261
|
+
} finally {
|
|
1262
|
+
N.value = !1;
|
|
1263
|
+
}
|
|
1264
|
+
}, y), kt = () => R(async () => {
|
|
1265
|
+
const i = le.value, o = En(i);
|
|
1266
|
+
if (J.value = o.issues, ee.value = [], o.issues.length > 0)
|
|
1267
|
+
throw _.value = "", new Error(`前端校验没过(${o.issues.length} 条),没有发请求:
|
|
1268
|
+
- ${o.issues.join(`
|
|
1269
|
+
- `)}`);
|
|
1270
|
+
Z.value = !0, _.value = "";
|
|
1271
|
+
try {
|
|
1272
|
+
const a = await re("/magic-blocks/import", { updatedBy: "", blocks: i });
|
|
1273
|
+
ee.value = a.results ?? [], _.value = `保存:成功 ${a.saved} 个、失败 ${a.failed} 个`, await X();
|
|
1274
|
+
const h = _.value;
|
|
1275
|
+
await De(), _.value = `${h};已重新读回该目录`;
|
|
1276
|
+
} finally {
|
|
1277
|
+
Z.value = !1;
|
|
1278
|
+
}
|
|
1279
|
+
}, y), wt = (i) => R(async () => {
|
|
1280
|
+
await W(`/magic-blocks/${encodeURIComponent(i.id)}/rollback`, { method: "POST" }), _.value = `「${i.name || i.id}」已回滚到上一版`, await X();
|
|
1281
|
+
}, y), $t = (i) => R(async () => {
|
|
1282
|
+
window.confirm(`删掉「${i.name || i.id}」?库里这一行(连同它的文件包)会一起没。`) && (await W(`/magic-blocks/${encodeURIComponent(i.id)}`, { method: "DELETE" }), _.value = `「${i.name || i.id}」已删除`, await X());
|
|
1283
|
+
}, y);
|
|
1284
|
+
function xt(i, o) {
|
|
1285
|
+
const a = o.target;
|
|
1286
|
+
ae.value[i] = a?.checked === !0;
|
|
1287
|
+
}
|
|
1288
|
+
function Fe(i) {
|
|
1289
|
+
const o = i.detail?.uuid ?? "";
|
|
1290
|
+
r.value = !0, d.value = o;
|
|
1291
|
+
}
|
|
1292
|
+
return Ze(() => {
|
|
1293
|
+
window.addEventListener("magic-block:open-console", Fe), X(), D().catch(() => {
|
|
1294
|
+
});
|
|
1295
|
+
}), Xe(() => window.removeEventListener("magic-block:open-console", Fe)), (i, o) => me(n) ? (g(), v(j, { key: 0 }, [
|
|
1296
|
+
p("button", {
|
|
1297
|
+
class: "fab",
|
|
1298
|
+
type: "button",
|
|
1299
|
+
title: "magic block 控制台",
|
|
1300
|
+
onClick: o[0] || (o[0] = (a) => r.value = !0)
|
|
1301
|
+
}, "块"),
|
|
1302
|
+
r.value ? (g(), v("aside", Fn, [
|
|
1303
|
+
p("header", Hn, [
|
|
1304
|
+
o[12] || (o[12] = p("strong", null, "magic block", -1)),
|
|
1305
|
+
Tt(p("input", {
|
|
1306
|
+
"onUpdate:modelValue": o[1] || (o[1] = (a) => c.value = a),
|
|
1307
|
+
class: "p-inputtext p-component mbc__grow",
|
|
1308
|
+
type: "search",
|
|
1309
|
+
placeholder: "筛选 module / 名字"
|
|
1310
|
+
}, null, 512), [
|
|
1311
|
+
[At, c.value]
|
|
1312
|
+
]),
|
|
1313
|
+
p("button", {
|
|
1314
|
+
class: "p-button p-button-sm",
|
|
1315
|
+
type: "button",
|
|
1316
|
+
disabled: y.value,
|
|
1317
|
+
onClick: o[2] || (o[2] = (a) => m())
|
|
1318
|
+
}, b(S.value ? "体检中…" : "重新体检全部"), 9, Kn),
|
|
1319
|
+
p("button", {
|
|
1320
|
+
class: "p-button p-button-sm p-button-text",
|
|
1321
|
+
type: "button",
|
|
1322
|
+
onClick: o[3] || (o[3] = (a) => r.value = !1)
|
|
1323
|
+
}, "关闭")
|
|
1324
|
+
]),
|
|
1325
|
+
f.value ? (g(), v("p", Un, b(f.value), 1)) : x("", !0),
|
|
1326
|
+
_.value ? (g(), v("p", qn, b(_.value), 1)) : x("", !0),
|
|
1327
|
+
p("section", Gn, [
|
|
1328
|
+
p("h3", Jn, "库里的块(" + b(ie.value.length) + " / " + b(u.value.length) + ")", 1),
|
|
1329
|
+
p("ul", Vn, [
|
|
1330
|
+
(g(!0), v(j, null, oe(ie.value, (a) => (g(), v("li", {
|
|
1331
|
+
key: a.id
|
|
1332
|
+
}, [
|
|
1333
|
+
p("button", {
|
|
1334
|
+
type: "button",
|
|
1335
|
+
class: Y(["mbc__row", { "is-active": a.id === d.value }]),
|
|
1336
|
+
title: xe(a),
|
|
1337
|
+
onClick: (h) => $e(a)
|
|
1338
|
+
}, [
|
|
1339
|
+
p("span", Wn, b(a.module || "unassigned"), 1),
|
|
1340
|
+
p("span", Zn, b(a.name || a.id), 1),
|
|
1341
|
+
te(a).length > 0 ? (g(), v("span", Xn, b(te(a).join(" / ")), 1)) : x("", !0),
|
|
1342
|
+
p("em", {
|
|
1343
|
+
class: Y({ "is-off": a.enabled === !1 })
|
|
1344
|
+
}, b(a.enabled === !1 ? U(a) || "校验失败已停用" : "可用"), 3)
|
|
1345
|
+
], 10, Yn),
|
|
1346
|
+
d.value === a.id ? (g(), v("div", Qn, [
|
|
1347
|
+
(g(!0), v(j, null, oe(pe(a).slice(0, Oe), (h, $) => (g(), v("p", {
|
|
1348
|
+
key: $,
|
|
1349
|
+
class: "mbc__problem"
|
|
1350
|
+
}, b(h), 1))), 128)),
|
|
1351
|
+
pe(a).length > Oe ? (g(), v("p", eo, "还有 " + b(pe(a).length - Oe) + " 条体检问题,点「体检这一块」看最新结论", 1)) : x("", !0),
|
|
1352
|
+
p("dl", to, [
|
|
1353
|
+
o[13] || (o[13] = p("dt", null, "文件", -1)),
|
|
1354
|
+
p("dd", null, b((a.fileNames || []).join(" / ") || "(没有文件)"), 1),
|
|
1355
|
+
o[14] || (o[14] = p("dt", null, "更新", -1)),
|
|
1356
|
+
p("dd", null, b(a.updatedAt || "—"), 1),
|
|
1357
|
+
o[15] || (o[15] = p("dt", null, "上一版", -1)),
|
|
1358
|
+
p("dd", null, b(a.hasPrev ? a.prevUpdatedAt || "有" : "没有"), 1),
|
|
1359
|
+
o[16] || (o[16] = p("dt", null, "id", -1)),
|
|
1360
|
+
p("dd", no, b(a.id), 1)
|
|
1361
|
+
]),
|
|
1362
|
+
p("div", oo, [
|
|
1363
|
+
p("button", {
|
|
1364
|
+
class: "p-button p-button-sm p-button-outlined",
|
|
1365
|
+
type: "button",
|
|
1366
|
+
disabled: y.value,
|
|
1367
|
+
onClick: (h) => m(a.id)
|
|
1368
|
+
}, "体检这一块", 8, so),
|
|
1369
|
+
p("button", {
|
|
1370
|
+
class: "p-button p-button-sm p-button-outlined",
|
|
1371
|
+
type: "button",
|
|
1372
|
+
disabled: y.value || !a.hasPrev,
|
|
1373
|
+
onClick: (h) => wt(a)
|
|
1374
|
+
}, "回滚上一版", 8, ao),
|
|
1375
|
+
p("button", {
|
|
1376
|
+
class: "p-button p-button-sm p-button-text",
|
|
1377
|
+
type: "button",
|
|
1378
|
+
disabled: y.value,
|
|
1379
|
+
onClick: (h) => $t(a)
|
|
1380
|
+
}, "删除", 8, io)
|
|
1381
|
+
])
|
|
1382
|
+
])) : x("", !0)
|
|
1383
|
+
]))), 128))
|
|
1384
|
+
])
|
|
1385
|
+
]),
|
|
1386
|
+
p("section", lo, [
|
|
1387
|
+
o[23] || (o[23] = p("h3", { class: "mbc__title" }, "导出去改(用你自己的编辑器)", -1)),
|
|
1388
|
+
p("div", ro, [
|
|
1389
|
+
o[17] || (o[17] = p("label", { for: "mbc-dir" }, "导出目录", -1)),
|
|
1390
|
+
p("input", {
|
|
1391
|
+
id: "mbc-dir",
|
|
1392
|
+
class: "p-inputtext p-component mbc__grow mbc__mono",
|
|
1393
|
+
type: "text",
|
|
1394
|
+
readonly: "",
|
|
1395
|
+
value: A.value ? A.value.name : "(还没选:点右边那个按钮选一个文件夹)"
|
|
1396
|
+
}, null, 8, co),
|
|
1397
|
+
p("button", {
|
|
1398
|
+
class: "p-button p-button-sm p-button-outlined",
|
|
1399
|
+
type: "button",
|
|
1400
|
+
disabled: y.value,
|
|
1401
|
+
onClick: o[4] || (o[4] = (a) => k("export"))
|
|
1402
|
+
}, "选择文件夹", 8, uo),
|
|
1403
|
+
p("button", {
|
|
1404
|
+
class: "p-button p-button-sm",
|
|
1405
|
+
type: "button",
|
|
1406
|
+
disabled: y.value,
|
|
1407
|
+
onClick: o[5] || (o[5] = (a) => Ee())
|
|
1408
|
+
}, b(C.value ? "导出中…" : "导出"), 9, po)
|
|
1409
|
+
]),
|
|
1410
|
+
o[24] || (o[24] = Ot('<p class="mbc__hint" data-v-7c262207> 代码会铺在 <span class="mbc__mono" data-v-7c262207>workspace/<可读名>-<块 uuid>/</span>, 顶层另附 <span class="mbc__mono" data-v-7c262207>GUIDE.lock.md</span>(怎么改)、<span class="mbc__mono" data-v-7c262207>pages.lock.ts</span>(Page 枚举)、 <span class="mbc__mono" data-v-7c262207>backend.lock.pyi</span>(能用的库 / ctx / 环境变量 / 模型)、 <span class="mbc__mono" data-v-7c262207>labull.css</span> 与 <span class="mbc__mono" data-v-7c262207>CONTROLS.lock.html</span>(主题与控件清单)—— 那几份都是**生成物**。 </p>', 1)),
|
|
1411
|
+
I.value ? (g(), v("p", fo, b(I.value), 1)) : x("", !0),
|
|
1412
|
+
E.value.active ? (g(), v("div", {
|
|
1413
|
+
key: 1,
|
|
1414
|
+
class: Y(["mbc__progress", { "is-failed": E.value.failed }]),
|
|
1415
|
+
role: "progressbar",
|
|
1416
|
+
"aria-valuenow": E.value.percent,
|
|
1417
|
+
"aria-valuemin": "0",
|
|
1418
|
+
"aria-valuemax": "100"
|
|
1419
|
+
}, [
|
|
1420
|
+
p("div", {
|
|
1421
|
+
class: "mbc__progress-bar",
|
|
1422
|
+
style: Qe(we.value)
|
|
1423
|
+
}, null, 4)
|
|
1424
|
+
], 10, mo)) : x("", !0),
|
|
1425
|
+
E.value.active ? (g(), v("p", {
|
|
1426
|
+
key: 2,
|
|
1427
|
+
class: Y(["mbc__hint", { "mbc__progress-note": E.value.failed }])
|
|
1428
|
+
}, b(E.value.failed ? "✗" : "") + " " + b(E.value.percent) + "% · " + b(E.value.stage), 3)) : x("", !0),
|
|
1429
|
+
M.value ? (g(), v("div", ho, [
|
|
1430
|
+
p("p", bo, "「" + b(M.value.target.name) + "」里现在有 " + b(M.value.inventory.dirs) + " 个文件夹 / " + b(M.value.inventory.files) + " 个文件", 1),
|
|
1431
|
+
p("p", go, [
|
|
1432
|
+
o[18] || (o[18] = ce(" 接下来会", -1)),
|
|
1433
|
+
o[19] || (o[19] = p("b", null, "先全部删掉", -1)),
|
|
1434
|
+
o[20] || (o[20] = ce("再铺一遍 —— 包括你改过、还没读回库的东西。 清空只动这个目录", -1)),
|
|
1435
|
+
o[21] || (o[21] = p("b", null, "里面", -1)),
|
|
1436
|
+
o[22] || (o[22] = ce("的子项:目录本身、以及它外面的任何东西都不会被碰。 ", -1)),
|
|
1437
|
+
M.value.inventory.items.length > 0 ? (g(), v("span", vo, "(" + b(M.value.inventory.items.slice(0, 6).map((a) => a.name).join(" / ")) + b(M.value.inventory.items.length > 6 ? " …" : "") + ")", 1)) : x("", !0)
|
|
1438
|
+
]),
|
|
1439
|
+
p("div", yo, [
|
|
1440
|
+
p("button", {
|
|
1441
|
+
class: "p-button p-button-sm",
|
|
1442
|
+
type: "button",
|
|
1443
|
+
disabled: y.value,
|
|
1444
|
+
onClick: o[6] || (o[6] = (a) => vt())
|
|
1445
|
+
}, "清空并导出", 8, _o),
|
|
1446
|
+
p("button", {
|
|
1447
|
+
class: "p-button p-button-sm p-button-outlined",
|
|
1448
|
+
type: "button",
|
|
1449
|
+
disabled: y.value,
|
|
1450
|
+
onClick: o[7] || (o[7] = (a) => yt())
|
|
1451
|
+
}, "只覆盖", 8, ko),
|
|
1452
|
+
p("button", {
|
|
1453
|
+
class: "p-button p-button-sm p-button-text",
|
|
1454
|
+
type: "button",
|
|
1455
|
+
disabled: y.value,
|
|
1456
|
+
onClick: o[8] || (o[8] = (a) => _t())
|
|
1457
|
+
}, "取消", 8, wo)
|
|
1458
|
+
])
|
|
1459
|
+
])) : x("", !0)
|
|
1460
|
+
]),
|
|
1461
|
+
p("section", $o, [
|
|
1462
|
+
o[28] || (o[28] = p("h3", { class: "mbc__title" }, "读回改动", -1)),
|
|
1463
|
+
p("div", xo, [
|
|
1464
|
+
o[25] || (o[25] = p("label", { for: "mbc-read-dir" }, "读回目录", -1)),
|
|
1465
|
+
p("input", {
|
|
1466
|
+
id: "mbc-read-dir",
|
|
1467
|
+
class: "p-inputtext p-component mbc__grow mbc__mono",
|
|
1468
|
+
type: "text",
|
|
1469
|
+
readonly: "",
|
|
1470
|
+
value: B.value ? B.value.name : "(还没选:导出会自动填上,也可以自己选一个)"
|
|
1471
|
+
}, null, 8, So),
|
|
1472
|
+
p("button", {
|
|
1473
|
+
class: "p-button p-button-sm p-button-outlined",
|
|
1474
|
+
type: "button",
|
|
1475
|
+
disabled: y.value,
|
|
1476
|
+
onClick: o[9] || (o[9] = (a) => k("read"))
|
|
1477
|
+
}, "选择文件夹", 8, Eo),
|
|
1478
|
+
p("button", {
|
|
1479
|
+
class: "p-button p-button-sm p-button-outlined",
|
|
1480
|
+
type: "button",
|
|
1481
|
+
disabled: y.value,
|
|
1482
|
+
onClick: o[10] || (o[10] = (a) => De())
|
|
1483
|
+
}, b(N.value ? "读回中…" : "读回改动"), 9, Co)
|
|
1484
|
+
]),
|
|
1485
|
+
B.value && A.value && B.value.name !== A.value.name ? (g(), v("p", Bo, " 读回的是「" + b(B.value.name) + "」(与导出目录「" + b(A.value.name) + "」不是同一个 —— 想同步就再选一次)。 ", 1)) : (g(), v("p", To, "读回目录默认跟着导出目录;要读别的文件夹就点「选择文件夹」单独选一个。")),
|
|
1486
|
+
L.value ? (g(), v(j, { key: 2 }, [
|
|
1487
|
+
L.value.blocks.length > 0 ? (g(), v("ul", Ao, [
|
|
1488
|
+
(g(!0), v(j, null, oe(L.value.blocks, (a) => (g(), v("li", {
|
|
1489
|
+
key: a.id,
|
|
1490
|
+
class: "mbc__block"
|
|
1491
|
+
}, [
|
|
1492
|
+
p("label", Oo, [
|
|
1493
|
+
p("span", {
|
|
1494
|
+
class: Y(["p-checkbox p-component", { "p-disabled": !a.canApply }])
|
|
1495
|
+
}, [
|
|
1496
|
+
p("input", {
|
|
1497
|
+
class: "p-checkbox-input",
|
|
1498
|
+
type: "checkbox",
|
|
1499
|
+
disabled: !a.canApply,
|
|
1500
|
+
checked: ae.value[a.id] === !0,
|
|
1501
|
+
onChange: (h) => xt(a.id, h)
|
|
1502
|
+
}, null, 40, Mo),
|
|
1503
|
+
o[26] || (o[26] = p("span", { class: "p-checkbox-box" }, null, -1))
|
|
1504
|
+
], 2),
|
|
1505
|
+
p("span", Po, b(a.name || a.id), 1),
|
|
1506
|
+
p("span", {
|
|
1507
|
+
class: Y(["p-tag", a.status === "modified" ? "p-tag-success" : a.status === "missing" ? "p-tag-warning" : ""])
|
|
1508
|
+
}, b(e[a.status]), 3),
|
|
1509
|
+
p("span", Ro, b(a.module || "—"), 1)
|
|
1510
|
+
]),
|
|
1511
|
+
p("p", jo, [
|
|
1512
|
+
a.status === "missing" ? (g(), v(j, { key: 0 }, [
|
|
1513
|
+
ce("目录里没有这一块,勾不上")
|
|
1514
|
+
], 64)) : a.changedFiles.length > 0 ? (g(), v(j, { key: 1 }, [
|
|
1515
|
+
o[27] || (o[27] = ce("改了:", -1)),
|
|
1516
|
+
p("span", No, b(a.changedFiles.join(" / ")), 1)
|
|
1517
|
+
], 64)) : (g(), v(j, { key: 2 }, [
|
|
1518
|
+
ce("内容与库里一致")
|
|
1519
|
+
], 64))
|
|
1520
|
+
]),
|
|
1521
|
+
a.blockedWhy ? (g(), v("p", Io, "不能存:" + b(a.blockedWhy), 1)) : x("", !0),
|
|
1522
|
+
p("p", Lo, b(a.folder || a.where), 1)
|
|
1523
|
+
]))), 128))
|
|
1524
|
+
])) : (g(), v("p", zo, "这个目录里没有认出任何块。")),
|
|
1525
|
+
L.value.unmatched.length > 0 ? (g(), v("p", Do, "认不出归属的文件夹(" + b(L.value.unmatched.length) + "):" + b(L.value.unmatched.join(" / ")), 1)) : x("", !0),
|
|
1526
|
+
L.value.failures.length > 0 ? (g(), v("ul", Fo, [
|
|
1527
|
+
(g(!0), v(j, null, oe(L.value.failures, (a, h) => (g(), v("li", { key: h }, b(a.where || a.kind) + ":" + b(a.message), 1))), 128))
|
|
1528
|
+
])) : x("", !0),
|
|
1529
|
+
p("div", Ho, [
|
|
1530
|
+
p("button", {
|
|
1531
|
+
class: "p-button p-button-sm",
|
|
1532
|
+
type: "button",
|
|
1533
|
+
disabled: y.value,
|
|
1534
|
+
onClick: o[11] || (o[11] = (a) => kt())
|
|
1535
|
+
}, b(Z.value ? "保存中…" : `保存选中的块(${le.value.length})`), 9, Ko)
|
|
1536
|
+
])
|
|
1537
|
+
], 64)) : x("", !0)
|
|
1538
|
+
]),
|
|
1539
|
+
J.value.length > 0 || ee.value.length > 0 ? (g(), v("section", Uo, [
|
|
1540
|
+
p("h3", qo, b(J.value.length > 0 ? "前端校验拦下的问题" : "保存结果"), 1),
|
|
1541
|
+
J.value.length > 0 ? (g(), v("ul", Go, [
|
|
1542
|
+
(g(!0), v(j, null, oe(J.value, (a, h) => (g(), v("li", { key: h }, b(a), 1))), 128))
|
|
1543
|
+
])) : (g(), v("ul", Jo, [
|
|
1544
|
+
(g(!0), v(j, null, oe(ee.value, (a) => (g(), v("li", {
|
|
1545
|
+
key: a.id,
|
|
1546
|
+
class: Y({ "is-ok": a.ok })
|
|
1547
|
+
}, [
|
|
1548
|
+
p("strong", null, b(a.name || a.id) + ":" + b(a.ok ? "已保存" : "没保存"), 1),
|
|
1549
|
+
!a.ok && a.error ? (g(), v("span", Vo, b(a.error), 1)) : x("", !0),
|
|
1550
|
+
!a.ok && (a.issues ?? []).length > 0 ? (g(), v("ul", Yo, [
|
|
1551
|
+
(g(!0), v(j, null, oe(a.issues, (h, $) => (g(), v("li", { key: $ }, b(h), 1))), 128))
|
|
1552
|
+
])) : x("", !0)
|
|
1553
|
+
], 2))), 128))
|
|
1554
|
+
]))
|
|
1555
|
+
])) : x("", !0)
|
|
1556
|
+
])) : x("", !0)
|
|
1557
|
+
], 64)) : x("", !0);
|
|
1558
|
+
}
|
|
1559
|
+
}), ns = /* @__PURE__ */ ot(Wo, [["__scopeId", "data-v-7c262207"]]);
|
|
1560
|
+
export {
|
|
1561
|
+
Mt as API_BASE,
|
|
1562
|
+
Lt as BLOCK_BACKEND_FILE,
|
|
1563
|
+
rt as BLOCK_ENTRY,
|
|
1564
|
+
Te as BLOCK_MANIFEST,
|
|
1565
|
+
Rt as CLIENT_SOURCE,
|
|
1566
|
+
_e as MANIFEST_KEYS,
|
|
1567
|
+
ns as MagicBlockConsole,
|
|
1568
|
+
ts as MagicBlockHost,
|
|
1569
|
+
ze as NAME_MAX,
|
|
1570
|
+
qt as buildPreview,
|
|
1571
|
+
En as checkSelection,
|
|
1572
|
+
It as installBridge,
|
|
1573
|
+
Vt as loadBlockRender,
|
|
1574
|
+
Gt as onBlockRenderRefresh,
|
|
1575
|
+
$n as parseManifest,
|
|
1576
|
+
Jt as refreshBlockRender,
|
|
1577
|
+
xn as validFileName,
|
|
1578
|
+
vn as validName,
|
|
1579
|
+
Sn as validateBlock
|
|
1580
|
+
};
|