@xtalpi/agentic-lab-skills 0.0.10 → 0.0.11

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.
Files changed (68) hide show
  1. package/README.md +1 -1
  2. package/package.json +14 -14
  3. package/skills/lab-flow-designer/SKILL.md +612 -612
  4. package/skills/lab-flow-designer/embedded-template/SKILL.md +103 -103
  5. package/skills/lab-flow-designer/embedded-template/pools//345/205/245/345/217/243/346/261/240.md +21 -21
  6. package/skills/lab-flow-designer/embedded-template/pools//345/207/272/345/217/243/346/261/240.md +21 -21
  7. package/skills/lab-flow-designer/embedded-template/scripts//347/244/272/344/276/213/346/225/260/346/215/256/344/270/216/346/240/241/351/252/214/351/227/250/346/216/247.js +142 -142
  8. package/skills/lab-flow-designer/embedded-template/valves//347/244/272/344/276/213/346/225/260/346/215/256/344/270/216/346/240/241/351/252/214/351/227/250/346/216/247.md +114 -114
  9. package/skills/lab-flow-designer/references/agentic-lab-processer.md +122 -122
  10. package/skills/lab-flow-designer/references/agentic-lab-sdk.md +534 -361
  11. package/skills/lab-flow-designer/references/rhea-api/README.md +7 -7
  12. package/skills/lab-flow-designer/references/rhea-api/execute_process_batch.md +58 -58
  13. package/skills/lab-flow-designer/references/skill-package-layout.md +268 -268
  14. package/skills/lab-flow-designer/references//344/270/232/345/212/241/346/265/201/347/250/213/346/226/207/346/241/243/346/240/207/345/207/206.md +216 -216
  15. package/skills/lab-flow-designer/templates//344/270/232/345/212/241/346/265/201/347/250/213/346/226/207/346/241/243/346/250/241/346/235/277.md +192 -192
  16. package/skills/lab-flow-designer/templates//344/270/232/345/212/241/346/265/201/347/250/213/346/226/207/346/241/243/347/244/272/344/276/213.md +207 -207
  17. package/skills/lab-flow-designer/testing/test-processer.mjs +1240 -1240
  18. package/skills/lab-nocobase-flow-generator/SKILL.md +164 -164
  19. package/skills/lab-nocobase-flow-generator/examples/setting/350/241/250/350/216/267/345/217/226/345/244/226/351/203/250/346/234/215/345/212/241.js +70 -70
  20. package/skills/lab-nocobase-flow-generator/examples//346/237/245/350/257/242/345/214/226/345/255/246/345/223/201/344/277/241/346/201/257.js +30 -30
  21. package/skills/lab-nocobase-flow-generator/references/doc-standard.md +84 -84
  22. package/skills/lab-nocobase-flow-generator/references/runtime-api.md +224 -224
  23. package/skills/lab-nocobase-flow-generator/templates//350/204/232/346/234/254/351/200/273/350/276/221/346/226/207/346/241/243/346/250/241/346/235/277.md +121 -121
  24. package/skills/lab-nocobase-flow-generator/templates//350/204/232/346/234/254/351/200/273/350/276/221/346/226/207/346/241/243/347/244/272/344/276/213.md +67 -67
  25. package/skills/lab-orbit-component-builder/SKILL.md +353 -353
  26. package/skills/lab-orbit-component-builder/examples/xnb-component-template/.env.local.example +27 -27
  27. package/skills/lab-orbit-component-builder/examples/xnb-component-template/.eslintignore +7 -7
  28. package/skills/lab-orbit-component-builder/examples/xnb-component-template/.eslintrc.cjs +88 -88
  29. package/skills/lab-orbit-component-builder/examples/xnb-component-template/.nvmrc +1 -1
  30. package/skills/lab-orbit-component-builder/examples/xnb-component-template/AgenticAppAPI.md +268 -268
  31. package/skills/lab-orbit-component-builder/examples/xnb-component-template/Jenkinsfile +106 -106
  32. package/skills/lab-orbit-component-builder/examples/xnb-component-template/OrbitAPI.md +453 -453
  33. package/skills/lab-orbit-component-builder/examples/xnb-component-template/README.md +176 -176
  34. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/public/index.html +12 -12
  35. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/App.vue +151 -151
  36. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/components/DevOpenerLauncher.vue +143 -143
  37. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/global.d.ts +77 -77
  38. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/main.ts +308 -308
  39. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/mockXNBBitable.ts +119 -119
  40. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/shims-vue.d.ts +6 -6
  41. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/utils/devOpenerHost.ts +75 -75
  42. package/skills/lab-orbit-component-builder/examples/xnb-component-template/index.html +13 -13
  43. package/skills/lab-orbit-component-builder/examples/xnb-component-template/package.json +60 -60
  44. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/api/agenticlabTickets.ts +110 -110
  45. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/entries/bitable.ts +4 -4
  46. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/entries/custom-page.ts +4 -4
  47. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/index.ts +1 -1
  48. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/styles/orbit-quasar-host.scss +19 -19
  49. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/types/context.ts +15 -15
  50. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/types/xnb-context.ts +70 -70
  51. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useBitablePage.ts +189 -189
  52. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useSuperCellDemo.ts +257 -257
  53. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useSuperTableBitableLifecycle.ts +555 -555
  54. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/openerInitParams.ts +158 -158
  55. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/openerTicketIds.ts +32 -32
  56. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/orbitHttpClient.ts +110 -110
  57. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/request.ts +92 -92
  58. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/views/bitable.vue +67 -67
  59. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/views/custom-page.vue +140 -140
  60. package/skills/lab-orbit-component-builder/examples/xnb-component-template/tsconfig.json +45 -45
  61. package/skills/lab-orbit-component-builder/examples/xnb-component-template/vite.config.ts +170 -170
  62. package/skills/lab-orbit-component-builder/examples/xnb-component-template/vite.dev.config.ts +58 -58
  63. package/skills/lab-orbit-component-builder/references/flow-document-human-ui.md +65 -65
  64. package/skills/lab-orbit-component-builder/references/orbit-vue-conventions.md +133 -133
  65. package/skills/lab-orbit-component-builder/references/pool-schema-to-columns.md +67 -67
  66. package/skills/lab-orbit-component-builder/references/vue-template-checklist.md +179 -179
  67. package/skills/lab-orbit-component-builder/references/xnb-context-vue-props.md +49 -49
  68. package/skills/lab-orbit-component-builder/references/xnbitable-vue-parity.md +32 -32
