plain-design 1.0.0-beta.132 → 1.0.0-beta.135
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/plain-design.commonjs.min.js +2 -18
- package/dist/plain-design.commonjs.min.js.LICENSE.txt +18 -0
- package/dist/plain-design.min.css +2 -1
- package/dist/plain-design.min.js +2 -18
- package/dist/plain-design.min.js.LICENSE.txt +18 -0
- package/dist/report.html +3 -3
- package/package.json +43 -40
- package/src/packages/components/$ai/index.tsx +214 -0
- package/src/packages/components/$configuration/index.tsx +2 -0
- package/src/packages/components/$file/index.tsx +43 -0
- package/src/packages/components/AiChatBox/ai-chat-box.scss +71 -0
- package/src/packages/components/AiChatBox/index.tsx +154 -0
- package/src/packages/components/AutoTable/auto-table.scss +4 -3
- package/src/packages/components/AutoTable/createTableOptionUser.tsx +3 -1
- package/src/packages/components/AutoTable/filter/useTableOption.filter.state.ts +4 -3
- package/src/packages/components/AutoTable/setting/useTableOption.setting.config.tsx +57 -15
- package/src/packages/components/AutoTable/setting/useTableOption.setting.export.tsx +2 -27
- package/src/packages/components/AutoTable/setting/useTableOption.setting.senior.filter.tsx +92 -92
- package/src/packages/components/AutoTable/setting/useTableOption.setting.senior.sort.tsx +14 -7
- package/src/packages/components/AutoTable/use/useTableOption.ai.tsx +485 -0
- package/src/packages/components/AutoTable/use/useTableOption.buttons.tsx +26 -16
- package/src/packages/components/AutoTable/use/useTableOption.methods.tsx +3 -0
- package/src/packages/components/AutoTable/use/useTableOption.sort.ts +4 -3
- package/src/packages/components/AutoTable/utils/AutoTable.utils.ts +75 -0
- package/src/packages/components/AutoTable/utils/TableOption.space.tsx +2 -1
- package/src/packages/components/FilterService/filter/filter.select.tsx +1 -0
- package/src/packages/components/Image/index.tsx +2 -2
- package/src/packages/components/ImageUploader/index.tsx +4 -4
- package/src/packages/components/PlcImage/index.tsx +5 -5
- package/src/packages/components/Table/editor/PlcSelect.tsx +1 -0
- package/src/packages/components/Table/table/use/useTableFormEditor.tsx +38 -34
- package/src/packages/components/Table/table/utils/table.utils.ts +1 -0
- package/src/packages/components/useDialog/DialogService.tsx +1 -1
- package/src/packages/entry.tsx +24 -0
- package/src/packages/i18n/lang/en-us.ts +23 -14
- package/src/packages/i18n/lang/zh-cn.ts +10 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plain-design",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.135",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/plain-design.min.js",
|
|
6
6
|
"module": "dist/plain-design.commonjs.min.js",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"local": "cross-env APP_ENV=local vue-cli-service serve",
|
|
15
15
|
"dev": "cross-env APP_ENV=prod vue-cli-service serve",
|
|
16
16
|
"build": "cross-env APP_ENV=prod vue-cli-service build",
|
|
17
|
-
"release": "cross-env NODE_ENV=production webpack --config ./build/webpack.release.js --progress"
|
|
17
|
+
"release": "cross-env NODE_ENV=production webpack --config ./build/webpack.release.js --progress",
|
|
18
|
+
"ferv": "ferv ./docs -b plain-design/ -c -s -p 3379"
|
|
18
19
|
},
|
|
19
20
|
"author": "",
|
|
20
21
|
"license": "ISC",
|
|
@@ -40,53 +41,55 @@
|
|
|
40
41
|
"react-transition-group": "4.4.5"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
|
-
"@peryl/react-compose": "0.0.225",
|
|
44
|
-
"@babel/plugin-proposal-optional-chaining": "7.21.0",
|
|
45
|
-
"@babel/plugin-transform-class-properties": "7.23.3",
|
|
46
|
-
"@babel/preset-env": "7.23.7",
|
|
47
|
-
"@babel/preset-react": "7.23.3",
|
|
48
|
-
"@babel/preset-typescript": "7.23.3",
|
|
49
|
-
"@types/classnames": "^2.2.11",
|
|
50
|
-
"@types/react": "18.2.4",
|
|
51
|
-
"@types/react-dom": "18.2.4",
|
|
52
|
-
"@typescript-eslint/eslint-plugin": "4.33.0",
|
|
53
|
-
"@typescript-eslint/parser": "4.33.0",
|
|
54
|
-
"@vue/cli-plugin-babel": "4.5.19",
|
|
55
44
|
"@vue/cli-plugin-eslint": "4.5.19",
|
|
56
|
-
"@vue/cli-plugin-typescript": "4.5.19",
|
|
57
|
-
"@vue/cli-service": "4.5.19",
|
|
58
45
|
"@vue/compiler-sfc": "3.4.3",
|
|
59
46
|
"@vue/eslint-config-typescript": "^7.0.0",
|
|
60
|
-
"autoprefixer": "8.6.5",
|
|
61
|
-
"axios": "1.6.8",
|
|
62
47
|
"babel-plugin-import": "1.13.8",
|
|
63
48
|
"babel-plugin-syntax-jsx": "6.18.0",
|
|
64
|
-
"core-js": "3.35.0",
|
|
65
|
-
"cross-env": "^7.0.3",
|
|
66
|
-
"css-loader": "3.6.0",
|
|
67
49
|
"eslint": "6.8.0",
|
|
68
50
|
"eslint-plugin-vue": "7.20.0",
|
|
69
51
|
"exceljs": "^4.2.1",
|
|
70
52
|
"file-saver": "^2.0.5",
|
|
71
|
-
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"react": "
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"typescript": "^4.
|
|
86
|
-
"
|
|
87
|
-
"vue": "
|
|
88
|
-
"vue-
|
|
53
|
+
|
|
54
|
+
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
|
|
55
|
+
"@babel/plugin-transform-class-properties": "^7.22.3",
|
|
56
|
+
"@babel/preset-env": "^7.13.15",
|
|
57
|
+
"@babel/preset-react": "^7.13.13",
|
|
58
|
+
"@babel/preset-typescript": "^7.13.0",
|
|
59
|
+
"@peryl/react-compose": "0.0.225",
|
|
60
|
+
"@peryl/utils": "0.1.73",
|
|
61
|
+
"@types/classnames": "^2.2.11",
|
|
62
|
+
"@types/node": "^20.8.6",
|
|
63
|
+
"@types/qs": "^6.9.8",
|
|
64
|
+
"@types/react": "18.0.0",
|
|
65
|
+
"@types/react-dom": "18.0.0",
|
|
66
|
+
"@typescript-eslint/eslint-plugin": "^4.18.0",
|
|
67
|
+
"@typescript-eslint/parser": "^4.18.0",
|
|
68
|
+
"@vue/cli-plugin-babel": "~5.0.0",
|
|
69
|
+
"@vue/cli-plugin-typescript": "~5.0.0",
|
|
70
|
+
"@vue/cli-service": "~5.0.0",
|
|
71
|
+
"autoprefixer": "^8.6.5",
|
|
72
|
+
"axios": "^1.7.7",
|
|
73
|
+
"core-js": "^3.8.3",
|
|
74
|
+
"cross-env": "^7.0.3",
|
|
75
|
+
"css-loader": "3.6.0",
|
|
76
|
+
"fork-ts-checker-webpack-plugin": "^6.2.4",
|
|
77
|
+
"mini-css-extract-plugin": "^1.4.1",
|
|
78
|
+
"postcss": "^8.2.13",
|
|
79
|
+
"postcss-loader": "^4.2.0",
|
|
80
|
+
"qs": "^6.10.1",
|
|
81
|
+
"react": "18.0.0",
|
|
82
|
+
"react-dom": "18.0.0",
|
|
83
|
+
"sass": "1.32.7",
|
|
84
|
+
"sass-loader": "12.0.0",
|
|
85
|
+
"style-loader": "^2.0.0",
|
|
86
|
+
"ts-loader": "^9.4.2",
|
|
87
|
+
"ts-node": "^9.1.1",
|
|
88
|
+
"tslib": "^2.5.0",
|
|
89
|
+
"typescript": "^5.5.4",
|
|
90
|
+
"url-loader": "^4.1.1",
|
|
91
|
+
"vue-template-compiler": "2.7.15",
|
|
89
92
|
"webpack-bundle-analyzer": "^4.4.1",
|
|
90
|
-
"webpack-cli": "4.
|
|
93
|
+
"webpack-cli": "^4.6.0"
|
|
91
94
|
}
|
|
92
95
|
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import $configuration from "../$configuration";
|
|
2
|
+
import Axios from "axios";
|
|
3
|
+
import $notice from "../$notice";
|
|
4
|
+
import {iTableAiOperatePlcMeta} from "../AutoTable/utils/AutoTable.utils";
|
|
5
|
+
import $dialog from "../$dialog";
|
|
6
|
+
import i18n from "../../i18n";
|
|
7
|
+
import AiChatBox, {iChatBoxHistory} from "../AiChatBox";
|
|
8
|
+
import {createSimpleDate} from "../createSimpleDate";
|
|
9
|
+
import {uuid} from "@peryl/utils/uuid";
|
|
10
|
+
import {PlainObject} from "@peryl/utils/event";
|
|
11
|
+
import {defer} from "@peryl/utils/defer";
|
|
12
|
+
import {toArray} from "@peryl/utils/toArray";
|
|
13
|
+
|
|
14
|
+
export const $ai = (() => {
|
|
15
|
+
const getConfig = (): iAiConfiguration => {
|
|
16
|
+
const aiConfig = $configuration.get('aiConfig');
|
|
17
|
+
if (!aiConfig) {
|
|
18
|
+
throw new Error('There is a lack of aiConfig when initializing the $configuration.');
|
|
19
|
+
}
|
|
20
|
+
return aiConfig;
|
|
21
|
+
};
|
|
22
|
+
const chat = async (userContent: string, systemContent?: string, _aiConfig?: iAiConfiguration): Promise<{ message: string, chatData: iAiChatResponse }> => {
|
|
23
|
+
const axios = Axios.create();
|
|
24
|
+
const aiConfig = _aiConfig || getConfig();
|
|
25
|
+
const messages: { role: 'system' | 'user', content: string }[] = [];
|
|
26
|
+
!!systemContent && messages.push({ role: 'system', content: systemContent });
|
|
27
|
+
messages.push({ role: 'user', content: userContent });
|
|
28
|
+
try {
|
|
29
|
+
const resp = await axios.request({
|
|
30
|
+
method: 'post',
|
|
31
|
+
url: aiConfig.api_url,
|
|
32
|
+
headers: {
|
|
33
|
+
'Authorization': `Bearer ${aiConfig.api_key}`,
|
|
34
|
+
"Content-Type": 'application/json'
|
|
35
|
+
},
|
|
36
|
+
data: {
|
|
37
|
+
"model": aiConfig.model,
|
|
38
|
+
"messages": messages
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const result = resp.data;
|
|
43
|
+
|
|
44
|
+
let message = result.choices[0].message.content;
|
|
45
|
+
|
|
46
|
+
/*todo 先在这里处理一下json的返回结果吧*/
|
|
47
|
+
/*const pattern = /^```json([\s\S]*?)```/;
|
|
48
|
+
if (pattern.test(message)) {
|
|
49
|
+
const match = message.match(pattern);
|
|
50
|
+
if (match && match[1]) {
|
|
51
|
+
message = match[1].trim();
|
|
52
|
+
}
|
|
53
|
+
}*/
|
|
54
|
+
|
|
55
|
+
return { message, chatData: result };
|
|
56
|
+
} catch (e: any) {
|
|
57
|
+
$notice.error((e.message || JSON.stringify(e)) + '\n' + e.response?.data?.error?.message);
|
|
58
|
+
throw e;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* 根据ai对话,将用户描述转化为表单数据对象,
|
|
63
|
+
* @author 韦胜健
|
|
64
|
+
* @date 2025/4/7 22:40
|
|
65
|
+
*/
|
|
66
|
+
const getFormDataByChat = async (fields: iTableAiOperatePlcMeta[], config?: { title?: string, placeholder?: string }): Promise<PlainObject[]> => {
|
|
67
|
+
|
|
68
|
+
const dfd = defer<PlainObject[]>();
|
|
69
|
+
|
|
70
|
+
fields = fields.filter(i =>
|
|
71
|
+
i.field != 'id' &&
|
|
72
|
+
i.field != 'createdAt' &&
|
|
73
|
+
i.field != 'createdBy' &&
|
|
74
|
+
i.field != 'updatedAt' &&
|
|
75
|
+
i.field != 'updatedBy'
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const systemContent = `
|
|
79
|
+
当前时间是:${createSimpleDate()["YYYY-MM-DD HH:mm:ss"]}
|
|
80
|
+
你是一名专业的自然一样分析师,你需要将用户描述转化为一个json对象数据;这个json对象的属性是确定的,json对象的属性名来源于以下的字段的field,这个属性对应的值需要你从用户描述中获取,没有的话就给默认值null;
|
|
81
|
+
目前支持的字段:
|
|
82
|
+
${JSON.stringify(fields)}
|
|
83
|
+
|
|
84
|
+
注意的是,如果字段的filterType为date,那么这个字段的值为日期字符串,格式为YYYY-MM-DD
|
|
85
|
+
如果这个字段的filterType为datetime,那么这个字段的值为日期时间字符串,格式为YYYY-MM-DD HH:mm:ss,如果用户仅给出了日期没有给出时间,那么给一个默认的时间,当这个字段是开始日期时间时,这个时间的默认值为00:00:00;当这个字段是是结束日期时间时,这个时间的默认值为23:59:59;
|
|
86
|
+
如果用户给出的某个字段的值有多个,那么这个字段的值用数字来表示;
|
|
87
|
+
|
|
88
|
+
如果用户给了多条数据,那么你应该返回这个json对象数组;
|
|
89
|
+
最后你给我返回的数据格式为{data,message},如果能解析出来上面说的json对象的话就放在这个结果的data属性中;如果遇到错误,比如用户指定了一个无法识别的字段,则需要将错误原因写在结果的message中;
|
|
90
|
+
如果用户提问其他的问题,一律拒绝回答;
|
|
91
|
+
`.trim();
|
|
92
|
+
|
|
93
|
+
const close = $dialog({
|
|
94
|
+
title: config?.title || i18n.$it('table.aiButton').d('AI对话'),
|
|
95
|
+
position: 'right',
|
|
96
|
+
noFoot: true,
|
|
97
|
+
width: '500px',
|
|
98
|
+
render: () => (
|
|
99
|
+
<AiChatBox
|
|
100
|
+
fitHeight
|
|
101
|
+
systemContent={systemContent}
|
|
102
|
+
handleMessage={handleMessage}
|
|
103
|
+
placeholder={config?.placeholder || i18n.$it('ai.formAiPlaceholder').d("一句话描述你要编辑的数据内容")}
|
|
104
|
+
/>
|
|
105
|
+
)
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const handleMessage = async ({ message }: { message: string }): Promise<iChatBoxHistory | iChatBoxHistory[] | void> => {
|
|
109
|
+
try {
|
|
110
|
+
const result: { data: PlainObject | PlainObject[], message?: string } = JSON.parse(message);
|
|
111
|
+
if (!!result.message) {
|
|
112
|
+
return [{ id: uuid(), role: 'assistant', content: result.message }];
|
|
113
|
+
} else {
|
|
114
|
+
close();
|
|
115
|
+
dfd.resolve(toArray(result.data));
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
} catch (e: any) {
|
|
119
|
+
console.error(e);
|
|
120
|
+
return [{ id: uuid(), role: 'assistant', content: JSON.stringify(e.message || e) }];
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
return dfd.promise;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
chat,
|
|
129
|
+
getConfig,
|
|
130
|
+
getFormDataByChat,
|
|
131
|
+
};
|
|
132
|
+
})();
|
|
133
|
+
|
|
134
|
+
export interface iAiConfiguration {
|
|
135
|
+
/*大模型的调用地址*/
|
|
136
|
+
api_url: string,
|
|
137
|
+
/*大模型的调用key*/
|
|
138
|
+
api_key: string,
|
|
139
|
+
/*调用的大模型名称*/
|
|
140
|
+
model: string,
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface iAiChoice {
|
|
144
|
+
/**
|
|
145
|
+
* 模型停止生成 token 的原因。取值范围:
|
|
146
|
+
*
|
|
147
|
+
* stop:模型输出自然结束,或因命中请求参数 stop 中指定的字段而被截断。
|
|
148
|
+
* length:模型输出因达到模型输出限制而被截断。
|
|
149
|
+
* max_token:回答内容的长度限制。
|
|
150
|
+
* max_completion_tokens:思维链内容+回答内容的长度限制。
|
|
151
|
+
* context window :输入内容+思维链内容+回答内容的长度限制。
|
|
152
|
+
* content_filter:模型输出被内容审核拦截。
|
|
153
|
+
* tool_calls:模型调用了工具。
|
|
154
|
+
*
|
|
155
|
+
* @author 韦胜健
|
|
156
|
+
* @date 2025/4/5 21:37
|
|
157
|
+
*/
|
|
158
|
+
finish_reason: string,
|
|
159
|
+
/*当前元素在choices列表中的索引*/
|
|
160
|
+
index: number,
|
|
161
|
+
/**
|
|
162
|
+
* 当前内容的对数概率信息。
|
|
163
|
+
* 默认不返回输出 tokens 的对数概率,该值为null。
|
|
164
|
+
* 如需返回对数概率,请将请求参数 logprobs 设置为 true。
|
|
165
|
+
* @author 韦胜健
|
|
166
|
+
* @date 2025/4/5 21:38
|
|
167
|
+
*/
|
|
168
|
+
logprobs: any,
|
|
169
|
+
/*模型输出内容*/
|
|
170
|
+
message: {
|
|
171
|
+
content: string,
|
|
172
|
+
role: string,
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface iAiChatResponse {
|
|
177
|
+
/*iAiChoice*/
|
|
178
|
+
choices: iAiChoice[],
|
|
179
|
+
/*本次请求创建时间的 Unix 时间戳(秒)*/
|
|
180
|
+
created: number,
|
|
181
|
+
/*请求id*/
|
|
182
|
+
id: string,
|
|
183
|
+
/*调用的模型名称*/
|
|
184
|
+
model: string,
|
|
185
|
+
/*固定值chat.completion*/
|
|
186
|
+
object: string,
|
|
187
|
+
/**
|
|
188
|
+
* 本次请求是否使用了TPM保障包。
|
|
189
|
+
*
|
|
190
|
+
* scale:本次请求使用TPM保障包额度。
|
|
191
|
+
* default:本次请求未使用TPM保障包额度。
|
|
192
|
+
*
|
|
193
|
+
* @author 韦胜健
|
|
194
|
+
* @date 2025/4/5 21:41
|
|
195
|
+
*/
|
|
196
|
+
service_tier: string,
|
|
197
|
+
/*本次请求的 token 用量。*/
|
|
198
|
+
usage: {
|
|
199
|
+
/*模型生成的 token 数量。*/
|
|
200
|
+
completion_tokens: number,
|
|
201
|
+
/*本次请求花费的 token 的细节。其中reasoning_tokens是指输出思维链内容花费的 token 。*/
|
|
202
|
+
completion_tokens_details: {
|
|
203
|
+
reasoning_tokens: number,
|
|
204
|
+
},
|
|
205
|
+
/*输入的 prompt token 数量。*/
|
|
206
|
+
prompt_tokens: number,
|
|
207
|
+
/*prompt_tokens中命中上下文缓存的tokens数。需要开通上下文缓存功能,并创建缓存才会启用*/
|
|
208
|
+
prompt_tokens_details: {
|
|
209
|
+
cached_tokens: number,
|
|
210
|
+
},
|
|
211
|
+
/*本次请求消耗的总 token 数量(输入 + 输出)。*/
|
|
212
|
+
total_tokens: number,
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -5,6 +5,7 @@ import {iAddressService} from "../$address/createAddressService";
|
|
|
5
5
|
import {iObjectService} from "../$object/createObjectService";
|
|
6
6
|
import {iOvService} from "../$ov/createOvService";
|
|
7
7
|
import TableOptionSpace from "../TableOptionSpace";
|
|
8
|
+
import {iAiConfiguration} from "../$ai";
|
|
8
9
|
|
|
9
10
|
export interface iConfiguration {
|
|
10
11
|
address?: iAddressService,
|
|
@@ -13,6 +14,7 @@ export interface iConfiguration {
|
|
|
13
14
|
object?: iObjectService,
|
|
14
15
|
http: iHttp,
|
|
15
16
|
useTableOption: TableOptionSpace.iTableOptionUser,
|
|
17
|
+
aiConfig?: iAiConfiguration,
|
|
16
18
|
getExcelJS?: () => Promise<{ Workbook: any }>,
|
|
17
19
|
getFileSaver?: () => Promise<{ saveAs: (...args: any[]) => void }>,
|
|
18
20
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {defer} from "@peryl/utils/defer";
|
|
2
2
|
import {$message} from "../$message";
|
|
3
3
|
import i18n from "../i18n";
|
|
4
|
+
import $configuration from "../$configuration";
|
|
5
|
+
import {createSimpleDate} from "../createSimpleDate";
|
|
4
6
|
|
|
5
7
|
export type FileServiceSingleFile = File & { calcSize: number, data?: any }
|
|
6
8
|
export type FileServiceValidator = (file: FileServiceSingleFile) => boolean | undefined
|
|
@@ -156,11 +158,52 @@ export function createFileService() {
|
|
|
156
158
|
return xhr;
|
|
157
159
|
};
|
|
158
160
|
|
|
161
|
+
/**
|
|
162
|
+
* 导出数据为excel文件
|
|
163
|
+
* @author 韦胜健
|
|
164
|
+
* @date 2025/4/8 12:32
|
|
165
|
+
*/
|
|
166
|
+
const export2file = async ({ exportData, fileName, columns }: { exportData: any[], fileName?: string, columns: { header?: string, key: string }[] }) => {
|
|
167
|
+
// 导出excel
|
|
168
|
+
let [ExcelJs, FileSaver] = await Promise.all([
|
|
169
|
+
$configuration.get('getExcelJS')?.(),
|
|
170
|
+
$configuration.get('getFileSaver')?.()
|
|
171
|
+
]);
|
|
172
|
+
|
|
173
|
+
if (!!ExcelJs && !!(ExcelJs as any).default) {ExcelJs = (ExcelJs as any).default;}
|
|
174
|
+
if (!!FileSaver && !!(FileSaver as any).default) {FileSaver = (FileSaver as any).default;}
|
|
175
|
+
|
|
176
|
+
if (!ExcelJs) {
|
|
177
|
+
throw new Error('请在$configuration中提供exceljs依赖');
|
|
178
|
+
}
|
|
179
|
+
if (!FileSaver) {
|
|
180
|
+
throw new Error('请在$configuration中提供filesaver依赖');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const workbook = new ExcelJs.Workbook();
|
|
184
|
+
const worksheet = workbook.addWorksheet('sheet');
|
|
185
|
+
worksheet.columns = [
|
|
186
|
+
{ header: 'Id', key: 'id', width: 10 },
|
|
187
|
+
{ header: 'Name', key: 'name', width: 32 },
|
|
188
|
+
{ header: 'D.O.B.', key: 'DOB', width: 10, outlineLevel: 1 }
|
|
189
|
+
];
|
|
190
|
+
worksheet.columns = columns;
|
|
191
|
+
exportData.forEach(data => {worksheet.addRow(data);});
|
|
192
|
+
|
|
193
|
+
const buffer = await workbook.xlsx.writeBuffer();
|
|
194
|
+
|
|
195
|
+
FileSaver.saveAs(
|
|
196
|
+
new Blob([buffer], { type: 'application/vnd.ms-excel' }),
|
|
197
|
+
`${fileName || createSimpleDate()["YYYY-MM-DD HH:mm:ss"]}.xlsx`
|
|
198
|
+
);
|
|
199
|
+
};
|
|
200
|
+
|
|
159
201
|
return {
|
|
160
202
|
chooseFile,
|
|
161
203
|
chooseImage,
|
|
162
204
|
readAsDataURL,
|
|
163
205
|
upload,
|
|
206
|
+
export2file,
|
|
164
207
|
};
|
|
165
208
|
}
|
|
166
209
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
@include comp(ai-chat-box) {
|
|
2
|
+
.ai-chat-box-input {
|
|
3
|
+
position: relative;
|
|
4
|
+
|
|
5
|
+
.ai-chat-box-send-button {
|
|
6
|
+
position: absolute;
|
|
7
|
+
bottom: 4px;
|
|
8
|
+
right: 4px;
|
|
9
|
+
z-index: 2;
|
|
10
|
+
|
|
11
|
+
&.button-loading {
|
|
12
|
+
@include comp(loading) {
|
|
13
|
+
top: 1px;
|
|
14
|
+
left: -0.5px;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.ai-chat-box-histories {
|
|
21
|
+
.ai-chat-box-history-item {
|
|
22
|
+
|
|
23
|
+
word-break: break-all;
|
|
24
|
+
|
|
25
|
+
&[data-role=user] {
|
|
26
|
+
text-align: right;
|
|
27
|
+
|
|
28
|
+
.ai-chat-box-history-item-inner {
|
|
29
|
+
display: inline-block;
|
|
30
|
+
background-color: plv(secondary-2);
|
|
31
|
+
color: plv(text-1);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&[data-role=assistant] {
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&.ai-chat-box-fit-height {
|
|
42
|
+
height: 100%;
|
|
43
|
+
display: flex;
|
|
44
|
+
flex-direction: column;
|
|
45
|
+
align-items: stretch;
|
|
46
|
+
|
|
47
|
+
.ai-chat-box-histories {
|
|
48
|
+
flex: 1;
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@include sizeMixin(ai-chat-box, ()) {
|
|
54
|
+
.ai-chat-box-history-item {
|
|
55
|
+
&[data-role=user] {
|
|
56
|
+
.ai-chat-box-history-item-inner {
|
|
57
|
+
padding: calc(#{$margin} / 2) $margin;
|
|
58
|
+
border-radius: $border-radius;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
& + * {
|
|
63
|
+
margin-top: $margin;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.ai-chat-box-histories {
|
|
68
|
+
margin-bottom: $margin;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import {designComponent, getComponentCls, PropType, reactive, useClasses, useRefs} from "@peryl/react-compose";
|
|
2
|
+
import Input from "../Input";
|
|
3
|
+
import {EditProps, useEdit} from "../../uses/useEdit";
|
|
4
|
+
import {StyleProps, useStyle} from "../../uses/useStyle";
|
|
5
|
+
import i18n from "../i18n";
|
|
6
|
+
import Button from "../Button";
|
|
7
|
+
import './ai-chat-box.scss';
|
|
8
|
+
import $message from "../$message";
|
|
9
|
+
import {uuid} from "@peryl/utils/uuid";
|
|
10
|
+
import {$ai, iAiChatResponse, iAiConfiguration} from "../$ai";
|
|
11
|
+
import $configuration from "../$configuration";
|
|
12
|
+
import Scroll from "../Scroll";
|
|
13
|
+
import {delay} from "@peryl/utils/delay";
|
|
14
|
+
import {toArray} from "@peryl/utils/toArray";
|
|
15
|
+
|
|
16
|
+
export const AiChatBox = designComponent({
|
|
17
|
+
props: {
|
|
18
|
+
...EditProps,
|
|
19
|
+
...StyleProps,
|
|
20
|
+
/*ai配置信息*/
|
|
21
|
+
aiConfig: { type: Object as PropType<iAiConfiguration> },
|
|
22
|
+
/*填充高度*/
|
|
23
|
+
fitHeight: { type: Boolean },
|
|
24
|
+
/*已有的对话历史信息*/
|
|
25
|
+
histories: { type: Array as PropType<iChatBoxHistory[]> },
|
|
26
|
+
/*系统提示词*/
|
|
27
|
+
systemContent: { type: String },
|
|
28
|
+
/*手动处理大模型返回的message*/
|
|
29
|
+
handleMessage: { type: Function as PropType<(param: { message: string, userContent: string, chatData: iAiChatResponse, addHistory: (history: iChatBoxHistory) => void }) => void | iChatBoxHistory | iChatBoxHistory[] | Promise<void | iChatBoxHistory | iChatBoxHistory[]>> },
|
|
30
|
+
inputMinHeight: { type: String, default: '80px' },
|
|
31
|
+
inputMaxHeight: { type: String, default: '225px' },
|
|
32
|
+
},
|
|
33
|
+
slots: ['prepend'],
|
|
34
|
+
setup({ props, slots }) {
|
|
35
|
+
|
|
36
|
+
const { refs, onRef } = useRefs({ scroll: Scroll });
|
|
37
|
+
|
|
38
|
+
useEdit();
|
|
39
|
+
const { styleComputed } = useStyle();
|
|
40
|
+
|
|
41
|
+
const aiConfig = props.aiConfig || $configuration.use('aiConfig');
|
|
42
|
+
|
|
43
|
+
const classes = useClasses(() => [
|
|
44
|
+
getComponentCls('ai-chat-box'),
|
|
45
|
+
`ai-chat-box-size-${styleComputed.value.size}`,
|
|
46
|
+
{
|
|
47
|
+
'ai-chat-box-fit-height': props.fitHeight
|
|
48
|
+
}
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
const state = reactive({
|
|
52
|
+
userContent: '',
|
|
53
|
+
histories: props.histories || [] as iChatBoxHistory[],
|
|
54
|
+
loading: false
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const methods = {
|
|
58
|
+
scrollEnd: async () => {
|
|
59
|
+
if (!!refs.scroll) {
|
|
60
|
+
await delay(78);
|
|
61
|
+
refs.scroll.methods.scrollEnd();
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
addHistory: async (history: iChatBoxHistory) => {
|
|
65
|
+
state.histories.push(history);
|
|
66
|
+
await methods.scrollEnd();
|
|
67
|
+
},
|
|
68
|
+
send: async (userContent: string) => {
|
|
69
|
+
await delay(23);
|
|
70
|
+
state.userContent = '';
|
|
71
|
+
state.histories.push({ role: 'user', content: userContent, id: uuid() });
|
|
72
|
+
await methods.scrollEnd();
|
|
73
|
+
|
|
74
|
+
const { message, chatData } = await $ai.chat(userContent, props.systemContent, aiConfig);
|
|
75
|
+
|
|
76
|
+
/*可以手动处理返回的消息,自定义往对话框插入内容*/
|
|
77
|
+
const newHistory: iChatBoxHistory | iChatBoxHistory[] = await props.handleMessage?.({ message, userContent, chatData, addHistory: methods.addHistory }) || ({ role: 'assistant', content: message, id: uuid() });
|
|
78
|
+
|
|
79
|
+
state.histories.push(...toArray(newHistory));
|
|
80
|
+
await methods.scrollEnd();
|
|
81
|
+
|
|
82
|
+
return { message, chatData };
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const handler = {
|
|
87
|
+
onClickSend: async () => {
|
|
88
|
+
state.loading = true;
|
|
89
|
+
try {
|
|
90
|
+
const userContent = state.userContent;
|
|
91
|
+
if (!userContent?.trim().length) {
|
|
92
|
+
$message.error(i18n.$it('ai.require').d('提问的内容不能为空'));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
await methods.send(userContent);
|
|
96
|
+
} finally {
|
|
97
|
+
state.loading = false;
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
refer: {},
|
|
104
|
+
render: () => (
|
|
105
|
+
<div className={classes.value}>
|
|
106
|
+
{(!!state.histories.length || props.fitHeight || slots.prepend.isExist()) && (
|
|
107
|
+
<div className="ai-chat-box-histories">
|
|
108
|
+
{slots.prepend.isExist() && (
|
|
109
|
+
<div className="ai-chat-box-history-item" key="prepend">
|
|
110
|
+
{slots.prepend()}
|
|
111
|
+
</div>
|
|
112
|
+
)}
|
|
113
|
+
{(() => {
|
|
114
|
+
const content = state.histories.map((history) => (
|
|
115
|
+
<div className="ai-chat-box-history-item" key={history.id} data-role={history.role}>
|
|
116
|
+
<div className="ai-chat-box-history-item-inner">
|
|
117
|
+
{!!history.render ? history.render() : history.content}
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
));
|
|
121
|
+
if (props.fitHeight) {
|
|
122
|
+
return <Scroll ref={onRef.scroll}>{content}</Scroll>;
|
|
123
|
+
} else {
|
|
124
|
+
return content;
|
|
125
|
+
}
|
|
126
|
+
})()}
|
|
127
|
+
</div>
|
|
128
|
+
)}
|
|
129
|
+
<div className="ai-chat-box-input">
|
|
130
|
+
<Input
|
|
131
|
+
readonly={state.loading}
|
|
132
|
+
textarea
|
|
133
|
+
v-model={state.userContent}
|
|
134
|
+
placeholder={props.placeholder || i18n.$it('ai.placeholder').d('请输入要发送给ai的内容如:你是谁')}
|
|
135
|
+
minHeight={props.inputMinHeight}
|
|
136
|
+
maxHeight={props.inputMaxHeight}
|
|
137
|
+
onEnter={handler.onClickSend}
|
|
138
|
+
/>
|
|
139
|
+
<Button shape="round" mode="fill" icon="pi-send" className="ai-chat-box-send-button" onClick={handler.onClickSend} loading={state.loading}/>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
)
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
export interface iChatBoxHistory {
|
|
148
|
+
id: string,
|
|
149
|
+
role: 'user' | 'assistant';
|
|
150
|
+
content?: string,
|
|
151
|
+
render?: () => any,
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export default AiChatBox;
|
|
@@ -29,6 +29,7 @@ import {useTableOptionState} from "./use/useTableOption.state";
|
|
|
29
29
|
import {useTableOptionHooks} from "./use/useTableOption.hooks";
|
|
30
30
|
import {useTableOptionPermission} from "./use/useTableOption.permission";
|
|
31
31
|
import {AutoModule} from "./utils/AutoModule";
|
|
32
|
+
import {useTableOptionAi} from "./use/useTableOption.ai";
|
|
32
33
|
|
|
33
34
|
AutoModule.globalRegistrationManager.use(0, '_', (option) => {
|
|
34
35
|
const applicationConfiguration = ApplicationConfigurationProvider.inject();
|
|
@@ -63,8 +64,9 @@ AutoModule.globalRegistrationManager.use(19, 'tips', useTableOptionTips);
|
|
|
63
64
|
AutoModule.globalRegistrationManager.use(20, 'fill', useTableOptionFill); // 自动行数
|
|
64
65
|
AutoModule.globalRegistrationManager.use(21, 'table', useTableOptionTable); // 渲染表格主体
|
|
65
66
|
AutoModule.globalRegistrationManager.use(22, 'parent', useTableOptionParent); // 父表
|
|
67
|
+
AutoModule.globalRegistrationManager.use(23, 'ai', useTableOptionAi); // AI对话
|
|
66
68
|
|
|
67
|
-
AutoModule.globalRegistrationManager.use(
|
|
69
|
+
AutoModule.globalRegistrationManager.use(24, 'settingAllFilter', useTableOptionSettingAllFilter); // 设置:所有筛选
|
|
68
70
|
AutoModule.globalRegistrationManager.use(25, 'settingCache', useTableOptionSettingCache); // 设置:缓存设置
|
|
69
71
|
AutoModule.globalRegistrationManager.use(26, 'settingImport', useTableOptionSettingImport); // 设置:导入数据
|
|
70
72
|
AutoModule.globalRegistrationManager.use(27, 'settingExport', useTableOptionSettingExport); // 设置:导出数据
|