@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.
- package/README.md +1 -0
- package/package.json +14 -0
- package/skills/lab-flow-designer/SKILL.md +360 -0
- package/skills/lab-flow-designer/embedded-template/SKILL.md +84 -0
- package/skills/lab-flow-designer/embedded-template/pools//345/205/245/345/217/243/346/261/240.md +12 -0
- package/skills/lab-flow-designer/embedded-template/pools//345/207/272/345/217/243/346/261/240.md +12 -0
- 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
- 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
- package/skills/lab-flow-designer/references/agentic-lab-processer.md +101 -0
- package/skills/lab-flow-designer/references/agentic-lab-sdk.md +440 -0
- package/skills/lab-flow-designer/references/rhea-api/README.md +7 -0
- package/skills/lab-flow-designer/references/rhea-api/execute_process_batch.md +58 -0
- package/skills/lab-flow-designer/references/skill-package-layout.md +199 -0
- 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
- package/skills/lab-orbit-component-builder/SKILL.md +305 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/.env.local.example +27 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/.eslintignore +7 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/.eslintrc.cjs +88 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/.nvmrc +1 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/AgenticAppAPI.md +268 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/Jenkinsfile +106 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/OrbitAPI.md +453 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/README.md +176 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/public/index.html +12 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/App.vue +151 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/components/DevOpenerLauncher.vue +143 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/global.d.ts +74 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/main.ts +305 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/mockXNBBitable.ts +119 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/shims-vue.d.ts +6 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/utils/devOpenerHost.ts +75 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/index.html +13 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/package.json +57 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/api/agenticlabTickets.ts +110 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/entries/bitable.ts +4 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/entries/custom-page.ts +4 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/index.ts +1 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/styles/orbit-quasar-host.scss +19 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/types/context.ts +15 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/types/xnb-context.ts +70 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useBitablePage.ts +180 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useSuperCellDemo.ts +257 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useSuperTableBitableLifecycle.ts +547 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/openerInitParams.ts +158 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/openerTicketIds.ts +32 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/orbitHttpClient.ts +110 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/request.ts +92 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/views/bitable.vue +65 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/views/custom-page.vue +140 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/tsconfig.json +48 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/vite.config.ts +165 -0
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/vite.dev.config.ts +51 -0
- package/skills/lab-orbit-component-builder/references/flow-document-human-ui.md +65 -0
- package/skills/lab-orbit-component-builder/references/pool-schema-to-columns.md +67 -0
- package/skills/lab-orbit-component-builder/references/vue-template-checklist.md +113 -0
- package/skills/lab-orbit-component-builder/references/xnb-context-vue-props.md +49 -0
- package/skills/lab-orbit-component-builder/references/xnbitable-vue-parity.md +32 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 从 opener 回包 `payload` 中解析 `ticket_ids`,供 `GET /api/ticket/list` 的 `ticket_id__in` 使用。
|
|
3
|
+
* 支持:`string[]`、`string`(逗号/分号/空白分隔)。
|
|
4
|
+
*/
|
|
5
|
+
export function extractTicketIdsFromPayload (payload: unknown): string[] {
|
|
6
|
+
if (payload == null || typeof payload !== 'object') return []
|
|
7
|
+
const raw = (payload as Record<string, unknown>).ticket_ids
|
|
8
|
+
if (Array.isArray(raw)) {
|
|
9
|
+
return raw.map((x: unknown) => String(x).trim()).filter((s: string) => s.length > 0)
|
|
10
|
+
}
|
|
11
|
+
if (typeof raw === 'string') {
|
|
12
|
+
return raw
|
|
13
|
+
.split(/[,;\s]+/)
|
|
14
|
+
.map((s: string) => s.trim())
|
|
15
|
+
.filter((s: string) => s.length > 0)
|
|
16
|
+
}
|
|
17
|
+
return []
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 从 opener `payload` 解析 **`valve_id`**,供 `POST /api/execution/complete` 请求体使用。
|
|
22
|
+
*/
|
|
23
|
+
export function extractValveIdFromPayload (payload: unknown): number | null {
|
|
24
|
+
if (payload == null || typeof payload !== 'object') return null
|
|
25
|
+
const raw = (payload as Record<string, unknown>).valve_id
|
|
26
|
+
if (typeof raw === 'number' && Number.isFinite(raw)) return raw
|
|
27
|
+
if (typeof raw === 'string' && raw.trim() !== '') {
|
|
28
|
+
const n = Number(raw)
|
|
29
|
+
if (Number.isFinite(n)) return n
|
|
30
|
+
}
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orbit 运行时 HTTP:优先 `window.xnb.http.client.request`(与 Book 代理、鉴权一致),
|
|
3
|
+
* 无注入时回退 `fetch`。约定见仓库根目录 `OrbitAPI.md` §6.3。
|
|
4
|
+
*
|
|
5
|
+
* 请求体形状与 **`dev/src/global.d.ts`** 中 `XnbHttpClient.request` 对齐,避免与宿主注入类型冲突。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type OrbitHttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch'
|
|
9
|
+
|
|
10
|
+
export interface OrbitRequestJsonInput {
|
|
11
|
+
method: OrbitHttpMethod
|
|
12
|
+
/** 完整 URL(已含 origin),如 `https://host/api/ticket/list` */
|
|
13
|
+
url: string
|
|
14
|
+
headers?: Record<string, string>
|
|
15
|
+
/** GET 时作为 query;值会转成 string */
|
|
16
|
+
query?: Record<string, string | number | boolean | undefined | null>
|
|
17
|
+
/** POST/PUT/PATCH 时作为 JSON 体(仅在使用 Orbit `Body.json` 时) */
|
|
18
|
+
jsonBody?: Record<string, unknown>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** 与 `dev/src/global.d.ts` → `XnbHttpClient.request` 的 `options` 一致 */
|
|
22
|
+
export interface XnbHttpClientRequestOptions {
|
|
23
|
+
method: string
|
|
24
|
+
url: string
|
|
25
|
+
headers?: Record<string, string>
|
|
26
|
+
responseType?: number
|
|
27
|
+
query?: Record<string, string>
|
|
28
|
+
body?: { type: string; data: unknown }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getXnbHttp (): Window['xnb']['http'] | undefined {
|
|
32
|
+
return window.xnb?.http
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 将 `client.request` 返回值规范为「业务 JSON 根对象」。
|
|
37
|
+
* 兼容 `res` 或 `res.data` 为 `{ code, data, msg }` 的形态。
|
|
38
|
+
*/
|
|
39
|
+
export function unwrapOrbitClientData (res: unknown): unknown {
|
|
40
|
+
if (res == null) return res
|
|
41
|
+
if (typeof res !== 'object') return res
|
|
42
|
+
const r = res as Record<string, unknown>
|
|
43
|
+
if ('code' in r && ('data' in r || 'msg' in r)) return res
|
|
44
|
+
if ('data' in r) {
|
|
45
|
+
const inner = r.data
|
|
46
|
+
if (inner != null && typeof inner === 'object' && 'code' in (inner as Record<string, unknown>)) {
|
|
47
|
+
return inner
|
|
48
|
+
}
|
|
49
|
+
return inner
|
|
50
|
+
}
|
|
51
|
+
return res
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 使用 Orbit `http.client.request`(若存在)或回退 `fetch` 发 JSON 请求。
|
|
56
|
+
*/
|
|
57
|
+
export async function orbitRequestJson (input: OrbitRequestJsonInput): Promise<unknown> {
|
|
58
|
+
const http = getXnbHttp()
|
|
59
|
+
const client = http?.client
|
|
60
|
+
const RT = http?.ResponseType
|
|
61
|
+
const Body = http?.Body
|
|
62
|
+
|
|
63
|
+
if (client?.request != null && RT?.JSON != null) {
|
|
64
|
+
const headers: Record<string, string> = {
|
|
65
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
66
|
+
...(input.headers ?? {})
|
|
67
|
+
}
|
|
68
|
+
const req: XnbHttpClientRequestOptions = {
|
|
69
|
+
method: input.method,
|
|
70
|
+
url: input.url,
|
|
71
|
+
headers,
|
|
72
|
+
responseType: RT.JSON
|
|
73
|
+
}
|
|
74
|
+
if (input.query != null) {
|
|
75
|
+
const q: Record<string, string> = {}
|
|
76
|
+
for (const [k, v] of Object.entries(input.query)) {
|
|
77
|
+
if (v !== undefined && v !== null) {
|
|
78
|
+
q[k] = String(v)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (Object.keys(q).length > 0) req.query = q
|
|
82
|
+
}
|
|
83
|
+
if (input.jsonBody != null && Body?.json != null) {
|
|
84
|
+
req.body = Body.json(input.jsonBody) as { type: string; data: unknown }
|
|
85
|
+
}
|
|
86
|
+
const raw = await client.request(req)
|
|
87
|
+
return unwrapOrbitClientData(raw.data ?? raw)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (input.method !== 'get') {
|
|
91
|
+
throw new Error('orbitRequestJson: 非 GET 且无 window.xnb.http.client 时无法回退 fetch')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const u = new URL(input.url)
|
|
95
|
+
if (input.query != null) {
|
|
96
|
+
for (const [k, v] of Object.entries(input.query)) {
|
|
97
|
+
if (v !== undefined && v !== null) {
|
|
98
|
+
u.searchParams.set(k, String(v))
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const res = await fetch(u.toString(), {
|
|
103
|
+
method: 'GET',
|
|
104
|
+
headers: input.headers ?? { Accept: 'application/json' }
|
|
105
|
+
})
|
|
106
|
+
if (!res.ok) {
|
|
107
|
+
throw new Error(`HTTP ${String(res.status)}`)
|
|
108
|
+
}
|
|
109
|
+
return await res.json() as unknown
|
|
110
|
+
}
|
package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/request.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
interface RequestOptions {
|
|
2
|
+
method: string
|
|
3
|
+
url: string
|
|
4
|
+
headers?: Record<string, string>
|
|
5
|
+
query?: Record<string, string | number | boolean | string[] | null | undefined>
|
|
6
|
+
body?: any
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface RequestResponse<T = any> {
|
|
10
|
+
data: T
|
|
11
|
+
headers: Record<string, string>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function getToken (): Promise<string> {
|
|
15
|
+
try {
|
|
16
|
+
return await window.xnb.choreo.getUserToken()
|
|
17
|
+
} catch {
|
|
18
|
+
return ''
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function request<T = any> (options: RequestOptions): Promise<T> {
|
|
23
|
+
const token = await getToken()
|
|
24
|
+
|
|
25
|
+
const headers: Record<string, string> = {
|
|
26
|
+
'Content-Type': 'application/json',
|
|
27
|
+
...options.headers
|
|
28
|
+
}
|
|
29
|
+
if (token) {
|
|
30
|
+
headers.Authorization = `Bearer ${token}`
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const httpOptions: any = {
|
|
34
|
+
method: options.method.toUpperCase(),
|
|
35
|
+
url: options.url,
|
|
36
|
+
headers,
|
|
37
|
+
responseType: window.xnb.http.ResponseType.JSON
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (options.query) {
|
|
41
|
+
httpOptions.query = options.query
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (options.body !== undefined) {
|
|
45
|
+
httpOptions.body = window.xnb.http.Body.json(options.body)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const res: RequestResponse<T> = await window.xnb.http.client.request(httpOptions)
|
|
49
|
+
return res.data
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function get<T = any> (
|
|
53
|
+
url: string,
|
|
54
|
+
query?: Record<string, string | number | boolean | string[] | null | undefined>,
|
|
55
|
+
headers?: Record<string, string>
|
|
56
|
+
): Promise<T> {
|
|
57
|
+
return request<T>({ method: 'GET', url, query, headers })
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function post<T = any> (
|
|
61
|
+
url: string,
|
|
62
|
+
body?: any,
|
|
63
|
+
headers?: Record<string, string>
|
|
64
|
+
): Promise<T> {
|
|
65
|
+
return request<T>({ method: 'POST', url, body, headers })
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function put<T = any> (
|
|
69
|
+
url: string,
|
|
70
|
+
body?: any,
|
|
71
|
+
headers?: Record<string, string>
|
|
72
|
+
): Promise<T> {
|
|
73
|
+
return request<T>({ method: 'PUT', url, body, headers })
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function del<T = any> (
|
|
77
|
+
url: string,
|
|
78
|
+
query?: Record<string, string | number | boolean | string[] | null | undefined>,
|
|
79
|
+
headers?: Record<string, string>
|
|
80
|
+
): Promise<T> {
|
|
81
|
+
return request<T>({ method: 'DELETE', url, query, headers })
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function patch<T = any> (
|
|
85
|
+
url: string,
|
|
86
|
+
body?: any,
|
|
87
|
+
headers?: Record<string, string>
|
|
88
|
+
): Promise<T> {
|
|
89
|
+
return request<T>({ method: 'PATCH', url, body, headers })
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export default request
|
package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/views/bitable.vue
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="orbit-quasar-host q-pa-md">
|
|
3
|
+
<q-banner v-if="openerInitError" rounded dense class="error-banner q-mb-sm">
|
|
4
|
+
{{ openerErrorText }}
|
|
5
|
+
</q-banner>
|
|
6
|
+
|
|
7
|
+
<div v-if="openerInitReady && props.xnbContext.XNBBitable" class="row items-center q-gutter-xs q-mb-sm flex-wrap">
|
|
8
|
+
<span class="text-subtitle2 q-mr-sm">超级表格</span>
|
|
9
|
+
<q-btn dense outline label="刷新" :loading="busyRefresh" :disable="!!openerInitError" @click="onRefresh" />
|
|
10
|
+
<q-btn dense outline label="检查" :loading="busyCheck" :disable="!!openerInitError" @click="onCheck" />
|
|
11
|
+
<q-btn color="primary" dense unelevated label="提交" :loading="busySubmit" :disable="!!openerInitError" @click="onSubmit" />
|
|
12
|
+
<span v-if="lastRowCount > 0" class="row-count">{{ lastRowCount }} 条</span>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<q-banner v-else-if="openerInitReady && !props.xnbContext.XNBBitable" rounded dense class="info-banner">
|
|
16
|
+
未注入 XNBBitable
|
|
17
|
+
</q-banner>
|
|
18
|
+
</div>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<script lang="ts" setup>
|
|
22
|
+
/**
|
|
23
|
+
* 模板内演示用超级表格工具栏示例(非业务交付物)。
|
|
24
|
+
* studio-orbit-vue-component 技能:向用户目标目录产出时勿原样复制本文件;请按流程文档新建具语义的 `src/views/<业务>.vue` 并参考此处写法。
|
|
25
|
+
*
|
|
26
|
+
* 刷新 / 提交:在 `useBitablePage` 内经 Quasar 对话框确认后再执行。
|
|
27
|
+
*/
|
|
28
|
+
import { useBitablePage } from '@/use/useBitablePage'
|
|
29
|
+
import type { XnbSuperCellContext } from '@/types/xnb-context'
|
|
30
|
+
|
|
31
|
+
const props = defineProps<{
|
|
32
|
+
xnbContext: XnbSuperCellContext
|
|
33
|
+
}>()
|
|
34
|
+
|
|
35
|
+
const {
|
|
36
|
+
openerInitReady,
|
|
37
|
+
openerInitError,
|
|
38
|
+
openerErrorText,
|
|
39
|
+
busyRefresh,
|
|
40
|
+
busyCheck,
|
|
41
|
+
busySubmit,
|
|
42
|
+
lastRowCount,
|
|
43
|
+
onRefresh,
|
|
44
|
+
onCheck,
|
|
45
|
+
onSubmit
|
|
46
|
+
} = useBitablePage(() => props.xnbContext)
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<style scoped lang="scss">
|
|
50
|
+
@import '@/styles/orbit-quasar-host.scss';
|
|
51
|
+
|
|
52
|
+
.error-banner {
|
|
53
|
+
background: #FEF2F2;
|
|
54
|
+
border-left: 3px solid #ffbe7d;
|
|
55
|
+
}
|
|
56
|
+
.info-banner {
|
|
57
|
+
background: #FFFFFF;
|
|
58
|
+
border: 1px solid #E5E5E3;
|
|
59
|
+
color: #6B6B6B;
|
|
60
|
+
}
|
|
61
|
+
.row-count {
|
|
62
|
+
font-size: 0.75rem;
|
|
63
|
+
color: #6B6B6B;
|
|
64
|
+
}
|
|
65
|
+
</style>
|
package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/views/custom-page.vue
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
模板内演示用多区页面示例(非业务交付物)。
|
|
3
|
+
studio-orbit-vue-component 技能:向用户目标目录产出时勿原样复制本文件;请按流程文档新建具语义的 views 与 entries。
|
|
4
|
+
|
|
5
|
+
场景:复杂多组件页面(不使用超级表格 / XNBBitable)
|
|
6
|
+
入口文件名:custom-page.vue(与 xnb-components 中 id 一致)
|
|
7
|
+
|
|
8
|
+
仅占位布局:顶栏 + 侧栏 + 多卡片主区;与 bitable(超级表格 Cell)分离,后续可单独扩展。
|
|
9
|
+
-->
|
|
10
|
+
<template>
|
|
11
|
+
<div class="orbit-quasar-host multi-page-demo">
|
|
12
|
+
<header class="demo-header row items-center q-px-md">
|
|
13
|
+
<div class="text-weight-medium">自定义多区页面(custom-page)</div>
|
|
14
|
+
<q-space />
|
|
15
|
+
<q-chip dense outline color="grey-8">无 XNBBitable / 子 Cell</q-chip>
|
|
16
|
+
</header>
|
|
17
|
+
|
|
18
|
+
<div class="demo-body row no-wrap">
|
|
19
|
+
<aside class="demo-aside q-pa-sm">
|
|
20
|
+
<div class="text-caption text-grey-7 q-mb-sm">侧栏占位</div>
|
|
21
|
+
<q-list bordered separator dense class="rounded-borders bg-white">
|
|
22
|
+
<q-item clickable v-ripple v-for="nav in navItems" :key="nav.id">
|
|
23
|
+
<q-item-section>{{ nav.label }}</q-item-section>
|
|
24
|
+
</q-item>
|
|
25
|
+
</q-list>
|
|
26
|
+
</aside>
|
|
27
|
+
|
|
28
|
+
<main class="demo-main col q-pa-md">
|
|
29
|
+
<p class="hint q-mb-md">
|
|
30
|
+
入口文件 <code>src/views/custom-page.vue</code>。用于「多区域、多卡片、无超级表格」类页面;
|
|
31
|
+
<code>xnbContext</code> 仍可从宿主注入(见下方节选)。可与
|
|
32
|
+
<code>bitable</code> 场景独立演进。
|
|
33
|
+
</p>
|
|
34
|
+
<div class="row q-col-gutter-md">
|
|
35
|
+
<div class="col-12 col-md-4" v-for="card in cards" :key="card.id">
|
|
36
|
+
<q-card flat bordered class="full-height">
|
|
37
|
+
<q-card-section class="text-subtitle2">{{ card.title }}</q-card-section>
|
|
38
|
+
<q-card-section class="text-caption text-grey-7">{{ card.desc }}</q-card-section>
|
|
39
|
+
</q-card>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
<q-expansion-item
|
|
43
|
+
class="q-mt-md rounded-borders bordered bg-white"
|
|
44
|
+
label="当前 xnbContext(节选)"
|
|
45
|
+
header-class="text-body2"
|
|
46
|
+
dense
|
|
47
|
+
>
|
|
48
|
+
<q-card flat>
|
|
49
|
+
<q-card-section>
|
|
50
|
+
<pre class="ctx-pre">{{ summary }}</pre>
|
|
51
|
+
</q-card-section>
|
|
52
|
+
</q-card>
|
|
53
|
+
</q-expansion-item>
|
|
54
|
+
</main>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</template>
|
|
58
|
+
|
|
59
|
+
<script lang="ts" setup>
|
|
60
|
+
import { computed, withDefaults } from 'vue'
|
|
61
|
+
|
|
62
|
+
export type XnbContextPageLike = {
|
|
63
|
+
bookPath?: string
|
|
64
|
+
cellUid?: string
|
|
65
|
+
[key: string]: unknown
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const props = withDefaults(
|
|
69
|
+
defineProps<{
|
|
70
|
+
xnbContext?: XnbContextPageLike
|
|
71
|
+
}>(),
|
|
72
|
+
{
|
|
73
|
+
xnbContext: () => ({})
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
const navItems = [
|
|
78
|
+
{ id: 'a', label: '模块占位 A' },
|
|
79
|
+
{ id: 'b', label: '模块占位 B' },
|
|
80
|
+
{ id: 'c', label: '模块占位 C' }
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
const cards = [
|
|
84
|
+
{ id: '1', title: '区域 1', desc: '实际功能组件、表格或图表等可放此处。' },
|
|
85
|
+
{ id: '2', title: '区域 2', desc: '与区域 1 并列,演示多列布局。' },
|
|
86
|
+
{ id: '3', title: '区域 3', desc: '后续可替换为真实功能模块。' }
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
const summary = computed(() => {
|
|
90
|
+
const c = props.xnbContext || {}
|
|
91
|
+
return JSON.stringify(
|
|
92
|
+
{ bookPath: c.bookPath, cellUid: c.cellUid },
|
|
93
|
+
null,
|
|
94
|
+
2
|
|
95
|
+
)
|
|
96
|
+
})
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
<style scoped lang="scss">
|
|
100
|
+
@import '@/styles/orbit-quasar-host.scss';
|
|
101
|
+
|
|
102
|
+
.multi-page-demo {
|
|
103
|
+
box-sizing: border-box;
|
|
104
|
+
height: 100%;
|
|
105
|
+
display: flex;
|
|
106
|
+
flex-direction: column;
|
|
107
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
108
|
+
color: #1a1a1a;
|
|
109
|
+
}
|
|
110
|
+
.demo-header {
|
|
111
|
+
min-height: 48px;
|
|
112
|
+
border-bottom: 1px solid #e0e0e0;
|
|
113
|
+
background: #fafafa;
|
|
114
|
+
}
|
|
115
|
+
.demo-body {
|
|
116
|
+
flex: 1;
|
|
117
|
+
min-height: 0;
|
|
118
|
+
overflow: auto;
|
|
119
|
+
}
|
|
120
|
+
.demo-aside {
|
|
121
|
+
width: 200px;
|
|
122
|
+
flex-shrink: 0;
|
|
123
|
+
border-right: 1px solid #e0e0e0;
|
|
124
|
+
background: #fcfcfc;
|
|
125
|
+
}
|
|
126
|
+
.demo-main {
|
|
127
|
+
min-width: 0;
|
|
128
|
+
}
|
|
129
|
+
.hint {
|
|
130
|
+
margin: 0;
|
|
131
|
+
color: #555;
|
|
132
|
+
font-size: 0.9rem;
|
|
133
|
+
line-height: 1.5;
|
|
134
|
+
}
|
|
135
|
+
.ctx-pre {
|
|
136
|
+
margin: 0;
|
|
137
|
+
font-size: 12px;
|
|
138
|
+
overflow: auto;
|
|
139
|
+
}
|
|
140
|
+
</style>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "esnext",
|
|
4
|
+
"module": "esnext",
|
|
5
|
+
"strict": true,
|
|
6
|
+
"jsx": "preserve",
|
|
7
|
+
"importHelpers": true,
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
|
+
"experimentalDecorators": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"allowSyntheticDefaultImports": true,
|
|
14
|
+
"sourceMap": true,
|
|
15
|
+
"baseUrl": ".",
|
|
16
|
+
"types": ["vite/client"],
|
|
17
|
+
"paths": {
|
|
18
|
+
"@/*": ["src/*"],
|
|
19
|
+
"Views/*": ["src/views/*"],
|
|
20
|
+
"Libs/*": ["src/libs/*"],
|
|
21
|
+
"Components/*": ["src/components/*"],
|
|
22
|
+
"Api/*": ["src/api/*"],
|
|
23
|
+
"Utils/*": ["src/utils/*"],
|
|
24
|
+
"Assets/*": ["src/assets/*"],
|
|
25
|
+
"Types/*": ["src/types/*"],
|
|
26
|
+
"Plugins/*": ["src/plugins/*"]
|
|
27
|
+
},
|
|
28
|
+
"lib": [
|
|
29
|
+
"esnext",
|
|
30
|
+
"dom",
|
|
31
|
+
"dom.iterable",
|
|
32
|
+
"scripthost"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
"include": [
|
|
36
|
+
"src/**/*.ts",
|
|
37
|
+
"src/**/*.tsx",
|
|
38
|
+
"src/**/*.vue",
|
|
39
|
+
"dev/src/**/*.ts",
|
|
40
|
+
"dev/src/**/*.tsx",
|
|
41
|
+
"dev/src/**/*.vue",
|
|
42
|
+
"vite.config.ts",
|
|
43
|
+
"vite.dev.config.ts"
|
|
44
|
+
],
|
|
45
|
+
"exclude": [
|
|
46
|
+
"node_modules"
|
|
47
|
+
]
|
|
48
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import vue from '@vitejs/plugin-vue'
|
|
3
|
+
import externalGlobals from 'rollup-plugin-external-globals'
|
|
4
|
+
import viteCompression from 'vite-plugin-compression'
|
|
5
|
+
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
|
6
|
+
import { resolve } from 'path'
|
|
7
|
+
import { readFileSync } from 'fs'
|
|
8
|
+
import type { Plugin } from 'vite'
|
|
9
|
+
|
|
10
|
+
const pkg = JSON.parse(readFileSync(resolve(__dirname, 'package.json'), 'utf-8'))
|
|
11
|
+
|
|
12
|
+
// ==================== 多入口组件清单 ====================
|
|
13
|
+
|
|
14
|
+
interface ComponentSpec {
|
|
15
|
+
distBaseName: string
|
|
16
|
+
registrarName: string
|
|
17
|
+
label?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const COMPONENT_MAP: Record<string, ComponentSpec> = {
|
|
21
|
+
bitable: {
|
|
22
|
+
label: '超级表格 Cell(bitable)',
|
|
23
|
+
distBaseName: 'DemoOrbitBitable',
|
|
24
|
+
registrarName: 'DemoOrbitBitable'
|
|
25
|
+
},
|
|
26
|
+
'custom-page': {
|
|
27
|
+
label: '自定义多区页面(custom-page)',
|
|
28
|
+
distBaseName: 'DemoOrbitCustomPage',
|
|
29
|
+
registrarName: 'DemoOrbitCustomPage'
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const DEFAULT_COMPONENT_ID = 'bitable'
|
|
34
|
+
|
|
35
|
+
// ==================== Webb 组件包装插件 ====================
|
|
36
|
+
|
|
37
|
+
function wrapComponentPlugin (funcMode: boolean): Plugin {
|
|
38
|
+
return {
|
|
39
|
+
name: 'wrap-component',
|
|
40
|
+
enforce: 'post',
|
|
41
|
+
generateBundle (_options, bundle) {
|
|
42
|
+
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
43
|
+
if (chunk.type !== 'chunk' || !fileName.endsWith('.js')) continue
|
|
44
|
+
|
|
45
|
+
const exportRegex = /export\s*\{([^}]+)\}\s*;?/
|
|
46
|
+
const match = chunk.code.match(exportRegex)
|
|
47
|
+
if (!match) continue
|
|
48
|
+
|
|
49
|
+
const mappings: Record<string, string> = {}
|
|
50
|
+
match[1].split(',').forEach((part: string) => {
|
|
51
|
+
const tokens = part.trim().split(/\s+/)
|
|
52
|
+
if (tokens.length >= 3 && tokens[1] === 'as') {
|
|
53
|
+
mappings[tokens[2]] = tokens[0]
|
|
54
|
+
} else {
|
|
55
|
+
mappings[tokens[0]] = tokens[0]
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const viewVar = mappings['View'] || 'View'
|
|
60
|
+
const pkgDataVar = mappings['packageData'] || 'packageData'
|
|
61
|
+
|
|
62
|
+
if (!funcMode) continue
|
|
63
|
+
|
|
64
|
+
const componentId = process.env.VITE_COMPONENT_ID || DEFAULT_COMPONENT_ID
|
|
65
|
+
const spec = COMPONENT_MAP[componentId]
|
|
66
|
+
const compName = spec ? spec.registrarName : pkg.name
|
|
67
|
+
|
|
68
|
+
const wrapCode = [
|
|
69
|
+
'return {',
|
|
70
|
+
' component: {',
|
|
71
|
+
` packageData: ${pkgDataVar},`,
|
|
72
|
+
` name: '${compName}',`,
|
|
73
|
+
` "${spec ? spec.distBaseName : pkg.name}": ${viewVar}`,
|
|
74
|
+
' }',
|
|
75
|
+
'}'
|
|
76
|
+
].join('\n')
|
|
77
|
+
chunk.code = chunk.code.replace(exportRegex, wrapCode)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ==================== Vite 配置 ====================
|
|
84
|
+
|
|
85
|
+
export default defineConfig(({ mode }) => {
|
|
86
|
+
const funcMode = mode === 'func'
|
|
87
|
+
const componentId = process.env.VITE_COMPONENT_ID || DEFAULT_COMPONENT_ID
|
|
88
|
+
const spec = COMPONENT_MAP[componentId]
|
|
89
|
+
|
|
90
|
+
if (componentId !== DEFAULT_COMPONENT_ID && !spec) {
|
|
91
|
+
const keys = Object.keys(COMPONENT_MAP).join(', ')
|
|
92
|
+
throw new Error(`[vite.config] 未知组件 id "${componentId}",应为: ${keys}`)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const entryFile = spec
|
|
96
|
+
? resolve(__dirname, `src/entries/${componentId}.ts`)
|
|
97
|
+
: resolve(__dirname, 'src/index.ts')
|
|
98
|
+
const outputName = spec ? spec.distBaseName : pkg.name
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
plugins: [
|
|
102
|
+
vue(),
|
|
103
|
+
cssInjectedByJsPlugin(),
|
|
104
|
+
wrapComponentPlugin(funcMode),
|
|
105
|
+
viteCompression({
|
|
106
|
+
algorithm: 'gzip',
|
|
107
|
+
ext: '.gz',
|
|
108
|
+
threshold: 30240,
|
|
109
|
+
deleteOriginFile: false
|
|
110
|
+
})
|
|
111
|
+
],
|
|
112
|
+
resolve: {
|
|
113
|
+
alias: {
|
|
114
|
+
'@': resolve(__dirname, 'src'),
|
|
115
|
+
'Views': resolve(__dirname, 'src/views'),
|
|
116
|
+
'Libs': resolve(__dirname, 'src/libs'),
|
|
117
|
+
'Components': resolve(__dirname, 'src/components'),
|
|
118
|
+
'Api': resolve(__dirname, 'src/api'),
|
|
119
|
+
'Utils': resolve(__dirname, 'src/utils'),
|
|
120
|
+
'Assets': resolve(__dirname, 'src/assets'),
|
|
121
|
+
'Types': resolve(__dirname, 'src/types'),
|
|
122
|
+
'Plugins': resolve(__dirname, 'src/plugins')
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
css: {
|
|
126
|
+
preprocessorOptions: {
|
|
127
|
+
sass: {
|
|
128
|
+
additionalData: '@import "quasar/src/css/variables.sass"\n',
|
|
129
|
+
silenceDeprecations: ['legacy-js-api', 'import', 'global-builtin', 'slash-div']
|
|
130
|
+
},
|
|
131
|
+
scss: {
|
|
132
|
+
additionalData: '@import "quasar/src/css/variables.sass";',
|
|
133
|
+
silenceDeprecations: ['legacy-js-api', 'import', 'global-builtin', 'slash-div']
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
build: {
|
|
138
|
+
outDir: resolve(__dirname, 'dist'),
|
|
139
|
+
sourcemap: true,
|
|
140
|
+
cssCodeSplit: false,
|
|
141
|
+
minify: false,
|
|
142
|
+
lib: {
|
|
143
|
+
entry: entryFile,
|
|
144
|
+
formats: ['es'],
|
|
145
|
+
fileName: () => `${outputName}.umd.js`
|
|
146
|
+
},
|
|
147
|
+
rollupOptions: {
|
|
148
|
+
external: ['vue', 'quasar'],
|
|
149
|
+
plugins: [
|
|
150
|
+
externalGlobals({
|
|
151
|
+
vue: 'window.Vue',
|
|
152
|
+
quasar: 'window.webb.utils.quasar'
|
|
153
|
+
})
|
|
154
|
+
],
|
|
155
|
+
onwarn (warning, warn) {
|
|
156
|
+
if (warning.code === 'UNUSED_EXTERNAL_IMPORT') return
|
|
157
|
+
warn(warning)
|
|
158
|
+
},
|
|
159
|
+
output: {
|
|
160
|
+
inlineDynamicImports: true
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
})
|