@xtalpi/agentic-lab-skills 0.0.1

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 (57) hide show
  1. package/README.md +1 -0
  2. package/package.json +14 -0
  3. package/skills/lab-flow-designer/SKILL.md +360 -0
  4. package/skills/lab-flow-designer/embedded-template/SKILL.md +84 -0
  5. package/skills/lab-flow-designer/embedded-template/pools//345/205/245/345/217/243/346/261/240.md +12 -0
  6. package/skills/lab-flow-designer/embedded-template/pools//345/207/272/345/217/243/346/261/240.md +12 -0
  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 +149 -0
  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 +100 -0
  9. package/skills/lab-flow-designer/references/agentic-lab-processer.md +101 -0
  10. package/skills/lab-flow-designer/references/agentic-lab-sdk.md +440 -0
  11. package/skills/lab-flow-designer/references/rhea-api/README.md +7 -0
  12. package/skills/lab-flow-designer/references/rhea-api/execute_process_batch.md +58 -0
  13. package/skills/lab-flow-designer/references/skill-package-layout.md +199 -0
  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 +208 -0
  15. package/skills/lab-orbit-component-builder/SKILL.md +305 -0
  16. package/skills/lab-orbit-component-builder/examples/xnb-component-template/.env.local.example +27 -0
  17. package/skills/lab-orbit-component-builder/examples/xnb-component-template/.eslintignore +7 -0
  18. package/skills/lab-orbit-component-builder/examples/xnb-component-template/.eslintrc.cjs +88 -0
  19. package/skills/lab-orbit-component-builder/examples/xnb-component-template/.nvmrc +1 -0
  20. package/skills/lab-orbit-component-builder/examples/xnb-component-template/AgenticAppAPI.md +268 -0
  21. package/skills/lab-orbit-component-builder/examples/xnb-component-template/Jenkinsfile +106 -0
  22. package/skills/lab-orbit-component-builder/examples/xnb-component-template/OrbitAPI.md +453 -0
  23. package/skills/lab-orbit-component-builder/examples/xnb-component-template/README.md +176 -0
  24. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/public/index.html +12 -0
  25. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/App.vue +151 -0
  26. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/components/DevOpenerLauncher.vue +143 -0
  27. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/global.d.ts +74 -0
  28. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/main.ts +305 -0
  29. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/mockXNBBitable.ts +119 -0
  30. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/shims-vue.d.ts +6 -0
  31. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/utils/devOpenerHost.ts +75 -0
  32. package/skills/lab-orbit-component-builder/examples/xnb-component-template/index.html +13 -0
  33. package/skills/lab-orbit-component-builder/examples/xnb-component-template/package.json +57 -0
  34. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/api/agenticlabTickets.ts +110 -0
  35. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/entries/bitable.ts +4 -0
  36. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/entries/custom-page.ts +4 -0
  37. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/index.ts +1 -0
  38. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/styles/orbit-quasar-host.scss +19 -0
  39. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/types/context.ts +15 -0
  40. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/types/xnb-context.ts +70 -0
  41. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useBitablePage.ts +180 -0
  42. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useSuperCellDemo.ts +257 -0
  43. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useSuperTableBitableLifecycle.ts +547 -0
  44. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/openerInitParams.ts +158 -0
  45. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/openerTicketIds.ts +32 -0
  46. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/orbitHttpClient.ts +110 -0
  47. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/request.ts +92 -0
  48. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/views/bitable.vue +65 -0
  49. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/views/custom-page.vue +140 -0
  50. package/skills/lab-orbit-component-builder/examples/xnb-component-template/tsconfig.json +48 -0
  51. package/skills/lab-orbit-component-builder/examples/xnb-component-template/vite.config.ts +165 -0
  52. package/skills/lab-orbit-component-builder/examples/xnb-component-template/vite.dev.config.ts +51 -0
  53. package/skills/lab-orbit-component-builder/references/flow-document-human-ui.md +65 -0
  54. package/skills/lab-orbit-component-builder/references/pool-schema-to-columns.md +67 -0
  55. package/skills/lab-orbit-component-builder/references/vue-template-checklist.md +113 -0
  56. package/skills/lab-orbit-component-builder/references/xnb-context-vue-props.md +49 -0
  57. package/skills/lab-orbit-component-builder/references/xnbitable-vue-parity.md +32 -0
