bi-sdk-react 0.0.55 → 0.0.57

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bi-sdk-react",
3
- "version": "0.0.55",
3
+ "version": "0.0.57",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "dist/umd/js/bi-sdk.umd.min.js",
@@ -539,22 +539,21 @@ return {
539
539
  {
540
540
  type: "b-table",
541
541
  name: "数据表格",
542
+ datasource: {
543
+ source: "custom",
544
+ custom:
545
+ '[\r\n {\r\n "name": "汉族",\r\n "count": 1204\r\n },\r\n {\r\n "name": "少数民族",\r\n "count": 80\r\n }\r\n]',
546
+ },
542
547
  props: {
543
548
  title: "民族分布",
544
549
  size: "small",
545
550
  bordered: true,
546
551
  showHeader: true,
552
+ showSizeChanger: true,
553
+ showQuickJumper: true,
554
+ hideOnSinglePage: true,
555
+ showTotal: true,
547
556
  pageSize: 10,
548
- dataSource: [
549
- {
550
- name: "汉族",
551
- count: 1204,
552
- },
553
- {
554
- name: "少数民族",
555
- count: 80,
556
- },
557
- ],
558
557
  columns: [
559
558
  {
560
559
  title: "民族",
@@ -1,4 +1,4 @@
1
- import { useContext, useEffect, useRef, useState } from "react";
1
+ import { useContext, useEffect, useState } from "react";
2
2
  import { PageContext } from "../context/PageContext";
3
3
  import { SchemaItemType } from "../typing";
4
4
 
@@ -7,9 +7,10 @@ export type Signal = string | number;
7
7
  type Props = {
8
8
  item: SchemaItemType;
9
9
  signal?: Signal;
10
+ query?: Record<string, any>;
10
11
  };
11
12
 
12
- export const useDatasource = ({ item, signal }: Props): any => {
13
+ export const useDatasource = ({ item, signal, query }: Props): any => {
13
14
  const { fetch, getVars, setItemData } = useContext(PageContext);
14
15
  const [datasource, setDatasource] = useState<any>(null);
15
16
  useEffect(() => {
@@ -44,7 +45,7 @@ export const useDatasource = ({ item, signal }: Props): any => {
44
45
  }
45
46
  const vars = getVars(item.id || "");
46
47
  fetch
47
- .dataset(item?.datasource.dataset.id, vars)
48
+ .dataset(item?.datasource.dataset.id, { ...vars, ...query })
48
49
  .then((res) => {
49
50
  setDatasource(res || null);
50
51
  setItemData?.(item.id || "", res || null);
@@ -60,7 +61,7 @@ export const useDatasource = ({ item, signal }: Props): any => {
60
61
  setItemData?.(item.id || "", null);
61
62
  break;
62
63
  }
63
- }, [item?.datasource, signal]);
64
+ }, [item?.datasource, signal, query]);
64
65
 
65
66
  return datasource;
66
67
  };
@@ -140,19 +140,17 @@ export const TablePlugin: PluginType = {
140
140
  source: "custom",
141
141
  datasourceId: null,
142
142
  scriptId: null,
143
- custom: null,
143
+ custom: "[{\"name\":\"苹果\",\"count\":12},{\"name\":\"华为\",\"count\":803},{\"name\":\"OPPO\",\"count\":654},{\"name\":\"vivo\",\"count\":719}]",
144
144
  },
145
145
  props: {
146
146
  size: "default",
147
147
  bordered: true,
148
148
  showHeader: true,
149
+ showSizeChanger: true,
150
+ showQuickJumper: true,
151
+ hideOnSinglePage: true,
152
+ showTotal: true,
149
153
  pageSize: 10,
150
- dataSource: [
151
- { name: "苹果", count: 12 },
152
- { name: "华为", count: 803 },
153
- { name: "OPPO", count: 654 },
154
- { name: "vivo", count: 719 },
155
- ],
156
154
  columns: [
157
155
  { title: "品牌", dataIndex: "name" },
158
156
  { title: "日销量(件)", dataIndex: "count" },
@@ -11,6 +11,7 @@ import {
11
11
  Modal,
12
12
  Input,
13
13
  type TableProps as AntTableProps,
14
+ Select,
14
15
  } from "antd";
15
16
  import type { PropEditorProps } from "./types";
16
17
  import {
@@ -23,14 +24,25 @@ import {
23
24
 
24
25
  export type TableColumnModel = {
25
26
  title: string;
26
- dataIndex: string;
27
+ dataIndex: string | string[];
27
28
  width?: string;
28
29
  align?: "left" | "center" | "right";
29
30
  ellipsis?: boolean;
31
+ customRender?: string;
32
+ className?: string;
30
33
  };
31
34
  export type TableModel = {
32
35
  pageSize?: number;
33
36
  columns: TableColumnModel[];
37
+ dataKey?: string;
38
+ showSizeChanger?: boolean;
39
+ showQuickJumper?: boolean;
40
+ hideOnSinglePage?: boolean;
41
+ showTotal?: boolean;
42
+ headerCellClassNames?: string[];
43
+ bodyCellClassNames?: string[];
44
+ paginationRootClassNames?: string[];
45
+ paginationItemClassNames?: string[];
34
46
  } & Pick<AntTableProps, "bordered" | "showHeader" | "size">;
35
47
 
36
48
  export const TableProps: React.FC<PropEditorProps<TableModel>> = ({
@@ -60,10 +72,14 @@ export const TableProps: React.FC<PropEditorProps<TableModel>> = ({
60
72
  const editingForm =
61
73
  editingIndex != null && model.columns[editingIndex]
62
74
  ? model.columns[editingIndex]
63
- : { title: "", dataIndex: "", width: "", align: "left", ellipsis: false };
75
+ : { title: "", dataIndex: "", width: "", align: "left", ellipsis: false, customRender: "", className: "" };
64
76
  const columns = [
65
77
  { title: "列名", dataIndex: "title" },
66
- { title: "数据索引", dataIndex: "dataIndex" },
78
+ {
79
+ title: "数据索引",
80
+ dataIndex: "dataIndex",
81
+ render: (v: string | string[]) => (typeof v === "string" ? v : v.join(".")),
82
+ },
67
83
  {
68
84
  title: "操作",
69
85
  width: 80,
@@ -88,6 +104,16 @@ export const TableProps: React.FC<PropEditorProps<TableModel>> = ({
88
104
  return (
89
105
  <div>
90
106
  <Form labelCol={{ span: 12 }} wrapperCol={{ span: 12 }}>
107
+ <Form.Item
108
+ label="数据键"
109
+ tooltip="数据键用于指定数据来源中的数据项,例如:'data.items'"
110
+ >
111
+ <Input
112
+ size="small"
113
+ value={model.dataKey}
114
+ onChange={(e) => trigger("dataKey", e.target.value)}
115
+ />
116
+ </Form.Item>
91
117
  <Form.Item label="大小">
92
118
  <Radio.Group
93
119
  size="small"
@@ -113,6 +139,34 @@ export const TableProps: React.FC<PropEditorProps<TableModel>> = ({
113
139
  onChange={(v) => trigger("showHeader", v)}
114
140
  />
115
141
  </Form.Item>
142
+ <Form.Item label="显示分页大小选择器">
143
+ <Switch
144
+ size="small"
145
+ checked={!!model.showSizeChanger}
146
+ onChange={(v) => trigger("showSizeChanger", v)}
147
+ />
148
+ </Form.Item>
149
+ <Form.Item label="显示快速跳转">
150
+ <Switch
151
+ size="small"
152
+ checked={!!model.showQuickJumper}
153
+ onChange={(v) => trigger("showQuickJumper", v)}
154
+ />
155
+ </Form.Item>
156
+ <Form.Item label="隐藏单页分页">
157
+ <Switch
158
+ size="small"
159
+ checked={!!model.hideOnSinglePage}
160
+ onChange={(v) => trigger("hideOnSinglePage", v)}
161
+ />
162
+ </Form.Item>
163
+ <Form.Item label="显示分页总数">
164
+ <Switch
165
+ size="small"
166
+ checked={!!model.showTotal}
167
+ onChange={(v) => trigger("showTotal", v)}
168
+ />
169
+ </Form.Item>
116
170
  <Form.Item label="分页大小">
117
171
  <InputNumber
118
172
  size="small"
@@ -133,6 +187,39 @@ export const TableProps: React.FC<PropEditorProps<TableModel>> = ({
133
187
  <Button size="small" onClick={addColumn} block>
134
188
  添加列
135
189
  </Button>
190
+ <Divider style={{ marginTop: 10 }}>样式设置</Divider>
191
+ <Form.Item label="表头单元格类名" layout="vertical">
192
+ <Select
193
+ mode="tags"
194
+ size="small"
195
+ value={model.headerCellClassNames || []}
196
+ onChange={(e) => trigger("headerCellClassNames", e)}
197
+ />
198
+ </Form.Item>
199
+ <Form.Item label="表格单元格类名" layout="vertical">
200
+ <Select
201
+ mode="tags"
202
+ size="small"
203
+ value={model.bodyCellClassNames || []}
204
+ onChange={(e) => trigger("bodyCellClassNames", e)}
205
+ />
206
+ </Form.Item>
207
+ <Form.Item label="分页根类名" layout="vertical">
208
+ <Select
209
+ mode="tags"
210
+ size="small"
211
+ value={model.paginationRootClassNames || []}
212
+ onChange={(e) => trigger("paginationRootClassNames", e)}
213
+ />
214
+ </Form.Item>
215
+ <Form.Item label="分页项类名" layout="vertical">
216
+ <Select
217
+ mode="tags"
218
+ size="small"
219
+ value={model.paginationItemClassNames || []}
220
+ onChange={(e) => trigger("paginationItemClassNames", e)}
221
+ />
222
+ </Form.Item>
136
223
  <Modal
137
224
  title="编辑列"
138
225
  open={editVisible}
@@ -140,11 +227,11 @@ export const TableProps: React.FC<PropEditorProps<TableModel>> = ({
140
227
  width={400}
141
228
  onCancel={() => setEditVisible(false)}
142
229
  >
143
- <Form layout="vertical">
230
+ <Form layout="vertical" size="small">
144
231
  <div
145
232
  style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}
146
233
  >
147
- <Form.Item label="列名">
234
+ <Form.Item label="列名" style={{marginBottom: 10}}>
148
235
  <Input
149
236
  value={editingForm.title}
150
237
  onChange={(e) =>
@@ -152,15 +239,18 @@ export const TableProps: React.FC<PropEditorProps<TableModel>> = ({
152
239
  }
153
240
  />
154
241
  </Form.Item>
155
- <Form.Item label="数据索引">
156
- <Input
157
- value={editingForm.dataIndex}
158
- onChange={(e) =>
159
- setColumn(editingIndex!, { dataIndex: e.target.value })
242
+ <Form.Item label="数据索引" style={{marginBottom: 10}}>
243
+ <Select
244
+ mode="tags"
245
+ value={
246
+ typeof editingForm.dataIndex === "string"
247
+ ? [editingForm.dataIndex]
248
+ : editingForm.dataIndex
160
249
  }
250
+ onChange={(e) => setColumn(editingIndex!, { dataIndex: e })}
161
251
  />
162
252
  </Form.Item>
163
- <Form.Item label="宽度">
253
+ <Form.Item label="宽度" style={{marginBottom: 10}}>
164
254
  <Input
165
255
  value={editingForm.width}
166
256
  onChange={(e) =>
@@ -168,7 +258,7 @@ export const TableProps: React.FC<PropEditorProps<TableModel>> = ({
168
258
  }
169
259
  />
170
260
  </Form.Item>
171
- <Form.Item label="对齐">
261
+ <Form.Item label="对齐" style={{marginBottom: 10}}>
172
262
  <Radio.Group
173
263
  value={editingForm.align}
174
264
  onChange={(e) =>
@@ -186,12 +276,28 @@ export const TableProps: React.FC<PropEditorProps<TableModel>> = ({
186
276
  </Radio.Button>
187
277
  </Radio.Group>
188
278
  </Form.Item>
189
- <Form.Item label="省略">
279
+ <Form.Item label="省略" style={{marginBottom: 10}}>
190
280
  <Switch
191
281
  checked={!!editingForm.ellipsis}
192
282
  onChange={(v) => setColumn(editingIndex!, { ellipsis: v })}
193
283
  />
194
284
  </Form.Item>
285
+ <Form.Item label="自定义渲染" tooltip="支持自定义渲染,使用{{}}包裹表达式,例如:{{text}},{{record.变量名}},{{index}},{{$g.变量名}},{{$e.变量名}}" style={{gridColumn: "span 2 / span 2", marginBottom: 10}}>
286
+ <Input.TextArea
287
+ value={editingForm.customRender}
288
+ onChange={(e) =>
289
+ setColumn(editingIndex!, { customRender: e.target.value })
290
+ }
291
+ />
292
+ </Form.Item>
293
+ <Form.Item label="类名" style={{gridColumn: "span 2 / span 2", marginBottom: 10}}>
294
+ <Input
295
+ value={editingForm.className}
296
+ onChange={(e) =>
297
+ setColumn(editingIndex!, { className: e.target.value })
298
+ }
299
+ />
300
+ </Form.Item>
195
301
  </div>
196
302
  </Form>
197
303
  </Modal>
@@ -1,45 +1,82 @@
1
- import { Table, type TableProps } from "antd";
2
- import React, { useContext, useEffect } from "react";
1
+ import { PaginationProps, Table, type TableProps } from "antd";
2
+ import React, { useContext, useEffect, useMemo, useState } from "react";
3
3
  import { PageContext } from "../../../context/PageContext";
4
4
  import { HtmlBaseProps } from "../../../typing";
5
+ import { useDatasource } from "../../../hooks/datasource";
5
6
 
6
7
  export type TableRenderProps = {
7
- dataSource?: any[];
8
8
  columns?: any[];
9
+ dataKey?: string;
9
10
  pageSize?: number;
10
11
  bordered?: boolean;
11
12
  size?: TableProps["size"];
12
13
  showHeader?: boolean;
13
14
  item: any;
15
+ showSizeChanger?: boolean;
16
+ showQuickJumper?: boolean;
17
+ hideOnSinglePage?: boolean;
18
+ showTotal?: boolean;
19
+ headerCellClassNames?: string[];
20
+ bodyCellClassNames?: string[];
21
+ paginationRootClassNames?: string[];
22
+ paginationItemClassNames?: string[];
14
23
  } & HtmlBaseProps;
15
24
 
16
25
  export const TableRender: React.FC<TableRenderProps> = ({
17
26
  id,
18
27
  item,
19
- dataSource = [],
20
28
  columns = [],
29
+ dataKey = "",
21
30
  pageSize = 10,
22
31
  bordered = true,
23
32
  size,
24
33
  showHeader = true,
34
+ showSizeChanger = true,
35
+ showQuickJumper = true,
36
+ hideOnSinglePage = true,
37
+ showTotal = true,
25
38
  style = {},
26
39
  className,
40
+ headerCellClassNames = [],
41
+ bodyCellClassNames = [],
42
+ paginationRootClassNames = [],
43
+ paginationItemClassNames = [],
27
44
  }) => {
28
- const { initCallback } = useContext(PageContext);
29
- const pagination =
30
- !pageSize || pageSize <= dataSource.length
31
- ? false
32
- : {
33
- total: dataSource.length,
34
- pageSize,
35
- size,
36
- showSizeChanger: true,
37
- showQuickJumper: true,
38
- hideOnSinglePage: true,
39
- pageSizeOptions: ["10", "20", "30", "40"],
40
- showTotal: (total: number, range: [number, number]) =>
41
- `共 ${total} 条记录,显示第 ${range[0]}-${range[1]} 条`,
42
- };
45
+ const { initCallback, globalVars, env } = useContext(PageContext);
46
+ const [page, setPage] = useState<{
47
+ current: number;
48
+ pageSize: number;
49
+ }>({ current: 1, pageSize });
50
+
51
+ const func = (path: string) =>
52
+ new Function(
53
+ "console",
54
+ "Math",
55
+ "Date",
56
+ "Array",
57
+ "Object",
58
+ "String",
59
+ "Number",
60
+ "Boolean",
61
+ "$g",
62
+ "$e",
63
+ "text",
64
+ "record",
65
+ "index",
66
+ `
67
+ "use strict";
68
+ const window = undefined;
69
+ const document = undefined;
70
+ const global = undefined;
71
+ const process = undefined;
72
+ const require = undefined;
73
+ const module = undefined;
74
+ const exports = undefined;
75
+ return ${path};
76
+ `,
77
+ );
78
+
79
+ const dataSource = useDatasource({ item, query: page });
43
80
  const realColumns = columns.map((item: any) => ({
44
81
  ...item,
45
82
  render:
@@ -47,50 +84,100 @@ export const TableRender: React.FC<TableRenderProps> = ({
47
84
  (typeof item.customRender === "string" && item.customRender.trim() === "")
48
85
  ? item.render
49
86
  : (text: any, record: any, index: number) => {
50
- const safeEval = new Function(
51
- "console",
52
- "Math",
53
- "Date",
54
- "Array",
55
- "Object",
56
- "String",
57
- "Number",
58
- "Boolean",
59
- "text",
60
- "record",
61
- "index",
62
- `
63
- "use strict";
64
- const window = undefined;
65
- const document = undefined;
66
- const global = undefined;
67
- const process = undefined;
68
- const require = undefined;
69
- const module = undefined;
70
- const exports = undefined;
71
- ${item.customRender}
72
- `
73
- );
74
- return (safeEval as any)(
75
- console,
76
- Math,
77
- Date,
78
- Array,
79
- Object,
80
- String,
81
- Number,
82
- Boolean,
83
- text,
84
- record,
85
- index
87
+ const html = (item.customRender || "").replace(
88
+ /{\s*{\s*([^}]+)\s*}\s*}/g,
89
+ (_match: any, p1: string) => {
90
+ try {
91
+ return (func(p1) as any)(
92
+ console,
93
+ Math,
94
+ Date,
95
+ Array,
96
+ Object,
97
+ String,
98
+ Number,
99
+ Boolean,
100
+ globalVars,
101
+ env.global,
102
+ text,
103
+ record,
104
+ index,
105
+ );
106
+ } catch {
107
+ return "";
108
+ }
109
+ },
86
110
  );
111
+ return <div dangerouslySetInnerHTML={{ __html: html }}></div>;
87
112
  },
88
113
  }));
89
114
 
115
+ const data = useMemo(() => {
116
+ if (!dataKey || dataKey.trim() === "") {
117
+ return dataSource || [];
118
+ }
119
+ const keys = dataKey.split(".");
120
+ let result = dataSource || [];
121
+ for (const key of keys) {
122
+ if (result[key]) {
123
+ result = result[key];
124
+ } else {
125
+ result = [];
126
+ break;
127
+ }
128
+ }
129
+ return result || [];
130
+ }, [dataKey, dataSource]);
131
+
132
+ const pagination = useMemo(
133
+ () =>
134
+ !pageSize || pageSize >= (data || []).length
135
+ ? false
136
+ : ({
137
+ total: (data || []).length,
138
+ current: page.current,
139
+ pageSize: page.pageSize,
140
+ size,
141
+ showSizeChanger,
142
+ showQuickJumper,
143
+ hideOnSinglePage,
144
+ pageSizeOptions: ["10", "20", "30", "40"],
145
+ showTotal: showTotal
146
+ ? (total: number, range: [number, number]) =>
147
+ `共 ${total} 条记录,显示第 ${range[0]}-${range[1]} 条`
148
+ : undefined,
149
+ locale: {
150
+ items_per_page: "/ 页",
151
+ jump_to: "跳转至第",
152
+ page: "页",
153
+ },
154
+ onChange: (page: number, pageSize: number) => {
155
+ setPage({ current: page, pageSize });
156
+ },
157
+ onShowSizeChange: (current: number, size: number) => {
158
+ setPage({ current, pageSize: size });
159
+ },
160
+ } as PaginationProps),
161
+ [
162
+ data,
163
+ page,
164
+ pageSize,
165
+ size,
166
+ showSizeChanger,
167
+ showQuickJumper,
168
+ hideOnSinglePage,
169
+ showTotal,
170
+ ],
171
+ );
172
+
90
173
  const callback = () => {
91
174
  console.log("callback table");
92
175
  };
93
176
 
177
+ useEffect(() => {
178
+ setPage({ current: 1, pageSize });
179
+ }, [pageSize]);
180
+
94
181
  useEffect(() => {
95
182
  initCallback({ id: item.id, callback });
96
183
  }, []);
@@ -98,7 +185,7 @@ export const TableRender: React.FC<TableRenderProps> = ({
98
185
  return (
99
186
  <Table
100
187
  id={id}
101
- dataSource={dataSource}
188
+ dataSource={data || []}
102
189
  columns={realColumns as any}
103
190
  pagination={pagination as any}
104
191
  bordered={bordered}
@@ -107,6 +194,18 @@ export const TableRender: React.FC<TableRenderProps> = ({
107
194
  rowKey={(_, index) => String(index)}
108
195
  style={{ padding: 1, ...style }}
109
196
  className={className}
197
+ classNames={{
198
+ header: {
199
+ cell: (headerCellClassNames || []).join(" "),
200
+ },
201
+ body: {
202
+ cell: (bodyCellClassNames || []).join(" "),
203
+ },
204
+ pagination: {
205
+ root: (paginationRootClassNames || []).join(" "),
206
+ item: (paginationItemClassNames || []).join(" "),
207
+ },
208
+ }}
110
209
  />
111
210
  );
112
211
  };