rj-grid 0.2.1 → 0.2.3

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/dist/style.css CHANGED
@@ -10,7 +10,7 @@
10
10
  --rj-text-secondary: #909399;
11
11
  --rj-primary: #3b76f6;
12
12
  --rj-primary-bg: #eaf1fe;
13
- --rj-hover-bg: #f2f6fd;
13
+ --rj-hover-bg: #e6edf9;
14
14
  --rj-selected-bg: #e2edfd;
15
15
  --rj-range-bg: rgba(59, 118, 246, 0.1);
16
16
  --rj-fill-bg: rgba(59, 118, 246, 0.16);
@@ -52,7 +52,7 @@
52
52
  --rj-text-secondary: #8a919f;
53
53
  --rj-primary: #5c8dfb;
54
54
  --rj-primary-bg: #24324d;
55
- --rj-hover-bg: #272d3a;
55
+ --rj-hover-bg: #313a4d;
56
56
  --rj-selected-bg: #2a3a5c;
57
57
  --rj-mask: rgba(30, 34, 42, 0.75);
58
58
  --rj-flash-bg: #5a4c1e;
@@ -261,6 +261,111 @@
261
261
  padding: 2px;
262
262
  }
263
263
 
264
+ .rj-query-select.rj-opt-select {
265
+ position: relative;
266
+ display: inline-flex;
267
+ min-width: 120px;
268
+ font-size: var(--rj-ui-font, 12px);
269
+ }
270
+
271
+ .rj-opt-trigger {
272
+ display: inline-flex;
273
+ flex: 1;
274
+ gap: 4px;
275
+ align-items: center;
276
+ justify-content: space-between;
277
+ width: 100%;
278
+ height: calc(var(--rj-ui-h, 26px) - 2px);
279
+ padding: 0 6px;
280
+ font-size: var(--rj-ui-font, 12px);
281
+ color: var(--rj-text);
282
+ cursor: pointer;
283
+ background: var(--rj-bg);
284
+ border: 1px solid var(--rj-border-strong);
285
+ border-radius: var(--rj-radius);
286
+ outline: none;
287
+ }
288
+ .rj-opt-trigger:focus, .rj-opt-trigger:hover {
289
+ border-color: var(--rj-primary);
290
+ }
291
+ .rj-opt-trigger.is-placeholder .rj-opt-text {
292
+ color: var(--rj-text-secondary);
293
+ }
294
+
295
+ .rj-opt-text {
296
+ overflow: hidden;
297
+ text-overflow: ellipsis;
298
+ white-space: nowrap;
299
+ }
300
+
301
+ .rj-opt-arrow {
302
+ flex: none;
303
+ color: var(--rj-text-secondary);
304
+ }
305
+
306
+ .rj-opt-panel {
307
+ position: absolute;
308
+ top: 100%;
309
+ left: 0;
310
+ z-index: 60;
311
+ display: flex;
312
+ flex-direction: column;
313
+ min-width: 100%;
314
+ max-width: 320px;
315
+ margin-top: 2px;
316
+ background: var(--rj-bg);
317
+ border: 1px solid var(--rj-border);
318
+ border-radius: var(--rj-radius);
319
+ box-shadow: var(--rj-shadow);
320
+ }
321
+
322
+ .rj-opt-search {
323
+ width: calc(100% - 8px);
324
+ height: calc(var(--rj-ui-h, 26px) - 2px);
325
+ padding: 0 6px;
326
+ margin: 4px;
327
+ font-size: var(--rj-ui-font, 12px);
328
+ color: var(--rj-text);
329
+ background: var(--rj-bg);
330
+ border: 1px solid var(--rj-border-strong);
331
+ border-radius: var(--rj-radius);
332
+ outline: none;
333
+ }
334
+ .rj-opt-search:focus {
335
+ border-color: var(--rj-primary);
336
+ }
337
+
338
+ .rj-opt-list {
339
+ max-height: 220px;
340
+ overflow: auto;
341
+ }
342
+
343
+ .rj-opt-row {
344
+ display: flex;
345
+ gap: 4px;
346
+ align-items: center;
347
+ padding: 4px 8px;
348
+ line-height: 1.5;
349
+ cursor: pointer;
350
+ }
351
+ .rj-opt-row:hover, .rj-opt-row.is-active {
352
+ color: var(--rj-primary);
353
+ background: var(--rj-primary-bg);
354
+ }
355
+ .rj-opt-row.is-selected .rj-option-label {
356
+ font-weight: 600;
357
+ }
358
+
359
+ .rj-opt-check {
360
+ flex: none;
361
+ margin: 0;
362
+ }
363
+
364
+ .rj-opt-empty {
365
+ padding: 6px 8px;
366
+ color: #999;
367
+ }
368
+
264
369
  .rj-query-add {
265
370
  border-style: dashed;
266
371
  }
