agentworkshop 0.7.21 → 0.7.23
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/.output/nitro.json +1 -1
- package/.output/public/_nuxt/BJbhwS5o.js +1 -0
- package/.output/public/_nuxt/Bo8t5fkJ.js +1 -0
- package/.output/public/_nuxt/{DBXhDLgl.js → BuiGDfMB.js} +1 -1
- package/.output/public/_nuxt/{QT54MQV9.js → C53drffV.js} +1 -1
- package/.output/public/_nuxt/{BA3XzwiD.js → CEHmSufQ.js} +2 -2
- package/.output/public/_nuxt/Dc32T41C.js +1 -0
- package/.output/public/_nuxt/DtzZqC0z.js +1 -0
- package/.output/public/_nuxt/{BOJaHO5V.js → Ko9mMsJj.js} +3 -3
- package/.output/public/_nuxt/{TownView.BHLKAjEu.css → TownView.zu2rU5nS.css} +1 -1
- package/.output/public/_nuxt/_id_.C-YXjn8Q.css +1 -0
- package/.output/public/_nuxt/_id_.MbzdFygI.css +1 -0
- package/.output/public/_nuxt/_wsId_.BqWBlY7X.css +1 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/1adc96e0-8bbd-4a79-ac7e-b6952556fef8.json +1 -0
- package/.output/public/_nuxt/daq.3OKj58HH.css +1 -0
- package/.output/public/_nuxt/dbi5bHkY.js +2 -0
- package/.output/public/_nuxt/dcw.rXH2fc2Z.css +1 -0
- package/.output/public/_nuxt/entry.OR_psZHp.css +1 -0
- package/.output/public/_nuxt/{DsOkkMX3.js → jyQyulh-.js} +1 -1
- package/.output/public/_nuxt/logs.DMOM-iHe.css +1 -0
- package/.output/public/_nuxt/{B-E8fXAO.js → n3vMFfrv.js} +1 -1
- package/.output/server/chunks/_/nitro.mjs +842 -717
- package/.output/server/chunks/_/nitro.mjs.map +1 -1
- package/.output/server/chunks/build/{TownView-ql2eHZOg.mjs → TownView-DxeQe1Bl.mjs} +144 -143
- package/.output/server/chunks/build/{TownView-ql2eHZOg.mjs.map → TownView-DxeQe1Bl.mjs.map} +1 -1
- package/.output/server/chunks/build/{_id_-CwR5oLu9.mjs → _id_-DQDqj-cZ.mjs} +33 -35
- package/.output/server/chunks/build/_id_-DQDqj-cZ.mjs.map +1 -0
- package/.output/server/chunks/build/{_id_-Bs-euJPu.mjs → _id_-DY6v6sqB.mjs} +138 -137
- package/.output/server/chunks/build/_id_-DY6v6sqB.mjs.map +1 -0
- package/.output/server/chunks/build/{_wsId_-B3aB9bIp.mjs → _wsId_-C2O5zZsI.mjs} +6 -6
- package/.output/server/chunks/build/{_wsId_-B3aB9bIp.mjs.map → _wsId_-C2O5zZsI.mjs.map} +1 -1
- package/.output/server/chunks/build/{daq-epWL_sC7.mjs → daq-B-3xCp_2.mjs} +146 -118
- package/.output/server/chunks/build/daq-B-3xCp_2.mjs.map +1 -0
- package/.output/server/chunks/build/{dcw-CBPaFj2f.mjs → dcw-BL2dhuz7.mjs} +43 -43
- package/.output/server/chunks/build/{dcw-CBPaFj2f.mjs.map → dcw-BL2dhuz7.mjs.map} +1 -1
- package/.output/server/chunks/build/{logs-Bz1p6x_5.mjs → logs-Dto_umFY.mjs} +44 -38
- package/.output/server/chunks/build/logs-Dto_umFY.mjs.map +1 -0
- package/.output/server/chunks/build/{pages-Dq7_H4FK.mjs → pages-D9tOoJsf.mjs} +2 -2
- package/.output/server/chunks/build/{pages-Dq7_H4FK.mjs.map → pages-D9tOoJsf.mjs.map} +1 -1
- package/.output/server/chunks/build/{town-CstiKxf_.mjs → town-Dsh0jlwZ.mjs} +3 -3
- package/.output/server/chunks/build/{town-CstiKxf_.mjs.map → town-Dsh0jlwZ.mjs.map} +1 -1
- package/.output/server/chunks/build/{useDaqStream-BOOZ-p-l.mjs → useDaqStream-DkRL3tEW.mjs} +9 -1
- package/.output/server/chunks/build/{useDaqStream-BOOZ-p-l.mjs.map → useDaqStream-DkRL3tEW.mjs.map} +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/bind.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/bind.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/bindings.put.mjs +34 -0
- package/.output/server/chunks/routes/api/workshop/daq/_id/bindings.put.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/dcw/_id/bind.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/bind.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/bindings.put.mjs +33 -0
- package/.output/server/chunks/routes/api/workshop/dcw/_id/bindings.put.mjs.map +1 -0
- package/.output/server/chunks/routes/renderer.mjs +1 -1
- package/.output/server/chunks/virtual/entry.mjs +16 -8
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/app/assets/css/main.css +306 -161
- package/app/components/workshop/blocks/ClusterArtifact.vue +0 -16
- package/app/components/workshop/town/TownView.vue +7082 -7076
- package/app/composables/workshop/useDaqStream.ts +10 -0
- package/app/pages/daq/[id].vue +919 -937
- package/app/pages/daq/index.vue +67 -43
- package/app/pages/dcw/[id].vue +6 -5
- package/app/pages/dcw/index.vue +1 -30
- package/app/pages/logs/index.vue +99 -87
- package/i18n/locales/en.ts +3 -0
- package/i18n/locales/zh-CN.ts +5 -0
- package/package.json +148 -148
- package/scripts/_dbg-device-bind-e2e.mjs +129 -0
- package/scripts/_dbg-logs-ui-e2e.py +125 -0
- package/server/api/workshop/daq/[id]/bind.post.ts +5 -2
- package/server/api/workshop/daq/[id]/bindings.put.ts +22 -0
- package/server/api/workshop/dcw/[id]/bind.post.ts +4 -2
- package/server/api/workshop/dcw/[id]/bindings.put.ts +21 -0
- package/server/data/daqs.json +405 -100
- package/server/data/dcw-lines.json +7 -0
- package/server/data/dcw-rollback.json +21 -10
- package/server/data/dcw-runs.json +50 -54
- package/server/data/dcw-writes.json +24 -0
- package/server/data/dcws.json +505 -216
- package/server/data/line-runs.json +18 -18
- package/server/services/workshop/agents/industrial-context.ts +231 -222
- package/server/services/workshop/daq/daq-controller.ts +76 -23
- package/server/services/workshop/daq/daq-node.ts +11 -1
- package/server/services/workshop/dcw/dcw-controller.ts +50 -2
- package/server/services/workshop/dcw/dcw-node.ts +10 -1
- package/shared/daq-protocol.ts +2 -0
- package/shared/dcw-protocol.ts +2 -0
- package/.output/public/_nuxt/B_C3GLsD.js +0 -1
- package/.output/public/_nuxt/Ba7YofCb.js +0 -2
- package/.output/public/_nuxt/C_2bTK0C.js +0 -1
- package/.output/public/_nuxt/CgT5wMfz.js +0 -1
- package/.output/public/_nuxt/D4CLp1hc.js +0 -1
- package/.output/public/_nuxt/_id_.DIxEKj88.css +0 -1
- package/.output/public/_nuxt/_id_.IbmpkhH4.css +0 -1
- package/.output/public/_nuxt/_wsId_.BFyCuUwS.css +0 -1
- package/.output/public/_nuxt/builds/meta/5d344836-03a5-4f47-a0a7-fd30192f5a8f.json +0 -1
- package/.output/public/_nuxt/daq.6IrhVVfy.css +0 -1
- package/.output/public/_nuxt/dcw.rpqpOYQ8.css +0 -1
- package/.output/public/_nuxt/entry.DRUzDwmX.css +0 -1
- package/.output/public/_nuxt/logs.BarkrvUp.css +0 -1
- package/.output/server/chunks/build/_id_-Bs-euJPu.mjs.map +0 -1
- package/.output/server/chunks/build/_id_-CwR5oLu9.mjs.map +0 -1
- package/.output/server/chunks/build/daq-epWL_sC7.mjs.map +0 -1
- package/.output/server/chunks/build/logs-Bz1p6x_5.mjs.map +0 -1
package/app/pages/daq/index.vue
CHANGED
|
@@ -40,17 +40,38 @@ async function setNodeLine(id: string, e: Event): Promise<void> {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
/** 节点绑定设备(
|
|
44
|
-
async function
|
|
43
|
+
/** 节点绑定设备(多对多:add 挂新设备 / remove 摘除一台;server 落库,与数字孪生同一 deviceIds 链路) */
|
|
44
|
+
async function addNodeDevice(id: string, e: Event): Promise<void> {
|
|
45
45
|
const deviceId = (e.target as HTMLSelectElement).value
|
|
46
|
+
if (!deviceId) return
|
|
47
|
+
const node = daq.nodes.find(n => n.id === id)
|
|
48
|
+
if (!node) return
|
|
49
|
+
if (node.deviceIds?.includes(deviceId)) return
|
|
46
50
|
try {
|
|
47
|
-
await daq.
|
|
51
|
+
await daq.setNodeBindings(id, [...(node.deviceIds ?? []), deviceId])
|
|
48
52
|
}
|
|
49
53
|
catch (err) {
|
|
50
54
|
message.error(apiErrorMessage(err))
|
|
51
55
|
}
|
|
56
|
+
;(e.target as HTMLSelectElement).value = ''
|
|
52
57
|
}
|
|
53
58
|
|
|
59
|
+
async function removeNodeDevice(id: string, deviceId: string): Promise<void> {
|
|
60
|
+
const node = daq.nodes.find(n => n.id === id)
|
|
61
|
+
if (!node) return
|
|
62
|
+
try {
|
|
63
|
+
await daq.setNodeBindings(id, (node.deviceIds ?? []).filter(d => d !== deviceId))
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
message.error(apiErrorMessage(err))
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const nodeDeviceIds = (n: DaqNodeView): string[] =>
|
|
71
|
+
n.deviceIds ?? (n.deviceBindingId ? [n.deviceBindingId] : [])
|
|
72
|
+
const deviceNameById = (id: string): string =>
|
|
73
|
+
nodeDevices.get(id) ?? deviceTwins.twins.find(t => t.id === id)?.name ?? id
|
|
74
|
+
|
|
54
75
|
/** 单节点独立 启动/停止采集(PATCH enabled;server 权威,本地乐观翻转 + WS/轮询收敛) */
|
|
55
76
|
async function toggleNodeEnabled(n: { id: string, enabled: boolean, state: string }): Promise<void> {
|
|
56
77
|
const next = !n.enabled
|
|
@@ -295,7 +316,9 @@ function fmtPoint(v: number | null | undefined): string {
|
|
|
295
316
|
const boundDevices = computed<Array<{ id: string, name: string, count: number }>>(() => {
|
|
296
317
|
const used = new Map<string, number>()
|
|
297
318
|
for (const n of daq.nodes) {
|
|
298
|
-
|
|
319
|
+
for (const d of n.deviceIds ?? (n.deviceBindingId ? [n.deviceBindingId] : [])) {
|
|
320
|
+
used.set(d, (used.get(d) ?? 0) + 1)
|
|
321
|
+
}
|
|
299
322
|
}
|
|
300
323
|
return deviceTwins.twins
|
|
301
324
|
.filter(t => used.has(t.id))
|
|
@@ -371,7 +394,8 @@ function statePillOf(n: DaqNodeView): { key: RowState, label: string, tip: strin
|
|
|
371
394
|
|
|
372
395
|
const filteredNodes = computed<DaqNodeView[]>(() => daq.nodes.filter((n) => {
|
|
373
396
|
if (filters.lineId && (filters.lineId === 'none' ? !!n.lineId : (n.lineId ?? '') !== filters.lineId)) return false
|
|
374
|
-
|
|
397
|
+
const nodeDevIds = n.deviceIds ?? (n.deviceBindingId ? [n.deviceBindingId] : [])
|
|
398
|
+
if (filters.deviceId && (filters.deviceId === 'none' ? nodeDevIds.length > 0 : !nodeDevIds.includes(filters.deviceId))) return false
|
|
375
399
|
if (filters.template && n.templateRef !== filters.template) return false
|
|
376
400
|
if (filters.driver && n.driver !== filters.driver) return false
|
|
377
401
|
if (!lineRunMatch(n)) return false
|
|
@@ -1771,23 +1795,39 @@ async function doReconnect(): Promise<void> {
|
|
|
1771
1795
|
>--</span>
|
|
1772
1796
|
</td>
|
|
1773
1797
|
<td>
|
|
1774
|
-
<
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
<option value="">
|
|
1781
|
-
{{ $t('daq.k3own4q056') }}
|
|
1782
|
-
</option>
|
|
1783
|
-
<option
|
|
1784
|
-
v-for="d in bindableDeviceTwins"
|
|
1785
|
-
:key="d.id"
|
|
1786
|
-
:value="d.id"
|
|
1798
|
+
<div class="dev-binds">
|
|
1799
|
+
<span
|
|
1800
|
+
v-for="did in nodeDeviceIds(n)"
|
|
1801
|
+
:key="did"
|
|
1802
|
+
class="dev-bind-chip"
|
|
1803
|
+
:title="$t('daq.k2bindtip137')"
|
|
1787
1804
|
>
|
|
1788
|
-
{{
|
|
1789
|
-
|
|
1790
|
-
|
|
1805
|
+
{{ deviceNameById(did) }}
|
|
1806
|
+
<span
|
|
1807
|
+
class="dev-unbind"
|
|
1808
|
+
title="unbind"
|
|
1809
|
+
@click="removeNodeDevice(n.id, did)"
|
|
1810
|
+
>✕</span>
|
|
1811
|
+
</span>
|
|
1812
|
+
<select
|
|
1813
|
+
class="line-sel dev-add"
|
|
1814
|
+
:value="''"
|
|
1815
|
+
:title="$t('daq.k2bindtip137')"
|
|
1816
|
+
@change="addNodeDevice(n.id, $event)"
|
|
1817
|
+
>
|
|
1818
|
+
<option value="">
|
|
1819
|
+
{{ $t('daq.k3own4q056') }}
|
|
1820
|
+
</option>
|
|
1821
|
+
<option
|
|
1822
|
+
v-for="d in bindableDeviceTwins"
|
|
1823
|
+
:key="d.id"
|
|
1824
|
+
:value="d.id"
|
|
1825
|
+
:disabled="(n.deviceIds ?? []).includes(d.id)"
|
|
1826
|
+
>
|
|
1827
|
+
{{ nodeDevices.get(d.id) ?? d.name }}
|
|
1828
|
+
</option>
|
|
1829
|
+
</select>
|
|
1830
|
+
</div>
|
|
1791
1831
|
</td>
|
|
1792
1832
|
<td class="right">
|
|
1793
1833
|
<button
|
|
@@ -2185,7 +2225,7 @@ tr.row-recipe-alarm td:first-child { box-shadow: inset 3px 0 0 var(--tone-danger
|
|
|
2185
2225
|
.infra-banner .txt a { color: var(--accent); text-decoration: underline; }
|
|
2186
2226
|
.infra-banner .txt b { font-weight: 600; }
|
|
2187
2227
|
.infra-banner .txt small { margin-left: 8px; font-size: 10.5px; opacity: 0.75; }
|
|
2188
|
-
.infra-banner .pill-btn { flex: 0 0 auto;
|
|
2228
|
+
.infra-banner .pill-btn { flex: 0 0 auto; }
|
|
2189
2229
|
|
|
2190
2230
|
/* ---------- 未确认报警条(S5) ---------- */
|
|
2191
2231
|
.alarm-bar {
|
|
@@ -2311,16 +2351,6 @@ tr.row-recipe-alarm td:first-child { box-shadow: inset 3px 0 0 var(--tone-danger
|
|
|
2311
2351
|
}
|
|
2312
2352
|
.hint { font-size: 10px; color: var(--ink-fainter); }
|
|
2313
2353
|
.test-row { display: flex; gap: 10px; align-items: center; margin: 6px 0 4px; }
|
|
2314
|
-
.pill-btn {
|
|
2315
|
-
padding: 7px 14px;
|
|
2316
|
-
font-size: 12.5px;
|
|
2317
|
-
cursor: pointer;
|
|
2318
|
-
color: var(--paper-raised);
|
|
2319
|
-
background: var(--accent);
|
|
2320
|
-
border: 1px solid var(--accent);
|
|
2321
|
-
border-radius: var(--radius-pill);
|
|
2322
|
-
}
|
|
2323
|
-
.pill-btn:disabled { opacity: 0.5; cursor: default; }
|
|
2324
2354
|
.test-result { font-family: var(--font-mono); font-size: 11px; }
|
|
2325
2355
|
.test-result.ok { color: var(--tone-success-dot); }
|
|
2326
2356
|
.test-result.bad { color: var(--tone-danger-dot); }
|
|
@@ -2343,17 +2373,6 @@ tr.row-recipe-alarm td:first-child { box-shadow: inset 3px 0 0 var(--tone-danger
|
|
|
2343
2373
|
.tpl-table .range { font-size: 11.5px; color: var(--ink-soft); }
|
|
2344
2374
|
.tpl-table .empty { color: var(--ink-fainter); font-size: 12px; text-align: center; padding: 14px 0; }
|
|
2345
2375
|
.tpl-table .actions { white-space: nowrap; }
|
|
2346
|
-
.mini-btn {
|
|
2347
|
-
padding: 3px 10px;
|
|
2348
|
-
font-size: 11.5px;
|
|
2349
|
-
cursor: pointer;
|
|
2350
|
-
color: var(--ink-soft);
|
|
2351
|
-
background: var(--paper-deep);
|
|
2352
|
-
border: 1px solid var(--line-strong);
|
|
2353
|
-
border-radius: var(--radius-chip);
|
|
2354
|
-
}
|
|
2355
|
-
.mini-btn.danger { color: var(--tone-danger-dot); }
|
|
2356
|
-
.mini-btn:hover { border-color: var(--accent); color: var(--accent); }
|
|
2357
2376
|
|
|
2358
2377
|
/* ---------- 节点筛选工具条 ---------- */
|
|
2359
2378
|
.tbl-toolbar {
|
|
@@ -2395,6 +2414,11 @@ tr.row-recipe-alarm td:first-child { box-shadow: inset 3px 0 0 var(--tone-danger
|
|
|
2395
2414
|
.flt-search::placeholder { color: var(--ink-fainter); }
|
|
2396
2415
|
.count { padding-bottom: 4px; font-size: 11px; color: var(--ink-faint); }
|
|
2397
2416
|
|
|
2417
|
+
.dev-binds { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
|
|
2418
|
+
.dev-bind-chip { display: inline-flex; gap: 4px; align-items: center; padding: 1px 7px; font-size: 11px; color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent); border-radius: var(--radius-chip); }
|
|
2419
|
+
.dev-unbind { cursor: pointer; opacity: 0.55; font-size: 10px; }
|
|
2420
|
+
.dev-unbind:hover { opacity: 1; color: var(--tone-danger-dot); }
|
|
2421
|
+
.dev-add { max-width: 130px; font-size: 11px; }
|
|
2398
2422
|
.line-sel {
|
|
2399
2423
|
max-width: 120px;
|
|
2400
2424
|
padding: 3px 6px;
|
package/app/pages/dcw/[id].vue
CHANGED
|
@@ -25,6 +25,10 @@ void dcw.load()
|
|
|
25
25
|
const unsubDcw = dcw.ensureWsFeed()
|
|
26
26
|
onUnmounted(() => unsubDcw())
|
|
27
27
|
|
|
28
|
+
const nodeDeviceNames = (n: { deviceIds?: string[], deviceBindingId?: string | null }): string =>
|
|
29
|
+
(n.deviceIds ?? (n.deviceBindingId ? [n.deviceBindingId] : []))
|
|
30
|
+
.map(id => deviceName(id))
|
|
31
|
+
.join(' / ') || '—'
|
|
28
32
|
const deviceName = (id: string | null): string =>
|
|
29
33
|
id ? (deviceTwins.twins.find(t => t.id === id)?.name ?? id) : t('dcwDetail.k3own4q121')
|
|
30
34
|
|
|
@@ -1341,7 +1345,7 @@ function fmtPoint(p: { value?: number, avg?: number } | undefined): string {
|
|
|
1341
1345
|
:title="$t('dcwDetail.k9r7d4e016')"
|
|
1342
1346
|
>· {{ n.readIntervalMs === 0 ? $t('dcwDetail.k9r7d4e022') : (n.readIntervalMs == null ? $t('dcwDetail.k9r7d4e023') : `${n.readIntervalMs}ms`) }}</small>
|
|
1343
1347
|
</td>
|
|
1344
|
-
<td>{{
|
|
1348
|
+
<td>{{ nodeDeviceNames(n) }}</td>
|
|
1345
1349
|
<td class="right">
|
|
1346
1350
|
<button
|
|
1347
1351
|
class="mini-btn danger"
|
|
@@ -2439,7 +2443,7 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
|
|
|
2439
2443
|
.write-row { display: flex; gap: 6px; align-items: center; }
|
|
2440
2444
|
.write-inp { width: 92px; padding: 4px 8px; }
|
|
2441
2445
|
.write-btn { padding: 5px 12px; }
|
|
2442
|
-
.mini-btn {
|
|
2446
|
+
.mini-btn { margin-right: 4px; }
|
|
2443
2447
|
.mini-btn.danger { color: var(--tone-danger-dot); }
|
|
2444
2448
|
.mini-btn:hover { border-color: var(--accent); color: var(--accent); }
|
|
2445
2449
|
.inp { padding: 5px 9px; font-size: 12.5px; color: var(--ink); background: var(--paper-deep); border: 1px solid var(--line-strong); border-radius: var(--radius-chip); }
|
|
@@ -2493,9 +2497,6 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
|
|
|
2493
2497
|
.ver-diff { margin: 4px 0 0; font-size: 11px; color: var(--ink-faint); }
|
|
2494
2498
|
.sec-label { margin: 14px 0 6px; font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); }
|
|
2495
2499
|
.runs { margin-top: 6px; }
|
|
2496
|
-
.pill-btn { padding: 6px 14px; font-size: 12.5px; cursor: pointer; color: var(--paper-raised); background: var(--accent); border: 1px solid var(--accent); border-radius: var(--radius-pill); }
|
|
2497
|
-
.pill-btn.outline { color: var(--ink); background: var(--paper-raised); border-color: var(--line-strong); }
|
|
2498
|
-
.pill-btn:disabled { opacity: 0.5; cursor: default; }
|
|
2499
2500
|
|
|
2500
2501
|
.modal-mask { position: fixed; z-index: 50; inset: 0; display: flex; align-items: center; justify-content: center; background: var(--scrim); backdrop-filter: blur(2px); }
|
|
2501
2502
|
.modal { width: 680px; max-width: 94vw; max-height: 88vh; overflow-y: auto; padding: 22px 24px; background: var(--surface-glass-strong); backdrop-filter: var(--aurora-blur); -webkit-backdrop-filter: var(--aurora-blur); border: 1px solid var(--glass-line); border-radius: var(--radius-panel); box-shadow: var(--glass-edge), var(--shadow-float); }
|
package/app/pages/dcw/index.vue
CHANGED
|
@@ -925,25 +925,7 @@ const builtinCount = computed(() => dcw.templates.filter(t => t.builtin).length)
|
|
|
925
925
|
border-radius: var(--radius-panel-sm);
|
|
926
926
|
}
|
|
927
927
|
.lc-ctl .inp:focus { outline: none; border-color: var(--tone-success-dot); box-shadow: 0 0 0 3px var(--accent-soft); }
|
|
928
|
-
.pill-btn {
|
|
929
|
-
flex: none;
|
|
930
|
-
height: 30px;
|
|
931
|
-
padding: 0 14px;
|
|
932
|
-
font-size: 11.5px;
|
|
933
|
-
font-weight: 600;
|
|
934
|
-
color: var(--on-accent);
|
|
935
|
-
background: var(--accent);
|
|
936
|
-
border: 0;
|
|
937
|
-
border-radius: var(--radius-panel-sm);
|
|
938
|
-
cursor: pointer;
|
|
939
|
-
transition: background 0.15s, box-shadow 0.15s;
|
|
940
|
-
}
|
|
941
|
-
.pill-btn:hover:not(:disabled) { background: var(--accent-strong); box-shadow: 0 0 14px var(--accent-soft); }
|
|
942
|
-
.pill-btn:disabled { opacity: 0.4; cursor: default; }
|
|
943
|
-
.pill-btn.danger,
|
|
944
|
-
.pill-btn.stop { background: var(--tone-danger-bg); color: var(--tone-danger-dot); }
|
|
945
|
-
.pill-btn.danger:hover:not(:disabled),
|
|
946
|
-
.pill-btn.stop:hover:not(:disabled) { background: color-mix(in srgb, var(--tone-danger-dot) 26%, transparent); box-shadow: none; }
|
|
928
|
+
.lc-ctl .pill-btn { flex: none; }
|
|
947
929
|
.lc-manage {
|
|
948
930
|
font-size: 11.5px;
|
|
949
931
|
font-weight: 600;
|
|
@@ -1065,15 +1047,4 @@ const builtinCount = computed(() => dcw.templates.filter(t => t.builtin).length)
|
|
|
1065
1047
|
.tpl-form { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
|
|
1066
1048
|
.tpl-sem { flex: 1; color: var(--ink-faint); }
|
|
1067
1049
|
textarea.inp { height: auto; padding: 6px 9px; font-size: 11.5px; resize: vertical; }
|
|
1068
|
-
.mini-btn {
|
|
1069
|
-
padding: 4px 10px;
|
|
1070
|
-
font-size: 10.5px;
|
|
1071
|
-
color: var(--ink-faint);
|
|
1072
|
-
background: var(--paper-deep);
|
|
1073
|
-
border: 1px solid var(--line-strong);
|
|
1074
|
-
border-radius: 7px;
|
|
1075
|
-
cursor: pointer;
|
|
1076
|
-
}
|
|
1077
|
-
.mini-btn:hover { border-color: var(--ink-faint); color: var(--ink); }
|
|
1078
|
-
.mini-btn.danger { color: var(--tone-danger-dot); }
|
|
1079
1050
|
</style>
|
package/app/pages/logs/index.vue
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="page">
|
|
3
|
-
<!--
|
|
4
|
-
<header class="head">
|
|
3
|
+
<!-- 页头(aw-page-head 规范:kicker + 大标题 + 描述)+ 手动记录入口 -->
|
|
4
|
+
<header class="aw-page-head">
|
|
5
5
|
<div>
|
|
6
6
|
<p class="aw-kicker">
|
|
7
7
|
agentworkshop / audit log
|
|
8
8
|
</p>
|
|
9
|
-
<h1
|
|
10
|
-
{{ $t('logs.title') }}
|
|
11
|
-
</h1>
|
|
9
|
+
<h1>{{ $t('logs.title') }}</h1>
|
|
12
10
|
<p class="sub">
|
|
13
11
|
{{ $t('logs.sub') }}
|
|
14
12
|
</p>
|
|
@@ -182,67 +180,78 @@
|
|
|
182
180
|
</tr>
|
|
183
181
|
</thead>
|
|
184
182
|
<tbody>
|
|
185
|
-
<
|
|
183
|
+
<template
|
|
186
184
|
v-for="row in opsLog.results"
|
|
187
|
-
:key="
|
|
185
|
+
:key="row.id"
|
|
188
186
|
>
|
|
189
|
-
<
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
<
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
<
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
<
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
187
|
+
<tr>
|
|
188
|
+
<td class="mono dim">
|
|
189
|
+
{{ fmtTime(row.at) }}
|
|
190
|
+
</td>
|
|
191
|
+
<td>
|
|
192
|
+
<span
|
|
193
|
+
class="src-badge"
|
|
194
|
+
:class="row.actorKind"
|
|
195
|
+
>{{ srcLabel(row.actorKind) }}</span>
|
|
196
|
+
</td>
|
|
197
|
+
<td class="actor">
|
|
198
|
+
{{ row.actorName || row.actor || '—' }}
|
|
199
|
+
</td>
|
|
200
|
+
<td>
|
|
201
|
+
<span
|
|
202
|
+
class="kind-chip"
|
|
203
|
+
:class="row.kind"
|
|
204
|
+
>{{ kindLabel(row.kind) }}</span>
|
|
205
|
+
</td>
|
|
206
|
+
<td class="summary">
|
|
207
|
+
{{ row.summary }}
|
|
208
|
+
<small class="mono dim action">{{ row.action }}</small>
|
|
209
|
+
</td>
|
|
210
|
+
<td class="scope">
|
|
211
|
+
<span
|
|
212
|
+
v-if="lineName(row.lineId)"
|
|
213
|
+
class="scope-chip"
|
|
214
|
+
:title="$t('logs.fLine')"
|
|
215
|
+
>{{ lineName(row.lineId) }}</span>
|
|
216
|
+
<span
|
|
217
|
+
v-if="productName(row.productId)"
|
|
218
|
+
class="scope-chip"
|
|
219
|
+
:title="$t('logs.fProduct')"
|
|
220
|
+
>{{ productName(row.productId) }}</span>
|
|
221
|
+
<span
|
|
222
|
+
v-if="recipeName(row.recipeId)"
|
|
223
|
+
class="scope-chip"
|
|
224
|
+
:title="'Recipe'"
|
|
225
|
+
>{{ recipeName(row.recipeId) }}</span>
|
|
226
|
+
<span
|
|
227
|
+
v-if="!row.lineId && !row.productId && !row.recipeId"
|
|
228
|
+
class="dim"
|
|
229
|
+
>—</span>
|
|
230
|
+
</td>
|
|
231
|
+
<td class="detail-cell">
|
|
232
|
+
<button
|
|
233
|
+
v-if="hasDetail(row)"
|
|
234
|
+
class="mini-btn"
|
|
235
|
+
@click="toggle(row)"
|
|
236
|
+
>
|
|
237
|
+
{{ expandedId === row.id ? $t('logs.fold') : $t('logs.expand') }}
|
|
238
|
+
</button>
|
|
239
|
+
<span
|
|
240
|
+
v-else
|
|
241
|
+
class="dim"
|
|
242
|
+
>{{ $t('logs.noDetail') }}</span>
|
|
243
|
+
</td>
|
|
244
|
+
</tr>
|
|
245
|
+
<!-- 详情行内展开:紧贴事件行,点击即见(不再沉到表尾) -->
|
|
246
|
+
<tr v-if="expandedId === row.id">
|
|
247
|
+
<td
|
|
248
|
+
colspan="7"
|
|
249
|
+
class="detail-td"
|
|
237
250
|
>
|
|
238
|
-
{{
|
|
239
|
-
</
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
class="dim"
|
|
243
|
-
>—</span>
|
|
244
|
-
</td>
|
|
245
|
-
</tr>
|
|
251
|
+
<pre class="detail-box mono">{{ pretty(row.detailJson) }}</pre>
|
|
252
|
+
</td>
|
|
253
|
+
</tr>
|
|
254
|
+
</template>
|
|
246
255
|
<tr>
|
|
247
256
|
<td
|
|
248
257
|
v-if="opsLog.results.length === 0"
|
|
@@ -259,10 +268,6 @@
|
|
|
259
268
|
</tr>
|
|
260
269
|
</tbody>
|
|
261
270
|
</table>
|
|
262
|
-
<pre
|
|
263
|
-
v-if="expanded"
|
|
264
|
-
class="detail-box mono"
|
|
265
|
-
>{{ pretty(expanded.detailJson) }}</pre>
|
|
266
271
|
</section>
|
|
267
272
|
|
|
268
273
|
<!-- 人工记录弹窗 -->
|
|
@@ -284,6 +289,14 @@
|
|
|
284
289
|
:placeholder="$t('logs.mSummaryPh')"
|
|
285
290
|
/>
|
|
286
291
|
</label>
|
|
292
|
+
<label class="m-f">
|
|
293
|
+
<span>{{ $t('logs.mDetail') }}</span>
|
|
294
|
+
<textarea
|
|
295
|
+
v-model="manual.detail"
|
|
296
|
+
rows="2"
|
|
297
|
+
:placeholder="$t('logs.mDetailPh')"
|
|
298
|
+
/>
|
|
299
|
+
</label>
|
|
287
300
|
<div class="m-grid">
|
|
288
301
|
<label class="m-f">
|
|
289
302
|
<span>{{ $t('logs.fLine') }}</span>
|
|
@@ -385,9 +398,10 @@ const opsLog = useOpsLog()
|
|
|
385
398
|
const KINDS = ['write', 'manual', 'alarm', 'line', 'recipe', 'rollback', 'daq', 'system'] as const
|
|
386
399
|
|
|
387
400
|
const q = reactive({ lineId: '', productId: '', recipeId: '', actorKind: '', kind: '', text: '' })
|
|
388
|
-
|
|
401
|
+
/** 当前展开详情的行(audit_log 行 id;null = 全部收起) */
|
|
402
|
+
const expandedId = ref<number | null>(null)
|
|
389
403
|
const manualOpen = ref(false)
|
|
390
|
-
const manual = reactive({ summary: '', lineId: '', productId: '', recipeId: '' })
|
|
404
|
+
const manual = reactive({ summary: '', detail: '', lineId: '', productId: '', recipeId: '' })
|
|
391
405
|
|
|
392
406
|
const hasFilter = computed(() => !!(q.lineId || q.productId || q.recipeId || q.actorKind || q.kind || q.text.trim()))
|
|
393
407
|
const productsOfLine = computed(() => dcw.products.filter(p => !q.lineId || p.lineId === q.lineId))
|
|
@@ -404,7 +418,7 @@ function doQuery(): void {
|
|
|
404
418
|
q: q.text.trim(),
|
|
405
419
|
limit: 300,
|
|
406
420
|
})
|
|
407
|
-
|
|
421
|
+
expandedId.value = null
|
|
408
422
|
}
|
|
409
423
|
|
|
410
424
|
function resetFilters(): void {
|
|
@@ -419,6 +433,7 @@ function resetFilters(): void {
|
|
|
419
433
|
|
|
420
434
|
function openManual(): void {
|
|
421
435
|
manual.summary = ''
|
|
436
|
+
manual.detail = ''
|
|
422
437
|
manual.lineId = q.lineId
|
|
423
438
|
manual.productId = q.productId
|
|
424
439
|
manual.recipeId = q.recipeId
|
|
@@ -427,11 +442,13 @@ function openManual(): void {
|
|
|
427
442
|
|
|
428
443
|
async function submitManual(): Promise<void> {
|
|
429
444
|
try {
|
|
445
|
+
const note = manual.detail.trim()
|
|
430
446
|
await opsLog.postManual({
|
|
431
447
|
summary: manual.summary.trim(),
|
|
432
448
|
lineId: manual.lineId,
|
|
433
449
|
productId: manual.productId,
|
|
434
450
|
recipeId: manual.recipeId,
|
|
451
|
+
...(note ? { detail: { note } } : {}),
|
|
435
452
|
})
|
|
436
453
|
message.success(tt('logs.mOk'))
|
|
437
454
|
manualOpen.value = false
|
|
@@ -442,8 +459,13 @@ async function submitManual(): Promise<void> {
|
|
|
442
459
|
}
|
|
443
460
|
}
|
|
444
461
|
|
|
462
|
+
/** 该行是否带结构化详情(空对象 = 无) */
|
|
463
|
+
function hasDetail(row: OpsLogRow): boolean {
|
|
464
|
+
return !!row.detailJson && row.detailJson !== '{}'
|
|
465
|
+
}
|
|
466
|
+
|
|
445
467
|
function toggle(row: OpsLogRow): void {
|
|
446
|
-
|
|
468
|
+
expandedId.value = expandedId.value === row.id ? null : row.id
|
|
447
469
|
}
|
|
448
470
|
|
|
449
471
|
function pretty(json: string): string {
|
|
@@ -496,10 +518,8 @@ export default { name: 'OpsLogsPage' }
|
|
|
496
518
|
|
|
497
519
|
<style scoped>
|
|
498
520
|
.page { display: flex; flex-direction: column; gap: 12px; }
|
|
499
|
-
.head { display: flex;
|
|
500
|
-
.
|
|
501
|
-
.sub { margin: 4px 0 0; font-size: 12.5px; color: var(--ink-faint); }
|
|
502
|
-
.head-actions { display: flex; gap: 10px; align-items: center; }
|
|
521
|
+
.head-actions { display: flex; gap: 10px; align-items: center; padding-bottom: 4px; }
|
|
522
|
+
.sub { margin: 8px 0 0; font-size: 12.5px; color: var(--ink-faint); }
|
|
503
523
|
.live-dot {
|
|
504
524
|
width: 8px; height: 8px; border-radius: 50%;
|
|
505
525
|
background: var(--tone-success-dot);
|
|
@@ -530,12 +550,6 @@ export default { name: 'OpsLogsPage' }
|
|
|
530
550
|
border: 1px solid var(--glass-line); border-radius: 7px; outline: none;
|
|
531
551
|
}
|
|
532
552
|
.inp-sel:focus { border-color: color-mix(in srgb, var(--tone-info-dot) 55%, transparent); }
|
|
533
|
-
.mini-btn {
|
|
534
|
-
padding: 6px 11px; font-size: 12px; color: var(--ink-soft);
|
|
535
|
-
background: var(--frost-bg); border: 1px solid var(--glass-line); border-radius: 7px;
|
|
536
|
-
cursor: pointer;
|
|
537
|
-
}
|
|
538
|
-
.mini-btn:hover { border-color: color-mix(in srgb, var(--tone-info-dot) 45%, transparent); }
|
|
539
553
|
|
|
540
554
|
.table-card {
|
|
541
555
|
background: var(--surface-glass);
|
|
@@ -569,10 +583,12 @@ export default { name: 'OpsLogsPage' }
|
|
|
569
583
|
}
|
|
570
584
|
.detail-cell { white-space: nowrap; }
|
|
571
585
|
.empty { padding: 22px 0 !important; color: var(--ink-faint); text-align: center; }
|
|
586
|
+
/* 详情行内展开:紧贴事件行的整行 pre 面板 */
|
|
587
|
+
.detail-td { padding: 0 !important; }
|
|
572
588
|
.detail-box {
|
|
573
589
|
max-height: 260px; margin: 0; padding: 10px 14px; overflow: auto;
|
|
574
|
-
font-size: 11.5px; color: var(--ink-soft);
|
|
575
|
-
background: var(--
|
|
590
|
+
font-size: 11.5px; color: var(--ink-soft); white-space: pre-wrap; word-break: break-word;
|
|
591
|
+
background: var(--paper-deep); border-top: 1px solid var(--glass-line);
|
|
576
592
|
}
|
|
577
593
|
.src-badge {
|
|
578
594
|
display: inline-block; padding: 1px 8px; font-size: 10.5px;
|
|
@@ -616,8 +632,4 @@ export default { name: 'OpsLogsPage' }
|
|
|
616
632
|
.m-f textarea:focus, .m-f .inp-sel:focus { border-color: color-mix(in srgb, var(--tone-info-dot) 55%, transparent); }
|
|
617
633
|
.m-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 8px; }
|
|
618
634
|
.m-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 12px; }
|
|
619
|
-
.ghost-btn {
|
|
620
|
-
padding: 7px 14px; font-size: 12.5px; color: var(--ink-soft);
|
|
621
|
-
background: transparent; border: 1px solid var(--glass-line); border-radius: 8px; cursor: pointer;
|
|
622
|
-
}
|
|
623
635
|
</style>
|
package/i18n/locales/en.ts
CHANGED
|
@@ -748,6 +748,7 @@ export default {
|
|
|
748
748
|
'thDetail': 'Detail',
|
|
749
749
|
'expand': 'Detail',
|
|
750
750
|
'fold': 'Fold',
|
|
751
|
+
'noDetail': 'None',
|
|
751
752
|
'loading': 'Querying…',
|
|
752
753
|
'empty': 'No matching log entries',
|
|
753
754
|
'src': {
|
|
@@ -766,6 +767,8 @@ export default {
|
|
|
766
767
|
},
|
|
767
768
|
'mSummary': 'Entry content',
|
|
768
769
|
'mSummaryPh': 'e.g. Replaced heater coil of oven #3; extended soaking for current batch by 10 min',
|
|
770
|
+
'mDetail': 'Detail (optional)',
|
|
771
|
+
'mDetailPh': 'Context, handling notes, related ticket… (stored in the log detail)',
|
|
769
772
|
'mSubmit': 'Record',
|
|
770
773
|
'mOk': 'Recorded',
|
|
771
774
|
},
|
package/i18n/locales/zh-CN.ts
CHANGED
|
@@ -705,6 +705,8 @@ export default {
|
|
|
705
705
|
'kind.system': '系统',
|
|
706
706
|
'mSummary': '记录内容',
|
|
707
707
|
'mSummaryPh': '如:更换 3 号烘箱加热丝,当前批次延长保温 10 分钟',
|
|
708
|
+
'mDetail': '详情(可选)',
|
|
709
|
+
'mDetailPh': '补充说明、处置过程、关联单号等(将存入日志详情)',
|
|
708
710
|
'mSubmit': '入册',
|
|
709
711
|
'mOk': '已入册',
|
|
710
712
|
'src.user': '用户',
|
|
@@ -748,6 +750,7 @@ export default {
|
|
|
748
750
|
'thDetail': '详情',
|
|
749
751
|
'expand': '详情',
|
|
750
752
|
'fold': '收起',
|
|
753
|
+
'noDetail': '无',
|
|
751
754
|
'loading': '查询中…',
|
|
752
755
|
'empty': '无匹配日志',
|
|
753
756
|
'src': {
|
|
@@ -766,6 +769,8 @@ export default {
|
|
|
766
769
|
},
|
|
767
770
|
'mSummary': '记录内容',
|
|
768
771
|
'mSummaryPh': '如:更换 3 号烘箱加热丝,当前批次延长保温 10 分钟',
|
|
772
|
+
'mDetail': '详情(可选)',
|
|
773
|
+
'mDetailPh': '补充说明、处置过程、关联单号等(将存入日志详情)',
|
|
769
774
|
'mSubmit': '入册',
|
|
770
775
|
'mOk': '已入册',
|
|
771
776
|
},
|