@gspwidget/wisedoc-mobile-selector 0.0.0-stage → 1.1.2

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 @@
1
+ {"type":"module","sideEffects":["*.css"]}
@@ -0,0 +1 @@
1
+ .mobile-frame[data-v-ff77d347]{--accent: #2660ff;font-family:PingFang SC,-apple-system,BlinkMacSystemFont,Microsoft YaHei,sans-serif;color:#1a1a1a;background:#fff;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased}.mobile-frame[data-v-ff77d347],.mobile-frame[data-v-ff77d347] *,.mobile-frame[data-v-ff77d347] *:before,.mobile-frame[data-v-ff77d347] *:after{box-sizing:border-box}[type=button][data-v-ff77d347]{margin-top:0;margin-bottom:0;font:inherit}[type=button][data-v-ff77d347]:after{border:none}[type=button][data-v-ff77d347]{cursor:pointer;-webkit-tap-highlight-color:transparent}[type=button][data-v-ff77d347]:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.mobile-frame[data-v-ff77d347]{display:flex;flex-direction:column;width:100%;height:100%;min-height:0;max-width:600px;margin:0 auto;background:#fff;overflow:hidden}.selector-sheet[data-v-ff77d347]{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;overflow:hidden;background:#fff}.sheet-header[data-v-ff77d347]{position:relative;display:flex;flex:0 0 76px;align-items:center;justify-content:center;padding-top:4px}.sheet-header h1[data-v-ff77d347]{margin:0;padding:0 48px;font-size:17px;font-weight:500;line-height:24px;letter-spacing:.3px}.header-back[data-v-ff77d347],.header-close[data-v-ff77d347]{position:absolute;top:18px;display:grid;width:44px;height:44px;padding:0;place-items:center;border:0;background:transparent;color:#1a1a1a}.header-back[data-v-ff77d347]{left:0}.header-close[data-v-ff77d347]{right:0}.header-icon[data-v-ff77d347]{display:block;width:16px;height:16px}.tabs[data-v-ff77d347],.breadcrumbs[data-v-ff77d347]{display:flex;align-items:center;min-width:0}.tabs[data-v-ff77d347]{flex:0 0 48px;height:48px;padding:0 16px;border-bottom:1px solid #e6e6e6;font-size:16px;line-height:24px;white-space:nowrap}.tab-active[data-v-ff77d347]{position:relative;display:flex;align-items:center;height:48px;color:var(--accent);font-weight:500}.tab-active[data-v-ff77d347]:after{position:absolute;bottom:0;left:0;width:100%;height:3px;border-radius:2px;background:linear-gradient(267deg,#2660ff,#3b82ff);content:""}.breadcrumbs[data-v-ff77d347]{flex:0 0 56px;height:56px;gap:0;padding:8px 16px 0;overflow-x:auto;scrollbar-width:none;white-space:nowrap}.breadcrumbs[data-v-ff77d347]::-webkit-scrollbar{display:none}.breadcrumbs [type=button][data-v-ff77d347]{flex:0 0 auto;max-width:min(47vw,176px);overflow:hidden;padding:0;border:0;background:transparent;color:#666;font-size:16px;line-height:24px;text-overflow:ellipsis;white-space:nowrap}.breadcrumbs .crumb-current[data-v-ff77d347]{max-width:min(30vw,160px);color:#1a1a1a;font-weight:500}.crumb-divider[data-v-ff77d347]{display:grid;flex:0 0 16px;place-items:center;width:16px;height:24px;color:#d1d1d1;font-size:16px;line-height:24px}.content-list[data-v-ff77d347]{flex:1 1 auto;min-height:0;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:none}.content-list[data-v-ff77d347]::-webkit-scrollbar{display:none}.root-list[data-v-ff77d347]{padding-top:8px}.entry-row[data-v-ff77d347]{display:flex;align-items:center;gap:12px;min-width:0;height:56px;padding:0 16px}.check-button[data-v-ff77d347]{position:relative;display:grid;flex:0 0 24px;place-items:center;width:24px;height:24px;padding:0;border:0;background:transparent}.check-button[data-v-ff77d347]:before{position:absolute;top:1px;right:1px;bottom:1px;left:1px;border:1px solid #999;border-radius:50%;background:#fff;content:""}.check-button.checked[data-v-ff77d347]:before,.check-button.indeterminate[data-v-ff77d347]:before{border-color:var(--accent);background:var(--accent)}.check-button.locked[data-v-ff77d347]{cursor:not-allowed}.content-list:not(.selected-list) .check-button[data-v-ff77d347]{margin-right:4px}.check-mark[data-v-ff77d347]{position:relative;width:12px;height:7px;margin-top:-2px;transform:rotate(-45deg);border-left:2px solid #fff;border-bottom:2px solid #fff}.half-mark[data-v-ff77d347]{position:relative;width:10px;height:2px;border-radius:1px;background:#fff}.entry-icon[data-v-ff77d347]{flex:0 0 32px;width:32px;height:32px;object-fit:contain}.entry-name[data-v-ff77d347]{flex:1 1 auto;min-width:0;overflow:hidden;padding:0;border:0;background:transparent;color:#1a1a1a;font-size:16px;font-weight:400;line-height:24px;text-align:left;text-overflow:ellipsis;white-space:nowrap}.row-next[data-v-ff77d347]{display:grid;flex:0 0 28px;place-items:center;width:28px;height:44px;margin-right:-8px;padding:0;border:0;background:transparent}.right-chevron[data-v-ff77d347]{display:inline-block;flex:0 0 auto;width:9px;height:9px;transform:rotate(-45deg);border-right:1.6px solid #a1a1a1;border-bottom:1.6px solid #a1a1a1}.status[data-v-ff77d347]{display:flex;flex-direction:column;align-items:center;gap:14px;justify-content:center;min-height:160px;padding:24px;color:#777;font-size:14px;text-align:center}.status.error[data-v-ff77d347]{color:#d9363e}.empty-state[data-v-ff77d347]{min-height:240px;gap:16px;color:gray;font-size:14px;line-height:22px}.empty-state img[data-v-ff77d347]{display:block;width:120px;height:100px}.status [type=button][data-v-ff77d347],.inline-error [type=button][data-v-ff77d347],.more-button[data-v-ff77d347]{padding:0;border:0;background:transparent;color:var(--accent)}.more-button[data-v-ff77d347]{display:block;width:100%;height:48px;font-size:14px}.more-button[data-v-ff77d347]:disabled{color:#aaa}.inline-error[data-v-ff77d347]{padding:12px 16px;color:#d9363e;font-size:13px;text-align:center}.selected-list[data-v-ff77d347]{padding-top:0}.selected-list .entry-row[data-v-ff77d347]{gap:12px}.selected-name[data-v-ff77d347]{margin-right:auto}.remove-button[data-v-ff77d347]{position:relative;display:grid;flex:0 0 32px;place-items:center;width:32px;height:44px;margin-right:-8px;margin-left:-4px;padding:0;border:0;background:transparent;font-size:0}.remove-icon[data-v-ff77d347]{display:block;flex:0 0 16px;width:16px;height:16px}.footer-bar[data-v-ff77d347]{display:flex;flex:0 0 64px;align-items:center;gap:16px;height:64px;margin:0;padding:0 16px;border-top:1px solid #e6e6e6;background:rgba(255,255,255,.95)}.selection-summary[data-v-ff77d347]{display:flex;flex:1 1 auto;align-items:center;gap:8px;min-width:0;min-height:40px;padding:0;border:0;background:transparent;color:#1a1a1a;font-size:14px;line-height:24px;text-align:left}.summary-arrow[data-v-ff77d347]{display:block;flex:0 0 12px;width:12px;height:12px;color:#999}.selection-summary .summary-label[data-v-ff77d347]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.summary-label strong[data-v-ff77d347]{font-weight:500}.selection-summary[data-v-ff77d347]:disabled{color:#777;cursor:default}.confirm-button[data-v-ff77d347]{flex:0 0 64px;width:64px;height:32px;line-height:32px;padding:0;border:0;border-radius:6px;background:var(--accent);color:#fff;font-size:14px;font-weight:500}.confirm-button[data-v-ff77d347]:disabled{background:#aabfff}.notice[data-v-ff77d347]{position:fixed;right:16px;bottom:80px;left:16px;max-width:500px;margin:0 auto;padding:10px 14px;border-radius:8px;background:rgba(0,0,0,.78);color:#fff;font-size:13px;text-align:center}@media (max-width: 350px){.entry-row[data-v-ff77d347]{gap:9px;padding:0 12px}.entry-name[data-v-ff77d347]{font-size:15px}.footer-bar[data-v-ff77d347]{gap:8px;padding-right:12px;padding-left:12px}}
@@ -0,0 +1,48 @@
1
+ import { type KnowledgeApiOptions } from './shared/data-source';
2
+ import type { KnowledgeDataSource, KnowledgeSelection, KnowledgeSelectionResult } from './shared/types';
3
+ declare function setSelection(value: unknown): void;
4
+ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<{
5
+ dataSource?: KnowledgeDataSource | undefined;
6
+ apiOptions?: KnowledgeApiOptions | undefined;
7
+ initialSelection?: KnowledgeSelection[] | undefined;
8
+ iframeMessaging?: boolean | undefined;
9
+ }>, {
10
+ iframeMessaging: boolean;
11
+ }>>, {
12
+ setSelection: typeof setSelection;
13
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
14
+ error: (error: unknown) => void;
15
+ close: () => void;
16
+ confirm: (payload: KnowledgeSelectionResult, selections: KnowledgeSelection[]) => void;
17
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<{
18
+ dataSource?: KnowledgeDataSource | undefined;
19
+ apiOptions?: KnowledgeApiOptions | undefined;
20
+ initialSelection?: KnowledgeSelection[] | undefined;
21
+ iframeMessaging?: boolean | undefined;
22
+ }>, {
23
+ iframeMessaging: boolean;
24
+ }>>> & Readonly<{
25
+ onError?: ((error: unknown) => any) | undefined;
26
+ onClose?: (() => any) | undefined;
27
+ onConfirm?: ((payload: KnowledgeSelectionResult, selections: KnowledgeSelection[]) => any) | undefined;
28
+ }>, {
29
+ iframeMessaging: boolean;
30
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
31
+ export default _default;
32
+ type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
33
+ type __VLS_TypePropsToRuntimeProps<T> = {
34
+ [K in keyof T]-?: {} extends Pick<T, K> ? {
35
+ type: import('vue').PropType<__VLS_NonUndefinedable<T[K]>>;
36
+ } : {
37
+ type: import('vue').PropType<T[K]>;
38
+ required: true;
39
+ };
40
+ };
41
+ type __VLS_WithDefaults<P, D> = {
42
+ [K in keyof Pick<P, keyof P>]: K extends keyof D ? __VLS_Prettify<P[K] & {
43
+ default: D[K];
44
+ }> : P[K];
45
+ };
46
+ type __VLS_Prettify<T> = {
47
+ [K in keyof T]: T[K];
48
+ } & {};
@@ -0,0 +1,3 @@
1
+ import type { KnowledgeDataSource } from './shared/types';
2
+ /** Development-only visual fixture; production always uses the real API source. */
3
+ export declare const demoSource: KnowledgeDataSource;
@@ -0,0 +1,8 @@
1
+ import type { MobileKnowledgeSelectorComponent } from './public-types';
2
+ export declare const MobileKnowledgeSelector: MobileKnowledgeSelectorComponent;
3
+ export default MobileKnowledgeSelector;
4
+ export type { MobileKnowledgeSelectorProps } from './public-types';
5
+ export { createKnowledgeDataSource, DEFAULT_KNOWLEDGE_SELECTOR_CONFIG_URL } from './shared/data-source';
6
+ export type { KnowledgeApiConfig, KnowledgeApiOptions } from './shared/data-source';
7
+ export type { KnowledgeRequestFn, KnowledgeRequest, KnowledgeResponse, KnowledgeHttpClient } from './shared/http';
8
+ export type { KnowledgeKind, KnowledgeNode, KnowledgeSelection, KnowledgeSelectionResult, KnowledgeChildren, KnowledgeDataSource } from './shared/types';
@@ -0,0 +1,15 @@
1
+ import type { ComponentOptionsMixin, DefineComponent } from 'vue';
2
+ import type { KnowledgeApiOptions } from './shared/data-source';
3
+ import type { KnowledgeDataSource, KnowledgeSelection, KnowledgeSelectionResult } from './shared/types';
4
+ export interface MobileKnowledgeSelectorProps {
5
+ dataSource?: KnowledgeDataSource;
6
+ apiOptions?: KnowledgeApiOptions;
7
+ initialSelection?: KnowledgeSelection[];
8
+ }
9
+ export type MobileKnowledgeSelectorComponent = DefineComponent<MobileKnowledgeSelectorProps, {
10
+ setSelection(value: KnowledgeSelection[]): void;
11
+ }, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {
12
+ confirm: (result: KnowledgeSelectionResult, selections: KnowledgeSelection[]) => void;
13
+ close: () => void;
14
+ error: (error: unknown) => void;
15
+ }>;
@@ -0,0 +1,27 @@
1
+ import type { KnowledgeHttpClient, KnowledgeRequestFn } from './http';
2
+ import type { KnowledgeDataSource } from './types';
3
+ export interface KnowledgeApiOptions {
4
+ /** Inject the host Axios instance to reuse authentication interceptors. */
5
+ http?: KnowledgeHttpClient;
6
+ /** Native hosts own authentication and supply the response envelope { data, status? }. */
7
+ request?: KnowledgeRequestFn;
8
+ baseURL?: string;
9
+ withCredentials?: boolean;
10
+ /** Resolve config, API and authentication URLs against the selector server when used as a remote script. */
11
+ serviceOrigin?: string;
12
+ /** Runtime configuration, useful when the host already loaded its own settings. */
13
+ config?: KnowledgeApiConfig;
14
+ /** Overrides the shared deploy-time JSON URL; an empty string disables file loading. */
15
+ configUrl?: string;
16
+ }
17
+ /** Shared static file under the 5300 service's web root, independent of the host app directory. */
18
+ export declare const DEFAULT_KNOWLEDGE_SELECTOR_CONFIG_URL = "/subapp/wisedoc/knowledge-selector.config.json";
19
+ export interface KnowledgeApiConfig {
20
+ /** true: /api/wisedoc/rest; false: /api/cms/cs/v1.0. Defaults to true. */
21
+ useWisedocApi?: boolean;
22
+ /** Optional origin or reverse-proxy prefix, e.g. https://host:5200 or /cms-proxy. */
23
+ apiBaseUrl?: string;
24
+ }
25
+ /** Uses the existing wisedoc/CMS endpoint contracts without depending on the host application. */
26
+ export declare function createKnowledgeDataSource(options?: KnowledgeApiOptions): KnowledgeDataSource;
27
+ export declare const knowledgeDataSource: KnowledgeDataSource;
@@ -0,0 +1,34 @@
1
+ /** Host transport receives the final URL, including encoded query parameters. */
2
+ export interface KnowledgeRequest {
3
+ url: string;
4
+ method: 'GET';
5
+ headers?: Record<string, string>;
6
+ signal?: AbortSignal;
7
+ timeout?: number;
8
+ withCredentials?: boolean;
9
+ }
10
+ export interface KnowledgeResponse {
11
+ data: any;
12
+ status?: number;
13
+ }
14
+ export type KnowledgeRequestFn = (request: KnowledgeRequest) => Promise<KnowledgeResponse>;
15
+ export interface KnowledgeHttpConfig {
16
+ params?: Record<string, unknown>;
17
+ headers?: Record<string, string>;
18
+ signal?: AbortSignal;
19
+ timeout?: number;
20
+ withCredentials?: boolean;
21
+ baseURL?: string;
22
+ }
23
+ /** Structural Axios-compatible interface; no Axios import or runtime dependency. */
24
+ export interface KnowledgeHttpClient {
25
+ get(url: string, config?: KnowledgeHttpConfig): Promise<KnowledgeResponse>;
26
+ }
27
+ export declare function responseOf(error: unknown): KnowledgeResponse | undefined;
28
+ export declare function resolveUrl(path: string, base: string): string;
29
+ export declare function createHttp(options: {
30
+ request?: KnowledgeRequestFn;
31
+ http?: KnowledgeHttpClient;
32
+ baseURL?: string;
33
+ withCredentials?: boolean;
34
+ }): KnowledgeHttpClient;
@@ -0,0 +1,14 @@
1
+ import type { KnowledgeSelection } from './types';
2
+ export declare const selectionKey: (item: KnowledgeSelection) => string;
3
+ export declare function covers(parent: KnowledgeSelection, child: KnowledgeSelection): boolean;
4
+ export declare function toSelection(item: KnowledgeSelection): KnowledgeSelection;
5
+ /** Also validates iframe input before it becomes reactive application state. */
6
+ export declare function normalizeSelection(value: unknown): KnowledgeSelection[];
7
+ export declare function selectionState(item: KnowledgeSelection, selected: KnowledgeSelection[]): {
8
+ checked: boolean;
9
+ disabled: boolean;
10
+ indeterminate: boolean;
11
+ inheritedFrom: string | undefined;
12
+ };
13
+ export declare function toggleSelection(item: KnowledgeSelection, selected: KnowledgeSelection[]): KnowledgeSelection[];
14
+ export declare function toIdResult(selected: KnowledgeSelection[]): import('./types').KnowledgeSelectionResult;
@@ -0,0 +1,34 @@
1
+ export type KnowledgeKind = 'library' | 'directory' | 'document';
2
+ /** A selected range, not an enumeration of every document it covers. */
3
+ export interface KnowledgeSelection {
4
+ id: string;
5
+ /** Document attachment metadata ID(s). Present for document selections. */
6
+ attachments?: string;
7
+ type: KnowledgeKind;
8
+ name: string;
9
+ libraryId: string;
10
+ parentId: string | null;
11
+ /** Ordered from the library to the immediate parent; library itself has []. */
12
+ ancestorIds: string[];
13
+ includeDescendants: boolean;
14
+ }
15
+ export interface KnowledgeNode extends KnowledgeSelection {
16
+ icon?: string;
17
+ pathNames: string[];
18
+ }
19
+ export interface KnowledgeChildren {
20
+ directories: KnowledgeNode[];
21
+ documents: KnowledgeNode[];
22
+ /** Total before backend permission filtering; do not infer the last page from row count. */
23
+ total: number;
24
+ }
25
+ export interface KnowledgeDataSource {
26
+ getLibraries(signal?: AbortSignal): Promise<KnowledgeNode[]>;
27
+ getChildren(parent: KnowledgeNode, page: number, size: number, signal?: AbortSignal): Promise<KnowledgeChildren>;
28
+ }
29
+ /** Explicit selected scopes grouped by entity kind. DocIds contains document attachments values. */
30
+ export interface KnowledgeSelectionResult {
31
+ KBIds: string[];
32
+ DirIds: string[];
33
+ DocIds: string[];
34
+ }
@@ -0,0 +1,167 @@
1
+ > 1.1.2 新增 uni-app 适配,ESM 自动加载 CSS。注入 request 后配置/认证由宿主负责,详见 [uni-app接入说明.md](uni-app接入说明.md)。
2
+
3
+ # 移动端知识库选择组件:npm 接入与发布
4
+
5
+ 包名:`@gspwidget/wisedoc-mobile-selector`,本次版本:`1.1.2`。适用于 **Vue 3.4.38 及以上的 Vue 3 项目**,不支持 Vue 2。uni-app Vue 3 App 端请按《uni-app接入说明.md》注入 request;原生 App 也可在 Vue WebView 中集成。
6
+
7
+ 该包只包含移动端选择组件、接口和选择逻辑、图标、组件样式及 TypeScript 类型,不包含独立页面、设计稿、示例项目、node_modules 或整个前端工程。Vue 由使用方工程提供,没有第三方运行依赖(Axios 仅用于源码测试)。原来的 iframe 页面仍可通过 `npm run build` 单独构建。
8
+
9
+ ## 1. 安装
10
+
11
+ 未发布时,把交付的 tgz 文件发给同事,在对方项目根目录执行(换成实际文件路径):
12
+
13
+ ```powershell
14
+ npm install 'D:\交付物\gspwidget-wisedoc-mobile-selector-1.1.2.tgz'
15
+ ```
16
+
17
+ 发布后:
18
+
19
+ ```powershell
20
+ npm install @gspwidget/wisedoc-mobile-selector --registry=https://registry.npmjs.org/
21
+ ```
22
+
23
+ ## 2. Vue 页面直接使用
24
+
25
+ ```vue
26
+ <script setup lang="ts">
27
+ import { ref } from 'vue'
28
+ import { MobileKnowledgeSelector } from '@gspwidget/wisedoc-mobile-selector'
29
+ import type { KnowledgeSelection, KnowledgeSelectionResult } from '@gspwidget/wisedoc-mobile-selector'
30
+ import '@gspwidget/wisedoc-mobile-selector/style.css'
31
+
32
+ const opened = ref(false)
33
+ const selection = ref<KnowledgeSelection[]>([])
34
+
35
+ function onConfirm(result: KnowledgeSelectionResult, selections: KnowledgeSelection[]) {
36
+ // result = { KBIds: string[], DirIds: string[], DocIds: string[] }
37
+ // DocIds 是文档 attachments 字段的原始字符串,不是文档主键。
38
+ console.log(result)
39
+ selection.value = selections // 下次打开时回显
40
+ opened.value = false
41
+ }
42
+ </script>
43
+
44
+ <template>
45
+ <button @click="opened = true">选择知识库文件</button>
46
+ <div v-if="opened" class="kb-mask">
47
+ <div class="kb-panel">
48
+ <MobileKnowledgeSelector
49
+ :initial-selection="selection"
50
+ @confirm="onConfirm"
51
+ @close="opened = false"
52
+ @error="error => console.error(error)"
53
+ />
54
+ </div>
55
+ </div>
56
+ </template>
57
+
58
+ <style scoped>
59
+ .kb-mask { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: flex-end; background: rgb(0 0 0 / 40%); }
60
+ .kb-panel { width: 100%; height: calc(100dvh - 44px); overflow: hidden; border-radius: 16px 16px 0 0; background: white; }
61
+ </style>
62
+ ```
63
+
64
+ 也可以放进已有的 Popup / Dialog 内容区域,**必须给组件父容器明确高度**,组件使用 100% 高度,内部列表滚动;组件不创建遮罩,不设置宿主 body 或 #app 样式。底部操作栏固定 64px,紧贴容器底部,不再自动叠加 safe-area。如果宿主需要避开系统 Home 指示器,由宿主容器统一处理,避免重复留白。
65
+
66
+ ### Props、事件和回显
67
+
68
+ | 名称 | 说明 |
69
+ | --- | --- |
70
+ | `initialSelection` | 可选 `KnowledgeSelection[]`,首次挂载时读取;传上次 `confirm` 的第二个参数用于回显 |
71
+ | `apiOptions` | 可选接口选项,首次挂载时读取,见下一节 |
72
+ | `dataSource` | 可选自定义 `KnowledgeDataSource`,优先于 `apiOptions` |
73
+ | `@confirm` | 两个参数:`(result, selections)`;第一个只含 KBIds / DirIds / DocIds,第二个用于回显 |
74
+ | `@close` | 点击右上角关闭按钮时触发,无参数,不提交本轮变更 |
75
+ | `@error` | 数据加载失败时触发,参数为错误对象,界面保留重试入口 |
76
+ | `ref.setSelection(selections)` | 组件实例方法,用于挂载后主动更新回显 |
77
+
78
+ 下钻时标题左侧返回上一级;已选列表的返回按钮回到之前的浏览位置。确定与关闭只发出事件,由宿主关闭自己的弹窗。示例使用 `v-if`,关闭时卸载,下次打开重建。若使用 `v-show` 保持实例,重新打开时可调用 `setSelection`;接口配置变化后请重新挂载。
79
+
80
+ 选择某知识库或目录即覆盖它的全部下级,下级不能单独取消;可以跨库混选。结果只列出显式选择的范围,不枚举所覆盖的全部子项。DocIds 中每个值保留对应 attachments 原始字符串,不按逗号拆分。
81
+
82
+ ## 3. 配置文件和接口地址
83
+
84
+ 默认仍从**宿主页面所在服务**读取:
85
+
86
+ ```text
87
+ /subapp/wisedoc/knowledge-selector.config.json
88
+ ```
89
+
90
+ 配置不存在(404)时默认走 Wisedoc;配置格式错误或其他请求错误会显示加载错误。配置示例:
91
+
92
+ ```json
93
+ { "useWisedocApi": false, "apiBaseUrl": "/cms-proxy" }
94
+ ```
95
+
96
+ `useWisedocApi` 缺省或为 true 时使用 Wisedoc;false 时使用 CMS,并保留 CMS 未登录认证、`Accept: text/html` 和会话建立等待逻辑。
97
+
98
+ 如果宿主也部署在 5300 的 web 目录下,无需配置 URL,不受宿主子目录影响。**npm 包安装位置不代表接口服务地址**。若宿主部署在另一个服务,而公共配置仍在 5300,显式提供接口选项:
99
+
100
+ ```ts
101
+ const apiOptions = {
102
+ serviceOrigin: 'https://实际5300服务域名:5300',
103
+ withCredentials: true
104
+ }
105
+ // <MobileKnowledgeSelector :api-options="apiOptions" ... />
106
+ ```
107
+
108
+ 此时公共 JSON、相对 API 路径、认证路径都以该服务为基准,JSON 中的绝对 apiBaseUrl 仍按原配置使用。也支持 `config` 直接传配置(跳过 JSON)、`configUrl` 覆盖配置路径、`http` 注入 Axios 实例。一般保持默认即可。同源网关代理最省事;跨域请求的 CORS、Cookie 和认证跳转仍须由服务端配置,npm 集成本身不会消除接口跨域。
109
+
110
+ ## 4. 本工程构建和打包
111
+
112
+ ```powershell
113
+ Set-Location 'D:\Java\doc\移动端选择\wisedoc-mobile-selector'
114
+ npm ci
115
+ npm test
116
+ npm run build:lib
117
+ npm run pack:lib
118
+ ```
119
+
120
+ - `build:lib`:生成 `dist-lib/index.js`(ESM)、`index.cjs`(CommonJS)、`style.css` 和 `types/` 声明文件,供打包工具引入。
121
+ - `pack:lib`:运行 `npm pack`,其 prepack 会自动重新执行 `build:lib`,然后按 package.json 的 files 白名单生成 `gspwidget-wisedoc-mobile-selector-1.1.2.tgz`。仅想生成 tgz,直接执行这一条即可。
122
+ - `npm run build`:仍生成 iframe 独立页面 `dist/`,与 npm 包分开。
123
+
124
+ ## 5. 第一次发布到 npm 官方仓库(由你操作)
125
+
126
+ `@gspwidget/widget-devkit` 是组织作用域下的一个包;本次会新增另一个包 `@gspwidget/wisedoc-mobile-selector`,不会覆盖 widget-devkit。你需要自己的 npm 账号,并请 @gspwidget 管理员为你授予该组织的创建/发布包权限。包名中的 @gspwidget 不能仅靠本地配置获得权限。若组织使用内部 registry,请先向管理员确认;以下命令明确针对 npm 官方仓库。
127
+
128
+ 1. 到 [npm 官网](https://www.npmjs.com/) 注册/登录账号,并按照页面提示启用发布所需的双因素认证。然后在终端登录(认证由你完成,不要将 token 写入交付物):
129
+
130
+ ```powershell
131
+ npm login --registry=https://registry.npmjs.org/
132
+ npm whoami --registry=https://registry.npmjs.org/
133
+ ```
134
+
135
+ 2. 在项目目录检查包名、版本和实际将上传的文件。dry-run 不发布:
136
+
137
+ ```powershell
138
+ npm pkg get name version
139
+ npm run pack:lib
140
+ npm publish ./gspwidget-wisedoc-mobile-selector-1.1.2.tgz --dry-run --access public --registry=https://registry.npmjs.org/
141
+ ```
142
+
143
+ 3. 确认预览后,发布已经检查过的同一个 tgz,按提示完成认证:
144
+
145
+ ```powershell
146
+ npm publish ./gspwidget-wisedoc-mobile-selector-1.1.2.tgz --access public --registry=https://registry.npmjs.org/
147
+ ```
148
+
149
+ `--access public` 表示任何人都能下载这个包。若希望仅公司人员访问,请由组织管理员确认私有包服务/权限后使用 restricted,勿直接执行公开发布命令。官方 scoped public 包的发布方式见 [npm 官方说明](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/)。发布认证要求见 [npm 双因素认证说明](https://docs.npmjs.com/requiring-2fa-for-package-publishing-and-settings-modification/)。
150
+
151
+ 4. 检查发布结果,让同事执行前面的安装命令:
152
+
153
+ ```powershell
154
+ npm view @gspwidget/wisedoc-mobile-selector version --registry=https://registry.npmjs.org/
155
+ ```
156
+
157
+ 若报 403,通常需要核对组织发布权限、包名归属或发布认证。后续更新不能覆盖已发布的同一版本。修改代码后可用下面的命令更新版本并重新打包(这些命令不执行 Git 操作):
158
+
159
+ ```powershell
160
+ npm pkg set version=1.1.3
161
+ npm install --package-lock-only --ignore-scripts
162
+ npm test
163
+ npm run pack:lib
164
+ # 然后对新生成的 1.1.3.tgz 做 dry-run 和 publish
165
+ ```
166
+
167
+ 本次只生成交付物,未登录或向 npm 上传任何包。
package/package.json CHANGED
@@ -1,6 +1,53 @@
1
- {
2
- "name": "@gspwidget/wisedoc-mobile-selector",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
1
+ {
2
+ "name": "@gspwidget/wisedoc-mobile-selector",
3
+ "version": "1.1.2",
4
+ "description": "Vue 3 移动端知识库、目录和文档选择组件",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "scripts": {
8
+ "dev": "vite --host 0.0.0.0 --port 4203",
9
+ "build": "vue-tsc --noEmit && vite build",
10
+ "build:page": "vue-tsc --noEmit && vite build",
11
+ "build:lib": "vue-tsc --noEmit && vite build --config vite.lib.config.ts && vue-tsc -p tsconfig.lib.json",
12
+ "pack:lib": "npm pack",
13
+ "prepack": "npm run build:lib",
14
+ "preview": "vite preview --host 0.0.0.0 --port 4203",
15
+ "test": "node tests/run-tests.mjs"
16
+ },
17
+ "main": "./dist-lib/index.cjs",
18
+ "module": "./dist-lib/index.js",
19
+ "types": "./dist-lib/types/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist-lib/types/index.d.ts",
23
+ "import": "./dist-lib/index.js",
24
+ "require": "./dist-lib/index.cjs"
25
+ },
26
+ "./style.css": "./dist-lib/style.css"
27
+ },
28
+ "files": [
29
+ "dist-lib",
30
+ "README.md",
31
+ "npm接入与发布说明.md",
32
+ "uni-app接入说明.md"
33
+ ],
34
+ "sideEffects": [
35
+ "**/*.css"
36
+ ],
37
+ "publishConfig": {
38
+ "registry": "https://registry.npmjs.org/",
39
+ "access": "public"
40
+ },
41
+ "peerDependencies": {
42
+ "vue": "^3.4.38"
43
+ },
44
+ "devDependencies": {
45
+ "vue": "^3.4.38",
46
+ "@types/node": "^20.19.0",
47
+ "@vitejs/plugin-vue": "^4.6.2",
48
+ "typescript": "~5.3.3",
49
+ "vite": "^4.5.5",
50
+ "vue-tsc": "^1.8.27",
51
+ "axios": "^1.7.9"
52
+ }
53
+ }
@@ -0,0 +1,69 @@
1
+ # uni-app Vue 3 接入(1.1.2)
2
+
3
+ 1.1.2 将已选项移除叉号改为内联 SVG 图片,避免与 uni-button 默认伪元素冲突;顶部关闭叉号已核对为独立 SVG。此前按《调整.md》修正的网络、CSS、按钮、图标和无浏览器环境适配均保留。选择规则、confirm/close 事件及 attachments 返回含义不变。
4
+
5
+ ## 安装
6
+
7
+ ```sh
8
+ npm install ./gspwidget-wisedoc-mobile-selector-1.1.2.tgz
9
+ ```
10
+
11
+ ESM 入口自动以相对路径 `import './style.css'` 加载 CSS,无 document 注入。继续显式 import 包的 style.css 也兼容,构建工具会去重。CommonJS 使用者仍需自行引入 style.css。
12
+
13
+ 运行时仅依赖宿主 Vue;Axios 已移至本工程测试用 devDependencies,不随包安装。所有 SVG(包括返回、关闭、箭头)在库构建期内联为 data URI,无需额外复制 assets。
14
+
15
+ ## 使用宿主网络函数
16
+
17
+ ```vue
18
+ <script setup lang="ts">
19
+ import { MobileKnowledgeSelector } from '@gspwidget/wisedoc-mobile-selector'
20
+ import type { KnowledgeApiOptions, KnowledgeRequestFn } from '@gspwidget/wisedoc-mobile-selector'
21
+
22
+ const request: KnowledgeRequestFn = (req) => new Promise((resolve, reject) => {
23
+ uni.request({
24
+ url: req.url, // 已包含组件编码过的 query,不要再拼一遍
25
+ method: req.method,
26
+ header: {
27
+ ...req.headers,
28
+ // 在此复用项目自己的 token/鉴权头,不要把真实 token 写进包
29
+ },
30
+ timeout: req.timeout,
31
+ success: (res) => resolve({ data: res.data, status: res.statusCode }),
32
+ fail: reject
33
+ })
34
+ })
35
+ const apiOptions: KnowledgeApiOptions = {
36
+ request,
37
+ config: {
38
+ useWisedocApi: false,
39
+ apiBaseUrl: 'https://实际接口服务器:5200'
40
+ }
41
+ }
42
+ function confirm(result, selections) {
43
+ // result: { KBIds: string[], DirIds: string[], DocIds: string[] }
44
+ // 保存 selections 以供下次回显,再由宿主关闭弹窗。
45
+ }
46
+ function close() { /* 宿主关闭弹窗 */ }
47
+ </script>
48
+ <template>
49
+ <view style="height: 80vh">
50
+ <MobileKnowledgeSelector :api-options="apiOptions" @confirm="confirm" @close="close" />
51
+ </view>
52
+ </template>
53
+ ```
54
+
55
+ 也可以把 request 实现替换为同事已有的统一网络封装。`uni.request` 的成功回调包含 HTTP 状态码,不能把 HTTP 401 当作业务成功;上述适配保留 status,组件会拒绝非 2xx 响应。不要将文档分页响应解包两次:应返回 `{ data: 后端完整响应体, status: HTTP状态码 }`,而非直接返回文档数组。[uni.request 官方说明](https://uniapp.dcloud.net.cn/api/request/request.html)
56
+
57
+ 网络优先级为 **request > http(可选宿主 Axios 实例)> fetch**。request 参数是 `{ url, method: 'GET', headers?, signal?, timeout?, withCredentials? }`;返回 Promise<{ data, status? }>。signal 是可选的,宿主可以结合自己的 requestTask.abort 实现取消;组件卸载后不会自动操作浏览器事件。
58
+
59
+ **只要注入 request,就不会自动请求公共 JSON,也不会调用 routerRedirect 或进行 Cookie 会话探测**。宿主先读取自己的配置并通过 `apiOptions.config` 传入(或直接填配置);不传 config 时仍默认 Wisedoc。认证、Token 刷新、重试与 Cookie 管理由宿主负责。App 绝对地址可以直接配置 apiBaseUrl;也可用 serviceOrigin 补齐相对地址,不依赖浏览器 URL API。
60
+
61
+ 浏览器/iframe 不注入 request 时,仍读取原来的 knowledge-selector.config.json,默认 fetch 保留 Cookie、Accept: text/html、共享认证与会话等待逻辑。选择器不会全局删除配置或认证能力。
62
+
63
+ ## 本次兼容处理和验证范围
64
+
65
+ - 按钮 reset 使用 `[type='button']`,所有按钮都设置 type;清除 uni-button 的 ::after 默认边框,确定按钮显式 height/line-height=32px。
66
+ - CSS 使用相对导入,根包及库目录均声明 CSS sideEffects,避免生产构建丢样式。
67
+ - 组件对 window、location、AbortController、scrollTo 均作环境/方法检查,不向逻辑层注入 document API。
68
+ - 已验证网络/选择自动化测试、无浏览器全局的组件初始化、npm 实际安装及 Vue 宿主生产构建。
69
+ - 当前机器没有完成 Android/iOS/鸿蒙真机运行验证;仍需同事在实际 uni-app Vue 3 项目复测。此包不是 nvue、uvue 或 uni-app x 原生组件。