@@ -404,6 +509,25 @@
404
509
  font-weight: 500;
405
510
  }
406
511
 
512
+ .rj-tip {
513
+ position: fixed;
514
+ z-index: 3100;
515
+ width: max-content;
516
+ max-width: 240px;
517
+ padding: 3px 8px;
518
+ font-size: 12px;
519
+ font-weight: 400;
520
+ line-height: 1.4;
521
+ color: var(--rj-bg);
522
+ text-align: left;
523
+ white-space: normal;
524
+ pointer-events: none;
525
+ background: var(--rj-text);
526
+ border-radius: var(--rj-radius);
527
+ box-shadow: var(--rj-shadow);
528
+ transform: translateX(-100%);
529
+ }
530
+
407
531
  .rj-pager {
408
532
  display: flex;
409
533
  align-items: center;
@@ -1139,6 +1263,27 @@
1139
1263
  color: var(--rj-primary);
1140
1264
  }
1141
1265
 
1266
+ .rj-option-tree .rj-option-row {
1267
+ display: flex;
1268
+ gap: 4px;
1269
+ align-items: center;
1270
+ }
1271
+ .rj-option-tree .rj-option-caret {
1272
+ flex: none;
1273
+ width: 14px;
1274
+ color: var(--rj-text-secondary);
1275
+ cursor: pointer;
1276
+ user-select: none;
1277
+ }
1278
+ .rj-option-tree .rj-option-caret.is-leaf {
1279
+ cursor: default;
1280
+ }
1281
+ .rj-option-tree .rj-option-label {
1282
+ overflow: hidden;
1283
+ text-overflow: ellipsis;
1284
+ white-space: nowrap;
1285
+ }
1286
+
1142
1287
  .rj-editor-error {
1143
1288
  position: absolute;
1144
1289
  top: 100%;
@@ -5,14 +5,14 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
5
5
  y: number;
6
6
  }>>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
7
7
  select: (item: RjMenuItem) => void;