@@ -1,151 +1,151 @@
1
- <template>
2
- <div class="dev-shell" :class="{ 'dev-shell--child': isChildPage }">
3
- <DevOpenerLauncher v-if="!isChildPage" class="dev-shell__launcher" />
4
- <q-banner v-else rounded dense class="dev-child-hint q-ma-sm">
5
- Opener 子页联调 · trace: <code>{{ childTrace }}</code> — 初始化结果见下方组件(成功则出现刷新/提交按钮)
6
- </q-banner>
7
- <div class="entry">
8
- <View ref="sheetRef" :xnbContext="xnbContext" />
9
- </div>
10
- </div>
11
- </template>
12
-
13
- <script lang="ts">
14
- import { computed, defineComponent, reactive, ref } from 'vue'
15
- import { Notify, Loading } from 'quasar'
16
- import { DevXNBBitable } from './mockXNBBitable'
17
- import DevOpenerLauncher from './components/DevOpenerLauncher.vue'
18
- import { isDevOpenerChildPage } from './utils/devOpenerHost'
19
-
20
- export default defineComponent({
21
- components: { DevOpenerLauncher },
22
- setup () {
23
- const isChildPage = isDevOpenerChildPage()
24
- const childTrace = computed(() => new URLSearchParams(window.location.search).get('trace') || '—')
25
- const cellParams = reactive({
26
- bookPath: '/dev/notebook/doe',
27
- uid: 'dev-cell-001'
28
- })
29
- const viewRef = ref()
30
-
31
- const DEV_REQUEST_URL = import.meta.env.VITE_REQUEST_URL || ''
32
-
33
- const userData: Record<string, any> = {
34
- projectURL: JSON.stringify({ request_url: DEV_REQUEST_URL })
35
- }
36
-
37
- const xnbContext = {
38
- bookPath: cellParams.bookPath,
39
- cellUid: cellParams.uid,
40
- XNBUtil: {
41
- async getWikiTables (_url: string): Promise<any> {
42
- return { bitable: 'dev-bitable', tables: [] }
43
- },
44
- async getLarkBitableById (_bitable: string, _tableId: string, _opts?: any): Promise<any[]> {
45
- return []
46
- },
47
- async batchCreateRecords (_opts: any): Promise<any> {
48
- return { records: [] }
49
- }
50
- },
51
- notifySuccess (msg: string): void {
52
- Notify.create({ type: 'positive', message: String(msg), position: 'top', timeout: 3000 })
53
- },
54
- notifyError (msg: any): void {
55
- const text = msg instanceof Error ? msg.message : String(msg)
56
- Notify.create({ type: 'negative', message: text, position: 'top', timeout: 5000 })
57
- },
58
- loadingShow (opts?: any): void {
59
- Loading.show(typeof opts === 'string' ? { message: opts } : opts)
60
- },
61
- loadingHide (): void {
62
- Loading.hide()
63
- },
64
- userData,
65
- getUserData (): Record<string, any> {
66
- return userData
67
- },
68
- updateUserData (data: any): void {
69
- Object.assign(userData, data)
70
- },
71
- mergeUserData (data: any): void {
72
- Object.assign(userData, data)
73
- },
74
- getInstanceById (_id: string): any { return null },
75
- getInstanceValueById (_id: string): any { return null },
76
- setInstanceValueById (_id: string, _value: any): void { /* noop */ },
77
- async saveBitableConfig (_bitable: Record<string, any>): Promise<void> {
78
- localStorage.setItem('DEV_BITABLE_CONFIG', JSON.stringify(_bitable))
79
- },
80
- async getBitableConfig (): Promise<string> {
81
- return localStorage.getItem('DEV_BITABLE_CONFIG') || '{}'
82
- },
83
- XNBBitable: DevXNBBitable
84
- }
85
-
86
- window.xnb.store.set('params', cellParams)
87
-
88
- return {
89
- isChildPage,
90
- childTrace,
91
- cellParams,
92
- viewRef,
93
- xnbContext
94
- }
95
- }
96
- })
97
- </script>
98
-
99
- <style lang="scss">
100
- html,
101
- body,
102
- #app {
103
- margin: 0;
104
- padding: 0;
105
- height: 100%;
106
- width: 100%;
107
- overflow: hidden;
108
- box-sizing: border-box;
109
- }
110
-
111
- #app {
112
- font-family: Arial, Helvetica, sans-serif;
113
- -webkit-font-smoothing: antialiased;
114
- -moz-osx-font-smoothing: grayscale;
115
- }
116
-
117
- .dev-shell {
118
- display: flex;
119
- flex-direction: column;
120
- height: 100%;
121
- width: 100%;
122
- box-sizing: border-box;
123
- &--child .entry {
124
- flex: 1;
125
- min-height: 0;
126
- }
127
- &__launcher {
128
- flex-shrink: 0;
129
- margin: 8px 8px 0;
130
- }
131
- }
132
-
133
- .dev-child-hint {
134
- flex-shrink: 0;
135
- background: #fffbeb;
136
- border: 1px solid #f0e4b8;
137
- code {
138
- font-size: 0.75rem;
139
- }
140
- }
141
-
142
- .entry {
143
- font-family: Arial, Helvetica, sans-serif;
144
- box-sizing: border-box;
145
- flex: 1;
146
- min-height: 0;
147
- width: 100%;
148
- margin: 0;
149
- overflow-y: auto;
150
- }
151
- </style>
1
+ <template>
2
+ <div class="dev-shell" :class="{ 'dev-shell--child': isChildPage }">
3
+ <DevOpenerLauncher v-if="!isChildPage" class="dev-shell__launcher" />
4
+ <q-banner v-else rounded dense class="dev-child-hint q-ma-sm">
5
+ Opener 子页联调 · trace: <code>{{ childTrace }}</code> — 初始化结果见下方组件(成功则出现刷新/提交按钮)
6
+ </q-banner>
7
+ <div class="entry">
8
+ <View ref="sheetRef" :xnbContext="xnbContext" />
9
+ </div>
10
+ </div>
11
+ </template>
12
+
13
+ <script lang="ts">
14
+ import { computed, defineComponent, reactive, ref } from 'vue'
15
+ import { Notify, Loading } from 'quasar'
16
+ import { DevXNBBitable } from './mockXNBBitable'
17
+ import DevOpenerLauncher from './components/DevOpenerLauncher.vue'
18
+ import { isDevOpenerChildPage } from './utils/devOpenerHost'
19
+
20
+ export default defineComponent({
21
+ components: { DevOpenerLauncher },
22
+ setup () {
23
+ const isChildPage = isDevOpenerChildPage()
24
+ const childTrace = computed(() => new URLSearchParams(window.location.search).get('trace') || '—')
25
+ const cellParams = reactive({
26
+ bookPath: '/dev/notebook/doe',
27
+ uid: 'dev-cell-001'
28
+ })
29
+ const viewRef = ref()
30
+
31
+ const DEV_REQUEST_URL = import.meta.env.VITE_REQUEST_URL || ''
32
+
33
+ const userData: Record<string, any> = {
34
+ projectURL: JSON.stringify({ request_url: DEV_REQUEST_URL })
35
+ }
36
+
37
+ const xnbContext = {
38
+ bookPath: cellParams.bookPath,
39
+ cellUid: cellParams.uid,
40
+ XNBUtil: {
41
+ async getWikiTables (_url: string): Promise<any> {
42
+ return { bitable: 'dev-bitable', tables: [] }
43
+ },
44
+ async getLarkBitableById (_bitable: string, _tableId: string, _opts?: any): Promise<any[]> {
45
+ return []
46
+ },
47
+ async batchCreateRecords (_opts: any): Promise<any> {
48
+ return { records: [] }
49
+ }
50
+ },
51
+ notifySuccess (msg: string): void {
52
+ Notify.create({ type: 'positive', message: String(msg), position: 'top', timeout: 3000 })
53
+ },
54
+ notifyError (msg: any): void {
55
+ const text = msg instanceof Error ? msg.message : String(msg)
56
+ Notify.create({ type: 'negative', message: text, position: 'top', timeout: 5000 })
57
+ },
58
+ loadingShow (opts?: any): void {
59
+ Loading.show(typeof opts === 'string' ? { message: opts } : opts)
60
+ },
61
+ loadingHide (): void {
62
+ Loading.hide()
63
+ },
64
+ userData,
65
+ getUserData (): Record<string, any> {
66
+ return userData
67
+ },
68
+ updateUserData (data: any): void {
69
+ Object.assign(userData, data)
70
+ },
71
+ mergeUserData (data: any): void {
72
+ Object.assign(userData, data)
73
+ },
74
+ getInstanceById (_id: string): any { return null },
75
+ getInstanceValueById (_id: string): any { return null },
76
+ setInstanceValueById (_id: string, _value: any): void { /* noop */ },
77
+ async saveBitableConfig (_bitable: Record<string, any>): Promise<void> {
78
+ localStorage.setItem('DEV_BITABLE_CONFIG', JSON.stringify(_bitable))
79
+ },
80
+ async getBitableConfig (): Promise<string> {
81
+ return localStorage.getItem('DEV_BITABLE_CONFIG') || '{}'
82
+ },
83
+ XNBBitable: DevXNBBitable
84
+ }
85
+
86
+ window.xnb.store.set('params', cellParams)
87
+
88
+ return {
89
+ isChildPage,
90
+ childTrace,
91
+ cellParams,
92
+ viewRef,
93
+ xnbContext
94
+ }
95
+ }
96
+ })
97
+ </script>
98
+
99
+ <style lang="scss">
100
+ html,
101
+ body,
102
+ #app {
103
+ margin: 0;
104
+ padding: 0;
105
+ height: 100%;
106
+ width: 100%;
107
+ overflow: hidden;
108
+ box-sizing: border-box;
109
+ }
110
+
111
+ #app {
112
+ font-family: Arial, Helvetica, sans-serif;
113
+ -webkit-font-smoothing: antialiased;
114
+ -moz-osx-font-smoothing: grayscale;
115
+ }
116
+
117
+ .dev-shell {
118
+ display: flex;
119
+ flex-direction: column;
120
+ height: 100%;
121
+ width: 100%;
122
+ box-sizing: border-box;
123
+ &--child .entry {
124
+ flex: 1;
125
+ min-height: 0;
126
+ }
127
+ &__launcher {
128
+ flex-shrink: 0;
129
+ margin: 8px 8px 0;
130
+ }
131
+ }
132
+
133
+ .dev-child-hint {
134
+ flex-shrink: 0;
135
+ background: #fffbeb;
136
+ border: 1px solid #f0e4b8;
137
+ code {
138
+ font-size: 0.75rem;
139
+ }
140
+ }
141
+
142
+ .entry {
143
+ font-family: Arial, Helvetica, sans-serif;
144
+ box-sizing: border-box;
145
+ flex: 1;
146
+ min-height: 0;
147
+ width: 100%;
148
+ margin: 0;
149
+ overflow-y: auto;
150
+ }
151
+ </style>
@@ -1,143 +1,143 @@
1
- <template>
2
- <q-banner rounded dense class="dev-opener-launcher q-mb-sm">
3
- <div class="text-subtitle2 q-mb-xs">Opener 联调(模拟宿主打开子页)</div>
4
- <p class="text-caption text-grey-8 q-mb-sm">
5
- 点击下方按钮在新窗口打开子页;子页通过 <code>?trace=</code> 与 <code>postMessage</code> 拉取下方 JSON。
6
- 组件:<strong>{{ componentId }}</strong>
7
- </p>
8
- <q-input
9
- v-model="payloadText"
10
- type="textarea"
11
- autogrow
12
- dense
13
- outlined
14
- label="模拟 opener payload(JSON)"
15
- :error="parseError != null"
16
- :error-message="parseError || undefined"
17
- class="q-mb-sm"
18
- />
19
- <div class="row items-center q-gutter-sm flex-wrap">
20
- <q-btn
21
- color="primary"
22
- dense
23
- unelevated
24
- label="以 opener 方式打开子页"
25
- :disable="parseError != null"
26
- @click="onOpenChild"
27
- />
28
- <span v-if="lastTrace" class="text-caption text-grey-7">
29
- 最近 trace: <code>{{ lastTrace }}</code>
30
- <span v-if="childBlocked" class="text-negative">(弹窗被拦截,请允许本站点弹窗)</span>
31
- </span>
32
- </div>
33
- </q-banner>
34
- </template>
35
-
36
- <script lang="ts">
37
- import { defineComponent, onMounted, onUnmounted, ref, watch } from 'vue'
38
- import { parseDevOpenerInitFromEnv } from '@/utils/openerInitParams'
39
- import {
40
- createDevOpenerTrace,
41
- installDevOpenerHost,
42
- openDevOpenerChild
43
- } from '../utils/devOpenerHost'
44
- import type { DevOpenerHostHandle } from '../utils/devOpenerHost'
45
-
46
- const DEFAULT_PAYLOAD = {
47
- ticket_ids: [450, 453],
48
- valve_id: 0
49
- }
50
-
51
- function defaultPayloadText (): string {
52
- const fromEnv = parseDevOpenerInitFromEnv()
53
- if (fromEnv != null) {
54
- try {
55
- return JSON.stringify(fromEnv, null, 2)
56
- } catch {
57
- /* fall through */
58
- }
59
- }
60
- return JSON.stringify(DEFAULT_PAYLOAD, null, 2)
61
- }
62
-
63
- function parsePayloadJson (text: string): { value: unknown | null; error: string | null } {
64
- const trimmed = text.trim()
65
- if (trimmed === '') return { value: null, error: 'JSON 不能为空' }
66
- try {
67
- return { value: JSON.parse(trimmed) as unknown, error: null }
68
- } catch {
69
- return { value: null, error: '不是合法 JSON' }
70
- }
71
- }
72
-
73
- export default defineComponent({
74
- name: 'DevOpenerLauncher',
75
- setup () {
76
- const componentId = import.meta.env.VITE_COMPONENT_ID || 'bitable'
77
- const payloadText = ref(defaultPayloadText())
78
- const parseError = ref<string | null>(null)
79
- const lastTrace = ref<string | null>(null)
80
- const childBlocked = ref(false)
81
-
82
- let hostHandle: DevOpenerHostHandle | null = null
83
-
84
- function syncParseError (): void {
85
- parseError.value = parsePayloadJson(payloadText.value).error
86
- }
87
-
88
- function getPayload (): unknown {
89
- const { value, error } = parsePayloadJson(payloadText.value)
90
- if (error != null) throw new Error(error)
91
- return value
92
- }
93
-
94
- function onOpenChild (): void {
95
- syncParseError()
96
- if (parseError.value != null) return
97
-
98
- const trace = createDevOpenerTrace()
99
- lastTrace.value = trace
100
- childBlocked.value = false
101
-
102
- const child = openDevOpenerChild(trace)
103
- if (child == null) {
104
- childBlocked.value = true
105
- console.warn('[devOpenerHost] window.open 返回 null,可能被浏览器拦截')
106
- }
107
- }
108
-
109
- watch(payloadText, () => {
110
- syncParseError()
111
- })
112
-
113
- onMounted(() => {
114
- syncParseError()
115
- hostHandle = installDevOpenerHost(() => getPayload())
116
- })
117
-
118
- onUnmounted(() => {
119
- hostHandle?.dispose()
120
- hostHandle = null
121
- })
122
-
123
- return {
124
- componentId,
125
- payloadText,
126
- parseError,
127
- lastTrace,
128
- childBlocked,
129
- onOpenChild
130
- }
131
- }
132
- })
133
- </script>
134
-
135
- <style scoped lang="scss">
136
- .dev-opener-launcher {
137
- background: #f0f7ff;
138
- border: 1px solid #c5d9f0;
139
- code {
140
- font-size: 0.75rem;
141
- }
142
- }
143
- </style>
1
+ <template>
2
+ <q-banner rounded dense class="dev-opener-launcher q-mb-sm">
3
+ <div class="text-subtitle2 q-mb-xs">Opener 联调(模拟宿主打开子页)</div>
4
+ <p class="text-caption text-grey-8 q-mb-sm">
5
+ 点击下方按钮在新窗口打开子页;子页通过 <code>?trace=</code> 与 <code>postMessage</code> 拉取下方 JSON。
6
+ 组件:<strong>{{ componentId }}</strong>
7
+ </p>
8
+ <q-input
9
+ v-model="payloadText"
10
+ type="textarea"
11
+ autogrow
12
+ dense
13
+ outlined
14
+ label="模拟 opener payload(JSON)"
15
+ :error="parseError != null"
16
+ :error-message="parseError || undefined"
17
+ class="q-mb-sm"
18
+ />
19
+ <div class="row items-center q-gutter-sm flex-wrap">
20
+ <q-btn
21
+ color="primary"
22
+ dense
23
+ unelevated
24
+ label="以 opener 方式打开子页"
25
+ :disable="parseError != null"
26
+ @click="onOpenChild"
27
+ />
28
+ <span v-if="lastTrace" class="text-caption text-grey-7">
29
+ 最近 trace: <code>{{ lastTrace }}</code>
30
+ <span v-if="childBlocked" class="text-negative">(弹窗被拦截,请允许本站点弹窗)</span>
31
+ </span>
32
+ </div>
33
+ </q-banner>
34
+ </template>
35
+
36
+ <script lang="ts">
37
+ import { defineComponent, onMounted, onUnmounted, ref, watch } from 'vue'
38
+ import { parseDevOpenerInitFromEnv } from '@/utils/openerInitParams'
39
+ import {
40
+ createDevOpenerTrace,
41
+ installDevOpenerHost,
42
+ openDevOpenerChild
43
+ } from '../utils/devOpenerHost'
44
+ import type { DevOpenerHostHandle } from '../utils/devOpenerHost'
45
+
46
+ const DEFAULT_PAYLOAD = {
47
+ ticket_ids: [450, 453],
48
+ valve_id: 0
49
+ }
50
+
51
+ function defaultPayloadText (): string {
52
+ const fromEnv = parseDevOpenerInitFromEnv()
53
+ if (fromEnv != null) {
54
+ try {
55
+ return JSON.stringify(fromEnv, null, 2)
56
+ } catch {
57
+ /* fall through */
58
+ }
59
+ }
60
+ return JSON.stringify(DEFAULT_PAYLOAD, null, 2)
61
+ }
62
+
63
+ function parsePayloadJson (text: string): { value: unknown | null; error: string | null } {
64
+ const trimmed = text.trim()
65
+ if (trimmed === '') return { value: null, error: 'JSON 不能为空' }
66
+ try {
67
+ return { value: JSON.parse(trimmed) as unknown, error: null }
68
+ } catch {
69
+ return { value: null, error: '不是合法 JSON' }
70
+ }
71
+ }
72
+
73
+ export default defineComponent({
74
+ name: 'DevOpenerLauncher',
75
+ setup () {
76
+ const componentId = import.meta.env.VITE_COMPONENT_ID || 'bitable'
77
+ const payloadText = ref(defaultPayloadText())
78
+ const parseError = ref<string | null>(null)
79
+ const lastTrace = ref<string | null>(null)
80
+ const childBlocked = ref(false)
81
+
82
+ let hostHandle: DevOpenerHostHandle | null = null
83
+
84
+ function syncParseError (): void {
85
+ parseError.value = parsePayloadJson(payloadText.value).error
86
+ }
87
+
88
+ function getPayload (): unknown {
89
+ const { value, error } = parsePayloadJson(payloadText.value)
90
+ if (error != null) throw new Error(error)
91
+ return value
92
+ }
93
+
94
+ function onOpenChild (): void {
95
+ syncParseError()
96
+ if (parseError.value != null) return
97
+
98
+ const trace = createDevOpenerTrace()
99
+ lastTrace.value = trace
100
+ childBlocked.value = false
101
+
102
+ const child = openDevOpenerChild(trace)
103
+ if (child == null) {
104
+ childBlocked.value = true
105
+ console.warn('[devOpenerHost] window.open 返回 null,可能被浏览器拦截')
106
+ }
107
+ }
108
+
109
+ watch(payloadText, () => {
110
+ syncParseError()
111
+ })
112
+
113
+ onMounted(() => {
114
+ syncParseError()
115
+ hostHandle = installDevOpenerHost(() => getPayload())
116
+ })
117
+
118
+ onUnmounted(() => {
119
+ hostHandle?.dispose()
120
+ hostHandle = null
121
+ })
122
+
123
+ return {
124
+ componentId,
125
+ payloadText,
126
+ parseError,
127
+ lastTrace,
128
+ childBlocked,
129
+ onOpenChild
130
+ }
131
+ }
132
+ })
133
+ </script>
134
+
135
+ <style scoped lang="scss">
136
+ .dev-opener-launcher {
137
+ background: #f0f7ff;
138
+ border: 1px solid #c5d9f0;
139
+ code {
140
+ font-size: 0.75rem;
141
+ }
142
+ }
143
+ </style>