@@ -0,0 +1,119 @@
1
+ import type { DemoBitableSheet, XNBBitableChoreo } from '../../src/types/xnb-context'
2
+
3
+ /** 与 `useSuperTableBitableLifecycle` 中 listSub.index 对齐,便于 dev 命中子表 */
4
+ const DEV_DEFAULT_INDEX = 'sheet-demo'
5
+ const DEV_DEFAULT_NAME = '演示'
6
+
7
+ type BitableItem = { id: string; fields: Record<string, string>; visible?: boolean }
8
+
9
+ type MainBitable = {
10
+ index: string
11
+ name: string
12
+ row: number
13
+ items: BitableItem[]
14
+ data_columns: Array<Record<string, unknown>>
15
+ config: Record<string, unknown>
16
+ xnb_show_sheetbar?: boolean
17
+ xnb_hide_statisticbar?: boolean
18
+ xnb_hide_toolbar?: boolean
19
+ xnb_hide_formulaBar?: boolean
20
+ xnb_excel_with_bg?: boolean
21
+ xnb_height?: string
22
+ }
23
+
24
+ /**
25
+ * Dev 专用:模拟宿主注入的 XNBBitable,便于 `bitable` 视图演练
26
+ * `new XNBBitable({ bookPath, cellUid })` 与 `getSubCell` / `getAllSubBitable` / `addColumns` 等调用链。
27
+ * 真机行为以 Orbit / 业务实现为准。
28
+ */
29
+ export class DevXNBBitable implements XNBBitableChoreo {
30
+ bookPath: string
31
+ cellUid: string
32
+
33
+ private readonly main: MainBitable
34
+
35
+ constructor (opts: { bookPath: string; cellUid: string }) {
36
+ this.bookPath = opts.bookPath
37
+ this.cellUid = opts.cellUid
38
+ this.main = {
39
+ index: DEV_DEFAULT_INDEX,
40
+ name: DEV_DEFAULT_NAME,
41
+ row: 200,
42
+ items: [],
43
+ data_columns: [],
44
+ config: {}
45
+ }
46
+ }
47
+
48
+ async getSubCell (): Promise<string | null> {
49
+ return `mock-subcell::${this.cellUid}`
50
+ }
51
+
52
+ async getAllSubBitable (refresh?: boolean): Promise<DemoBitableSheet[]> {
53
+ if (refresh === true) {
54
+ /* dev mock 不区分 refresh;与真机签名对齐 */
55
+ }
56
+ return [this.main]
57
+ }
58
+
59
+ async getSubBitable (refresh?: boolean): Promise<DemoBitableSheet> {
60
+ if (refresh === true) {
61
+ /* dev mock 不区分 refresh;与真机签名对齐 */
62
+ }
63
+ return this.main
64
+ }
65
+
66
+ async addColumns (bitable: Record<string, unknown>, cols: unknown[], extra: unknown[]): Promise<void> {
67
+ if (Array.isArray(extra) && extra.length > 0) {
68
+ /* 第三参占位;真机可能传列元数据 */
69
+ }
70
+ const list = Array.isArray(cols) ? cols : []
71
+ const prev = Array.isArray(bitable.data_columns)
72
+ ? (bitable.data_columns as Array<Record<string, unknown>>)
73
+ : []
74
+ const offset = prev.length
75
+ bitable.data_columns = [
76
+ ...prev,
77
+ ...list.map((col: unknown, i: number) => ({
78
+ ...(typeof col === 'object' && col !== null ? (col as Record<string, unknown>) : {}),
79
+ c: offset + i
80
+ }))
81
+ ]
82
+ }
83
+
84
+ async createSubBitable (bitables: unknown[], sheetName: string): Promise<string> {
85
+ console.log('[DevXNBBitable] createSubBitable', sheetName, bitables)
86
+ const first = bitables[0] as Partial<MainBitable> | undefined
87
+ if (first != null) {
88
+ if (typeof first.index === 'string') this.main.index = first.index
89
+ if (typeof first.name === 'string') this.main.name = first.name
90
+ if (typeof first.row === 'number') this.main.row = first.row
91
+ if (Array.isArray(first.items)) this.main.items = [...first.items] as BitableItem[]
92
+ if (Array.isArray(first.data_columns)) {
93
+ this.main.data_columns = [...first.data_columns] as MainBitable['data_columns']
94
+ }
95
+ if (first.config != null && typeof first.config === 'object' && !Array.isArray(first.config)) {
96
+ this.main.config = { ...(first.config as Record<string, unknown>) }
97
+ }
98
+ }
99
+ return `mock-created-${sheetName}`
100
+ }
101
+
102
+ async updateSubBitable (bitables: unknown[], _opts?: Record<string, unknown>): Promise<void> {
103
+ const first = bitables[0] as Partial<MainBitable> | undefined
104
+ if (first == null) return
105
+ if (first !== this.main) {
106
+ if (Array.isArray(first.items)) this.main.items = [...first.items] as BitableItem[]
107
+ if (Array.isArray(first.data_columns)) {
108
+ this.main.data_columns = [...first.data_columns]
109
+ }
110
+ }
111
+ console.log('[DevXNBBitable] updateSubBitable', bitables, _opts)
112
+ }
113
+
114
+ async delay (ms: number): Promise<void> {
115
+ await new Promise<void>((resolve: () => void) => {
116
+ setTimeout(resolve, ms)
117
+ })
118
+ }
119
+ }
@@ -0,0 +1,6 @@
1
+ /* eslint-disable */
2
+ declare module '*.vue' {
3
+ import type { DefineComponent } from 'vue'
4
+ const component: DefineComponent<{}, {}, any>
5
+ export default component
6
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Dev 联调:当前页作为 opener 宿主,子页带 `?trace=` 调用 `fetchOpenerInitPayload`。
3
+ * 回包格式与流程页一致:`get-data-response` + 平铺 `ticket_ids` / `valve_id`。
4
+ */
5
+
6
+ import { OPENER_MSG_GET_DATA_RESPONSE } from '@/utils/openerInitParams'
7
+
8
+ export const DEV_OPENER_CHILD_QUERY = 'devOpenerChild'
9
+
10
+ export function isDevOpenerChildPage (): boolean {
11
+ const params = new URLSearchParams(window.location.search)
12
+ return (
13
+ params.get(DEV_OPENER_CHILD_QUERY) === '1'
14
+ && window.opener != null
15
+ && !window.opener.closed
16
+ )
17
+ }
18
+
19
+ export function createDevOpenerTrace (): string {
20
+ return `dev-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
21
+ }
22
+
23
+ export interface DevOpenerHostHandle {
24
+ dispose: () => void
25
+ }
26
+
27
+ function flatFieldsFromPayload (payload: unknown): Record<string, unknown> {
28
+ if (payload == null || typeof payload !== 'object') return {}
29
+ const p = payload as Record<string, unknown>
30
+ const out: Record<string, unknown> = {}
31
+ if ('ticket_ids' in p) out.ticket_ids = p.ticket_ids
32
+ if ('valve_id' in p) out.valve_id = p.valve_id
33
+ return out
34
+ }
35
+
36
+ /**
37
+ * 监听子页 `{ msg: 'get-data', trace }`,回 `{ msg: 'get-data-response', trace, ticket_ids, valve_id }`。
38
+ */
39
+ export function installDevOpenerHost (getPayload: () => unknown): DevOpenerHostHandle {
40
+ const onMessage = (e: MessageEvent): void => {
41
+ if (e.source == null || e.source === window) return
42
+ const d = e.data as Record<string, unknown> | null
43
+ if (d == null || typeof d !== 'object') return
44
+ if (d.msg !== 'get-data') return
45
+ const { trace } = d
46
+ if (typeof trace !== 'string' || trace === '') return
47
+
48
+ const flat = flatFieldsFromPayload(getPayload())
49
+ const child = e.source as Window
50
+ window.setTimeout(() => {
51
+ child.postMessage(
52
+ { msg: OPENER_MSG_GET_DATA_RESPONSE, trace, ...flat },
53
+ '*'
54
+ )
55
+ console.log('[devOpenerHost] 已回包 get-data-response', { trace, ...flat })
56
+ }, 0)
57
+ }
58
+
59
+ window.addEventListener('message', onMessage)
60
+ return {
61
+ dispose: (): void => {
62
+ window.removeEventListener('message', onMessage)
63
+ }
64
+ }
65
+ }
66
+
67
+ /**
68
+ * 打开子页(同源),URL 携带 `trace` 与 `devOpenerChild=1`。
69
+ */
70
+ export function openDevOpenerChild (trace: string): Window | null {
71
+ const url = new URL(window.location.href)
72
+ url.searchParams.set('trace', trace)
73
+ url.searchParams.set(DEV_OPENER_CHILD_QUERY, '1')
74
+ return window.open(url.toString(), `dev-opener-${trace}`)
75
+ }
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html lang="">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
7
+ <title>Xnb Component Dev</title>
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+ <script type="module" src="/dev/src/main.ts"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "xnbOrbitComponent",
3
+ "version": "0.2.0",
4
+ "author": "yansen.lei",
5
+ "main": "./dist/DemoOrbitBitable.umd.js",
6
+ "type": "module",
7
+ "engines": {
8
+ "node": ">=22"
9
+ },
10
+ "scripts": {
11
+ "dev": "vite --config vite.dev.config.ts",
12
+ "dev:bitable": "VITE_COMPONENT_ID=bitable vite --config vite.dev.config.ts",
13
+ "dev:custom-page": "VITE_COMPONENT_ID=custom-page vite --config vite.dev.config.ts",
14
+ "build": "vite build --mode func",
15
+ "build:bitable": "VITE_COMPONENT_ID=bitable vite build --mode func",
16
+ "build:custom-page": "VITE_COMPONENT_ID=custom-page vite build --mode func",
17
+ "build:all": "npm run build:bitable && npm run build:custom-page",
18
+ "build:es": "vite build",
19
+ "lint": "eslint --ext .js,.ts,.tsx,.vue src"
20
+ },
21
+ "dependencies": {
22
+ "@vueuse/core": "^10.2.1"
23
+ },
24
+ "devDependencies": {
25
+ "dayjs": "^1.11.7",
26
+ "@quasar/extras": "^1.14.1",
27
+ "@quasar/vite-plugin": "1.6.0",
28
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
29
+ "@typescript-eslint/parser": "^7.0.0",
30
+ "@vitejs/plugin-vue": "^5.0.0",
31
+ "@vue/compiler-sfc": "3.2.29",
32
+ "@xtalpi/webb-api": "0.1.0",
33
+ "axios": "^1.7.0",
34
+ "eslint": "^8.57.0",
35
+ "eslint-plugin-vue": "^9.28.0",
36
+ "lodash": "^4.17.21",
37
+ "quasar": "2.12.7",
38
+ "rollup-plugin-external-globals": "^0.13.0",
39
+ "sass": "~1.77.6",
40
+ "typescript": "~5.5.0",
41
+ "vite": "^5.4.0",
42
+ "vite-plugin-compression": "^0.5.1",
43
+ "vite-plugin-css-injected-by-js": "^3.5.2",
44
+ "vue": "3.2.29"
45
+ },
46
+ "webb": {
47
+ "name": "DemoOrbitVueLib",
48
+ "type": "element",
49
+ "exports": [
50
+ {
51
+ "name": "View",
52
+ "category": "view"
53
+ }
54
+ ],
55
+ "dependencies": {}
56
+ }
57
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * AgenticLab App:Ticket 列表 HTTP。
3
+ * 请求走 **`orbitRequestJson`**(`src/utils/orbitHttpClient.ts`),优先 **`window.xnb.http.client.request`**,见 **OrbitAPI.md** §6.3。
4
+ */
5
+
6
+ import { orbitRequestJson } from '@/utils/orbitHttpClient'
7
+
8
+ export interface FetchAgenticlabTicketListParams {
9
+ /** 无尾部斜杠亦可,如 `https://agenticlab.example.com`;嵌入 Orbit 且仅用 http.client 时仍建议填写以便回退 fetch */
10
+ baseUrl: string
11
+ /**
12
+ * 可选静态 Bearer;为空时依次尝试:
13
+ * `await window.xnb.choreo.getUserToken()`(无参)、`VITE_DEV_TOKEN`。
14
+ */
15
+ token?: string
16
+ /** 业务工单号列表,对应 query `ticket_id__in`(多值逗号拼接) */
17
+ ticketIds: string[]
18
+ limit?: number
19
+ offset?: number
20
+ }
21
+
22
+ function joinBaseAndPath (baseUrl: string, path: string): string {
23
+ const trimmed = baseUrl.replace(/\/+$/, '')
24
+ const p = path.startsWith('/') ? path : `/${path}`
25
+ return `${trimmed}${p}`
26
+ }
27
+
28
+ async function resolveBearerToken (opts: { token?: string }): Promise<string | undefined> {
29
+ if (opts.token != null && opts.token !== '') return opts.token
30
+ const w = window as Window & { xnb?: { choreo?: { getUserToken?: () => Promise<string> } } }
31
+ const getToken = w.xnb?.choreo?.getUserToken
32
+ if (getToken != null) {
33
+ try {
34
+ return await getToken()
35
+ } catch {
36
+ return undefined
37
+ }
38
+ }
39
+ const env = import.meta.env.VITE_DEV_TOKEN
40
+ return env != null && env !== '' ? env : undefined
41
+ }
42
+
43
+ /**
44
+ * `GET /api/ticket/list`,`ticket_id__in` 为逗号拼接的 ticket 业务号(与 `detail.ticket_id` 对应)。
45
+ */
46
+ export async function fetchAgenticlabTicketList (params: FetchAgenticlabTicketListParams): Promise<unknown[]> {
47
+ const { baseUrl, token, ticketIds, limit = 999999, offset = 0 } = params
48
+ if (!baseUrl || ticketIds.length === 0) return []
49
+
50
+ const url = joinBaseAndPath(baseUrl, '/api/ticket/list')
51
+ const bearer = await resolveBearerToken({ token })
52
+ const headers: Record<string, string> = { Accept: 'application/json' }
53
+ if (bearer != null && bearer !== '') {
54
+ headers.Authorization = `Bearer ${bearer}`
55
+ }
56
+
57
+ const raw = await orbitRequestJson({
58
+ method: 'get',
59
+ url,
60
+ headers,
61
+ query: {
62
+ limit,
63
+ offset,
64
+ ticket_id__in: ticketIds.join(',')
65
+ }
66
+ })
67
+ const body = raw as { code?: number; msg?: string; data?: { list?: unknown[] } }
68
+ if (body.code !== 200) {
69
+ throw new Error(body.msg != null && body.msg !== '' ? String(body.msg) : `业务 code ${String(body.code)}`)
70
+ }
71
+ return body.data?.list ?? []
72
+ }
73
+
74
+ export interface PostAgenticlabExecutionCompleteParams {
75
+ baseUrl: string
76
+ token?: string
77
+ /** 门控 / 执行实例标识,与后端约定一致;缺省可由 opener 或环境变量提供 */
78
+ valveId: number
79
+ /** 与列表项结构一致的工单对象数组,字段须与 `GET /api/ticket/list` 的 `list[]` 项对齐(含编辑后的 `detail` 等) */
80
+ tickets: Record<string, unknown>[]
81
+ }
82
+
83
+ /**
84
+ * `POST /api/execution/complete`:提交编辑后的Ticket列表,由后端完成执行闭环。
85
+ */
86
+ export async function postAgenticlabExecutionComplete (params: PostAgenticlabExecutionCompleteParams): Promise<void> {
87
+ const { baseUrl, token, valveId, tickets } = params
88
+ if (!baseUrl) throw new Error('缺少 AgenticLab baseUrl')
89
+
90
+ const url = joinBaseAndPath(baseUrl, '/api/execution/complete')
91
+ const bearer = await resolveBearerToken({ token })
92
+ const headers: Record<string, string> = {
93
+ Accept: 'application/json',
94
+ 'Content-Type': 'application/json; charset=utf-8'
95
+ }
96
+ if (bearer != null && bearer !== '') {
97
+ headers.Authorization = `Bearer ${bearer}`
98
+ }
99
+
100
+ const raw = await orbitRequestJson({
101
+ method: 'post',
102
+ url,
103
+ headers,
104
+ jsonBody: { valve_id: valveId, tickets }
105
+ })
106
+ const body = raw as { code?: number; msg?: string }
107
+ if (body.code !== 200) {
108
+ throw new Error(body.msg != null && body.msg !== '' ? String(body.msg) : `业务 code ${String(body.code)}`)
109
+ }
110
+ }
@@ -0,0 +1,4 @@
1
+ import View from '../views/bitable.vue'
2
+ import packageData from '../../package.json'
3
+
4
+ export { View, packageData }
@@ -0,0 +1,4 @@
1
+ import View from '../views/custom-page.vue'
2
+ import packageData from '../../package.json'
3
+
4
+ export { View, packageData }
@@ -0,0 +1 @@
1
+ export { View, packageData } from './entries/bitable'
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Orbit Cell 内 Quasar 子树:根节点加 `class="orbit-quasar-host"` 后,本节点以下生效。
3
+ * - 主色:`color="primary"` / `.bg-primary` → `var(--q-primary)`
4
+ * - `q-btn`:紧凑内边距;关闭默认全大写
5
+ *
6
+ * 复用:在 SFC 的 `<style scoped lang="scss">` 内首行 `@import '@/styles/orbit-quasar-host.scss';`
7
+ * (须 scoped + `:deep(.q-btn)`,否则子组件 DOM 无法命中)
8
+ * 主题调整:新增/修改主色或全局 `q-btn` 表现请只改本文件;页面结构见 `src/views/bitable.vue`。
9
+ * 按钮分工:`primary` 仅提交等持久化写回;`outline` 仅刷新/检查等页面数据交互(见 studio-orbit-vue-component SKILL 规则 2)。
10
+ */
11
+ .orbit-quasar-host {
12
+ --orbit-primary: #123aff;
13
+ --q-primary: var(--orbit-primary);
14
+
15
+ :deep(.q-btn) {
16
+ padding: 0 10px;
17
+ text-transform: none;
18
+ }
19
+ }
@@ -0,0 +1,15 @@
1
+ export interface XnbContext {
2
+ bookPath: string
3
+ cellUid: string
4
+ notifySuccess: (msg: string) => void
5
+ notifyError: (msg: any) => void
6
+ loadingShow: (opts?: any) => void
7
+ loadingHide: () => void
8
+ userData: Record<string, any>
9
+ getUserData: () => Record<string, any>
10
+ updateUserData: (data: any) => void
11
+ mergeUserData: (data: any) => void
12
+ getInstanceById: (id: string) => any
13
+ getInstanceValueById: (id: string) => any
14
+ setInstanceValueById: (id: string, value: any) => void
15
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * 与「超级表格 / 子 Cell」协作相关的 xnbContext 形状(精简版,便于本模板内类型对齐)。
3
+ * 完整类型定义以你们宿主或正式项目中的 `Types/context` 等为准;本处仅保留模板示例所需字段。
4
+ */
5
+
6
+ /**
7
+ * 单张子表在 XNBBitable 侧的常见形状(`getSubBitable` / `getAllSubBitable` 返回值对齐用)。
8
+ * 宿主实现字段可能更多;此处保留示例所需最小集。
9
+ */
10
+ export interface DemoBitableSheet {
11
+ index?: string
12
+ name?: string
13
+ row?: number
14
+ items: unknown[]
15
+ data_columns: unknown[]
16
+ config?: unknown
17
+ [key: string]: unknown
18
+ }
19
+
20
+ /** 子表创建时单张 bitable 的配置骨架(创建前常补全 items / data_columns / config) */
21
+ export interface DemoSubBitableOption {
22
+ index?: string
23
+ name?: string
24
+ rowFocus?: number
25
+ columnFocus?: number
26
+ items?: unknown[]
27
+ data_columns?: unknown[]
28
+ config?: unknown[]
29
+ [key: string]: unknown
30
+ }
31
+
32
+ /** 宿主注入的 XNBBitable 构造函数(典型用法:`new XNBBitable({ bookPath, cellUid })`) */
33
+ export interface XNBBitableChoreo {
34
+ getSubCell (): Promise<string | null>
35
+ createSubBitable (bitables: unknown[], sheetName: string): Promise<string>
36
+ /** 多子表时用于按 `index` 命中目标子表;单表场景常返回长度为 1 的数组 */
37
+ getAllSubBitable (refresh?: boolean): Promise<DemoBitableSheet[]>
38
+ getSubBitable (refresh?: boolean): Promise<DemoBitableSheet>
39
+ /**
40
+ * 为子表追加列定义(常见签名第三参为 `[]`)。
41
+ * 与 `bitable-list-lifecycle.js` 一致:重建列前常先清空 `bitable.data_columns` 再调用。
42
+ */
43
+ addColumns (bitable: Record<string, unknown>, dataColumns: unknown[], extra: unknown[]): Promise<void>
44
+ updateSubBitable (bitables: unknown[], opts?: Record<string, unknown>): Promise<void>
45
+ delay (ms: number): Promise<void>
46
+ }
47
+
48
+ export type XNBBitableConstructor = new (opts: { bookPath: string; cellUid: string }) => XNBBitableChoreo
49
+
50
+ /** window.xnb.event 最小形状(仅示例里 on/off/emit) */
51
+ export interface XnbEventBus {
52
+ on: (channel: string, handler: (...args: unknown[]) => void) => void
53
+ off: (channel: string, handler: (...args: unknown[]) => void) => void
54
+ emit: (channel: string, ...args: unknown[]) => void
55
+ }
56
+
57
+ /** 超级表格 Cell 场景下 xnbContext 常用字段(宿主注入上下文 + XNBBitable 类) */
58
+ export interface XnbSuperCellContext {
59
+ bookPath: string
60
+ cellUid: string
61
+ /** 类本身,由宿主注入;dev 环境见 dev/src/mockXNBBitable.ts */
62
+ XNBBitable?: XNBBitableConstructor
63
+ loadingShow?: (opts?: unknown) => void
64
+ loadingHide?: () => void
65
+ notifySuccess?: (msg: string) => void
66
+ notifyError?: (msg: unknown, ...rest: unknown[]) => void
67
+ getUserData?: () => Record<string, unknown>
68
+ updateUserData?: (data: Record<string, unknown>) => Promise<void> | void
69
+ [key: string]: unknown
70
+ }
@@ -0,0 +1,180 @@
1
+ import { computed, onMounted, ref, watch } from 'vue'
2
+ import type { ComputedRef, Ref } from 'vue'
3
+ import { useQuasar } from 'quasar'
4
+ import type { XnbSuperCellContext } from '@/types/xnb-context'
5
+ import type { OpenerInitResult } from '@/utils/openerInitParams'
6
+ import { fetchOpenerInitPayload } from '@/utils/openerInitParams'
7
+ import { useSuperTableBitableLifecycle } from '@/use/useSuperTableBitableLifecycle'
8
+
9
+ export interface UseBitablePageResult {
10
+ openerInitReady: Ref<boolean>
11
+ openerInitError: Ref<unknown>
12
+ openerErrorText: ComputedRef<string>
13
+ busyRefresh: Ref<boolean>
14
+ busyCheck: Ref<boolean>
15
+ busySubmit: Ref<boolean>
16
+ lastRowCount: Ref<number>
17
+ onRefresh: () => Promise<void>
18
+ onCheck: () => Promise<void>
19
+ onSubmit: () => Promise<void>
20
+ }
21
+
22
+ /**
23
+ * 超级表格 Cell 页:opener 初始化 + `useSuperTableBitableLifecycle`(刷新 / 检查 / 提交)。
24
+ */
25
+ export function useBitablePage (getContext: () => XnbSuperCellContext): UseBitablePageResult {
26
+ const $q = useQuasar()
27
+
28
+ function confirmDataMutation (title: string, message: string): Promise<boolean> {
29
+ return new Promise<boolean>((resolve: (v: boolean) => void) => {
30
+ let settled = false
31
+ const finish = (v: boolean): void => {
32
+ if (settled) return
33
+ settled = true
34
+ resolve(v)
35
+ }
36
+ $q.dialog({
37
+ title,
38
+ message,
39
+ persistent: true,
40
+ ok: { label: '确定', color: 'primary', flat: false },
41
+ cancel: { label: '取消', flat: true, color: 'grey-8' }
42
+ })
43
+ .onOk(() => {
44
+ finish(true)
45
+ })
46
+ .onCancel(() => {
47
+ finish(false)
48
+ })
49
+ .onDismiss(() => {
50
+ finish(false)
51
+ })
52
+ })
53
+ }
54
+
55
+ const openerInit = ref<OpenerInitResult | null>(null)
56
+ const openerInitError = ref<unknown>(null)
57
+ const openerInitReady = ref(false)
58
+
59
+ const openerPayload = computed(() => {
60
+ if (openerInit.value?.ok === true) return openerInit.value.payload
61
+ return null
62
+ })
63
+
64
+ const openerErrorText = computed(() => '没有获得有效参数,请关闭页面重试。')
65
+
66
+ const { choreo, lastRowCount, bootstrap, refreshTable, checkRows, submitRows } = useSuperTableBitableLifecycle(
67
+ getContext,
68
+ () => openerPayload.value
69
+ )
70
+
71
+ const busyRefresh = ref(false)
72
+ const busyCheck = ref(false)
73
+ const busySubmit = ref(false)
74
+
75
+ async function runOpenerInit (): Promise<void> {
76
+ const ctx = getContext()
77
+ try {
78
+ openerInit.value = await fetchOpenerInitPayload({
79
+ loadingShow: () => {
80
+ ctx.loadingShow?.()
81
+ },
82
+ loadingHide: () => {
83
+ ctx.loadingHide?.()
84
+ },
85
+ notifyError: (msg: unknown, ...rest: unknown[]) => {
86
+ console.warn('[opener] init error:', msg, ...rest)
87
+ }
88
+ })
89
+ if (openerInit.value && !openerInit.value.ok) {
90
+ console.warn('[opener] init not ok:', openerInit.value.skipped)
91
+ openerInitError.value = new Error(openerInit.value.skipped || 'init-failed')
92
+ }
93
+ } catch (err) {
94
+ console.warn('[opener] init exception:', err)
95
+ openerInitError.value = err
96
+ } finally {
97
+ openerInitReady.value = true
98
+ }
99
+ }
100
+
101
+ async function runBootstrapSafe (): Promise<void> {
102
+ if (!choreo.value) return
103
+ try {
104
+ await bootstrap()
105
+ } catch {
106
+ /* composable 内已 notify */
107
+ }
108
+ }
109
+
110
+ async function onRefresh (): Promise<void> {
111
+ const ok = await confirmDataMutation(
112
+ '确认刷新',
113
+ '将重新拉取数据并覆盖当前表格内容,是否继续?'
114
+ )
115
+ if (!ok) return
116
+ busyRefresh.value = true
117
+ try {
118
+ await refreshTable({})
119
+ } finally {
120
+ busyRefresh.value = false
121
+ }
122
+ }
123
+
124
+ async function onCheck (): Promise<void> {
125
+ busyCheck.value = true
126
+ try {
127
+ await checkRows()
128
+ } finally {
129
+ busyCheck.value = false
130
+ }
131
+ }
132
+
133
+ async function onSubmit (): Promise<void> {
134
+ const ok = await confirmDataMutation(
135
+ '确认提交',
136
+ '将把当前表格中的工单提交至服务器(execution/complete),是否继续?'
137
+ )
138
+ if (!ok) return
139
+ busySubmit.value = true
140
+ try {
141
+ await submitRows()
142
+ } finally {
143
+ busySubmit.value = false
144
+ }
145
+ }
146
+
147
+ onMounted(() => {
148
+ runOpenerInit().catch(() => {})
149
+ })
150
+
151
+ watch(
152
+ [openerInitReady, openerPayload, choreo],
153
+ () => {
154
+ if (!openerInitReady.value || !choreo.value) return
155
+ runBootstrapSafe().catch(() => {})
156
+ },
157
+ { immediate: true }
158
+ )
159
+
160
+ watch(
161
+ () => [getContext().bookPath, getContext().cellUid, getContext().XNBBitable] as const,
162
+ () => {
163
+ if (!openerInitReady.value || !choreo.value) return
164
+ runBootstrapSafe().catch(() => {})
165
+ }
166
+ )
167
+
168
+ return {
169
+ openerInitReady,
170
+ openerInitError,
171
+ openerErrorText,
172
+ busyRefresh,
173
+ busyCheck,
174
+ busySubmit,
175
+ lastRowCount,
176
+ onRefresh,
177
+ onCheck,
178
+ onSubmit
179
+ }
180
+ }