8
- reposition: (h: number) => void;
8
+ reposition: (h: number, w: number) => void;
9
9
  }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{
10
10
  items: RjMenuItem[];
11
11
  x: number;
12
12
  y: number;
13
13
  }>>> & Readonly<{
14
14
  onSelect?: ((item: RjMenuItem) => any) | undefined;
15
- onReposition?: ((h: number) => any) | undefined;
15
+ onReposition?: ((h: number, w: number) => any) | undefined;
16
16
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
17
17
  export default _default;
18
18
  type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
@@ -96,6 +96,12 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<im
96
96
  */
97
97
  serverExport?: ((params: RjServerExportParams) => void | Promise<void>) | undefined;
98
98
  exportFileName?: string | undefined;
99
+ /**
100
+ * 打印 / PDF 单次最大行数护栏:浏览器打印靠把整表排版进隐藏 iframe,
101
+ * 上万行会生成十几 MB HTML 并让主线程卡死(页面假死)。超过此上限只打印前 N 行并提示,
102
+ * 需全量请先筛选 / 用分页 / 走后端导出,或按宿主机器性能调大本值。置 0 或负数则不限。
103
+ */
104
+ printMaxRows?: number | undefined;
99
105
  /** 导出取值口径:默认导出「所见即所得」的格式化文本(与屏幕一致、单文件内口径统一);
100
106
  * 置 true 则导出原始类型值,让 Excel 里数字仍是数字(对齐 AG Grid useCellValuesForExports) */
101
107
  exportRawValues?: boolean | undefined;
@@ -182,6 +188,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<im
182
188
  copyHeadersToClipboard: boolean;
183
189
  markDirtyCells: boolean;
184
190
  rowHover: boolean;
191
+ printMaxRows: number;
185
192
  queryable: boolean;
186
193
  viewable: boolean;
187
194
  queryFields: () => never[];
@@ -303,6 +310,12 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<im
303
310
  */
304
311
  serverExport?: ((params: RjServerExportParams) => void | Promise<void>) | undefined;
305
312
  exportFileName?: string | undefined;
313
+ /**
314
+ * 打印 / PDF 单次最大行数护栏:浏览器打印靠把整表排版进隐藏 iframe,
315
+ * 上万行会生成十几 MB HTML 并让主线程卡死(页面假死)。超过此上限只打印前 N 行并提示,
316
+ * 需全量请先筛选 / 用分页 / 走后端导出,或按宿主机器性能调大本值。置 0 或负数则不限。
317
+ */
318
+ printMaxRows?: number | undefined;
306
319
  /** 导出取值口径:默认导出「所见即所得」的格式化文本(与屏幕一致、单文件内口径统一);
307
320
  * 置 true 则导出原始类型值,让 Excel 里数字仍是数字(对齐 AG Grid useCellValuesForExports) */
308
321
  exportRawValues?: boolean | undefined;
@@ -389,6 +402,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<im
389
402
  copyHeadersToClipboard: boolean;
390
403
  markDirtyCells: boolean;
391
404
  rowHover: boolean;
405
+ printMaxRows: number;
392
406
  queryable: boolean;
393
407
  viewable: boolean;
394
408
  queryFields: () => never[];
@@ -472,6 +486,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<im
472
486
  detailHeight: number;
473
487
  fullWidthHeight: number;
474
488
  exportRange: "auto" | RjExportScopeResolved;
489
+ printMaxRows: number;
475
490
  statusBar: boolean;
476
491
  statusAggregations: ("sum" | "avg" | "min" | "max" | "count")[];
477
492
  undoRedoCellEditing: boolean;
@@ -594,6 +609,8 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<im
594
609
  autoSizeColumn: (colId: string) => void;
595
610
  autoSizeAll: () => void;
596
611
  sizeColumnsToFit: () => void;
612
+ /** 清除宽度覆盖:把列宽还原到列定义/内容自适应默认值(配合 autoSizeAll 做「自适应↔还原」切换) */
613
+ resetColumnWidths: () => void;
597
614
  setTheme: (params: RjThemeParams) => void;
598
615
  getTheme: () => {
599
616
  preset?: "legacy" | "alpine" | "quartz" | "material" | undefined;
@@ -728,6 +745,8 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<im
728
745
  autoSizeColumn: (colId: string) => void;
729
746
  autoSizeAll: () => void;
730
747
  sizeColumnsToFit: () => void;
748
+ /** 清除宽度覆盖:把列宽还原到列定义/内容自适应默认值(配合 autoSizeAll 做「自适应↔还原」切换) */
749
+ resetColumnWidths: () => void;
731
750
  setTheme: (params: RjThemeParams) => void;
732
751
  getTheme: () => {
733
752
  preset?: "legacy" | "alpine" | "quartz" | "material" | undefined;
@@ -866,6 +885,8 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<im
866
885
  autoSizeColumn: (colId: string) => void;
867
886
  autoSizeAll: () => void;
868
887
  sizeColumnsToFit: () => void;
888
+ /** 清除宽度覆盖:把列宽还原到列定义/内容自适应默认值(配合 autoSizeAll 做「自适应↔还原」切换) */
889
+ resetColumnWidths: () => void;
869
890
  setTheme: (params: RjThemeParams) => void;
870
891
  getTheme: () => {
871
892
  preset?: "legacy" | "alpine" | "quartz" | "material" | undefined;
@@ -1001,6 +1022,8 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<im
1001
1022
  autoSizeColumn: (colId: string) => void;
1002
1023
  autoSizeAll: () => void;
1003
1024
  sizeColumnsToFit: () => void;
1025
+ /** 清除宽度覆盖:把列宽还原到列定义/内容自适应默认值(配合 autoSizeAll 做「自适应↔还原」切换) */
1026
+ resetColumnWidths: () => void;
1004
1027
  setTheme: (params: RjThemeParams) => void;
1005
1028
  getTheme: () => {
1006
1029
  preset?: "legacy" | "alpine" | "quartz" | "material" | undefined;
@@ -0,0 +1,49 @@
1
+ import type { RjEditorOption } from './types';
2
+ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<{
3
+ options?: RjEditorOption[] | undefined;
4
+ modelValue?: any;
5
+ multiple?: boolean | undefined;
6
+ placeholder?: string | undefined;
7
+ }>, {
8
+ options: () => never[];
9
+ modelValue: undefined;
10
+ multiple: boolean;
11
+ placeholder: string;
12
+ }>>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
13
+ "update:modelValue": (v: any) => void;
14
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<{
15
+ options?: RjEditorOption[] | undefined;
16
+ modelValue?: any;
17
+ multiple?: boolean | undefined;
18
+ placeholder?: string | undefined;
19
+ }>, {
20
+ options: () => never[];
21
+ modelValue: undefined;
22
+ multiple: boolean;
23
+ placeholder: string;
24
+ }>>> & Readonly<{
25
+ "onUpdate:modelValue"?: ((v: any) => any) | undefined;
26
+ }>, {
27
+ options: RjEditorOption[];
28
+ placeholder: string;
29
+ modelValue: any;
30
+ multiple: boolean;
31
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
32
+ export default _default;
33
+ type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
34
+ type __VLS_TypePropsToRuntimeProps<T> = {
35
+ [K in keyof T]-?: {} extends Pick<T, K> ? {
36
+ type: import('vue').PropType<__VLS_NonUndefinedable<T[K]>>;
37
+ } : {
38
+ type: import('vue').PropType<T[K]>;
39
+ required: true;
40
+ };
41
+ };
42
+ type __VLS_WithDefaults<P, D> = {
43
+ [K in keyof Pick<P, keyof P>]: K extends keyof D ? __VLS_Prettify<P[K] & {
44
+ default: D[K];
45
+ }> : P[K];
46
+ };
47
+ type __VLS_Prettify<T> = {
48
+ [K in keyof T]: T[K];
49
+ } & {};
@@ -1,4 +1,5 @@
1
1
  import type { RjFilterModel, RjSortState } from './types';
2
+ import type { RjLang } from './locale';
2
3
  export type RjNlqFilterType = 'text' | 'number' | 'date' | 'select';
3
4
  /** 参与解析的列描述(由网格从列定义构建) */
4
5
  export interface RjNlqColumn {
@@ -39,13 +40,20 @@ export interface RjNlqResult {
39
40
  model: RjFilterModel;
40
41
  }[];
41
42
  sort?: RjSortState;
43
+ /** 排序列展示名(title,供 explainNLQ 本地化回显) */
44
+ sortTitle?: string;
42
45
  groupBy?: string;
43
46
  groupColId?: string;
47
+ /** 分组列展示名(title,供 explainNLQ 本地化回显) */
48
+ groupTitle?: string;
44
49
  limit?: number;
45
50
  search?: string;
46
51
  clauses: RjNlqClause[];
47
52
  }
48
53
  /** 解析自然语言查询 */
49
54
  export declare function parseNLQ(text: string, columns: RjNlqColumn[]): RjNlqResult;
50
- /** 生成人类可读的解析摘要(供 UI 回显,key 交由上层 i18n;此处给结构串) */
51
- export declare function explainNLQ(r: RjNlqResult): string;
55
+ /**
56
+ * 生成人类可读的解析摘要(供 UI 回显)。传 lang 则随语言本地化:
57
+ * 中文如「数量 > 100 · 按金额 降序 · 前5 · 搜索“轴承”」,英文如「qty > 100 · sort by amount desc · top 5 · search "bolt"」。
58
+ */
59
+ export declare function explainNLQ(r: RjNlqResult, lang?: RjLang): string;
@@ -22,9 +22,32 @@ export declare const defaultOptionsAccessor: RjOptionsAccessor;
22
22
  * 把任意接口返回归一成 RjEditorOption[]:
23
23
  * - map 命中优先(完全自定义);
24
24
  * - 否则按 labelKey/valueKey(或默认字段)取;
25
- * - childrenKey(默认 'children')命中数组即递归拍平(depth 缩进不写进 label,保持干净 value→label 表)。
25
+ * - childrenKey(默认 'children')命中数组即保留为 children,递归归一(不再拍平),供下拉按层级渲染。
26
26
  */
27
27
  export declare function normalizeOptions(raw: unknown, shape?: RjOptionShape): RjEditorOption[];
28
+ /** 深度优先拍平选项树(丢弃 children 引用),供 value→label 映射 / 枚举候选等需要扁平列表的消费点 */
29
+ export declare function flattenOptions(options: RjEditorOption[]): RjEditorOption[];
30
+ /**
31
+ * 按搜索词剪枝选项树(大小写不敏 contains):
32
+ * - 节点自身命中 → 保留该节点及其完整子树(可继续下钻);
33
+ * - 自身未命中但有后代命中 → 作为祖先路径保留(children 为剪枝后的子集);
34
+ * - 空查询原样返回。返回全新节点对象,不污染缓存。
35
+ */
36
+ export declare function filterOptionTree(options: RjEditorOption[], query: string): RjEditorOption[];
37
+ /** 树渲染行:option + 层级深度 + 是否可展开 + 当前是否已展开 */
38
+ export interface RjOptionRow {
39
+ option: RjEditorOption;
40
+ depth: number;
41
+ hasChildren: boolean;
42
+ expanded: boolean;
43
+ }
44
+ /**
45
+ * 按展开集合把(已剪枝的)选项树铺成可渲染的可见行(深度优先)。
46
+ * expanded 以 String(value) 为键;未展开节点的子树不进入可见行。
47
+ */
48
+ export declare function flattenTreeForRender(options: RjEditorOption[], expanded: Set<string>): RjOptionRow[];
49
+ /** 收集树中所有“有子节点”的 value(String 化),用于默认展开全部 */
50
+ export declare function collectParentKeys(options: RjEditorOption[], acc?: Set<string>): Set<string>;
28
51
  /** 解析选项源所需的网格级加载器 */
29
52
  export interface RjOptionLoaders {
30
53
  dictLoader?: (key: string) => unknown | Promise<unknown>;
@@ -108,11 +108,14 @@ export type RjEditorType = 'input' | 'number' | 'date' | 'select' | 'richSelect'
108
108
  export interface RjEditorOption {
109
109
  label: string;
110
110
  value: any;
111
+ /** 树形子节点:载体源含 childrenKey 时保留层级(下拉按树渲染,显示/标签映射自动拍平) */
112
+ children?: RjEditorOption[];
111
113
  }
112
114
  /**
113
115
  * 统一选项载体:一份数据源同时驱动 显示(value→label) / select 筛选候选 / select 编辑器 / NLQ 枚举。
114
116
  * 数组=静态候选;函数=工厂(可同步/异步,网格级按列只解析一次);对象=按 load/ref/dict 取数,
115
- * 再用 map 或 labelKey/valueKey 归一,childrenKey(默认 'children')命中即递归拍平树(label 不加缩进)。
117
+ * 再用 map 或 labelKey/valueKey 归一,childrenKey(默认 'children')命中数组即保留为 children 树,
118
+ * 供下拉按层级渲染(显示标签映射会自动深度优先拍平,父/子节点均可命中 value→label)。
116
119
  */
117
120
  export type RjOptionSource = RjEditorOption[] | ((row?: RjRowData) => RjEditorOption[] | Promise<RjEditorOption[]>) | {
118
121
  /** 静态候选(对象式) */
@@ -627,6 +630,8 @@ export interface RjGridApi extends ComponentPublicInstance {
627
630
  autoSizeAll: () => void;
628
631
  /** 列宽:按视口宽度等比缩放普通区列(对标 sizeColumnsToFit) */
629
632
  sizeColumnsToFit: () => void;
633
+ /** 清除全部列宽覆盖,还原到列定义/内容自适应默认宽(与 autoSizeAll 配合做自适应↔还原切换) */
634
+ resetColumnWidths: () => void;
630
635
  /** 主题引擎(对标 AG Grid Theming):运行时替换预设/明暗/强调色等 */
631
636
  setTheme: (params: RjThemeParams) => void;
632
637
  /** 读取当前生效的主题参数 */
@@ -60,6 +60,7 @@ export declare function useColumnState(sourceColumns: () => RjColumn[]): {
60
60
  setAutoWidth: (colId: string, width: number) => void;
61
61
  hasSizedWidth: (colId: string) => boolean;
62
62
  sizeToFit: () => void;
63
+ clearWidths: () => void;
63
64
  moveColumn: (dragId: string, targetId: string) => void;
64
65
  togglePin: (colId: string, pin: 'left' | 'right' | null) => void;
65
66
  toggleHide: (colId: string, hide?: boolean) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rj-grid",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "零第三方依赖的自研 Vue3 虚拟表格:虚拟滚动 / 冻结列 / 行拖拽 / 分组透视 / 公式 / SSRM / 主题 i18n / 自然语言查询",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -46,6 +46,7 @@
46
46
  }
47
47
  },
48
48
  "scripts": {
49
+ "start": "vite --config vite.playground.config.ts",
49
50
  "dev": "vite --config vite.playground.config.ts",
50
51
  "build:play": "vite build --config vite.playground.config.ts",
51
52
  "preview:play": "vite preview --config vite.playground.config.ts",
@@ -55,6 +56,8 @@
55
56
  "lint": "eslint --fix --ext .ts,.vue index.ts src __tests__ playground && stylelint \"src/styles/*.scss\"",
56
57
  "ts": "node --max_old_space_size=8192 ./node_modules/vue-tsc/bin/vue-tsc.js --noEmit -p tsconfig.json",
57
58
  "size": "node ./node_modules/vite/bin/vite.js build --config vite.size.config.ts && node build/rjSizeReport.mjs",
59
+ "publish:github": "node scripts/publish-github.mjs",
60
+ "publish:github:push": "node scripts/publish-github.mjs --push",
58
61
  "prepublishOnly": "pnpm build && pnpm test"
59
62
  },
60
63
  "keywords": [