@manohub/skill-select-workbench 0.0.1-beta1

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.
@@ -0,0 +1,32 @@
1
+ import type { PageResponse, SkillSummaryResponse } from '@aihub/api-client';
2
+ import type { SkillDomainTreeNode } from '../types';
3
+ /** "全部技能"虚拟树节点 ID(选中时不做业务域过滤) */
4
+ export declare const ALL_SKILLS_NODE_ID = "__all_skills__";
5
+ /** 业务域树(含"全部技能"虚拟根,直接作为 FTreeView 的 data) */
6
+ export declare function loadDomainTreeWithRoot(): Promise<SkillDomainTreeNode[]>;
7
+ /** 业务域树(真实接口返回,缓存在模块级) */
8
+ export declare function loadDomainTree(): Promise<SkillDomainTreeNode[]>;
9
+ /** 扁平化业务域树,code → name 映射(表格"业务域"列展示用) */
10
+ export declare function flattenDomainCodeName(nodes: SkillDomainTreeNode[]): Map<string, string>;
11
+ /** topic → 名称 映射(模块级缓存):技能的 topic 字段可能存 id 或 code,两种 key 都登记 */
12
+ export declare function loadTopicNameMap(): Promise<Map<string, string>>;
13
+ /** 技能分类导航节点(扁平):每个 topic 一个节点,code 为空者跳过(无法作为过滤参数) */
14
+ export declare function loadTopicNavNodes(): Promise<SkillDomainTreeNode[]>;
15
+ /** 技能分类导航树(含"全部技能"虚拟根,供通用办公类技能左侧导航) */
16
+ export declare function loadTopicTreeWithRoot(): Promise<SkillDomainTreeNode[]>;
17
+ /** 技能查询参数(封装 listSkills 的过滤子集) */
18
+ export interface SkillPageFilter {
19
+ page?: number;
20
+ size?: number;
21
+ keyword?: string;
22
+ businessDomain?: string;
23
+ /** 技能分类(topic code)过滤 */
24
+ topic?: string;
25
+ /** 使用类别(技能类型)过滤:GENERAL_OFFICE / BUSINESS_SYSTEM,缺省查全部 */
26
+ category?: string;
27
+ }
28
+ /**
29
+ * 技能分页查询(按新契约:分页从 0 开始,返回 { items, total, page, size })
30
+ * @param signal 供调用方取消请求
31
+ */
32
+ export declare function querySkillPage(filter: SkillPageFilter, signal?: AbortSignal): Promise<PageResponse<SkillSummaryResponse>>;
@@ -0,0 +1,63 @@
1
+ import { type PropType } from 'vue';
2
+ import type { SkillSelectItem } from '../types';
3
+ /** 使用类别常量(与后端 SkillCategory 枚举一致) */
4
+ export declare const SKILL_CATEGORY_GENERAL = "GENERAL_OFFICE";
5
+ export declare const SKILL_CATEGORY_BIZ = "BUSINESS_SYSTEM";
6
+ export declare const SkillSelectDialog: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
7
+ modelValue: {
8
+ type: BooleanConstructor;
9
+ default: boolean;
10
+ };
11
+ /** 单选模式:一次只能选定一个技能 */
12
+ singleSelect: {
13
+ type: BooleanConstructor;
14
+ default: boolean;
15
+ };
16
+ /** 打开弹窗时的初始已选项(回显) */
17
+ initSelected: {
18
+ type: PropType<SkillSelectItem[]>;
19
+ default: () => SkillSelectItem[];
20
+ };
21
+ /** 调用方指定的技能类型(使用类别):GENERAL_OFFICE / BUSINESS_SYSTEM。非空=锁定该类型,隐藏类型切换行,仅加载该类型数据;'' = 不锁定(弹窗内可选全部) */
22
+ initCategory: {
23
+ type: StringConstructor;
24
+ default: string;
25
+ };
26
+ title: {
27
+ type: StringConstructor;
28
+ default: string;
29
+ };
30
+ }>, () => JSX.Element | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("update:modelValue" | "confirm")[], "update:modelValue" | "confirm", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
31
+ modelValue: {
32
+ type: BooleanConstructor;
33
+ default: boolean;
34
+ };
35
+ /** 单选模式:一次只能选定一个技能 */
36
+ singleSelect: {
37
+ type: BooleanConstructor;
38
+ default: boolean;
39
+ };
40
+ /** 打开弹窗时的初始已选项(回显) */
41
+ initSelected: {
42
+ type: PropType<SkillSelectItem[]>;
43
+ default: () => SkillSelectItem[];
44
+ };
45
+ /** 调用方指定的技能类型(使用类别):GENERAL_OFFICE / BUSINESS_SYSTEM。非空=锁定该类型,隐藏类型切换行,仅加载该类型数据;'' = 不锁定(弹窗内可选全部) */
46
+ initCategory: {
47
+ type: StringConstructor;
48
+ default: string;
49
+ };
50
+ title: {
51
+ type: StringConstructor;
52
+ default: string;
53
+ };
54
+ }>> & Readonly<{
55
+ "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
56
+ onConfirm?: ((...args: any[]) => any) | undefined;
57
+ }>, {
58
+ modelValue: boolean;
59
+ singleSelect: boolean;
60
+ initSelected: SkillSelectItem[];
61
+ initCategory: string;
62
+ title: string;
63
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -0,0 +1,65 @@
1
+ import { type PropType } from 'vue';
2
+ import type { SkillSelectItem } from '../types';
3
+ /**
4
+ * 技能选择字段帮助(对标 personnel-workbench 的 TreeSelectField 用法):
5
+ * 只读展示已选技能,点击唤起 SkillSelectDialog。
6
+ * 用法:
7
+ * <SkillSelectField :selectedItems="items" singleSelect @update:selectedItems="v => (items = v)" />
8
+ */
9
+ export declare const SkillSelectField: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
10
+ /** 单选模式 */
11
+ singleSelect: {
12
+ type: BooleanConstructor;
13
+ default: boolean;
14
+ };
15
+ /** 兼容字段帮助的 asyncMode 开关(本组件始终按需异步加载,保留以对齐既有调用方) */
16
+ asyncMode: {
17
+ type: BooleanConstructor;
18
+ default: boolean;
19
+ };
20
+ disabled: {
21
+ type: BooleanConstructor;
22
+ default: boolean;
23
+ };
24
+ placeholder: {
25
+ type: StringConstructor;
26
+ default: string;
27
+ };
28
+ /** 当前已选技能(v-model) */
29
+ selectedItems: {
30
+ type: PropType<SkillSelectItem[]>;
31
+ default: () => SkillSelectItem[];
32
+ };
33
+ }>, () => JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "update:selectedItems"[], "update:selectedItems", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
34
+ /** 单选模式 */
35
+ singleSelect: {
36
+ type: BooleanConstructor;
37
+ default: boolean;
38
+ };
39
+ /** 兼容字段帮助的 asyncMode 开关(本组件始终按需异步加载,保留以对齐既有调用方) */
40
+ asyncMode: {
41
+ type: BooleanConstructor;
42
+ default: boolean;
43
+ };
44
+ disabled: {
45
+ type: BooleanConstructor;
46
+ default: boolean;
47
+ };
48
+ placeholder: {
49
+ type: StringConstructor;
50
+ default: string;
51
+ };
52
+ /** 当前已选技能(v-model) */
53
+ selectedItems: {
54
+ type: PropType<SkillSelectItem[]>;
55
+ default: () => SkillSelectItem[];
56
+ };
57
+ }>> & Readonly<{
58
+ "onUpdate:selectedItems"?: ((...args: any[]) => any) | undefined;
59
+ }>, {
60
+ singleSelect: boolean;
61
+ disabled: boolean;
62
+ asyncMode: boolean;
63
+ placeholder: string;
64
+ selectedItems: SkillSelectItem[];
65
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -0,0 +1,26 @@
1
+ import type { SkillSummaryResponse } from '@aihub/api-client';
2
+ import type { SkillSelectItem } from '../types';
3
+ export interface UseSkillSelectionOptions {
4
+ /** 单选模式:任一时刻最多保留一个选中项 */
5
+ singleSelect?: boolean;
6
+ }
7
+ /**
8
+ * 技能选中集管理(仿 personnel 帮助的 useSelectionState):
9
+ * - FDataGrid 勾选通过 selectionValues(受控)+ onSelectionUpdate 全量同步
10
+ * - 跨页保持:grid 侧 keepSelectingOnPaging,本 composable 维护全量 Map
11
+ * - 详情补齐:indexPageItems 登记当前页技能,勾选上报的 id 据此补全快照
12
+ */
13
+ export declare function useSkillSelection(options?: UseSkillSelectionOptions): {
14
+ singleSelect: boolean;
15
+ selectedIds: import("vue").ComputedRef<string[]>;
16
+ items: import("vue").ComputedRef<SkillSelectItem[]>;
17
+ count: import("vue").ComputedRef<number>;
18
+ indexPageItems: (items: SkillSummaryResponse[] | null | undefined) => void;
19
+ handleGridSelectionUpdate: (rows: unknown[] | undefined, ids?: string[], currentPageIds?: Set<string>) => void;
20
+ toggleItem: (row: unknown) => void;
21
+ selectRow: (row: unknown) => void;
22
+ removeItem: (id: string) => void;
23
+ clear: () => void;
24
+ restore: (items: SkillSelectItem[] | null | undefined) => void;
25
+ };
26
+ export type SkillSelectionStore = ReturnType<typeof useSkillSelection>;
@@ -0,0 +1,17 @@
1
+ /** 技能使用类别(对应 api-client SkillCategory 枚举,与 aihub-skill 共享常量保持一致) */
2
+ export declare const SKILL_CATEGORIES: readonly [{
3
+ readonly value: "GENERAL_OFFICE";
4
+ readonly label: "通用办公技能";
5
+ }, {
6
+ readonly value: "BUSINESS_SYSTEM";
7
+ readonly label: "业务系统技能";
8
+ }];
9
+ export type SkillCategoryValue = (typeof SKILL_CATEGORIES)[number]['value'];
10
+ /** 默认使用类别:通用办公技能 */
11
+ export declare const DEFAULT_CATEGORY = "GENERAL_OFFICE";
12
+ /** 类别编码 → 中文名(未匹配返回原值) */
13
+ export declare function categoryLabel(code: string | null | undefined): string;
14
+ /** 表格默认每页条数 */
15
+ export declare const SKILL_PAGE_SIZE = 10;
16
+ /** 分页可选项 */
17
+ export declare const SKILL_PAGE_SIZE_OPTIONS: number[];
package/dist/index.css ADDED
@@ -0,0 +1,2 @@
1
+ .ssw-overlay{z-index:10000;background:#0f172a73;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.ssw-dialog{color:#232428;background:#fff;border-radius:8px;flex-direction:column;width:960px;max-width:calc(100vw - 48px);height:640px;max-height:calc(100vh - 64px);font-size:13px;display:flex;overflow:hidden;box-shadow:0 12px 40px #0f172a47}.ssw-dialog__header{background:#fff;border-bottom:1px solid #ebeef5;flex-shrink:0;justify-content:space-between;align-items:center;height:44px;padding:0 16px;display:flex}.ssw-dialog__title{font-size:15px;font-weight:600}.ssw-dialog__close{cursor:pointer;background:0 0;border:0;border-radius:4px;justify-content:center;align-items:center;width:28px;height:28px;display:inline-flex}.ssw-dialog__close:hover{background:#f2f3f5}.ssw-icon-close{color:#6b7280;font-size:18px;line-height:1}.ssw-dialog__body{flex:1;min-height:0;display:flex}.ssw-pane{flex-direction:column;min-height:0;display:flex}.ssw-pane--tree{background:#fafbfc;border-right:1px solid #ebeef5;flex-shrink:0;width:240px}.ssw-pane__head{flex-shrink:0;align-items:baseline;gap:8px;padding:10px 14px 8px;font-size:13px;font-weight:600;display:flex}.ssw-tree-wrap{flex:1;min-height:0;padding:0 8px 8px;overflow:hidden}.ssw-tree-empty{text-align:center;color:#a0a4ab;padding:24px 8px;font-size:12px}.ssw-pane--grid{flex:1;min-width:0;padding:10px 16px 8px}.ssw-toolbar{flex-shrink:0;justify-content:flex-end;align-items:center;gap:12px;padding-bottom:10px;display:flex}.ssw-footer-count{color:#4b5563;white-space:nowrap;margin-right:8px;font-size:12px}.ssw-clear-all{color:#2660ff;cursor:pointer;margin-left:8px;text-decoration:none}.ssw-clear-all:hover{opacity:.8}.ssw-search{align-items:center;width:260px;display:flex;position:relative}.ssw-search__input{color:#232428;background:#fff;border:1px solid #d7dbe0;border-radius:4px;outline:none;width:100%;height:30px;padding:0 28px 0 10px;font-size:13px}.ssw-search__input:focus{border-color:#2660ff;box-shadow:0 0 0 2px #2660ff1f}.ssw-search__clear{color:#9ca3af;cursor:pointer;background:0 0;border:0;padding:2px;font-size:14px;line-height:1;position:absolute;right:6px}.ssw-search__clear:hover{color:#4b5563}.ssw-filter{flex-shrink:0;align-items:center;gap:6px;padding-bottom:10px;display:flex}.ssw-filter__label{color:#6b7280;flex-shrink:0;font-size:12px}.ssw-filter__btn{color:#4b5563;cursor:pointer;background:#fff;border:1px solid #d7dbe0;border-radius:4px;height:26px;padding:0 10px;font-size:12px;transition:border-color .15s,color .15s}.ssw-filter__btn:hover:not(.ssw-filter__btn--active){color:#2660ff;border-color:#2660ff}.ssw-filter__btn--active{color:#fff;background:#2660ff;border-color:#2660ff}.ssw-grid-wrap{background:#fff;border:1px solid #ebeef5;border-radius:4px;flex:1;min-height:0;position:relative;overflow:hidden}.ssw-grid-loading,.ssw-grid-empty{color:#6b7280;z-index:5;pointer-events:none;background:#ffffffbf;justify-content:center;align-items:center;font-size:13px;display:flex;position:absolute;inset:0}.ssw-grid-empty{background:#fff}.ssw-pagination{flex-shrink:0;justify-content:flex-end;padding-top:8px;display:flex}.ssw-dialog__footer{background:#fafbfc;border-top:1px solid #ebeef5;flex-shrink:0;align-items:center;gap:16px;min-height:56px;padding:10px 16px;display:flex}.ssw-footer-selection{flex:1;align-items:center;gap:6px;min-width:0;display:flex;overflow-x:auto}.ssw-footer-placeholder{color:#a0a4ab;font-size:12px}.ssw-chip{color:#2660ff;background:#eef3ff;border-radius:12px;flex-shrink:0;align-items:center;gap:4px;max-width:220px;padding:2px 6px 2px 10px;font-size:12px;display:inline-flex}.ssw-chip__text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.ssw-chip__remove{color:#2660ff;cursor:pointer;padding:0 2px;font-size:14px;line-height:1}.ssw-chip__remove:hover{color:#1d4ed8}.ssw-footer-actions{flex-shrink:0;align-items:center;gap:8px;display:flex}.ssw-field{color:#6b7280;cursor:pointer;box-sizing:border-box;background:#f5f6fa;border:1px solid #d7dbe0;border-radius:4px;justify-content:space-between;align-items:center;gap:8px;width:100%;min-width:200px;height:32px;padding:0 8px 0 12px;transition:border-color .2s;display:inline-flex}.ssw-field:hover:not(.ssw-field--disabled){border-color:#2660ff}.ssw-field--disabled{cursor:not-allowed;opacity:.7}.ssw-field__placeholder{color:#a0a4ab;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.ssw-field__value{text-overflow:ellipsis;white-space:nowrap;color:#232428;flex:1;min-width:0;overflow:hidden}.ssw-field__actions{flex-shrink:0;align-items:center;gap:6px;display:inline-flex}.ssw-field__clear,.ssw-field__open{color:#9ca3af;justify-content:center;align-items:center;padding:2px;font-size:14px;line-height:1;display:inline-flex}.ssw-field__clear:hover,.ssw-field__open:hover{color:#4b5563}.ssw-field__open{color:#2660ff;font-weight:700}
2
+ /*$vite$:1*/
@@ -0,0 +1,9 @@
1
+ import './index.css';
2
+ export * from './types';
3
+ export * from './constants';
4
+ export { ALL_SKILLS_NODE_ID, loadDomainTree, loadDomainTreeWithRoot, loadTopicNameMap, querySkillPage, } from './api/skill-select-data';
5
+ export type { SkillPageFilter } from './api/skill-select-data';
6
+ export { useSkillSelection } from './composables/use-skill-selection';
7
+ export type { UseSkillSelectionOptions, SkillSelectionStore } from './composables/use-skill-selection';
8
+ export { SkillSelectDialog } from './components/skill-select-dialog';
9
+ export { SkillSelectField } from './components/skill-select-field';
package/dist/index.js ADDED
@@ -0,0 +1,763 @@
1
+ import { request as e } from "@gsp-sys/utils";
2
+ import { computed as t, createTextVNode as n, createVNode as r, defineComponent as i, ref as a, shallowRef as o, watch as s } from "vue";
3
+ import { FButton as c, FDataGrid as ee, FPagination as l, FTreeView as u } from "@farris/ui-vue";
4
+ //#region src/types.ts
5
+ var d = { items: [] };
6
+ function f(e) {
7
+ let t = e.displayName || e.name;
8
+ return {
9
+ value: e.id,
10
+ text: t,
11
+ skill: e
12
+ };
13
+ }
14
+ //#endregion
15
+ //#region src/constants.ts
16
+ var p = [{
17
+ value: "GENERAL_OFFICE",
18
+ label: "通用办公技能"
19
+ }, {
20
+ value: "BUSINESS_SYSTEM",
21
+ label: "业务系统技能"
22
+ }], m = "GENERAL_OFFICE";
23
+ function h(e) {
24
+ if (!e) return "-";
25
+ let t = p.find((t) => t.value === e);
26
+ return t ? t.label : e;
27
+ }
28
+ var g = 10, _ = [
29
+ 10,
30
+ 20,
31
+ 50
32
+ ], v = class extends Error {
33
+ status;
34
+ details;
35
+ code;
36
+ category;
37
+ constructor(e, t, n, r, i) {
38
+ super(e), this.name = "ApiError", this.status = t, this.details = n, this.code = r, this.category = i;
39
+ }
40
+ get serverMessage() {
41
+ return this.details;
42
+ }
43
+ get innerMessage() {
44
+ return this.details;
45
+ }
46
+ }, y = "/api/runtime/aihub/v1.0";
47
+ function b() {
48
+ return typeof window > "u" ? {} : window.__SKILLHUB_RUNTIME_CONFIG__ ?? {};
49
+ }
50
+ function x(e) {
51
+ let t = b().apiBaseUrl;
52
+ return t && e.startsWith("/") ? `${t.replace(/\/$/, "")}${e}` : e;
53
+ }
54
+ var S = {
55
+ GET: "get",
56
+ POST: "post",
57
+ PUT: "put",
58
+ DELETE: "delete",
59
+ PATCH: "patch"
60
+ };
61
+ function C(e) {
62
+ let t = {};
63
+ typeof e?.body == "string" && (t["Content-Type"] = "application/json");
64
+ try {
65
+ t["Accept-Language"] = localStorage.getItem("language") || navigator.language.split("-")[0] || "en";
66
+ } catch {}
67
+ return t;
68
+ }
69
+ function te(e) {
70
+ if (e && typeof e == "object" && !Array.isArray(e)) {
71
+ let t = e;
72
+ if ("code" in t && "data" in t || "success" in t && "data" in t) return null;
73
+ }
74
+ return e;
75
+ }
76
+ function w(e) {
77
+ let t = e?.message ?? e?.Message ?? e?.errorMessage;
78
+ return typeof t == "string" && t ? t : void 0;
79
+ }
80
+ function T(e) {
81
+ if (e instanceof v) return e;
82
+ let t = e;
83
+ if (t?.isAxiosError) {
84
+ if (t.code === "ECONNABORTED" || t.code === "ETIMEDOUT") return new v("error.request.timeout", 408, "");
85
+ let e = t.response?.status ?? 0, n = w(t.response?.data) ?? `HTTP ${e || 0}`;
86
+ return new v(n, e, n);
87
+ }
88
+ if (e && typeof e == "object") {
89
+ let t = e, n = w(t) ?? "error", r = Number(t.code ?? t.Code ?? t.status) || 0, i = t.error;
90
+ return new v(n, r, n, i?.code, i?.category);
91
+ }
92
+ return typeof e == "string" ? new v(e || "error", 0, e || "error") : e instanceof Error ? new v(e.message || "error", 0, e.message || "error") : new v("error", 0, "");
93
+ }
94
+ async function E(t, n, r) {
95
+ let i = (n?.method ? S[n.method.toUpperCase()] : "get") ?? "get";
96
+ try {
97
+ return te(await e({
98
+ url: t,
99
+ method: i,
100
+ headers: C(n),
101
+ data: n?.body,
102
+ notLoading: !0,
103
+ ...r ? { responseType: r } : {}
104
+ }));
105
+ } catch (e) {
106
+ throw T(e);
107
+ }
108
+ }
109
+ function D(e) {
110
+ let t = new URLSearchParams();
111
+ for (let [n, r] of Object.entries(e)) r != null && r !== "" && t.append(n, String(r));
112
+ let n = t.toString();
113
+ return n ? `?${n}` : "";
114
+ }
115
+ //#endregion
116
+ //#region ../api-client/src/fetch/repo.ts
117
+ function O(e, t) {
118
+ return E(x(e), t);
119
+ }
120
+ //#endregion
121
+ //#region ../api-client/src/fetch/skill.ts
122
+ function k(e, t) {
123
+ return E(x(e), t);
124
+ }
125
+ //#endregion
126
+ //#region ../api-client/src/api/custom-skills.ts
127
+ var A = `${y}/customSkills`;
128
+ async function j(e, t) {
129
+ return k(`${A}/listByAuth${D({
130
+ page: e.page,
131
+ size: e.size,
132
+ keyword: e.keyword,
133
+ group: e.group,
134
+ businessDomain: e.businessDomain,
135
+ topic: e.topic,
136
+ category: e.category,
137
+ repo: e.repo
138
+ })}`, { signal: t });
139
+ }
140
+ `${y}`;
141
+ //#endregion
142
+ //#region ../api-client/src/api/topics.ts
143
+ var M = `${y}/topics`;
144
+ async function N() {
145
+ return O(M);
146
+ }
147
+ `${y}`;
148
+ //#endregion
149
+ //#region ../api-client/src/api/business-domains.ts
150
+ async function P() {
151
+ return await k("/api/runtime/aihub/v1.0/business-domains/tree") ?? [];
152
+ }
153
+ //#endregion
154
+ //#region src/api/skill-select-data.ts
155
+ var F = "__all_skills__", I = null, L = null, R = null, z = null;
156
+ async function B() {
157
+ return R || z || (z = N().then((e) => (R = e ?? [], R)).finally(() => {
158
+ z = null;
159
+ }), z);
160
+ }
161
+ function V(e) {
162
+ return {
163
+ id: e?.id ?? e?.code ?? "",
164
+ code: e?.code ?? e?.id,
165
+ name: e?.name ?? e?.code ?? "未命名",
166
+ children: Array.isArray(e?.children) ? e.children.map(V) : void 0
167
+ };
168
+ }
169
+ async function ne() {
170
+ return [{
171
+ id: F,
172
+ name: "全部技能",
173
+ children: await H()
174
+ }];
175
+ }
176
+ async function H() {
177
+ return I || L || (L = P().then((e) => (e ?? []).map(V)).then((e) => (I = e, e)).finally(() => {
178
+ L = null;
179
+ }), L);
180
+ }
181
+ function re(e) {
182
+ let t = /* @__PURE__ */ new Map(), n = (e) => {
183
+ for (let r of e) r.code && t.set(r.code, r.name), r.children && n(r.children);
184
+ };
185
+ return n(e), t;
186
+ }
187
+ async function U() {
188
+ let e = await B(), t = /* @__PURE__ */ new Map();
189
+ for (let n of e) {
190
+ let e = n?.name ?? n?.code ?? n?.id;
191
+ e && (n?.code && t.set(n.code, e), n?.id && t.set(n.id, e));
192
+ }
193
+ return t;
194
+ }
195
+ async function W() {
196
+ return (await B()).filter((e) => !!e?.code).map((e) => ({
197
+ id: e.code,
198
+ code: e.code,
199
+ name: e?.name ?? e.code
200
+ }));
201
+ }
202
+ async function ie() {
203
+ return [{
204
+ id: F,
205
+ name: "全部技能",
206
+ children: await W()
207
+ }];
208
+ }
209
+ function G(e, t) {
210
+ return j({
211
+ page: e.page ?? 0,
212
+ size: e.size ?? 10,
213
+ keyword: e.keyword?.trim() || void 0,
214
+ businessDomain: e.businessDomain || void 0,
215
+ topic: e.topic || void 0,
216
+ category: e.category || void 0
217
+ }, t);
218
+ }
219
+ //#endregion
220
+ //#region src/composables/use-skill-selection.ts
221
+ function K(e) {
222
+ let t = e ?? {};
223
+ return (t?.raw ?? t)?.id ?? t?.id;
224
+ }
225
+ function q(e = {}) {
226
+ let n = e.singleSelect ?? !1, r = o(/* @__PURE__ */ new Map()), i = o(/* @__PURE__ */ new Map());
227
+ function a(e) {
228
+ if (!e?.length) return;
229
+ let t = new Map(i.value);
230
+ for (let n of e) t.set(n.id, n);
231
+ i.value = t;
232
+ }
233
+ function s(e, t) {
234
+ let a = n && e.length > 1 ? e.slice(-1) : e, o = /* @__PURE__ */ new Map();
235
+ for (let e of a) {
236
+ let t = r.value.get(e);
237
+ if (t) {
238
+ o.set(e, t);
239
+ continue;
240
+ }
241
+ let n = i.value.get(e);
242
+ n ? o.set(e, f(n)) : o.set(e, {
243
+ value: e,
244
+ text: e
245
+ });
246
+ }
247
+ if (t) for (let [e, n] of r.value) t.has(e) || o.set(e, n);
248
+ r.value = o;
249
+ }
250
+ function c(e, t, n) {
251
+ s(Array.isArray(t) && t.length ? t : Array.isArray(e) ? e.map(K).filter(Boolean) : [], n);
252
+ }
253
+ function ee(e) {
254
+ let t = K(e);
255
+ if (!t) return;
256
+ let n = new Map(r.value);
257
+ if (n.has(t)) n.delete(t);
258
+ else {
259
+ let e = i.value.get(t);
260
+ n.set(t, e ? f(e) : {
261
+ value: t,
262
+ text: t
263
+ });
264
+ }
265
+ r.value = n;
266
+ }
267
+ function l(e) {
268
+ let t = K(e);
269
+ t && s([t]);
270
+ }
271
+ function u(e) {
272
+ let t = new Map(r.value);
273
+ t.delete(e), r.value = t;
274
+ }
275
+ function d() {
276
+ r.value = /* @__PURE__ */ new Map();
277
+ }
278
+ function p(e) {
279
+ if (!e?.length) {
280
+ r.value = /* @__PURE__ */ new Map();
281
+ return;
282
+ }
283
+ let t = /* @__PURE__ */ new Map();
284
+ for (let n of e) n?.value && t.set(n.value, n);
285
+ r.value = t;
286
+ }
287
+ return {
288
+ singleSelect: n,
289
+ selectedIds: t(() => [...r.value.keys()]),
290
+ items: t(() => [...r.value.values()]),
291
+ count: t(() => r.value.size),
292
+ indexPageItems: a,
293
+ handleGridSelectionUpdate: c,
294
+ toggleItem: ee,
295
+ selectRow: l,
296
+ removeItem: u,
297
+ clear: d,
298
+ restore: p
299
+ };
300
+ }
301
+ //#endregion
302
+ //#region src/components/skill-select-dialog.tsx
303
+ var ae = [
304
+ {
305
+ field: "_name",
306
+ title: "技能名称",
307
+ width: "26%",
308
+ showEllipsis: !0,
309
+ showTips: !0,
310
+ tipMode: "auto"
311
+ },
312
+ {
313
+ field: "_version",
314
+ title: "最新版本",
315
+ width: "10%"
316
+ },
317
+ {
318
+ field: "_category",
319
+ title: "使用类别",
320
+ width: "14%",
321
+ showEllipsis: !0
322
+ },
323
+ {
324
+ field: "_topicName",
325
+ title: "技能分类",
326
+ width: "17%",
327
+ showEllipsis: !0,
328
+ showTips: !0,
329
+ tipMode: "auto"
330
+ },
331
+ {
332
+ field: "_domainName",
333
+ title: "所属业务域",
334
+ width: "18%",
335
+ showEllipsis: !0,
336
+ showTips: !0,
337
+ tipMode: "auto"
338
+ },
339
+ {
340
+ field: "_author",
341
+ title: "作者",
342
+ width: "15%",
343
+ showEllipsis: !0
344
+ }
345
+ ], oe = "GENERAL_OFFICE", se = [
346
+ {
347
+ field: "_name",
348
+ title: "技能名称",
349
+ width: "33%",
350
+ showEllipsis: !0,
351
+ showTips: !0,
352
+ tipMode: "auto"
353
+ },
354
+ {
355
+ field: "_version",
356
+ title: "最新版本",
357
+ width: "34%"
358
+ },
359
+ {
360
+ field: "_topicName",
361
+ title: "技能分类",
362
+ width: "33%",
363
+ showEllipsis: !0,
364
+ showTips: !0,
365
+ tipMode: "auto"
366
+ }
367
+ ], ce = [
368
+ {
369
+ field: "_name",
370
+ title: "技能名称",
371
+ width: "33%",
372
+ showEllipsis: !0,
373
+ showTips: !0,
374
+ tipMode: "auto"
375
+ },
376
+ {
377
+ field: "_version",
378
+ title: "最新版本",
379
+ width: "34%"
380
+ },
381
+ {
382
+ field: "_domainName",
383
+ title: "所属业务域",
384
+ width: "33%",
385
+ showEllipsis: !0,
386
+ showTips: !0,
387
+ tipMode: "auto"
388
+ }
389
+ ], J = /* @__PURE__ */ i({
390
+ name: "SkillSelectDialog",
391
+ props: {
392
+ modelValue: {
393
+ type: Boolean,
394
+ default: !1
395
+ },
396
+ singleSelect: {
397
+ type: Boolean,
398
+ default: !1
399
+ },
400
+ initSelected: {
401
+ type: Array,
402
+ default: () => []
403
+ },
404
+ initCategory: {
405
+ type: String,
406
+ default: ""
407
+ },
408
+ title: {
409
+ type: String,
410
+ default: "选择技能"
411
+ }
412
+ },
413
+ emits: ["update:modelValue", "confirm"],
414
+ setup(e, { emit: i }) {
415
+ let o = t(() => e.modelValue), d = t(() => e.initCategory === oe), f = a(null), m = a(null), g = a(0), v = a(F), y = a(/* @__PURE__ */ new Map()), b = a(/* @__PURE__ */ new Map()), x = t(() => d.value ? m.value : f.value), S = t(() => x.value ? [x.value] : []), C = t(() => e.initCategory === "GENERAL_OFFICE" ? se : e.initCategory === "BUSINESS_SYSTEM" ? ce : ae), te = {
416
+ enableSelectRow: !0,
417
+ multiSelect: !1,
418
+ showCheckbox: !1,
419
+ showSelectAll: !1,
420
+ showSelection: !0
421
+ }, w = a(""), T = a(""), E = a(""), D = a(0), O = a(10), k = a(0), A = a([]), j = a(!1), M = a(null), N = a(0), P = q({ singleSelect: e.singleSelect }), I = t(() => e.singleSelect ? {
422
+ enableSelectRow: !1,
423
+ multiSelect: !1,
424
+ showCheckbox: !0,
425
+ showSelectAll: !1,
426
+ showSelection: !1
427
+ } : {
428
+ enableSelectRow: !1,
429
+ multiSelect: !0,
430
+ showCheckbox: !0,
431
+ showSelectAll: !0,
432
+ showSelection: !1,
433
+ multiSelectMode: "DependOnCheck",
434
+ keepSelectingOnPaging: !0
435
+ });
436
+ function L(e) {
437
+ return {
438
+ ...e,
439
+ _name: e.displayName || e.name,
440
+ _version: e.latestVersion || "-",
441
+ _category: h(e.category),
442
+ _topicName: e.topic ? b.value.get(e.topic) ?? e.topic : "-",
443
+ _domainName: e.businessDomain ? y.value.get(e.businessDomain) ?? e.businessDomain : "-",
444
+ _author: e.ownerDisplayName || e.author || "-"
445
+ };
446
+ }
447
+ let R = t(() => A.value.map(L));
448
+ s(R, (e) => {
449
+ M.value?.updateDataSource && M.value.updateDataSource(e);
450
+ }, { flush: "post" });
451
+ async function z() {
452
+ let e = ++N.value;
453
+ j.value = !0;
454
+ try {
455
+ let t = B(), n = await G({
456
+ page: D.value,
457
+ size: O.value,
458
+ keyword: T.value,
459
+ businessDomain: d.value ? void 0 : t,
460
+ topic: d.value ? t : void 0,
461
+ category: E.value || void 0
462
+ });
463
+ if (e !== N.value) return;
464
+ A.value = n?.items ?? [], k.value = n?.total ?? 0, P.indexPageItems(A.value);
465
+ } catch (t) {
466
+ if (e !== N.value) return;
467
+ A.value = [], k.value = 0, console.error("[skill-select-workbench] 查询技能列表失败", t);
468
+ } finally {
469
+ e === N.value && (j.value = !1);
470
+ }
471
+ }
472
+ function B() {
473
+ let e = x.value;
474
+ if (!e || v.value === "__all_skills__") return;
475
+ let t = (e) => {
476
+ if (e) for (let n of e) {
477
+ if (n.id === v.value) return n.code;
478
+ let e = t(n.children);
479
+ if (e) return e;
480
+ }
481
+ };
482
+ return t(e.children);
483
+ }
484
+ function V(e) {
485
+ e?.id && (v.value = e.id, D.value = 0, z());
486
+ }
487
+ let H = t(() => (A.value.length, P.selectedIds.value));
488
+ function W(e, t) {
489
+ let n = new Set(A.value.map((e) => e.id));
490
+ P.handleGridSelectionUpdate(e, t, n);
491
+ }
492
+ let K = null;
493
+ function J(e) {
494
+ K = e.target;
495
+ }
496
+ function Y() {
497
+ let e = K;
498
+ return !!e && typeof e.closest == "function" && !!e.closest(".custom-checkbox, label, input[type=\"checkbox\"]");
499
+ }
500
+ function le(e) {
501
+ let t = e ?? {};
502
+ return t.raw?.id ?? t.id;
503
+ }
504
+ function ue(t, n) {
505
+ if (!Y()) {
506
+ if (e.singleSelect) {
507
+ let e = le(n), t = P.items.value;
508
+ if (e && t.length === 1 && t[0].value === e) {
509
+ P.clear();
510
+ return;
511
+ }
512
+ P.selectRow(n);
513
+ return;
514
+ }
515
+ P.toggleItem(n);
516
+ }
517
+ }
518
+ function de(e) {
519
+ D.value = e, z();
520
+ }
521
+ function fe(e) {
522
+ O.value = e, D.value = 0, z();
523
+ }
524
+ function X(e) {
525
+ E.value = e, D.value = 0, z();
526
+ }
527
+ let Z;
528
+ function pe(e) {
529
+ w.value = e.target.value, Z && clearTimeout(Z), Z = setTimeout(() => {
530
+ T.value = w.value.trim(), D.value = 0, z();
531
+ }, 350);
532
+ }
533
+ function me() {
534
+ w.value = "", T.value = "", D.value = 0, z();
535
+ }
536
+ let Q = !1;
537
+ async function he() {
538
+ if (!Q) {
539
+ Q = !0;
540
+ try {
541
+ w.value = "", T.value = "", E.value = e.initCategory, D.value = 0, O.value = 10, k.value = 0, A.value = [], N.value++, v.value = F, P.restore(e.initSelected);
542
+ try {
543
+ if (d.value) {
544
+ if (!m.value) {
545
+ let e = await ie();
546
+ m.value = e[0], g.value++;
547
+ }
548
+ } else if (!f.value) {
549
+ let e = await ne();
550
+ f.value = e[0], y.value = re(e[0]?.children ?? []), g.value++;
551
+ }
552
+ if (b.value.size === 0) {
553
+ let e = await U();
554
+ b.value.size === 0 && (b.value = e);
555
+ }
556
+ } catch (e) {
557
+ console.error("[skill-select-workbench] 加载筛选数据失败", e);
558
+ }
559
+ await z();
560
+ } finally {
561
+ Q = !1;
562
+ }
563
+ }
564
+ }
565
+ s(o, (e) => {
566
+ e && he();
567
+ }, { immediate: !0 });
568
+ function $() {
569
+ i("update:modelValue", !1);
570
+ }
571
+ function ge() {
572
+ i("confirm", { items: P.items.value }), $();
573
+ }
574
+ function _e(e) {
575
+ P.removeItem(e);
576
+ }
577
+ function ve() {
578
+ P.clear();
579
+ }
580
+ function ye(e) {
581
+ e.stopPropagation();
582
+ }
583
+ return () => o.value ? r("div", {
584
+ class: "ssw-overlay",
585
+ onMousedown: ye,
586
+ onClick: ye
587
+ }, [r("div", {
588
+ class: "ssw-dialog",
589
+ role: "dialog",
590
+ "aria-modal": "true"
591
+ }, [
592
+ r("div", { class: "ssw-dialog__header" }, [r("span", { class: "ssw-dialog__title" }, [e.title]), r("button", {
593
+ type: "button",
594
+ class: "ssw-dialog__close",
595
+ onClick: $,
596
+ "aria-label": "关闭"
597
+ }, [r("span", { class: "ssw-icon-close" }, [n("×")])])]),
598
+ r("div", { class: "ssw-dialog__body" }, [r("div", { class: "ssw-pane ssw-pane--tree" }, [r("div", { class: "ssw-pane__head" }, [r("span", null, [d.value ? "技能分类" : "业务域"])]), r("div", { class: "ssw-tree-wrap" }, [S.value.length ? r(u, {
599
+ key: g.value,
600
+ data: S.value,
601
+ idField: "id",
602
+ displayField: "name",
603
+ columns: [{
604
+ field: "name",
605
+ title: "",
606
+ width: "100%",
607
+ showEllipsis: !0,
608
+ showTips: !0,
609
+ tipMode: "auto"
610
+ }],
611
+ selection: te,
612
+ selectionValues: [v.value],
613
+ fit: !0,
614
+ onClickRow: (e, t) => V(t)
615
+ }, null) : r("div", { class: "ssw-tree-empty" }, [n("加载中…")])])]), r("div", { class: "ssw-pane ssw-pane--grid" }, [
616
+ r("div", { class: "ssw-toolbar" }, [r("div", { class: "ssw-search" }, [r("input", {
617
+ class: "ssw-search__input",
618
+ placeholder: "搜索技能名称",
619
+ value: w.value,
620
+ onInput: pe
621
+ }, null), w.value ? r("button", {
622
+ type: "button",
623
+ class: "ssw-search__clear",
624
+ onClick: me
625
+ }, [n("×")]) : null])]),
626
+ !e.initCategory && r("div", { class: "ssw-filter" }, [
627
+ r("span", { class: "ssw-filter__label" }, [n("技能类型")]),
628
+ r("button", {
629
+ type: "button",
630
+ class: ["ssw-filter__btn", E.value === "" ? "ssw-filter__btn--active" : ""].join(" "),
631
+ onClick: () => X("")
632
+ }, [n("全部")]),
633
+ p.map((e) => r("button", {
634
+ type: "button",
635
+ class: ["ssw-filter__btn", E.value === e.value ? "ssw-filter__btn--active" : ""].join(" "),
636
+ onClick: () => X(e.value)
637
+ }, [e.label]))
638
+ ]),
639
+ r("div", {
640
+ class: "ssw-grid-wrap",
641
+ onClickCapture: J
642
+ }, [
643
+ r(ee, {
644
+ ref: M,
645
+ idField: "id",
646
+ data: R.value,
647
+ columns: C.value,
648
+ selection: I.value,
649
+ selectionValues: H.value,
650
+ onSelectionUpdate: W,
651
+ onClickRow: ue,
652
+ fit: !0,
653
+ showHeader: !0
654
+ }, null),
655
+ j.value && r("div", { class: "ssw-grid-loading" }, [n("加载中…")]),
656
+ !j.value && R.value.length === 0 && r("div", { class: "ssw-grid-empty" }, [n("暂无可选技能")])
657
+ ]),
658
+ k.value > 0 && r("div", { class: "ssw-pagination" }, [r(l, {
659
+ currentPage: D.value + 1,
660
+ totalItems: k.value,
661
+ pageSize: O.value,
662
+ pageList: _,
663
+ showPageList: !0,
664
+ showGoButton: !1,
665
+ showPageInfo: !0,
666
+ "onUpdate:currentPage": (e) => de(e - 1),
667
+ "onUpdate:pageSize": (e) => fe(e)
668
+ }, null)])
669
+ ])]),
670
+ r("div", { class: "ssw-dialog__footer" }, [r("div", { class: "ssw-footer-selection" }, [P.items.value.length === 0 ? r("span", { class: "ssw-footer-placeholder" }, [e.singleSelect ? "请选择一个技能" : "请选择技能,可跨页多选"]) : P.items.value.map((e) => r("span", {
671
+ key: e.value,
672
+ class: "ssw-chip",
673
+ title: e.text
674
+ }, [r("span", { class: "ssw-chip__text" }, [e.text]), r("a", {
675
+ class: "ssw-chip__remove",
676
+ onClick: (t) => {
677
+ t.stopPropagation(), _e(e.value);
678
+ }
679
+ }, [n("×")])]))]), r("div", { class: "ssw-footer-actions" }, [
680
+ r("span", { class: "ssw-footer-count" }, [
681
+ n("已选 "),
682
+ P.count.value,
683
+ n(" 条"),
684
+ P.count.value > 0 && r("a", {
685
+ class: "ssw-clear-all",
686
+ onClick: ve
687
+ }, [n("清空")])
688
+ ]),
689
+ r(c, { onClick: $ }, { default: () => [n("取消")] }),
690
+ r(c, {
691
+ type: "primary",
692
+ onClick: ge
693
+ }, { default: () => [n("确定")] })
694
+ ])])
695
+ ])]) : null;
696
+ }
697
+ }), Y = /* @__PURE__ */ i({
698
+ name: "SkillSelectField",
699
+ props: {
700
+ singleSelect: {
701
+ type: Boolean,
702
+ default: !1
703
+ },
704
+ asyncMode: {
705
+ type: Boolean,
706
+ default: !0
707
+ },
708
+ disabled: {
709
+ type: Boolean,
710
+ default: !1
711
+ },
712
+ placeholder: {
713
+ type: String,
714
+ default: "请选择技能"
715
+ },
716
+ selectedItems: {
717
+ type: Array,
718
+ default: () => []
719
+ }
720
+ },
721
+ emits: ["update:selectedItems"],
722
+ setup(e, { emit: t }) {
723
+ let i = a(!1);
724
+ function o() {
725
+ e.disabled || (i.value = !0);
726
+ }
727
+ function s(e) {
728
+ t("update:selectedItems", e.items), i.value = !1;
729
+ }
730
+ function c(e) {
731
+ e.stopPropagation();
732
+ }
733
+ return () => {
734
+ let a = e.selectedItems ?? [];
735
+ return r("div", null, [r("div", {
736
+ class: ["ssw-field", { "ssw-field--disabled": e.disabled }].join(" "),
737
+ onClick: o
738
+ }, [a.length === 0 ? r("span", { class: "ssw-field__placeholder" }, [e.placeholder]) : r("span", {
739
+ class: "ssw-field__value",
740
+ title: a.map((e) => e.text).join("、")
741
+ }, [e.singleSelect ? a[a.length - 1]?.text : `${a[0]?.text}${a.length > 1 ? ` 等 ${a.length} 项` : ""}`]), r("span", { class: "ssw-field__actions" }, [a.length > 0 && r("a", {
742
+ class: "ssw-field__clear",
743
+ role: "button",
744
+ "aria-label": "清空",
745
+ onClick: (e) => {
746
+ c(e), t("update:selectedItems", []);
747
+ }
748
+ }, [n("×")]), r("span", {
749
+ class: "ssw-field__open",
750
+ role: "button",
751
+ "aria-label": "选择"
752
+ }, [n("···")])])]), i.value && r(J, {
753
+ modelValue: i.value,
754
+ "onUpdate:modelValue": (e) => i.value = e,
755
+ singleSelect: e.singleSelect,
756
+ initSelected: a,
757
+ onConfirm: s
758
+ }, null)]);
759
+ };
760
+ }
761
+ });
762
+ //#endregion
763
+ export { F as ALL_SKILLS_NODE_ID, m as DEFAULT_CATEGORY, d as EMPTY_SKILL_SELECT_PAYLOAD, p as SKILL_CATEGORIES, g as SKILL_PAGE_SIZE, _ as SKILL_PAGE_SIZE_OPTIONS, J as SkillSelectDialog, Y as SkillSelectField, h as categoryLabel, H as loadDomainTree, ne as loadDomainTreeWithRoot, U as loadTopicNameMap, G as querySkillPage, f as toSkillSelectItem, q as useSkillSelection };
@@ -0,0 +1,31 @@
1
+ import type { SkillSummaryResponse } from '@aihub/api-client';
2
+ /** 左侧筛选树的节点(业务域层级),数据结构与 listBusinessDomains 返回值对齐 */
3
+ export interface SkillDomainTreeNode {
4
+ /** 节点 ID(业务域 id;"全部技能"虚拟节点为固定串 __all_skills__) */
5
+ id: string;
6
+ /** 业务域编码(作为 listSkills 的 businessDomain 过滤值;缺省回退为 id) */
7
+ code?: string;
8
+ /** 展示名称 */
9
+ name: string;
10
+ /** 子业务域 */
11
+ children?: SkillDomainTreeNode[];
12
+ }
13
+ /**
14
+ * 选中的技能项(对外契约,语义对齐 @gsp-sys 组织选择帮助的 selectedItems 条目):
15
+ * - value:技能 id,用于保存/回传
16
+ * - text:回显文本(displayName || name)
17
+ * - skill:完整技能快照,供确认后消费方直接使用
18
+ */
19
+ export interface SkillSelectItem {
20
+ value: string;
21
+ text: string;
22
+ skill?: SkillSummaryResponse | null;
23
+ }
24
+ /** 弹窗确认回调的载荷 */
25
+ export interface SkillSelectConfirmPayload {
26
+ items: SkillSelectItem[];
27
+ }
28
+ /** 空载荷(确认时无可选技能时使用) */
29
+ export declare const EMPTY_SKILL_SELECT_PAYLOAD: SkillSelectConfirmPayload;
30
+ /** 由技能快照构造对外条目(文本取 displayName || name) */
31
+ export declare function toSkillSelectItem(skill: SkillSummaryResponse): SkillSelectItem;
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@manohub/skill-select-workbench",
3
+ "version": "0.0.1-beta1",
4
+ "type": "module",
5
+ "description": "技能选择帮助(共享组件包):左业务域树 + 右技能表双面板选择,基于 @farris/ui-vue 与 @aihub/api-client 新契约",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./index.css": "./dist/index.css"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "sideEffects": [
20
+ "**/*.css"
21
+ ],
22
+ "peerDependencies": {
23
+ "@farris/ui-vue": "^1.8.0",
24
+ "vue": "^3.4.0"
25
+ },
26
+ "devDependencies": {
27
+ "@farris/ui-vue": "1.8.4",
28
+ "@gsp-sys/utils": "^0.0.6",
29
+ "@vitejs/plugin-vue-jsx": "^5.1.6",
30
+ "typescript": "^5.5.3",
31
+ "vite": "^8.2.2",
32
+ "vue": "^3.4.37",
33
+ "vue-tsc": "^2.0.29",
34
+ "@aihub/api-client": "0.0.1"
35
+ },
36
+ "scripts": {
37
+ "typecheck": "vue-tsc --noEmit -p tsconfig.json",
38
+ "build": "pnpm typecheck && vite build && vue-tsc -p tsconfig.build.json",
39
+ "playground": "vite --config playground/vite.config.ts"
40
+ }
41
+ }