From 37179a65229455f540dc3ca62d31c4716e15d12d Mon Sep 17 00:00:00 2001
From: gerson <1405270578@qq.com>
Date: 星期六, 07 九月 2024 14:32:17 +0800
Subject: [PATCH] 集成 chat组件

---
 src/components/chat/Chat.vue                                                                    |  383 +++++
 src/components/chat/chatComponents/normalTextCom/NormalTextCom.vue                              |   13 
 src/components/form/datepicker/constants.ts                                                     |    3 
 src/components/chat/chatComponents/mapCom/TestData.ts                                           |  287 ++++
 src/components/chat/chatComponents/summaryCom/SummaryCom.vue                                    |   35 
 src/components/chat/chatComponents/summaryCom/components/recordSetTable/RecordSetTable.vue      |  136 +
 src/components/chat/hooks/useQueryProcess.ts                                                    |   41 
 src/components/chat/components/model/Record.ts                                                  |   68 
 src/components/chat/chatComponents/summaryCom/components/deviceLastValue/types.ts               |   21 
 src/utils/util.ts                                                                               |   26 
 src/views/project/yw/lowCode/sqlAmis/SqlAmis.vue                                                |    1 
 src/components/chat/chatComponents/hooks/useDrawChatChart.ts                                    |   42 
 src/api/supervisorAdmin/index.ts                                                                |   13 
 src/components/chat/chatComponents/summaryCom/components/recordSet/components/YRange.vue        |   48 
 src/components/chat/chatComponents/summaryCom/components/recordSet/components/Timestamp.vue     |   59 
 src/components/chat/chatComponents/summaryCom/components/deviceLastValue/DeviceLastValueCom.vue |  328 ++++
 customer_list/yw/static/images/role/assistant-200x192.png                                       |    0 
 src/components/chat/chatComponents/common.ts                                                    |  126 +
 src/components/chat/chatComponents/mapCom/img/monitor-point.svg                                 |    1 
 src/components/chat/hooks/useAssistantContentOpt.ts                                             |  144 ++
 src/components/chat/libs/markdown.ts                                                            |   21 
 src/components/chat/chatComponents/summaryCom/components/recordSet/components/constants.ts      |    2 
 src/components/chat/chatComponents/summaryCom/components/summary/Summary.vue                    |   32 
 src/components/chat/chatComponents/summaryCom/components/recordSet/RecordSet.vue                |  457 ++++++
 src/components/chat/chatComponents/types.ts                                                     |   18 
 src/components/chat/chatComponents/summaryCom/components/amisPage/testData.json                 |  424 ++++++
 package-lock.json                                                                               |   82 +
 src/components/chat/chatComponents/mapCom/types.ts                                              |   18 
 src/components/chat/chatComponents/summaryCom/components/recordSet/RecordSetDialog.vue          |   91 +
 src/api/ai/chat.ts                                                                              |   82 +
 src/components/chat/chatComponents/htmlCom/HTMLCom.vue                                          |   24 
 src/components/chat/components/model/types.ts                                                   |   61 
 src/components/chat/chatComponents/summaryCom/components/recordSet/types.ts                     |   64 
 src/components/chat/chatComponents/mapCom/MapCom.vue                                            |  130 +
 src/components/chat/chatComponents/recordSetCom/RecordSetCom.vue                                |  112 +
 src/components/chat/chatComponents/summaryCom/components/amisPage/AmisPage.vue                  |   28 
 src/components/chat/components/Loading/Loading.vue                                              |  114 +
 src/components/chat/hooks/useScrollToBottom.ts                                                  |   37 
 src/components/chat/chatComponents/summaryCom/components/types.ts                               |   31 
 src/components/chat/chatComponents/summaryCom/components/recordSet/components/TimeRange.vue     |  146 ++
 src/components/chat/chatComponents/summaryCom/components/recordSet/components/List.vue          |   60 
 src/components/chat/chatComponents/summaryCom/components/recordSet/components/types.ts          |   18 
 src/components/chat/chatComponents/summaryCom/components/deviceLastValue/constants.ts           |   18 
 src/components/chat/model/Record.ts                                                             |   68 
 customer_list/yw/static/images/role/user-200x206.png                                            |    0 
 src/components/chat/chatComponents/knowledgeCom/KnowledgeCom.vue                                |   34 
 package.json                                                                                    |    4 
 src/components/chat/chatComponents/summaryCom/components/deviceLastValue/MonitorContent.vue     |  117 +
 src/components/chat/model/types.ts                                                              |   61 
 src/components/chat/libs/gpt.ts                                                                 |   21 
 50 files changed, 4,148 insertions(+), 2 deletions(-)

diff --git a/customer_list/yw/static/images/role/assistant-200x192.png b/customer_list/yw/static/images/role/assistant-200x192.png
new file mode 100644
index 0000000..69a2d41
--- /dev/null
+++ b/customer_list/yw/static/images/role/assistant-200x192.png
Binary files differ
diff --git a/customer_list/yw/static/images/role/user-200x206.png b/customer_list/yw/static/images/role/user-200x206.png
new file mode 100644
index 0000000..37359c5
--- /dev/null
+++ b/customer_list/yw/static/images/role/user-200x206.png
Binary files differ
diff --git a/package-lock.json b/package-lock.json
index b5d147c..51ae22b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,7 @@
 				"@codemirror/lang-xml": "^6.1.0",
 				"@codemirror/state": "6.x",
 				"@element-plus/icons-vue": "^2.1.0",
+				"@iframe-resizer/vue": "^5.1.5",
 				"@types/three": "^0.164.1",
 				"@uiw/codemirror-theme-vscode": "^4.23.0",
 				"@wangeditor/editor": "^5.1.23",
@@ -29,11 +30,13 @@
 				"echarts-wordcloud": "^2.1.0",
 				"element-plus": "^2.2.36",
 				"fast-xml-parser": "^4.4.1",
+				"highlight.js": "^11.7.0",
 				"js-cookie": "^3.0.1",
 				"js-table2excel": "^1.0.3",
 				"json-bigint": "^1.0.0",
 				"json-editor-vue3": "^1.1.1",
 				"lodash": "^4.17.21",
+				"markdown-it": "^13.0.1",
 				"mitt": "^3.0.0",
 				"moment": "^2.29.4",
 				"nprogress": "^0.2.0",
@@ -763,6 +766,30 @@
 			"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
 			"deprecated": "Use @eslint/object-schema instead",
 			"dev": true
+		},
+		"node_modules/@iframe-resizer/core": {
+			"version": "5.3.0",
+			"resolved": "https://registry.npmmirror.com/@iframe-resizer/core/-/core-5.3.0.tgz",
+			"integrity": "sha512-6yJBzMIbVMZA0KDtuTweyE0YiiYpG9p2041WEIurtP6X2RL++tJLu8xqF28S0agKg1OGpBRrv3P6mGxsVMWoLg==",
+			"funding": {
+				"type": "individual",
+				"url": "https://iframe-resizer.com/pricing/"
+			}
+		},
+		"node_modules/@iframe-resizer/vue": {
+			"version": "5.3.0",
+			"resolved": "https://registry.npmmirror.com/@iframe-resizer/vue/-/vue-5.3.0.tgz",
+			"integrity": "sha512-IUNJjHJLgvj5hIYTsrqufAe6Qk8ByKH+IHTUZTeYXp2yBlUEtXLRfKVFv9Q2pjsoXTtJTsbaDTu9rqvMcs11MQ==",
+			"dependencies": {
+				"@iframe-resizer/core": "5.3.0"
+			},
+			"funding": {
+				"type": "individual",
+				"url": "https://iframe-resizer.com/pricing/"
+			},
+			"peerDependencies": {
+				"vue": "^2.6.0 || ^3.0.0"
+			}
 		},
 		"node_modules/@interactjs/actions": {
 			"version": "1.10.27",
@@ -2244,8 +2271,7 @@
 		"node_modules/argparse": {
 			"version": "2.0.1",
 			"resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
-			"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
-			"dev": true
+			"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
 		},
 		"node_modules/array-union": {
 			"version": "2.1.0",
@@ -3938,6 +3964,14 @@
 				"node": ">= 0.4"
 			}
 		},
+		"node_modules/highlight.js": {
+			"version": "11.10.0",
+			"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.10.0.tgz",
+			"integrity": "sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==",
+			"engines": {
+				"node": ">=12.0.0"
+			}
+		},
 		"node_modules/html-void-elements": {
 			"version": "2.0.1",
 			"resolved": "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-2.0.1.tgz",
@@ -4302,6 +4336,14 @@
 			"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
 			"dev": true
 		},
+		"node_modules/linkify-it": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmmirror.com/linkify-it/-/linkify-it-4.0.1.tgz",
+			"integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
+			"dependencies": {
+				"uc.micro": "^1.0.1"
+			}
+		},
 		"node_modules/locate-path": {
 			"version": "6.0.0",
 			"resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz",
@@ -4391,6 +4433,37 @@
 			"dependencies": {
 				"@jridgewell/sourcemap-codec": "^1.5.0"
 			}
+		},
+		"node_modules/markdown-it": {
+			"version": "13.0.2",
+			"resolved": "https://registry.npmmirror.com/markdown-it/-/markdown-it-13.0.2.tgz",
+			"integrity": "sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==",
+			"dependencies": {
+				"argparse": "^2.0.1",
+				"entities": "~3.0.1",
+				"linkify-it": "^4.0.1",
+				"mdurl": "^1.0.1",
+				"uc.micro": "^1.0.5"
+			},
+			"bin": {
+				"markdown-it": "bin/markdown-it.js"
+			}
+		},
+		"node_modules/markdown-it/node_modules/entities": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmmirror.com/entities/-/entities-3.0.1.tgz",
+			"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
+			"engines": {
+				"node": ">=0.12"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/entities?sponsor=1"
+			}
+		},
+		"node_modules/mdurl": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmmirror.com/mdurl/-/mdurl-1.0.1.tgz",
+			"integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g=="
 		},
 		"node_modules/memoize-one": {
 			"version": "6.0.0",
@@ -5786,6 +5859,11 @@
 				"node": ">=4.2.0"
 			}
 		},
+		"node_modules/uc.micro": {
+			"version": "1.0.6",
+			"resolved": "https://registry.npmmirror.com/uc.micro/-/uc.micro-1.0.6.tgz",
+			"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
+		},
 		"node_modules/undici-types": {
 			"version": "5.26.5",
 			"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz",
diff --git a/package.json b/package.json
index 199c228..42fd8d7 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,10 @@
 		"fix-memory-limit": "cross-env LIMIT=8048 increase-memory-limit"
 	},
 	"dependencies": {
+		"highlight.js": "^11.7.0",
+		"markdown-it": "^13.0.1",
+		"@iframe-resizer/vue": "^5.1.5",
+
 		"@amap/amap-jsapi-loader": "^1.0.1",
 		"@amap/amap-jsapi-types": "^0.0.15",
 		"@codemirror/lang-json": "^6.0.1",
diff --git a/src/api/ai/chat.ts b/src/api/ai/chat.ts
new file mode 100644
index 0000000..1bfa871
--- /dev/null
+++ b/src/api/ai/chat.ts
@@ -0,0 +1,82 @@
+import request from '/@/utils/request';
+export interface RecordSetValues {
+	names: string[];
+	values: Array<Array<any>>;
+	type: string;
+	title: string;
+}
+export const GetHistoryAnswer = async (params, req: any = request) => {
+	return req({
+		url: '/history/get_history_answer',
+		method: 'POST',
+		data: params,
+		headers: {
+			'Content-Type': 'application/x-www-form-urlencoded',
+		},
+	});
+};
+export const filterQuery = async (params, req: any = request) => {
+	return req({
+		url: 'chat/filter_query',
+		method: 'POST',
+		data: params,
+		headers: {
+			'Content-Type': 'application/x-www-form-urlencoded',
+		},
+	});
+};
+
+export const queryScadaTimeValues = async (params, req: any = request) => {
+	return req({
+		url: 'data/query_scada_time_values',
+		method: 'POST',
+		data: params,
+		headers: {
+			'Content-Type': 'application/x-www-form-urlencoded',
+		},
+	});
+};
+
+
+/**
+ * @summary 璁剧疆鍘嗗彶瀵硅瘽鐘舵�侊紙鏈缃細NULL锛岄《1锛岃俯0
+ */
+export const SetHistoryAnswerState = async (params, req: any = request) => {
+	return req({
+		url: '/history/set_history_answer_state',
+		method: 'POST',
+		data: params,
+		headers: {
+			'Content-Type': 'application/x-www-form-urlencoded',
+		},
+	});
+};
+
+/** @description 鍏宠仈鏌ヨ */
+export const extCallQuery = async (params, req: any = request) => {
+	return req({
+		url: 'chat/ext_call_query',
+		method: 'POST',
+		data: params,
+		headers: {
+			'Content-Type': 'application/x-www-form-urlencoded',
+		},
+	});
+};
+
+/**
+ * 鏌ヨ闂杩涘害
+ * @param params
+ * @param req
+ * @returns
+ */
+export const getQuestionProcess = async (params, req: any = request) => {
+	return req({
+		url: 'chat/get_question_process',
+		method: 'POST',
+		data: params,
+		headers: {
+			'Content-Type': 'application/x-www-form-urlencoded',
+		},
+	});
+};
\ No newline at end of file
diff --git a/src/api/supervisorAdmin/index.ts b/src/api/supervisorAdmin/index.ts
index 0340e02..6d07a6b 100644
--- a/src/api/supervisorAdmin/index.ts
+++ b/src/api/supervisorAdmin/index.ts
@@ -87,3 +87,16 @@
 		data: params,
 	});
 };
+
+
+
+/**
+ * @summary description
+ */
+export const checkSupervisorValidate = async (params, req: any = request) => {
+	return req({
+		url: '/admin/supervisor/check_amis_supervisor_validate',
+		method: 'POST',
+		data: params,
+	});
+};
\ No newline at end of file
diff --git a/src/components/chat/Chat.vue b/src/components/chat/Chat.vue
new file mode 100644
index 0000000..7991371
--- /dev/null
+++ b/src/components/chat/Chat.vue
@@ -0,0 +1,383 @@
+<template>
+	<div class="flex h-full">
+		<div class="flex flex-col h-full flex-auto">
+			<div class="h-full flex flex-col items-center overflow-y-auto">
+				<div ref="chatListDom" class="h-full" :style="{ width: chatWidth }">
+					<div
+						class="group flex px-4 py-6 hover:bg-slate-100 rounded-lg relative"
+						:class="{ 'flex-row-reverse': item.role === RoleEnum.user }"
+						v-for="(item, index) of computedMessageList"
+						:key="index"
+					>
+						<img
+							class="rounded-full size-12 flex-0"
+							:class="{ 'mr-4': item.role === RoleEnum.assistant, 'ml-4': item.role === RoleEnum.user }"
+							:src="roleImageMap[item.role]"
+							alt=""
+							srcset=""
+						/>
+						<div class="flex-auto flex" :class="{ 'justify-end': item.role === RoleEnum.user }">
+							<div class="inline-flex flex-col" :class="{ 'w-full': item.role === RoleEnum.assistant }">
+								<div class="w-full" v-if="item.content?.values">
+									<div
+										class="text-sm rounded-[6px] p-4 leading-relaxed"
+										:style="{ backgroundColor: item.role === RoleEnum.user ? 'rgb(197 224 255)' : 'white' }"
+									>
+										<div v-if="item.content.errCode === ErrorCode.Message" class="flex-column w-full">
+											<p class="text-red-500">
+												{{ item.content.errMsg }}
+											</p>
+											<div class="mt-5 flex items-center" v-if="showFixQuestion(item)">
+												<div class="text-gray-600 flex-0">
+													{{ item.content.origin.err_json.fix_question.title + '锛�' }}
+												</div>
+												<div class="ml-1 space-x-2 inline-flex flex-wrap">
+													<div
+														v-for="fixItem in item.content.origin.err_json.fix_question?.values"
+														:key="fixItem"
+														class="bg-gray-200 p-3 hover:bg-[#c5e0ff] hover:text-[#1c86ff] cursor-pointer rounded-lg"
+														@click="fixQuestionClick(fixItem, item.content.origin)"
+													>
+														{{ fixItem.title }}
+													</div>
+												</div>
+											</div>
+										</div>
+										<template v-else>
+											<component :is="answerTypeMapCom[item.content.type]" :data="item.content.values" :originData="item" />
+
+											<div
+												v-if="item.role === RoleEnum.assistant && item.content.origin?.ext_call_list"
+												class="flex font-bold items-center mt-6"
+											>
+												<div class="flex-0 mb-auto -mr-4">鍏宠仈鍔熻兘锛�</div>
+												<!-- <div class="space-x-5 flex flex-wrap">
+													<div
+														v-for="callItem in item.content.origin?.ext_call_list"
+														:key="callItem.call_ext_id"
+														@click="relativeQueryClick(callItem)"
+														class="cursor-pointer hover:underline first-of-type:ml-5"
+													>
+														{{ callItem.question }}
+													</div>
+												</div> -->
+											</div>
+										</template>
+									</div>
+
+									<!-- 鎿嶄綔 -->
+									<div v-if="item.role === RoleEnum.assistant" class="absolute flex items-center right-0 mr-4 mt-2 space-x-2">
+										<div
+											class="flex items-center justify-center size-[15px]"
+											v-if="item.content?.type === AnswerType.Text || item.content?.type === AnswerType.Knowledge"
+										>
+											<i
+												class="p-2 ywifont ywicon-copy cursor-pointer hover:text-[#0284ff] hover:!text-[18px]"
+												@click="copyClick(item)"
+											/>
+										</div>
+										<template v-if="item.content.errCode !== ErrorCode.Message">
+											<div class="flex items-center justify-center size-[15px]">
+												<i
+													:class="{ 'text-[#0284ff]': item.state === AnswerState.Like }"
+													class="p-2 ywifont ywicon-dianzan cursor-pointer hover:text-[#0284ff] font-medium hover:!text-[18px]"
+													@click="likeClick(item)"
+												/>
+											</div>
+											<div class="flex items-center justify-center size-[15px]">
+												<i
+													:class="{ 'text-[#0284ff]': item.state === AnswerState.Unlike }"
+													class="p-2 ywifont ywicon-buzan cursor-pointer hover:text-[#0284ff] !text-[13px] hover:!text-[15px]"
+													@click="unLikeClick(item)"
+												/>
+											</div>
+										</template>
+
+										<div class="flex items-center justify-center size-[15px] relative">
+											<i
+												class="p-2 ywifont ywicon-wentifankui cursor-pointer hover:text-[#0284ff] !text-[13px] hover:!text-[15px]"
+												@click="
+													($event) =>
+														feedbackClick(
+															$event,
+															item,
+															computedMessageList
+																.filter((v) => v.role === RoleEnum.assistant)
+																.findIndex((v) => v.historyId === item.historyId)
+														)
+												"
+											/>
+											<FeedbackPanel
+												v-show="feedbackIsShow && currentFeedbackMapItem === item"
+												ref="feedbackPanelRef"
+												v-model:isShow="feedbackIsShow"
+												v-model:content="feedbackContent"
+												:chatItem="currentFeedbackMapItem"
+												:position="feedbackPosition"
+											/>
+										</div>
+									</div>
+								</div>
+
+								<Loading v-if="isTalking && index === messageList.length - 1" class="w-fit" />
+							</div>
+						</div>
+					</div>
+					<div v-if="showAskMore" class="ml-4 mt-5 text-sm">
+						<div class="text-gray-600 mb-5">浣犲彲浠ョ户缁棶鎴戯細</div>
+						<div class="space-y-2 inline-flex flex-col">
+							<div
+								v-for="item in computedMessageList.at(-1).content.askMoreList"
+								:key="item.history_id"
+								class="bg-white p-3 hover:bg-[#c5e0ff] hover:text-[#1c86ff] cursor-pointer rounded-lg"
+								@click="askMoreClick(item)"
+							>
+								{{ item.question }}
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+
+			<div class="sticky bottom-0 w-full p-6 pb-8 bg-[rgb(247,248,250)] flex justify-center">
+				<PlayBar
+					v-model:voicePageIsShow="voicePageIsShow"
+					:isTalking="isTalking"
+					:isHome="false"
+					v-model="messageContent.values"
+					@sendClick="sendClick"
+					:style="{ width: chatWidth }"
+				></PlayBar>
+			</div>
+		</div>
+
+		<!-- <CustomDrawer v-model:isShow="drawerIsShow" @updateChatInput="updateChatInput" /> -->
+	</div>
+</template>
+
+<script setup lang="ts">
+import _ from 'lodash';
+import { computed, ref } from 'vue';
+import FeedbackPanel from './components/FeedbackPanel.vue';
+import Loading from './components/Loading/Loading.vue';
+import { useAssistantContentOpt } from './hooks/useAssistantContentOpt';
+import { useScrollToBottom } from './hooks/useScrollToBottom';
+import type { ChatContent } from './model/types';
+import { AnswerState, AnswerType, RoleEnum, answerTypeMapCom, roleImageMap, type ChatMessage } from './model/types';
+import { GetHistoryAnswer, extCallQuery } from '/@/api/ai/chat';
+import PlayBar from '/@/components/chat/components/playBar/PlayBar.vue';
+import { ErrorCode } from '/@/utils/request';
+import { checkSupervisorValidate } from '/@/api/supervisorAdmin';
+
+const emit = defineEmits<{
+	(event: 'question',inputText): any;
+}>();
+
+const chatWidth = '75%';
+const voicePageIsShow = ref(false);
+let isTalking = ref(false);
+let messageContent = ref<ChatContent>({
+	type: AnswerType.Text,
+	values: '',
+});
+
+const chatListDom = ref<HTMLDivElement>();
+const messageList = ref<ChatMessage[]>([]);
+const computedMessageList = computed(() => {
+	return messageList.value.filter((v) => !!v);
+});
+
+const parseContent = (res) => {
+	if (!res) return null;
+	let content: ChatContent = {
+		type: AnswerType.Text,
+		values: '瑙f瀽澶辫触锛�',
+	};
+
+	switch (res.answer_type) {
+		case AnswerType.RecordSet:
+			content = {
+				type: AnswerType.RecordSet,
+				values: res.values,
+			};
+			break;
+		case AnswerType.Text:
+			content = {
+				type: AnswerType.Text,
+				values: res.values ?? res.answer,
+			};
+			break;
+
+		case AnswerType.Knowledge:
+			content = {
+				type: AnswerType.Knowledge,
+				values: res.knowledge,
+			};
+
+			break;
+		case AnswerType.Summary:
+			content = {
+				type: AnswerType.Summary,
+				values: res.summary,
+			};
+			break;
+		case AnswerType.Url:
+			content = {
+				type: AnswerType.Url,
+				values: res.url,
+			};
+			break;
+		case AnswerType.Map:
+			content = {
+				type: AnswerType.Map,
+				values: res.values,
+			};
+			break;
+		default:
+			content = {
+				type: AnswerType.Text,
+				values: '瑙f瀽澶辫触锛�',
+			};
+			break;
+	}
+	content.askMoreList = _.orderBy(res.context_history, [(item) => Number(item.radio)], ['desc']);
+	content.errCode = res?.err_code;
+	content.errMsg = res?.json_msg;
+	content.origin = res;
+	return content;
+};
+
+let questionRes = null;
+const questionAi = async (text) => {
+	const params = {
+		id: 'net3_summary',
+		question: text,
+	} as any;
+
+	// const res = await emit('question',text);
+	const res = await checkSupervisorValidate(params);
+	questionRes = res?.values;
+	const content = parseContent(res?.values);
+	return content;
+};
+
+const clearMessageContent = () =>
+	(messageContent.value = {
+		type: AnswerType.Text,
+		values: '',
+	});
+
+const getAnswerById = async (historyId: string) => {
+	return await GetHistoryAnswer({
+		history_id: historyId,
+	});
+};
+
+const sendChatMessage = async (content: ChatContent = messageContent.value, cb?: any, isCallExtParams?: any) => {
+	if (!content?.values || isTalking.value) return;
+	const isNewChat = messageList.value.length === 0;
+
+	let resMsgContent: ChatContent = null;
+
+	try {
+		isTalking.value = true;
+		const userItem: ChatMessage = { role: RoleEnum.user, content } as any;
+		const assistantItem: ChatMessage = { role: RoleEnum.assistant, content: null, state: AnswerState.Null } as any;
+		// 鍙戦�佸綋鍓�
+		messageList.value.push(userItem);
+		// 娓呯┖杈撳叆妗�
+		clearMessageContent();
+
+		// 鍑虹幇鍥炲锛岀疆绌哄嚭鐜扮瓑寰呭姩鐢�
+		messageList.value.push(assistantItem);
+		if (isCallExtParams) {
+			const extRes = await extCallQuery(isCallExtParams);
+			questionRes = extRes;
+			resMsgContent = parseContent(extRes);
+		} else {
+			resMsgContent = await questionAi(content.values);
+		}
+
+		userItem.historyId = questionRes.history_id;
+		userItem.content.values = questionRes?.question ?? userItem.content.values;
+		assistantItem.historyId = questionRes.history_id;
+		appendLastMessageContent(resMsgContent);
+	} catch (error: any) {
+		// appendLastMessageContent({
+		// 	type: AnswerType.Text,
+		// 	values: '鍙戠敓閿欒锛�',
+		// });
+	} finally {
+		isTalking.value = false;
+	}
+};
+
+const sendClick = (cb) => {
+	sendChatMessage(messageContent.value, cb);
+};
+const appendLastMessageContent = (content: ChatContent) => {
+	if (messageList.value.at(-1)) {
+		messageList.value.at(-1).content = content;
+	}
+};
+
+const { forbidScroll } = useScrollToBottom({
+	chatListDom: chatListDom,
+	displayMessageList: computedMessageList,
+});
+
+//#region ====================== 鍏宠仈鏌ヨ ======================
+// const relativeQueryClick = async (val) => {
+// 	sendChatMessage(
+// 		{
+// 			type: AnswerType.Text,
+// 			values: val.question,
+// 		},
+// 		undefined,
+// 		{
+// 			history_group_id: currentRouteId,
+// 			question: val.question,
+// 			call_ext_id: val.call_ext_id,
+// 			call_ext_args: val.agrs ? JSON.stringify(val.agrs) : null,
+// 		}
+// 	);
+// };
+//#endregion
+
+const {
+	copyClick,
+	likeClick,
+	unLikeClick,
+	feedbackPosition,
+	feedbackIsShow,
+	feedbackContent,
+	feedbackPanelRef,
+	currentFeedbackMapItem,
+	feedbackClick,
+	askMoreClick,
+	fixQuestionClick,
+	preQuestion,
+	showFixQuestion,
+	showAskMore,
+} = useAssistantContentOpt({
+	forbidScroll,
+	sendChatMessage,
+	displayMessageList: computedMessageList,
+});
+
+//#region ====================== 渚ц竟鏍廳rawer ======================
+const drawerIsShow = ref(false);
+
+const updateChatInput = (content) => {
+	messageContent.value.values = content;
+};
+//#endregion
+</script>
+
+<style scoped>
+pre {
+	font-family: -apple-system, 'Noto Sans', 'Helvetica Neue', Helvetica, 'Nimbus Sans L', Arial, 'Liberation Sans', 'PingFang SC',
+		'Hiragino Sans GB', 'Noto Sans CJK SC', 'Source Han Sans SC', 'Source Han Sans CN', 'Microsoft YaHei', 'Wenquanyi Micro Hei',
+		'WenQuanYi Zen Hei', 'ST Heiti', SimHei, 'WenQuanYi Zen Hei Sharp', sans-serif;
+}
+</style>
diff --git a/src/components/chat/chatComponents/common.ts b/src/components/chat/chatComponents/common.ts
new file mode 100644
index 0000000..0188716
--- /dev/null
+++ b/src/components/chat/chatComponents/common.ts
@@ -0,0 +1,126 @@
+import type * as echarts from 'echarts';
+import { buildProps } from 'element-plus/es/utils/vue/props/runtime';
+import _ from 'lodash';
+import type { ExtractPropTypes, PropType } from 'vue';
+import { axisLabelFormatter } from '/@/utils/chart';
+
+export const timeDataOptionToContent = (opt) => {
+	const headerList = [opt.xAxis[0]]
+		.concat(opt.yAxis)
+		.map((item) => `<td>${item.name}</td>`)
+		.join('');
+	const title = opt.title?.[0]?.text ?? '';
+	let table =
+		'<div style="border:1px solid black"><h3>' +
+		title +
+		'</h3><table style="width:100%;text-align:center"><tbody><tr>' +
+		headerList +
+		'</tr>';
+	const timeData = new Set();
+	const dataMap = opt.series.map((item) => {
+		for (const subItem of item.data) {
+			timeData.add(subItem[0]);
+		}
+		return new Map(item.data);
+	});
+	const bodyList = Array.from(timeData)
+		.toSorted((a, b) => {
+			return (a as any).localeCompare(b);
+		})
+		.map((item) => {
+			return `<tr><td>${item}</td>${dataMap.map((itemMap) => `<td>${itemMap.get(item) ?? ''}</td>`)}</tr>`;
+		})
+		.join('');
+	table += bodyList;
+
+	table += '</tbody></table></div>';
+	return table;
+};
+
+export const PATH_ICON = {
+	scatter:
+		'path://M445.7 609.8c0 19.4 10.3 37.3 27.1 46.9 16.8 9.7 37.4 9.7 54.2 0 16.8-9.7 27.1-27.6 27.1-46.9 0-29.9-24.3-54.2-54.2-54.2s-54.2 24.3-54.2 54.2z m0 0M179.2 613.8c-42.2 0-76.5 34.3-76.5 76.5s34.3 76.5 76.5 76.5 76.5-34.3 76.5-76.5-34.3-76.5-76.5-76.5z m0 0M144.9 401.1c0 29 23.5 52.5 52.5 52.5s52.5-23.5 52.5-52.5-23.5-52.5-52.5-52.5-52.5 23.5-52.5 52.5z m0 0M598.7 404c0 42.2 34.3 76.5 76.5 76.5 42.3 0 76.5-34.3 76.5-76.5 0-42.3-34.3-76.5-76.5-76.5-42.3 0-76.5 34.3-76.5 76.5z m0 0M849.3 169.2c-42.2 0-76.5 34.3-76.5 76.5s34.3 76.5 76.5 76.5 76.5-34.3 76.5-76.5-34.3-76.5-76.5-76.5z m0 0M261.6 583.1c0 13.2 7.1 25.5 18.5 32.1 11.5 6.6 25.6 6.6 37.1 0s18.5-18.9 18.5-32.1c0-20.5-16.6-37.1-37.1-37.1-20.4 0.1-37 16.7-37 37.1z m0 0M276.8 425.1c0 42.3 34.3 76.5 76.5 76.5 42.3 0 76.5-34.3 76.5-76.5s-34.3-76.5-76.5-76.5-76.5 34.3-76.5 76.5z m0 0M445.7 421.4c0 18.5 9.9 35.5 25.8 44.8 16 9.2 35.7 9.2 51.7 0s25.8-26.3 25.8-44.8c0-28.5-23.1-51.7-51.7-51.7-28.5 0-51.6 23.2-51.6 51.7z m0 0M398.2 208.8c0 42.3 34.3 76.5 76.5 76.5s76.5-34.3 76.5-76.5c0-42.3-34.3-76.5-76.5-76.5s-76.5 34.3-76.5 76.5z m0 0M693.7 599.2c0 42.3 34.3 76.5 76.5 76.5s76.5-34.3 76.5-76.5-34.3-76.5-76.5-76.5c-42.3 0-76.5 34.3-76.5 76.5z m0 0M62.1 828.9H959v60.7H62.1z',
+	bar: 'path://M580.8 228.8h-136v500.8h136V228.8z m-40 460.8h-56V268.8h56v420.8zM788.8 420.8h-136v308.8h136V420.8z m-40 268.8h-56V460.8h56v228.8zM372.8 326.4h-136v401.6h136V326.4z m-40 363.2h-56V366.4h56v323.2zM208 788.8h608v40H208z',
+
+	line: 'path://M664.1 783c-3.8 0-7.6-0.2-11.3-0.5-36.3-3.1-68.9-20.6-96.9-52.1-28.4-32-52.5-79.4-71.4-140.8-32.4-105-66.1-182.6-100.3-230.6-25.5-35.8-50.2-54-73.4-54h-0.4c-38.8 0.4-84.8 51-129.3 142.7-37 76.2-59.4 153-59.7 153.8L64 582.6c1-3.4 24.3-83.1 63.7-164.3 56.8-117 118.1-176.6 182.1-177.3h1c43 0 83.8 26.7 121.2 79.3 38.2 53.7 75.1 137.5 109.5 249.3 29.3 94.9 68.3 145.1 116 149.1 21.3 1.8 45.6-5.2 72.2-20.9 24.5-14.4 50.3-35.8 76.8-63.5 49.6-51.9 87.7-111.9 100.1-137.6L960 526c-14.6 30.4-56.4 96.4-111.4 154-30.4 31.8-60.6 56.6-89.9 73.9-32.8 19.3-64.6 29.1-94.6 29.1z',
+};
+
+export const SCATTER_SYMBOL_SIZE = 4;
+
+export const chatComProps = buildProps({
+	data: {
+		type: Object as PropType<any>,
+	},
+	originData: {
+		type: Object as PropType<any>,
+	},
+} as const);
+export type ChatComPropsType = ExtractPropTypes<typeof chatComProps>;
+
+export const getChatChartOption = () => {
+	const option = {
+		grid: {
+			// bottom: 120,
+			// right: '15%',
+			top: 65,
+			left: 65,
+			right: 45,
+		},
+		tooltip: {
+			show: true,
+			trigger: 'axis',
+		
+		},
+		toolbox: {
+			show: true,
+			feature: {
+				dataZoom: {
+					yAxisIndex: 'none',
+				},
+
+				myBar: {
+					title: '杞寲涓烘煴鐘跺浘',
+					show: true,
+					icon: PATH_ICON.bar,
+				},
+
+				myScatter: {
+					title: '杞寲涓烘暎鐐瑰浘',
+					show: true,
+					icon: PATH_ICON.scatter,
+				},
+				myLine: {
+					title: '杞寲涓烘洸绾垮浘',
+					show: true,
+					icon: PATH_ICON.line,
+				},
+				dataView: {
+					readOnly: true,
+					optionToContent: timeDataOptionToContent,
+				},
+				saveAsImage: {},
+			},
+		},
+
+		title: {
+			left: 'center',
+			textStyle: {
+				fontSize: 14,
+			},
+		},
+		xAxis: {
+			type: 'time',
+		},
+		yAxis: {
+			type: 'value',
+			axisLabel: {
+				formatter: axisLabelFormatter,
+			},
+		},
+		dataZoom: {
+			type: 'inside',
+		},
+	} as echarts.EChartsOption;
+
+	return _.cloneDeep(option);
+};
diff --git a/src/components/chat/chatComponents/hooks/useDrawChatChart.ts b/src/components/chat/chatComponents/hooks/useDrawChatChart.ts
new file mode 100644
index 0000000..fc80503
--- /dev/null
+++ b/src/components/chat/chatComponents/hooks/useDrawChatChart.ts
@@ -0,0 +1,42 @@
+import * as echarts from 'echarts';
+import type { Ref } from 'vue';
+import { onMounted, shallowRef } from 'vue';
+import { debounce } from '/@/utils/util';
+
+export type UseDrawChatChartOption = {
+	chartRef: Ref<HTMLDivElement>;
+	drawChart: () => void;
+};
+export const useDrawChatChart = (option: UseDrawChatChartOption) => {
+	const { chartRef, drawChart } = option;
+	const chartInstance = shallowRef<echarts.ECharts>(null);
+
+	let resizeChart = null;
+	const chartContainerResize = ({ width, height }) => {
+		resizeChart?.(width, height);
+	};
+
+	onMounted(() => {
+		setTimeout(() => {
+			const parent = chartRef.value.parentElement;
+			const parentBound = parent.getBoundingClientRect();
+			chartInstance.value = echarts.init(chartRef.value, undefined, {
+				width: parentBound.width,
+				height: parentBound.height,
+			});
+			resizeChart = debounce((width, height) => {
+				chartInstance.value.resize({
+					width: width,
+					height: height,
+				});
+			});
+
+			drawChart();
+		}, 100);
+	});
+
+	return {
+		chartContainerResize,
+		chartInstance,
+	};
+};
diff --git a/src/components/chat/chatComponents/htmlCom/HTMLCom.vue b/src/components/chat/chatComponents/htmlCom/HTMLCom.vue
new file mode 100644
index 0000000..d6e2cb8
--- /dev/null
+++ b/src/components/chat/chatComponents/htmlCom/HTMLCom.vue
@@ -0,0 +1,24 @@
+<template>
+	<IframeResizer
+		class="iframe-resizer"
+		license="GPLv3"
+		:src="data.url"
+		width="100%"
+		frameborder="no"
+		border="0"
+		marginwidth="0"
+		marginheight="0"
+		scrolling="no"
+		
+	/>
+</template>
+
+<script setup lang="ts">
+import IframeResizer from '@iframe-resizer/vue/iframe-resizer.vue';
+import { ref } from 'vue';
+import { chatComProps } from '../common';
+const iframeRef = ref<HTMLIFrameElement>(null);
+
+const props = defineProps(chatComProps);
+</script>
+<style scoped lang="scss"></style>
diff --git a/src/components/chat/chatComponents/knowledgeCom/KnowledgeCom.vue b/src/components/chat/chatComponents/knowledgeCom/KnowledgeCom.vue
new file mode 100644
index 0000000..dff677d
--- /dev/null
+++ b/src/components/chat/chatComponents/knowledgeCom/KnowledgeCom.vue
@@ -0,0 +1,34 @@
+<template>
+	<div class="space-y-7">
+		<div v-for="(item, index) in data" :key="index">
+			<div v-html="md.render(item.answer)"></div>
+			<div class="space-y-1 mt-2">
+				<div v-for="(cItem, index) in item.contexts" :key="index">
+					<div class="text-blue-500 cursor-pointer inline-block" @click="pageLinkClick(cItem)">
+						<SvgIcon name="ele-Link" />
+						<span class="ml-2">{{ cItem.metadata?.Title }}</span>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { md } from '../../libs/markdown';
+import { chatComProps } from '../common';
+
+
+const props = defineProps(chatComProps);
+
+const pageLinkClick = (item) => {
+	const nwin = window.open('');
+	nwin.document.write(md.render(item.page_content))
+	nwin.focus();
+	if(item.metadata.Title){
+		nwin.document.title = item.metadata.Title
+
+	}
+};
+</script>
+<style scoped lang="scss"></style>
diff --git a/src/components/chat/chatComponents/mapCom/MapCom.vue b/src/components/chat/chatComponents/mapCom/MapCom.vue
new file mode 100644
index 0000000..299b2c7
--- /dev/null
+++ b/src/components/chat/chatComponents/mapCom/MapCom.vue
@@ -0,0 +1,130 @@
+<template>
+	<div class="h-[70vh] relative">
+		<div ref="containerRef" class="h-full"></div>
+		<div v-if="bottomBarIsShow" class="absolute w-full bottom-0 bg-white border-gray-300 border border-solid">
+			<div
+				class="w-28 h-5 absolute left-1/2 -translate-x-1/2 -translate-y-[100%] cursor-pointer bg-[#4974f3] rounded-t-lg flex-center"
+				@click="toggleShowChart"
+			>
+				<div
+					class="ywifont -rotate-90 text-white !text-[13px]"
+					:class="{ 'ywicon-zuoyoujiantou': chartIsShow, 'ywicon-zuoyoujiantou1': !chartIsShow }"
+				></div>
+			</div>
+			<RecordSet v-if="chartIsShow" chartHeight="23vh" :data="CHART_DATA" class="mt-2" />
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { onMounted, ref } from 'vue';
+import { chatComProps } from '../common';
+import RecordSet from '../summaryCom/components/recordSet/RecordSet.vue';
+import { CHART_DATA } from './TestData';
+import monitorPointPic from './img/monitor-point.svg';
+import type { MapData } from './types';
+import type { GaoDePosition, LabelMarkerData } from '/@/model/map/GaoDeMap';
+import { GaoDeMap } from '/@/model/map/GaoDeMap';
+
+let gaoDeMap = new GaoDeMap();
+const containerRef = ref<HTMLDivElement>(null);
+const props = defineProps(chatComProps) as {
+	data: MapData;
+};
+const createInfoWindow = () => {
+	const dom = `<div class="w-48 bg-white p-1 flex flex-col border-blue-500 border-solid border">
+			<div class="bg-[#ca0dab] flex py-1 text-white px-5">
+				<div class="pointer-title  text-nowrap overflow-hidden text-ellipsis mx-auto"></div>	
+			</div>
+		
+		</div>`;
+
+	// const dom = `<div class="w-32 bg-white p-1 flex flex-col border-blue-500 border-solid border">
+	// 	<div class="bg-[#ca0dab] flex-center py-1 text-white">
+	// 		姘存湀浜�
+	// 	</div>
+	// 	<div class="flex flex-col mt-2">
+	// 		<div class="flex w-full text-blue-800">
+	// 			<div class="overflow-hidden text-nowrap overflow-ellipsis self-end flex-auto text-right">
+	// 				0.72332
+	// 			</div>
+	// 			<div class="flex-0 ml-4">
+	// 				m
+	// 			</div>
+
+	// 		</div>
+
+	// 	</div>
+	// </div>`;
+	return dom;
+};
+
+const updateInfoWindow = (title: string) => {
+	const pointerTitle = infoWindow.dom.querySelector('.pointer-title');
+	pointerTitle.innerHTML = title +'';
+};
+
+const addMarkerLayer = () => {
+	const dataList = (props.data?.values ?? []).map<LabelMarkerData>((item) => ({
+		position: [item.posx, item.posy],
+		textColor: item.color,
+		extData: item,
+		title: item.title,
+	}));
+	gaoDeMap.addMarkerLayer(dataList, {
+		markerOpt: {
+			icon: {
+				url: monitorPointPic,
+				size: 30,
+			},
+			click(e, label) {
+				if (!bottomBarIsShow.value) {
+					bottomBarIsShow.value = true;
+				}
+				if (!chartIsShow.value) {
+					chartIsShow.value = true;
+				}
+				infoWindow.open(gaoDeMap.map, label.getPosition() as any);
+				const extData = label.getExtData();
+				updateInfoWindow(extData.title);
+
+			},
+		},
+		layerOpt: {
+			// allowCollision:false
+		},
+	});
+};
+const bottomBarIsShow = ref(false);
+const chartIsShow = ref(true);
+const toggleShowChart = () => {
+	chartIsShow.value = !chartIsShow.value;
+};
+
+let infoWindow: AMap.InfoWindow;
+
+onMounted(async () => {
+	await gaoDeMap.init({
+		container: containerRef.value,
+		aMapOption: {
+			resizeEnable: true,
+		},
+	});
+	const southWest: GaoDePosition = [props.data.minx, props.data.miny];
+	const northEast: GaoDePosition = [props.data.maxx, props.data.maxy];
+	gaoDeMap.zoomToRect(southWest, northEast);
+	gaoDeMap.applyBasicPlugins();
+	addMarkerLayer();
+	infoWindow = new AMap.InfoWindow({
+		content: createInfoWindow(),
+		offset: [3,-34],
+		closeWhenClickMap: true,
+	});
+	
+});
+</script>
+<style scoped lang="scss">
+:deep(.amap-info-content) {
+	padding: 0;
+}
+</style>
diff --git a/src/components/chat/chatComponents/mapCom/TestData.ts b/src/components/chat/chatComponents/mapCom/TestData.ts
new file mode 100644
index 0000000..6b8e9cb
--- /dev/null
+++ b/src/components/chat/chatComponents/mapCom/TestData.ts
@@ -0,0 +1,287 @@
+export const CHART_DATA = {
+	cols: [
+		{ type: 'time', title: '鏃堕棿' },
+		{ type: 'name', title: '鍚嶇О' },
+		{ type: 'value', title: '鍊�' },
+	],
+	type: 'recordset',
+	chart: 'muli_line',
+	title: '浜斾竴骞垮満DN200鐬椂鍘嬪姏',
+	values: [
+		['2024-07-20 00:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.698],
+		['2024-07-20 00:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.62575],
+		['2024-07-20 00:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.49825],
+		['2024-07-20 00:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.562],
+		['2024-07-20 00:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.448667],
+		['2024-07-20 00:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.1285],
+		['2024-07-20 00:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.188],
+		['2024-07-20 00:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.210667],
+		['2024-07-20 00:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.26875],
+		['2024-07-20 00:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.301333],
+		['2024-07-20 00:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.31975],
+		['2024-07-20 00:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.3325],
+		['2024-07-20 01:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.2662],
+		['2024-07-20 01:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.511],
+		['2024-07-20 01:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.205],
+		['2024-07-20 01:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.766],
+		['2024-07-20 01:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.57475],
+		['2024-07-20 01:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.358],
+		['2024-07-20 01:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.686667],
+		['2024-07-20 01:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.312667],
+		['2024-07-20 01:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.21775],
+		['2024-07-20 01:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.307],
+		['2024-07-20 01:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.426],
+		['2024-07-20 01:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.42175],
+		['2024-07-20 02:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.62575],
+		['2024-07-20 02:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.618667],
+		['2024-07-20 02:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.686667],
+		['2024-07-20 02:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.715],
+		['2024-07-20 02:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.732],
+		['2024-07-20 02:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.732],
+		['2024-07-20 02:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.7915],
+		['2024-07-20 02:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.766],
+		['2024-07-20 02:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.8],
+		['2024-07-20 02:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.82975],
+		['2024-07-20 02:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.88075],
+		['2024-07-20 02:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.936],
+		['2024-07-20 03:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.93175],
+		['2024-07-20 03:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.9292],
+		['2024-07-20 03:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.9445],
+		['2024-07-20 03:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.95725],
+		['2024-07-20 03:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.947333],
+		['2024-07-20 03:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.924667],
+		['2024-07-20 03:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.90625],
+		['2024-07-20 03:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.766],
+		['2024-07-20 03:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.890667],
+		['2024-07-20 03:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.890667],
+		['2024-07-20 03:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.97],
+		['2024-07-20 03:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.981333],
+		['2024-07-20 04:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.856667],
+		['2024-07-20 04:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.90625],
+		['2024-07-20 04:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.924667],
+		['2024-07-20 04:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.95725],
+		['2024-07-20 04:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.77875],
+		['2024-07-20 04:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.7966],
+		['2024-07-20 04:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.70225],
+		['2024-07-20 04:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.80425],
+		['2024-07-20 04:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.8068],
+		['2024-07-20 04:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.766],
+		['2024-07-20 04:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.95725],
+		['2024-07-20 04:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.811333],
+		['2024-07-20 05:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.777333],
+		['2024-07-20 05:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.7915],
+		['2024-07-20 05:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.67675],
+		['2024-07-20 05:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.664],
+		['2024-07-20 05:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.46],
+		['2024-07-20 05:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.37075],
+		['2024-07-20 05:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.335333],
+		['2024-07-20 05:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.414667],
+		['2024-07-20 05:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.34525],
+		['2024-07-20 05:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.335333],
+		['2024-07-20 05:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.26875],
+		['2024-07-20 05:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.34525],
+		['2024-07-20 06:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.312667],
+		['2024-07-20 06:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.21775],
+		['2024-07-20 06:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.1285],
+		['2024-07-20 06:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.103],
+		['2024-07-20 06:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.0265],
+		['2024-07-20 06:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.21775],
+		['2024-07-20 06:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.1642],
+		['2024-07-20 06:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.11575],
+		['2024-07-20 06:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.11575],
+		['2024-07-20 06:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.142667],
+		['2024-07-20 06:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.369333],
+		['2024-07-20 06:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.4345],
+		['2024-07-20 07:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.6385],
+		['2024-07-20 07:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.573333],
+		['2024-07-20 07:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.766],
+		['2024-07-20 07:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.722286],
+		['2024-07-20 07:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.698],
+		['2024-07-20 07:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.5365],
+		['2024-07-20 07:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.52375],
+		['2024-07-20 07:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.539333],
+		['2024-07-20 07:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.7405],
+		['2024-07-20 07:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.54925],
+		['2024-07-20 07:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.5926],
+		['2024-07-20 07:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.31975],
+		['2024-07-20 08:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.39625],
+		['2024-07-20 08:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.46],
+		['2024-07-20 08:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.21775],
+		['2024-07-20 08:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.409],
+		['2024-07-20 08:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.063333],
+		['2024-07-20 08:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.301333],
+		['2024-07-20 08:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.1795],
+		['2024-07-20 08:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.21775],
+		['2024-07-20 08:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.972667],
+		['2024-07-20 08:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.108667],
+		['2024-07-20 08:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.9245],
+		['2024-07-20 08:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.836667],
+		['2024-07-20 09:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.791333],
+		['2024-07-20 09:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.75875],
+		['2024-07-20 09:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.870667],
+		['2024-07-20 09:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.78],
+		['2024-07-20 09:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.91175],
+		['2024-07-20 09:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.018],
+		['2024-07-20 09:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.9245],
+		['2024-07-20 09:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.86075],
+		['2024-07-20 09:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.6644],
+		['2024-07-20 09:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.83525],
+		['2024-07-20 09:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.98825],
+		['2024-07-20 09:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.1744],
+		['2024-07-20 10:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.98825],
+		['2024-07-20 10:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.2815],
+		['2024-07-20 10:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.312667],
+		['2024-07-20 10:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.301333],
+		['2024-07-20 10:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.29425],
+		['2024-07-20 10:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.26875],
+		['2024-07-20 10:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.358],
+		['2024-07-20 10:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.267333],
+		['2024-07-20 10:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.29425],
+		['2024-07-20 10:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.346667],
+		['2024-07-20 10:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.256],
+		['2024-07-20 10:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.29425],
+		['2024-07-20 11:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.176667],
+		['2024-07-20 11:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.19225],
+		['2024-07-20 11:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.307],
+		['2024-07-20 11:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.2662],
+		['2024-07-20 11:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.2305],
+		['2024-07-20 11:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.24325],
+		['2024-07-20 11:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.2152],
+		['2024-07-20 11:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.205],
+		['2024-07-20 11:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.19225],
+		['2024-07-20 11:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.233333],
+		['2024-07-20 11:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.233333],
+		['2024-07-20 11:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.2815],
+		['2024-07-20 12:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.154],
+		['2024-07-20 12:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.233333],
+		['2024-07-20 12:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.244667],
+		['2024-07-20 12:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.2305],
+		['2024-07-20 12:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.324],
+		['2024-07-20 12:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.097333],
+		['2024-07-20 12:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.16675],
+		['2024-07-20 12:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.244667],
+		['2024-07-20 12:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.26875],
+		['2024-07-20 12:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.24325],
+		['2024-07-20 12:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.307],
+		['2024-07-20 12:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.369333],
+		['2024-07-20 13:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.3325],
+		['2024-07-20 13:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.2866],
+		['2024-07-20 13:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.307],
+		['2024-07-20 13:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.409],
+		['2024-07-20 13:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.409],
+		['2024-07-20 13:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.307],
+		['2024-07-20 13:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.4855],
+		['2024-07-20 13:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.573333],
+		['2024-07-20 13:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.426],
+		['2024-07-20 13:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.82975],
+		['2024-07-20 13:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.919],
+		['2024-07-20 13:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.573333],
+		['2024-07-20 14:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.754667],
+		['2024-07-20 14:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.307],
+		['2024-07-20 14:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.584667],
+		['2024-07-20 14:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.46],
+		['2024-07-20 14:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.7405],
+		['2024-07-20 14:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.732],
+		['2024-07-20 14:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.75325],
+		['2024-07-20 14:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.65125],
+		['2024-07-20 14:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.664],
+		['2024-07-20 14:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.72775],
+		['2024-07-20 14:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.766],
+		['2024-07-20 14:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.8068],
+		['2024-07-20 15:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.817],
+		['2024-07-20 15:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.75325],
+		['2024-07-20 15:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.766],
+		['2024-07-20 15:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.822667],
+		['2024-07-20 15:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.70225],
+		['2024-07-20 15:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.62575],
+		['2024-07-20 15:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.618667],
+		['2024-07-20 15:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.49825],
+		['2024-07-20 15:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.664],
+		['2024-07-20 15:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.698],
+		['2024-07-20 15:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.664],
+		['2024-07-20 15:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.62575],
+		['2024-07-20 16:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.550667],
+		['2024-07-20 16:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.49825],
+		['2024-07-20 16:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.52375],
+		['2024-07-20 16:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.550667],
+		['2024-07-20 16:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.47275],
+		['2024-07-20 16:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.52375],
+		['2024-07-20 16:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.4906],
+		['2024-07-20 16:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.511],
+		['2024-07-20 16:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.511],
+		['2024-07-20 16:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.539333],
+		['2024-07-20 16:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.607333],
+		['2024-07-20 16:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.60025],
+		['2024-07-20 17:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.618667],
+		['2024-07-20 17:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.63],
+		['2024-07-20 17:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.5365],
+		['2024-07-20 17:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.57475],
+		['2024-07-20 17:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.562],
+		['2024-07-20 17:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.437333],
+		['2024-07-20 17:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.29425],
+		['2024-07-20 17:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.414667],
+		['2024-07-20 17:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.39625],
+		['2024-07-20 17:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.409],
+		['2024-07-20 17:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.375],
+		['2024-07-20 17:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.256],
+		['2024-07-20 18:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.31975],
+		['2024-07-20 18:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.2458],
+		['2024-07-20 18:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.3835],
+		['2024-07-20 18:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.256],
+		['2024-07-20 18:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.3478],
+		['2024-07-20 18:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.154],
+		['2024-07-20 18:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.31975],
+		['2024-07-20 18:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.12],
+		['2024-07-20 18:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.165333],
+		['2024-07-20 18:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.212286],
+		['2024-07-20 19:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.494],
+		['2024-07-20 20:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.54925],
+		['2024-07-20 20:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.57475],
+		['2024-07-20 20:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.63],
+		['2024-07-20 20:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.528],
+		['2024-07-20 20:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.29425],
+		['2024-07-20 20:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.52375],
+		['2024-07-20 20:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.244667],
+		['2024-07-20 20:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.24325],
+		['2024-07-20 20:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.21775],
+		['2024-07-20 20:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.95],
+		['2024-07-20 20:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.176667],
+		['2024-07-20 20:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.11575],
+		['2024-07-20 21:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.12],
+		['2024-07-20 21:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.03925],
+		['2024-07-20 21:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.06475],
+		['2024-07-20 21:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.108667],
+		['2024-07-20 21:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.9245],
+		['2024-07-20 21:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.95],
+		['2024-07-20 21:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.9704],
+		['2024-07-20 21:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.001],
+		['2024-07-20 21:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.91175],
+		['2024-07-20 21:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.0826],
+		['2024-07-20 21:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.103],
+		['2024-07-20 21:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.307],
+		['2024-07-20 22:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.086],
+		['2024-07-20 22:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.961333],
+		['2024-07-20 22:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.9245],
+		['2024-07-20 22:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 22.9755],
+		['2024-07-20 22:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.052],
+		['2024-07-20 22:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.052],
+		['2024-07-20 22:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.1285],
+		['2024-07-20 22:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.188],
+		['2024-07-20 22:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.12],
+		['2024-07-20 22:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.14125],
+		['2024-07-20 22:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.063333],
+		['2024-07-20 22:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.154],
+		['2024-07-20 23:00:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.285143],
+		['2024-07-20 23:05:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.1846],
+		['2024-07-20 23:10:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.2815],
+		['2024-07-20 23:15:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.358],
+		['2024-07-20 23:20:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.4396],
+		['2024-07-20 23:25:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.3325],
+		['2024-07-20 23:30:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.4345],
+		['2024-07-20 23:35:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.437333],
+		['2024-07-20 23:40:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.437333],
+		['2024-07-20 23:45:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.49825],
+		['2024-07-20 23:50:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.256],
+		['2024-07-20 23:55:00', '浜斾竴骞垮満DN200鐬椂鍘嬪姏', 23.46],
+	],
+};
diff --git a/src/components/chat/chatComponents/mapCom/img/monitor-point.svg b/src/components/chat/chatComponents/mapCom/img/monitor-point.svg
new file mode 100644
index 0000000..c199370
--- /dev/null
+++ b/src/components/chat/chatComponents/mapCom/img/monitor-point.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1721287703583" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2874" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 816.3c-82.2 0-159.4-32-217.5-90.1-58.1-58.1-90.1-135.3-90.1-217.5 0-75.1 27.3-147.4 77-203.6l48 42.4c-39.3 44.5-61 101.7-61 161.2 0 134.3 109.3 243.6 243.6 243.6S755.6 643 755.6 508.7c0-59.5-21.7-116.9-61.1-161.4l47.9-42.4c49.8 56.2 77.2 128.6 77.2 203.8 0 82.2-32 159.4-90.1 217.5-58.1 58.1-135.3 90.1-217.5 90.1z" fill="#1c86ff" p-id="2875"></path><path d="M544 417.1V207.7h-64v209.4c-40.1 13.4-69.1 51.3-69.1 95.9 0 55.8 45.4 101.1 101.1 101.1S613.1 568.8 613.1 513c0-44.5-29-82.5-69.1-95.9z m-32 133.1c-20.5 0-37.1-16.7-37.1-37.1S491.6 476 512 476s37.1 16.7 37.1 37.1-16.6 37.1-37.1 37.1z" fill="#1c86ff" p-id="2876"></path><path d="M510 957.5c-60.3 0-118.7-11.8-173.8-35.1-53.2-22.5-100.9-54.7-141.9-95.7S121.1 738 98.6 684.8C75.3 629.7 63.5 571.3 63.5 511s11.8-118.7 35.1-173.8c22.5-53.2 54.7-100.9 95.7-141.9s88.7-73.2 141.9-95.7C391.3 76.3 449.7 64.5 510 64.5s118.7 11.8 173.8 35.1c53.2 22.5 100.9 54.7 141.9 95.7s73.2 88.7 95.7 141.9c23.3 55.1 35.1 113.5 35.1 173.8s-11.8 118.7-35.1 173.8c-22.5 53.2-54.7 100.9-95.7 141.9s-88.7 73.2-141.9 95.7c-55.1 23.3-113.5 35.1-173.8 35.1z m0-829c-210.9 0-382.5 171.6-382.5 382.5S299.1 893.5 510 893.5 892.5 721.9 892.5 511 720.9 128.5 510 128.5z" fill="#1c86ff" p-id="2877"></path></svg>
\ No newline at end of file
diff --git a/src/components/chat/chatComponents/mapCom/types.ts b/src/components/chat/chatComponents/mapCom/types.ts
new file mode 100644
index 0000000..2aa15c9
--- /dev/null
+++ b/src/components/chat/chatComponents/mapCom/types.ts
@@ -0,0 +1,18 @@
+export interface MapData {
+    type: string
+    title: string
+    minx: number
+    maxx: number
+    miny: number
+    maxy: number
+    values: MapDataValue[]
+  }
+  
+  export interface MapDataValue {
+    type: string
+    posx: number
+    posy: number
+    color: string
+    title: string
+  }
+  
\ No newline at end of file
diff --git a/src/components/chat/chatComponents/normalTextCom/NormalTextCom.vue b/src/components/chat/chatComponents/normalTextCom/NormalTextCom.vue
new file mode 100644
index 0000000..46eeafa
--- /dev/null
+++ b/src/components/chat/chatComponents/normalTextCom/NormalTextCom.vue
@@ -0,0 +1,13 @@
+<template>
+	<div v-html="md.render(data)"></div>
+</template>
+
+<script setup lang="ts">
+import { md } from '../../libs/markdown';
+import { chatComProps } from '../common';
+
+const props = defineProps({
+	data:String
+});
+</script>
+<style scoped lang="scss"></style>
diff --git a/src/components/chat/chatComponents/recordSetCom/RecordSetCom.vue b/src/components/chat/chatComponents/recordSetCom/RecordSetCom.vue
new file mode 100644
index 0000000..8f2ad08
--- /dev/null
+++ b/src/components/chat/chatComponents/recordSetCom/RecordSetCom.vue
@@ -0,0 +1,112 @@
+<template>
+	<el-tabs v-model="activeName" class="demo-tabs min-w-[38rem] flex-column">
+		<el-tab-pane class="h-full" label="Chart" name="first">
+			<div class="flex-auto" v-resize="chartContainerResize">
+				<div ref="chartRef" class="h-full"></div>
+			</div>
+		</el-tab-pane>
+		<el-tab-pane class="h-full" label="Data" name="second">
+			<el-table :data="data.values" style="width: 100%" cellClassName="text-sm" headerCellClassName="text-sm">
+				<el-table-column v-for="(item, index) in data?.names" :label="item" :key="index">
+					<template #default="scope">
+						{{ scope.row[index] }}
+					</template>
+				</el-table-column>
+			</el-table>
+		</el-tab-pane>
+	</el-tabs>
+</template>
+<script lang="ts" setup>
+import type * as echarts from 'echarts';
+import _ from 'lodash';
+import { ref } from 'vue';
+import { SCATTER_SYMBOL_SIZE, chatComProps, getChatChartOption } from '../common';
+import { useDrawChatChart } from '../hooks/useDrawChatChart';
+const activeName = ref('first');
+const chartRef = ref<HTMLDivElement>(null);
+
+const defaultChartType = 'line';
+const props = defineProps(chatComProps);
+
+const drawChart = () => {
+	chartInstance.value.setOption(
+		_.defaultsDeep(getChatChartOption(), {
+			grid: {
+				bottom: '5%',
+			},
+
+			toolbox: {
+				feature: {
+					myBar: {
+						onclick: () => {
+							chartInstance.value.setOption({
+								series: {
+									data: props.data.values,
+									type: 'bar',
+									symbol: 'none',
+								},
+							});
+						},
+					},
+
+					myScatter: {
+						onclick: () => {
+							chartInstance.value.setOption({
+								data: props.data.values,
+								type: 'scatter',
+								symbol: 'circle',
+								symbolSize: SCATTER_SYMBOL_SIZE,
+							});
+						},
+					},
+					myLine: {
+						onclick: () => {
+							chartInstance.value.setOption({
+								data: props.data.values,
+								type: 'line',
+								symbol: 'none',
+								smooth: true,
+							});
+						},
+					},
+				},
+			},
+
+			title: {
+				text: props.data.title,
+			},
+			xAxis: {
+				name: props.data?.names[0],
+			},
+			yAxis: {
+				name: props.data?.names[1],
+			},
+			series: [
+				{
+					data: props.data.values,
+					symbol: 'none',
+					smooth: true,
+					type: defaultChartType,
+				},
+			],
+		} as echarts.EChartsOption)
+	);
+};
+const { chartContainerResize, chartInstance } = useDrawChatChart({ chartRef, drawChart });
+</script>
+
+<style scoped lang="scss">
+.el-tabs {
+	:deep(.el-tabs__header) {
+		flex: 0 0 auto;
+	}
+	:deep(.el-tabs__content) {
+		flex: 1;
+		.el-tab-pane {
+			display: flex;
+			flex-direction: column;
+			min-height: 24rem;
+		}
+	}
+}
+</style>
diff --git a/src/components/chat/chatComponents/summaryCom/SummaryCom.vue b/src/components/chat/chatComponents/summaryCom/SummaryCom.vue
new file mode 100644
index 0000000..189921d
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/SummaryCom.vue
@@ -0,0 +1,35 @@
+<template>
+	<div class="w-full space-y-3">
+		<template v-if="parsedData && parsedData.length > 0">
+			<component
+				v-for="(item, index) in parsedData"
+				:key="item.id"
+				:id="item.id"
+				:is="summaryAnswerTypeMapCom[item.type]"
+				:data="item"
+				:originData="originData"
+				:summaryIndex="index"
+			></component>
+		</template>
+		<!-- <AmisPageTest /> -->
+	</div>
+</template>
+
+<script setup lang="ts">
+import { computed } from 'vue';
+import { chatComProps } from '../common';
+import { SummaryAnswerType, summaryAnswerTypeMapCom } from './components/types';
+// import AmisPageTest from './components/amisPage/AmisPageTest.vue';
+const props = defineProps(chatComProps);
+
+const parsedData = computed(() => {
+	const newData = (props.data ?? []).map((item) => {
+		if (item.type === SummaryAnswerType.RecordSet && item.chart === 'table') {
+			item.type = SummaryAnswerType.RecordSetTable;
+		}
+		return item;
+	});
+	return newData;
+});
+</script>
+<style scoped lang="scss"></style>
diff --git a/src/components/chat/chatComponents/summaryCom/components/amisPage/AmisPage.vue b/src/components/chat/chatComponents/summaryCom/components/amisPage/AmisPage.vue
new file mode 100644
index 0000000..4e26c7c
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/amisPage/AmisPage.vue
@@ -0,0 +1,28 @@
+<template>
+	<div>
+		<span v-if="data?.title" class="text-base font-bold flex-center">{{ data?.title }}</span>
+		<AMISRenderer :schema="data?.amis_json" :locals="data?.amis_data" />
+	</div>
+</template>
+
+<script setup lang="ts">
+import type { PropType } from 'vue';
+import AMISRenderer from '/@/components/amis/AMISRenderer.vue';
+
+// import  鍛ㄧぞ浼氬瓨閿�姣� from './testData/鍛ㄧぞ浼氬瓨閿�姣�.json'
+// import  瀹㈡埛鎯呭喌 from './testData/瀹㈡埛鎯呭喌.json'
+
+// import  甯傚満缁煎悎鐘舵�� from './testData/甯傚満缁煎悎鐘舵��.json'
+// import  閿�鍞搴﹂攢閲� from './testData/閿�鍞搴﹂攢閲�.json'
+// import  缁忔祹杩愯 from './testData/缁忔祹杩愯.json'
+// import testData from './testData.json'
+
+const props = defineProps({
+	data: {
+		type: Object as PropType<any>,
+	},
+});
+
+
+</script>
+<style scoped lang="scss"></style>
diff --git a/src/components/chat/chatComponents/summaryCom/components/amisPage/testData.json b/src/components/chat/chatComponents/summaryCom/components/amisPage/testData.json
new file mode 100644
index 0000000..e25a5ab
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/amisPage/testData.json
@@ -0,0 +1,424 @@
+{
+	"type": "page",
+	"id": "u:bad3f34e15c1",
+	"title": "閿�鍞搴﹂攢閲�",
+	"body": [
+		{
+			"type": "flex",
+			"id": "u:9fc2a7909ab0",
+			"items": [
+				{
+					"type": "container",
+					"body": [
+						{
+							"type": "table-view",
+							"trs": [
+								{
+									"background": "#F7F7F7",
+									"tds": [
+										{
+											"body": {
+												"type": "tpl",
+												"wrapperComponent": "",
+												"tpl": "鍦板尯",
+												"id": "u:fcd843188090"
+											},
+											"id": "u:115c8c523ab3"
+										},
+										{
+											"body": {
+												"type": "tpl",
+												"wrapperComponent": "",
+												"tpl": "鐑熻崏鍚�",
+												"id": "u:f1453a2e11f6"
+											},
+											"id": "u:ff36072024db",
+											"width": 215.5
+										},
+										{
+											"body": {
+												"type": "tpl",
+												"wrapperComponent": "",
+												"tpl": "閿�閲�",
+												"id": "u:afefdd621d12"
+											},
+											"id": "u:3066304d8519"
+										}
+									],
+									"id": "u:80ceaef8a6a1",
+									"height": 44
+								},
+								{
+									"tds": [
+										{
+											"body": [
+												{
+													"type": "tpl",
+													"wrapperComponent": "",
+													"tpl": "鍗庡寳鍦板尯",
+													"id": "u:a8b388a2ec7c"
+												}
+											],
+											"id": "u:1e47918dbad8"
+										},
+										{
+											"body": [
+												{
+													"type": "tpl",
+													"tpl": "鍗椾含",
+													"inline": true,
+													"wrapperComponent": "",
+													"id": "u:9ddb428ec014"
+												}
+											],
+											"id": "u:2816fc14a1d4"
+										},
+										{
+											"body": {
+												"type": "tpl",
+												"wrapperComponent": "",
+												"tpl": "382994",
+												"id": "u:04f3b72b0b64"
+											},
+											"id": "u:aa5d09a6d0c7"
+										}
+									],
+									"id": "u:cadd57703de0",
+									"height": 44
+								},
+								{
+									"tds": [
+										{
+											"body": {
+												"type": "tpl",
+												"wrapperComponent": "",
+												"tpl": "涓崡鍦板尯",
+												"id": "u:a0389abbed7e"
+											},
+											"id": "u:04778c3e37a9"
+										},
+										{
+											"body": {
+												"type": "tpl",
+												"wrapperComponent": "",
+												"tpl": "鐜夋邯",
+												"id": "u:c920488d04e7"
+											},
+											"id": "u:041e86eee08f"
+										},
+										{
+											"body": {
+												"type": "tpl",
+												"wrapperComponent": "",
+												"tpl": "234554",
+												"id": "u:18a2014d5e0b",
+												"maxLine": 2321423
+											},
+											"id": "u:f101b1f4e14e"
+										}
+									],
+									"id": "u:6eab80ddca59",
+									"height": 44
+								},
+								{
+									"tds": [
+										{
+											"body": {
+												"type": "tpl",
+												"wrapperComponent": "",
+												"tpl": "瑗垮崡鍦板尯",
+												"id": "u:0fb619f91806"
+											},
+											"id": "u:de232323b71a"
+										},
+										{
+											"body": [
+												{
+													"type": "tpl",
+													"tpl": "涓崕",
+													"inline": true,
+													"wrapperComponent": "",
+													"id": "u:d0bc2b1558b4"
+												}
+											],
+											"id": "u:2f07492deaa7"
+										},
+										{
+											"body": {
+												"type": "tpl",
+												"wrapperComponent": "",
+												"tpl": "435463",
+												"id": "u:3577ce80f306"
+											},
+											"id": "u:1e94b54401f7"
+										}
+									],
+									"id": "u:b5b4f3a75485",
+									"height": 43
+								},
+								{
+									"tds": [
+										{
+											"body": {
+												"type": "tpl",
+												"wrapperComponent": "",
+												"tpl": "瑗垮寳鍦板尯",
+												"id": "u:ce9c80378a1d"
+											},
+											"id": "u:7af839fa58f9"
+										},
+										{
+											"body": {
+												"type": "tpl",
+												"wrapperComponent": "",
+												"tpl": "鑺欒搲鐜�",
+												"id": "u:3459e0c78922"
+											},
+											"id": "u:26d5ad53a5ac"
+										},
+										{
+											"body": {
+												"type": "tpl",
+												"wrapperComponent": "",
+												"tpl": "64563543",
+												"id": "u:8623aaa2642b"
+											},
+											"id": "u:f10464ace5ef"
+										}
+									],
+									"id": "u:e5b8d15c2b4a",
+									"height": 48
+								}
+							],
+							"id": "u:286f1761479c",
+							"themeCss": {
+								"baseControlClassName": {
+									"padding-and-margin:default": {
+										"marginTop": "var(--sizes-size-2)",
+										"marginRight": "var(--sizes-size-2)",
+										"marginBottom": "var(--sizes-size-2)",
+										"marginLeft": "var(--sizes-size-2)"
+									}
+								}
+							}
+						}
+					],
+					"size": "none",
+					"style": {
+						"position": "static",
+						"display": "block",
+						"flex": "1 1 auto",
+						"flexGrow": 1
+					},
+					"mobile": {
+						"style": {
+							"padding": 0,
+							"margin": 0
+						}
+					},
+					"wrapperBody": false,
+					"isFixedHeight": false,
+					"isFixedWidth": false,
+					"id": "u:8fcecf1e670c"
+				},
+				{
+					"type": "container",
+					"body": [
+						{
+							"type": "tpl",
+							"tpl": "閿�閲忓墠鍥涚殑鍝佽鍜屾暟閲�(鏉�)",
+							"inline": true,
+							"wrapperComponent": "",
+							"id": "u:e755fb569b63",
+							"themeCss": {
+								"baseControlClassName": {
+									"padding-and-margin:default": {
+										"paddingTop": "var(--sizes-size-5)",
+										"paddingRight": "var(--sizes-size-5)",
+										"paddingBottom": "var(--sizes-size-5)",
+										"paddingLeft": "var(--sizes-size-5)",
+										"marginTop": "var(--sizes-size-3)",
+										"marginRight": "var(--sizes-size-3)",
+										"marginBottom": "var(--sizes-size-3)",
+										"marginLeft": "var(--sizes-size-3)"
+									},
+									"font:default": {
+										"fontSize": "var(--fonts-size-6)"
+									}
+								}
+							}
+						},
+						{
+							"type": "progress",
+							"mode": "line",
+							"value": 40,
+							"strokeWidth": 9,
+							"valueTpl": "${value}%",
+							"id": "u:0a829cacfc23",
+							"placeholder": "-",
+							"progressClassName": "",
+							"map": [
+								{
+									"color": "#528afb",
+									"value": 20
+								}
+							],
+							"className": "p-xs"
+						},
+						{
+							"type": "tpl",
+							"tpl": "鍗椾含",
+							"inline": true,
+							"wrapperComponent": "",
+							"id": "u:01d253c393e6",
+							"themeCss": {
+								"baseControlClassName": {
+									"padding-and-margin:default": {
+										"marginTop": "var(--sizes-size-9)",
+										"marginRight": "var(--sizes-size-9)",
+										"marginBottom": "var(--sizes-size-9)",
+										"marginLeft": "var(--sizes-size-9)"
+									}
+								}
+							}
+						},
+						{
+							"type": "progress",
+							"mode": "line",
+							"value": 60,
+							"strokeWidth": 9,
+							"valueTpl": "${value}%",
+							"id": "u:5d4e2a882a92",
+							"placeholder": "-",
+							"progressClassName": "",
+							"map": [
+								{
+									"color": "#528afb",
+									"value": 20
+								}
+							],
+							"className": "p-xs"
+						},
+						{
+							"type": "tpl",
+							"tpl": "涓崕",
+							"inline": true,
+							"wrapperComponent": "",
+							"id": "u:5d7403fe6a93",
+							"themeCss": {
+								"baseControlClassName": {
+									"padding-and-margin:default": {
+										"marginTop": "var(--sizes-size-9)",
+										"marginRight": "var(--sizes-size-9)",
+										"marginBottom": "var(--sizes-size-9)",
+										"marginLeft": "var(--sizes-size-9)"
+									}
+								}
+							}
+						},
+						{
+							"type": "progress",
+							"mode": "line",
+							"value": 34,
+							"strokeWidth": 9,
+							"valueTpl": "${value}%",
+							"id": "u:d85d1fdcd367",
+							"placeholder": "-",
+							"progressClassName": "",
+							"map": [
+								{
+									"color": "#528afb",
+									"value": 20
+								}
+							],
+							"className": "p-xs"
+						},
+						{
+							"type": "tpl",
+							"tpl": "鐜夋邯",
+							"inline": true,
+							"wrapperComponent": "",
+							"id": "u:695ed7cb1479",
+							"themeCss": {
+								"baseControlClassName": {
+									"padding-and-margin:default": {
+										"marginTop": "var(--sizes-size-9)",
+										"marginRight": "var(--sizes-size-9)",
+										"marginBottom": "var(--sizes-size-9)",
+										"marginLeft": "var(--sizes-size-9)"
+									}
+								}
+							}
+						},
+						{
+							"type": "progress",
+							"mode": "line",
+							"value": 24,
+							"strokeWidth": 9,
+							"valueTpl": "${value}%",
+							"id": "u:294c476cfd16",
+							"placeholder": "-",
+							"progressClassName": "",
+							"map": [
+								{
+									"color": "#528afb",
+									"value": 20
+								}
+							],
+							"className": "p-xs"
+						},
+						{
+							"type": "tpl",
+							"tpl": "鑺欒搲鐜�",
+							"inline": true,
+							"wrapperComponent": "",
+							"id": "u:9720acc51721",
+							"themeCss": {
+								"baseControlClassName": {
+									"padding-and-margin:default": {
+										"marginTop": "var(--sizes-size-9)",
+										"marginRight": "var(--sizes-size-9)",
+										"marginBottom": "var(--sizes-size-9)",
+										"marginLeft": "var(--sizes-size-9)"
+									}
+								}
+							}
+						}
+					],
+					"size": "none",
+					"style": {
+						"position": "static",
+						"display": "block",
+						"flex": "1 1 auto",
+						"flexGrow": 1
+					},
+					"wrapperBody": false,
+					"isFixedHeight": false,
+					"isFixedWidth": false,
+					"id": "u:5c102f463e25"
+				}
+			],
+			"style": {
+				"position": "relative",
+				"rowGap": "10px",
+				"columnGap": "10px",
+				"flexWrap": "nowrap",
+				"inset": "auto",
+				"mobile": {
+					"flexDirection": "column",
+					"marginTop": "18px"
+				}
+			},
+			"isFixedHeight": false,
+			"isFixedWidth": false
+		}
+	],
+	"bodyClassName": "m:p-0",
+	"headerClassName": "m:pt-0 m:pl-0 m:pb-1",
+
+	"asideResizor": false,
+	"pullRefresh": {
+		"disabled": true
+	},
+	"regions": ["body", "header"]
+}
diff --git a/src/components/chat/chatComponents/summaryCom/components/deviceLastValue/DeviceLastValueCom.vue b/src/components/chat/chatComponents/summaryCom/components/deviceLastValue/DeviceLastValueCom.vue
new file mode 100644
index 0000000..63c58e4
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/deviceLastValue/DeviceLastValueCom.vue
@@ -0,0 +1,328 @@
+<template>
+	<!-- 瀹炴椂鐩戞祴鍒楄〃 -->
+	<div ref="containerRef" class="w-full flex justify-center" v-resize="resizeHandler">
+		<div class="inline-block">
+			<div
+				:class="`space-y-[${THICK_BORDER_WIDTH}px]`"
+				:style="{
+					border: `${THICK_BORDER_WIDTH}px solid ${BORDER_COLOR}`,
+					backgroundColor: BORDER_COLOR,
+				}"
+			>
+				<div class="flex-center font-bold text-base bg-[#8db4e2]" :style="{ height: `${CELL_HEIGHT}px` }">
+					{{ data.title }}
+				</div>
+				<div
+					v-for="(rowChunk, index) in currentRowChunkList"
+					:key="index"
+					:class="`space-y-[${THIN_BORDER_WIDTH}px]`"
+					:style="{
+						backgroundColor: BORDER_COLOR,
+					}"
+				>
+					<!-- 琛ㄥご锛屽拰琛ㄦ牸鍐呭绗竴琛� -->
+					<div
+						:class="`space-y-[${THICK_BORDER_WIDTH}px]`"
+						:style="{
+							backgroundColor: BORDER_COLOR,
+						}"
+					>
+						<div
+							class="flex"
+							:class="`space-x-[${THICK_BORDER_WIDTH}px]`"
+							:style="{
+								backgroundColor: BORDER_COLOR,
+							}"
+						>
+							<div
+								v-for="(item, index) in maxColsNum"
+								:key="item"
+								:class="COL_HEADER_CELL_CLASS"
+								:style="{
+									width: `${index === 0 ? firstColWidth : restColWidth}px`,
+									height: `${CELL_HEIGHT}px`,
+									backgroundColor: COL_HEADER_CELL_BG_COLOR,
+								}"
+							>
+								{{ index === 0 ? '' : rowChunk[index - 1]?.OTITLE }}
+							</div>
+						</div>
+						<MonitorContent
+							v-if="firstRow"
+							:firstColWidth="firstColWidth"
+							:restColWidth="restColWidth"
+							:title="firstRow.title"
+							:type="firstRow.id"
+							:values="rowChunk"
+							@itemClick="valueClick"
+						/>
+					</div>
+					<!-- 鍓╀綑琛� -->
+					<MonitorContent
+						v-for="(row, index) in restRows"
+						:key="index"
+						:firstColWidth="firstColWidth"
+						:restColWidth="restColWidth"
+						:title="row.title"
+						:type="row.id"
+						:values="rowChunk"
+						@itemClick="valueClick"
+					/>
+				</div>
+			</div>
+			<el-pagination
+				style="margin-bottom: 0 !important"
+				small
+				hide-on-single-page
+				v-model:current-page="pageIndex"
+				v-model:page-size="pageSize"
+				layout="total,prev,pager,next,jumper"
+				:total="total"
+				@current-change="handleCurrentChange"
+			/>
+		</div>
+		<RecordSetDialog v-model="chartDlgIsShow" :otype="chartDlgMapRow?.OTYPE" :oname="chartDlgMapRow?.ONAME" :indexName="indexName"/>
+	</div>
+</template>
+
+<script setup lang="ts">
+import _ from 'lodash';
+import { computed, onActivated, onMounted, ref } from 'vue';
+import MonitorContent from './MonitorContent.vue';
+import { debounce, getTextWidth } from '/@/utils/util';
+
+import { chatComProps } from '../../../common';
+import {
+	BORDER_COLOR,
+	CELL_HEIGHT,
+	CELL_MAX_WIDTH,
+	COL_HEADER_CELL_BG_COLOR,
+	COL_HEADER_CELL_CLASS,
+	PAGE_HEIGHT,
+	ROW_HEADER_CELL_MAX_WIDTH,
+	THICK_BORDER_WIDTH,
+	THIN_BORDER_WIDTH,
+} from './constants';
+import type { Monitor, MonitorValue } from './types';
+import RecordSetDialog from '../recordSet/RecordSetDialog.vue';
+
+const props = defineProps(chatComProps) as {
+	data: Monitor;
+};
+const total = props.data?.values?.length ?? 0;
+const pageIndex = ref(null);
+const pageSize = ref(null);
+
+const containerRef = ref<HTMLDivElement>(null);
+const measureWidthOffset = 2;
+/** @description 娴嬮噺棣栧垪鍐呭瀹藉害 */
+const rowHeaderCellContentWidth = computed(() => {
+	if (!props.data.rows || props.data.rows.length === 0) return 0;
+	let maxLen = 0;
+	let maxTitle = '';
+	for (const item of props.data.rows) {
+		const { title } = item;
+		const len = title.gblen();
+		if (len > maxLen) {
+			maxLen = len;
+			maxTitle = title;
+		}
+	}
+	let maxWidth = getTextWidth(maxTitle, {
+		size: '0.875rem',
+	});
+
+	maxWidth += measureWidthOffset;
+
+	if (maxWidth > ROW_HEADER_CELL_MAX_WIDTH) {
+		maxWidth = ROW_HEADER_CELL_MAX_WIDTH;
+	}
+
+	return maxWidth;
+});
+/** @description 娴嬮噺鍏朵粬鍒楀唴瀹瑰搴� */
+const colHeaderCellContentWidth = computed(() => {
+	if (!props.data.values || props.data.values.length === 0) return 0;
+	let maxLen = 0;
+	let maxTitle = '';
+	for (const item of props.data.values) {
+		const { OTITLE } = item;
+		const len = OTITLE.gblen();
+		if (len > maxLen) {
+			maxLen = len;
+			maxTitle = OTITLE;
+		}
+	}
+
+	let maxWidth = getTextWidth(maxTitle, {
+		size: '0.875rem',
+	});
+	maxWidth += measureWidthOffset;
+
+	if (maxWidth > CELL_MAX_WIDTH) {
+		maxWidth = CELL_MAX_WIDTH;
+	}
+
+	return maxWidth;
+});
+
+const firstColWidth = ref(rowHeaderCellContentWidth.value);
+const restColWidth = ref(colHeaderCellContentWidth.value);
+const calcMaxRowsNum = (groupCount: number, height, extraHeight = 0) => {
+	return Math.floor(
+		(height - 2 * THICK_BORDER_WIDTH - CELL_HEIGHT - extraHeight) /
+			(CELL_HEIGHT * groupCount + 2 * THICK_BORDER_WIDTH + THIN_BORDER_WIDTH * (groupCount - 2))
+	);
+};
+let maxColsNum = ref<number>(null);
+const resizeEvent = ({ width, height }) => {
+	if (width === 0 || height === 0) {
+		return;
+	}
+	// 鎸夋渶澶у搴︾畻鏈�澶у垪鏁�
+	maxColsNum.value = Math.floor(
+		(width - THICK_BORDER_WIDTH + colHeaderCellContentWidth.value - rowHeaderCellContentWidth.value) /
+			(colHeaderCellContentWidth.value + THICK_BORDER_WIDTH)
+	);
+
+	const currentWidth =
+		(maxColsNum.value - 1) * colHeaderCellContentWidth.value +
+		rowHeaderCellContentWidth.value +
+		THICK_BORDER_WIDTH * (maxColsNum.value - 1) +
+		THICK_BORDER_WIDTH * 2;
+	let restWidth = width - currentWidth;
+	if (restWidth > 0) {
+		// 灏藉彲鑳藉垎缁欑涓�鍒�
+		if (rowHeaderCellContentWidth.value + restWidth > ROW_HEADER_CELL_MAX_WIDTH) {
+			restWidth = rowHeaderCellContentWidth.value + restWidth - ROW_HEADER_CELL_MAX_WIDTH;
+			firstColWidth.value = ROW_HEADER_CELL_MAX_WIDTH;
+		} else {
+			firstColWidth.value = rowHeaderCellContentWidth.value + restWidth;
+			restWidth = 0;
+		}
+
+		// 鍏朵綑鍒嗙粰鍏朵粬鍒�
+		if (restWidth !== 0) {
+			const averageWidth = restWidth / (maxColsNum.value - 1);
+			const currentWidth = colHeaderCellContentWidth.value + averageWidth;
+			restColWidth.value = currentWidth > CELL_MAX_WIDTH ? CELL_MAX_WIDTH : currentWidth;
+		}
+	}
+
+	const groupCount = (props.data?.rows?.length ?? 0) + 1;
+
+	// 鎸夋渶澶ч珮搴︾畻鏈�澶ц鏁�
+	let rowsNum = calcMaxRowsNum(groupCount, height);
+
+	// 鎸夋渶澶у垪鏁伴摵
+	const maxColsRowsNum = Math.ceil(total / maxColsNum.value);
+	const isNeedPage = maxColsRowsNum > rowsNum;
+	rowsNum = isNeedPage ? calcMaxRowsNum(groupCount, height, PAGE_HEIGHT) : maxColsRowsNum;
+	// rowsNum 琛岋紝maxColsNum鍒楋紝绗竴鍒椾笉绠�
+	pageSize.value = isNeedPage
+		? rowsNum * (maxColsNum.value - 1)
+		: total % (maxColsNum.value - 1) === 0
+		? total
+		: (Math.floor(total / (maxColsNum.value - 1)) + 1) * (maxColsNum.value - 1);
+	pageIndex.value = 1;
+	// isNeedPage 鏄惁鍒嗛〉锛宺owsNum 琛屾暟锛宮axColsNum 鍒楁暟锛�
+};
+const resizeHandler = debounce(resizeEvent);
+
+const handleCurrentChange = () => {};
+
+const firstRow = computed(() => props.data?.rows?.[0]);
+const restRows = computed(() => props.data?.rows?.slice(1));
+const pageChunkList = computed(() => {
+	const chunkResult = _.chunk(props.data.values ?? [], pageSize.value);
+	const last = chunkResult.at(-1);
+	if (last) {
+		const restNum = pageSize.value - last.length;
+		const emptyData = _.fill(Array(restNum), {
+			ONAME: '',
+			OTIME: '',
+			OTITLE: '',
+			OTYPE: '',
+		} as MonitorValue);
+		const lastData = last.concat(emptyData);
+		chunkResult[chunkResult.length - 1] = lastData;
+	}
+
+	return chunkResult;
+});
+const currentPageChunk = computed(() => {
+	pageChunkList.value;
+	if (pageIndex.value == null) return [];
+	const pageChunk = pageChunkList.value[pageIndex.value - 1];
+
+	return pageChunk;
+});
+
+const currentRowChunkList = computed(() => {
+	if (!currentPageChunk.value || currentPageChunk.value.length === 0) return [];
+	const chunkResult = _.chunk(currentPageChunk.value, maxColsNum.value - 1);
+	return chunkResult;
+});
+
+//#region ====================== 鐐瑰嚮鐪嬫洸绾� ======================
+
+const chartDlgIsShow = ref(false);
+const chartDlgMapRow = ref(null);
+/** @description 鎸囨爣鍚嶇О */
+const indexName = ref(null);
+const valueClick = (item,type) => {
+	chartDlgMapRow.value = item;
+	chartDlgIsShow.value = true;
+	indexName.value = type;
+};
+//#endregion
+
+// 璁$畻鏈�澶у垪鏁�
+// (x-1)* cellWidth + rowHeaderCellContentWidth.value+thickBorderWidth*(x-1)+thickBorderWidth*2 <= width;
+
+// x*(cellWidth+thickBorderWidth)+thickBorderWidth - cellWidth + rowHeaderCellContentWidth.value<=width;
+// x<= (width-thickBorderWidth + cellWidth-rowHeaderCellContentWidth.value)/(cellWidth+thickBorderWidth);
+
+// const groupCount = (TEST_DATA?.rows?.length ?? 0) + 1;
+// 璁$畻鏈�澶ц鏁�
+// y * (cellHeight * groupCount) +
+// 	(y - 1) * (2 * thickBorderWidth) +cellHeight+thickBorderWidth
+// 	thickBorderWidth +
+// 	thickBorderWidth * 2 +
+// 	y * (groupCount - 2) * thinBorderWidth <=
+// 	height;
+
+// (cellHeight * groupCount + 2 * thickBorderWidth + thinBorderWidth(groupCount - 2)  * y + thickBorderWidth <=height;
+
+// y<= (height-thickBorderWidth)/(cellHeight * groupCount + 2 * thickBorderWidth + thinBorderWidth(groupCount - 2) )
+
+onMounted(() => {
+	resizeEvent({
+		width: containerRef.value.clientWidth,
+		height: 0.7 * document.body.clientHeight,
+	});
+});
+</script>
+<style scoped lang="scss">
+:deep(.space-y-\[2px\] > :not([hidden]) ~ :not([hidden])) {
+	--tw-space-y-reverse: 0;
+	margin-top: calc(2px * calc(1 - var(--tw-space-y-reverse)));
+	margin-bottom: calc(2px * var(--tw-space-y-reverse));
+}
+:deep(.space-y-\[1px\] > :not([hidden]) ~ :not([hidden])) {
+	--tw-space-y-reverse: 0;
+	margin-top: calc(1px * calc(1 - var(--tw-space-y-reverse)));
+	margin-bottom: calc(1px * var(--tw-space-y-reverse));
+}
+
+:deep(.space-x-\[2px\] > :not([hidden]) ~ :not([hidden])) {
+	--tw-space-x-reverse: 0;
+	margin-right: calc(2px * var(--tw-space-x-reverse));
+	margin-left: calc(2px * calc(1 - var(--tw-space-x-reverse)));
+}
+:deep(.space-x-\[1px\] > :not([hidden]) ~ :not([hidden])) {
+	--tw-space-x-reverse: 0;
+	margin-right: calc(1px * var(--tw-space-x-reverse));
+	margin-left: calc(1px * calc(1 - var(--tw-space-x-reverse)));
+}
+</style>
diff --git a/src/components/chat/chatComponents/summaryCom/components/deviceLastValue/MonitorContent.vue b/src/components/chat/chatComponents/summaryCom/components/deviceLastValue/MonitorContent.vue
new file mode 100644
index 0000000..23721e6
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/deviceLastValue/MonitorContent.vue
@@ -0,0 +1,117 @@
+<template>
+	<div
+		class="flex"
+		:class="`space-x-[${THICK_BORDER_WIDTH}px]`"
+		:style="{
+			backgroundColor: BORDER_COLOR,
+		}"
+	>
+		<div
+			:class="ROW_HEADER_CELL_CLASS"
+			:style="{
+				width: `${firstColWidth}px`,
+				height: `${CELL_HEIGHT}px`,
+			}"
+		>
+			{{ title }}
+		</div>
+		<div
+			v-for="(item, index) in values"
+			:key="index"
+			:class="CONTENT_CELL_CLASS"
+			:style="{
+				width: `${restColWidth}px`,
+				height: `${CELL_HEIGHT}px`,
+			}"
+		>
+			<span
+				class="cursor-pointer"
+				@mouseover="valueMouseOver($event, item)"
+				@mouseleave="valueMouseLeave"
+				@click="emit('itemClick', item, type)"
+			>
+				{{ item[type] }}
+			</span>
+		</div>
+		<div
+			v-show="hoverState.show && hoverState.data"
+			class="z-40 fixed p-2 bg-white"
+			style="transform-origin: center top"
+			:style="{
+				left: hoverState.left + 'px',
+				top: hoverState.top + 'px',
+			}"
+		>
+			<div v-if="hoverState.data?.OTITLE" class="font-bold mb-1">{{ hoverState.data?.OTITLE }}</div>
+			<div class="w-full space-y-1">
+				<div v-if="hoverState.data?.OTYPE" class="flex">
+					<div class="w-8">绫诲瀷</div>
+					<div class="before:content-[':'] before:pr-1.5">{{ hoverState.data?.OTYPE }}</div>
+				</div>
+				<div v-if="hoverState.data?.ONAME" class="flex">
+					<div class="w-8">缂栧彿</div>
+					<div class="before:content-[':'] before:pr-1.5">{{ hoverState.data?.ONAME }}</div>
+				</div>
+				<div v-if="hoverState.data?.[type] || hoverState.data?.[type] === 0" class="flex">
+					<div class="w-8">鐩戞祴</div>
+					<div class="before:content-[':'] before:pr-1.5">{{ hoverState.data?.[type] }}</div>
+				</div>
+				<div class="flex" v-if="hoverState.data?.OTIME">
+					<div class="w-8">鏃堕棿</div>
+					<div class="before:content-[':'] before:pr-1.5">{{ hoverState.data?.OTIME }}</div>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { reactive, type PropType } from 'vue';
+import { BORDER_COLOR, CELL_HEIGHT, CONTENT_CELL_CLASS, ROW_HEADER_CELL_CLASS, THICK_BORDER_WIDTH } from './constants';
+import type { MonitorValue } from './types';
+
+const emit = defineEmits(['itemClick']);
+const props = defineProps({
+	/** @description 鏍囬 */
+	title: {
+		type: String,
+	},
+	/** @description 绫诲瀷 */
+	type: {
+		type: String,
+	},
+	/** @description 鍊� */
+	values: {
+		type: Object as PropType<MonitorValue[]>,
+	},
+	firstColWidth: {
+		type: Number,
+	},
+
+	restColWidth: {
+		type: Number,
+	},
+});
+
+const hoverState = reactive({
+	left: 0,
+	top: 0,
+	show: false,
+	data: null as MonitorValue,
+});
+
+const TIP_OFFSET = 10;
+const valueMouseOver = (e, item: MonitorValue) => {
+	hoverState.show = true;
+	const { pageX, pageY } = e;
+	hoverState.left = pageX + TIP_OFFSET;
+	hoverState.top = pageY + TIP_OFFSET;
+	hoverState.data = item;
+};
+
+const valueMouseLeave = () => {
+	hoverState.show = false;
+	hoverState.data = null;
+};
+</script>
+<style scoped lang="scss"></style>
diff --git a/src/components/chat/chatComponents/summaryCom/components/deviceLastValue/constants.ts b/src/components/chat/chatComponents/summaryCom/components/deviceLastValue/constants.ts
new file mode 100644
index 0000000..7af0c75
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/deviceLastValue/constants.ts
@@ -0,0 +1,18 @@
+
+export const CONTENT_CELL_CLASS = 'bg-white flex-center flex-0 !text-[#7331a5] cursor-default';
+export const ROW_HEADER_CELL_CLASS = 'bg-white flex-center flex-0 cursor-default';
+export const ROW_HEADER_CELL_MAX_WIDTH = 198;
+export const COL_HEADER_CELL_CLASS = 'font-bold  flex-center flex-0 cursor-default';
+export const COL_HEADER_CELL_BG_COLOR = '#c5d9f1'
+export const CELL_MAX_WIDTH = 198;
+export const CELL_HEIGHT = 32;
+export const THIN_BORDER_WIDTH = 1;
+export const THICK_BORDER_WIDTH = 2;
+export const PAGE_HEIGHT = 39;
+
+export const rowCount = 5;
+export const BORDER_COLOR = 'rgb(115 168 231)';
+
+
+export const FIRST_COL_MAX_OFFSET = 4;
+export const REST_COL_MAX_OFFSET = 4;
\ No newline at end of file
diff --git a/src/components/chat/chatComponents/summaryCom/components/deviceLastValue/types.ts b/src/components/chat/chatComponents/summaryCom/components/deviceLastValue/types.ts
new file mode 100644
index 0000000..6d663c5
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/deviceLastValue/types.ts
@@ -0,0 +1,21 @@
+import type { SummaryAnswerType } from '../types';
+
+export type MonitorRow = {
+	id: string;
+	title: string;
+};
+
+export type MonitorValue = {
+	OTYPE: string;
+	ONAME: string;
+	OTITLE: string;
+	OTIME: string;
+	ZD: number;
+	YL: number;
+};
+export type Monitor = {
+	title:string;
+	type: SummaryAnswerType.DeviceLastValue;
+	rows: MonitorRow[];
+	values: MonitorValue[];
+};
diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSet/RecordSet.vue b/src/components/chat/chatComponents/summaryCom/components/recordSet/RecordSet.vue
new file mode 100644
index 0000000..70dab88
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/recordSet/RecordSet.vue
@@ -0,0 +1,457 @@
+<!-- 鏄ㄦ棩渚涙按绠$綉姒傚喌 -->
+<template>
+	<div class="w-full">
+		<div class="flex mb-4 flex-wrap">
+			<!-- TimeRange v-model 璺� @change 涓殑鍊间細涓嶄竴鏍凤紝浠change 涓负鍑� -->
+			<template v-if="visibleParams && visibleParams.length > 0">
+				<component
+					class="flex-0 m-1"
+					v-model="paramsValueList[index].value"
+					v-for="(item, index) in visibleParams as any"
+					:key="item.id"
+					:id="item.id"
+					:is="recordSetMapCom[item.type]"
+					:data="item"
+					:originData="originData"
+					@change="(val) => handleQueryChange(val, item)"
+					:disabled="chartLoading"
+				></component>
+			</template>
+			<slot> </slot>
+
+			<YRange v-model="yRange" @input="yRangeInput" />
+			<el-checkbox class="m-1" v-model="isMultiCompare" label="澶氭棩瀵规瘮" @change="multiCompareChange"></el-checkbox>
+		</div>
+		<div :style="{ height: chartHeight }" v-resize="chartContainerResize" v-loading="chartLoading">
+			<div ref="chartRef"></div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import type * as echarts from 'echarts';
+import _ from 'lodash';
+import moment from 'moment';
+import type { PropType } from 'vue';
+import { computed, ref } from 'vue';
+import { SCATTER_SYMBOL_SIZE, getChatChartOption } from '../../../common';
+import { useDrawChatChart } from '../../../hooks/useDrawChatChart';
+import YRange from './components/YRange.vue';
+import type { RecordSet, RecordSetParamsItem } from './types';
+import { RecordSetParamsType, recordSetMapCom } from './types';
+import { filterQuery } from '/@/api/ai/chat';
+import { deepClone } from '/@/utils/other';
+import { debounce } from '/@/utils/util';
+import { ChartTypeEnum } from '../../../types';
+
+const chartRef = ref<HTMLDivElement>(null);
+const defaultDisplayType = 'line';
+const yRange = ref({
+	min: null as number,
+	max: null as number,
+});
+// const props = defineProps({
+// 	data: {
+// 		type: Object as PropType<RecordSet>,
+// 	},
+// });
+
+const props = defineProps({
+	data: {
+		type: Object as PropType<any>,
+	},
+	originData: {
+		type: Object as PropType<any>,
+	},
+	summaryIndex: {
+		type: Number,
+	},
+	chartHeight: {
+		type: String,
+		default: '20rem',
+	},
+}) as {
+	data: RecordSet;
+};
+const chartLoading = ref(false);
+
+const visibleParams = computed(() => {
+	const visibleList = props.data?.params?.filter((item) => !item?.hide) ?? [];
+
+	const newList: RecordSetParamsItem[] = [];
+	let nextMatchIndex = null;
+	for (let index = 0; index < visibleList.length; index++) {
+		if (nextMatchIndex === index) continue;
+		const current = visibleList[index];
+		const currentAny = current as any;
+		if (index !== visibleList.length - 1 && currentAny.type === RecordSetParamsType.StartTime) {
+			const next = visibleList[index + 1];
+			const nextAny = next as any;
+
+			if (nextAny.group === currentAny.group && nextAny.type === RecordSetParamsType.EndTime) {
+				newList.push({
+					type: RecordSetParamsType.TimeRange,
+					value: [currentAny.value, nextAny.value],
+					group: currentAny.group,
+					range: [currentAny, nextAny],
+				} as any);
+				nextMatchIndex = index + 1;
+			} else {
+				newList.push(current);
+			}
+		} else {
+			newList.push(current);
+		}
+	}
+
+	return newList;
+});
+const paramsValueList = ref(deepClone(visibleParams.value));
+let groupedValues = null;
+let timeIndex = undefined;
+let valueIndex = undefined;
+let nameIndex = undefined;
+
+let timeCol = null;
+let valueCol = null;
+
+let preData = null;
+
+let activeChartType: ChartTypeEnum = ChartTypeEnum.Line;
+
+const getChartTypeSeriesOption = (type: ChartTypeEnum) => {
+	let result = {};
+	switch (type) {
+		case ChartTypeEnum.Bar:
+			result = {
+				type: 'bar',
+				symbol: 'none',
+			};
+
+			break;
+		case ChartTypeEnum.Line:
+			result = {
+				type: 'line',
+				symbol: 'none',
+				smooth: true,
+			};
+
+			break;
+
+		case ChartTypeEnum.Scatter:
+			result = {
+				type: 'scatter',
+				symbol: 'circle',
+				symbolSize: SCATTER_SYMBOL_SIZE,
+			};
+
+			break;
+		default:
+			break;
+	}
+
+	return result;
+};
+
+const setNewOption = (series?: any[], extraOption: echarts.EChartsOption = {}) => {
+	const isEmpty = !series || series.length === 0;
+	if (isEmpty) {
+		series = Object.keys(groupedValues).map((item) => {
+			const values = groupedValues[item];
+			return {
+				name: item === 'default' ? '' : item,
+				data: values.map((item) => [item[timeIndex], item[valueIndex]]),
+				type: defaultDisplayType,
+				symbol: 'none',
+				smooth: true,
+			};
+		});
+	}
+	const combineOption = _.defaultsDeep(extraOption, getChatChartOption(), {
+		grid: {
+			bottom: 20,
+		},
+		legend: {
+			top: 19,
+			show: series?.length > 1,
+			type: 'scroll',
+		},
+		toolbox: {
+			show: true,
+			feature: {
+				myBar: {
+					onclick: () => {
+						activeChartType = ChartTypeEnum.Bar;
+						chartInstance.value.setOption({
+							series: series.map((item) => ({
+								...item,
+								...getChartTypeSeriesOption(activeChartType),
+							})),
+						});
+					},
+				},
+
+				myScatter: {
+					onclick: () => {
+						activeChartType = ChartTypeEnum.Scatter;
+
+						chartInstance.value.setOption({
+							series: series.map((item) => ({
+								...item,
+								...getChartTypeSeriesOption(activeChartType),
+							})),
+						});
+					},
+				},
+				myLine: {
+					onclick: () => {
+						activeChartType = ChartTypeEnum.Line;
+						chartInstance.value.setOption({
+							series: series.map((item) => ({
+								...item,
+								...getChartTypeSeriesOption(activeChartType),
+							})),
+						});
+					},
+				},
+			},
+		},
+
+		title: {
+			text: preData?.title,
+		},
+		xAxis: {
+			name: timeCol?.title,
+		},
+		yAxis: {
+			name: valueCol?.title,
+			/** @description 涓嶅己鍒朵繚鐣�0 */
+			scale: true,
+		},
+		series: series,
+	} as echarts.EChartsOption);
+	chartInstance.value.setOption(combineOption, {
+		notMerge: true,
+	});
+};
+
+const handleData = () => {
+	const data = props.data;
+	if (!data || !data.cols || !data.values) {
+		return;
+	}
+	preData = data;
+	const xType = 'time';
+	timeIndex = data.cols.findIndex((item) => item.type === 'time');
+	if (timeIndex === -1) {
+		timeIndex = 0;
+	}
+	timeCol = data.cols[timeIndex];
+
+	valueIndex = data.cols.findIndex((item) => item.type === 'value');
+	if (valueIndex === -1) {
+		valueIndex = 2;
+	}
+	valueCol = data.cols[valueIndex];
+
+	let nameCol = null;
+	groupedValues = null;
+	if (data.chart === 'muli_line') {
+		nameIndex = data.cols.findIndex((item) => item.type === 'name');
+		if (nameIndex === -1) {
+			nameIndex = 1;
+		}
+		nameCol = data.cols[nameIndex];
+		groupedValues = _.groupBy(data.values, (item) => item[nameIndex]);
+	} else if (data.chart === 'single_line') {
+		groupedValues = {
+			default: data.values,
+		};
+	} else {
+		// 榛樿閮藉綋muli_line
+		let nameIndex = data.cols.findIndex((item) => item.type === 'name');
+		if (nameIndex === -1) {
+			nameIndex = 1;
+		}
+		nameCol = data.cols[nameIndex];
+		groupedValues = _.groupBy(data.values, (item) => item[nameIndex]);
+	}
+};
+
+const drawChart = () => {
+	const data = props.data;
+	if (!data || !data.cols || !data.values) {
+		return;
+	}
+	handleData();
+	setNewOption();
+};
+const { chartContainerResize, chartInstance } = useDrawChatChart({ chartRef, drawChart });
+
+// 鏇存崲鍒楄〃
+const changeMap = new Map<string, string>(null);
+
+const handleQueryChange = async (val: any, item: RecordSetParamsItem) => {
+	if (!val) return;
+
+	const historyId = (props as any).originData.historyId;
+	const summaryIndex = (props as any).summaryIndex;
+	let res = null;
+
+	try {
+		if (item.type === RecordSetParamsType.TimeRange) {
+			changeMap.set(item.range[0].id, val[0]), changeMap.set(item.range[1].id, val[1]);
+		} else {
+			changeMap.set(item.id, val);
+		}
+		const paramsObj = {};
+		for (const [key, value] of changeMap) {
+			paramsObj[key] = value;
+		}
+		const params = {
+			history_id: historyId,
+			query_index: summaryIndex,
+			param_json: JSON.stringify(paramsObj),
+		};
+		res = await filterQuery(params);
+		chartLoading.value = true;
+	} finally {
+		chartLoading.value = false;
+	}
+
+	const title = res?.values?.title;
+	const values = res?.values?.values ?? [];
+	groupedValues = _.groupBy(values, (item) => item[nameIndex]);
+
+	if (isMultiCompare.value) {
+		handleMultiCompare();
+	} else {
+		chartInstance.value.setOption({
+			title: {
+				text: title,
+			},
+			series:
+				groupedValues &&
+				Object.keys(groupedValues).map((item) => {
+					const values = groupedValues[item];
+					return {
+						data: values.map((item) => [item[timeIndex], item[valueIndex]]),
+					};
+				}),
+		});
+	}
+};
+
+const getSingleDayOption = (day=COMMON_DAY) => ({
+	tooltip: {
+		show: true,
+		trigger: 'axis',
+		formatter(params) {
+			const itemList = params.map((item, index) => {
+				return `<div style="margin: ${index === 0 ? 0 : 10}px 0 0; line-height: 1">
+				<div style="margin: 0px 0 0; line-height: 1">
+					${item.marker}<span style="font-size: 14px; color: #666; font-weight: 400; margin-left: 2px"
+						>${item.seriesName}</span
+					><span style="float: right; margin-left: 20px; font-size: 14px; color: #666; font-weight: 900">${item.data[1]}</span>
+					<div style="clear: both"></div>
+				</div>
+				<div style="clear: both"></div>
+			</div>`;
+			});
+
+			const result = `<div style="margin: 0px 0 0; line-height: 1">
+				<div style="margin: 0px 0 0; line-height: 1">
+					<div style="font-size: 14px; color: #666; font-weight: 400; line-height: 1">${params?.[0]?.data[0]?.slice(10, 16)}</div>
+					<div style="margin: 10px 0 0; line-height: 1">
+					${itemList.join('')}
+						<div style="clear: both"></div>
+					</div>
+					<div style="clear: both"></div>
+				</div>
+				<div style="clear: both"></div>
+			</div>`;
+			return result;
+		},
+	},
+	xAxis: {
+		min: day + ' 00:00:00',
+		max: day + ' 23:59:59',
+		splitNumber: 10,
+		axisLabel: {
+			formatter: (val) => {
+				const newVal = moment(val).format('HH:mm');
+				return newVal;
+			},
+			showMaxLabel: true,
+		},
+	},
+} as echarts.EChartsOption);
+//#region ====================== 璁剧疆Y鑼冨洿 ======================
+const debounceSetYRange = debounce((val) => {
+	chartInstance.value.setOption({
+		yAxis: {
+			min: val.min,
+			max: val.max,
+		},
+	});
+});
+
+const yRangeInput = (val) => {
+	debounceSetYRange(val);
+};
+
+//#endregion
+
+//#region ====================== 澶氭棩瀵规瘮 ======================
+// 澶氭棩瀵规瘮鍩哄噯鏃堕棿
+const COMMON_DAY = '2024-07-26';
+
+const isMultiCompare = ref(false);
+const handleMultiCompare = () => {
+	if (!isMultiCompare.value) return;
+	const cloneData = deepClone(groupedValues);
+	const seriesData = Object.keys(cloneData).reduce((preVal, curVal, curIndex, arr) => {
+		const values = cloneData[curVal];
+		const isMulti = arr.length > 1;
+		const groupByDateValues = _.groupBy(values, (item) => moment(item[timeIndex]).format('YYYY-MM-DD'));
+		for (const key in groupByDateValues) {
+			if (Object.prototype.hasOwnProperty.call(groupByDateValues, key)) {
+				const val = groupByDateValues[key];
+
+				const newVal = val.map((item) => {
+					// 鏂板悕绉�
+					item[nameIndex] = isMulti ? `${curVal}_${key}` : `${key}`;
+					item[timeIndex] = COMMON_DAY + ' ' + moment(item[timeIndex]).format('HH:mm:ss');
+					return item;
+				});
+
+				preVal.push(newVal);
+			}
+		}
+		return preVal;
+	}, []);
+	const series = seriesData.map<echarts.SeriesOption>((item) => ({
+		name: item[0]?.[nameIndex],
+		data: item.map((item) => [item[timeIndex], item[valueIndex]]),
+		...getChartTypeSeriesOption(activeChartType),
+	}));
+	setNewOption(series, getSingleDayOption());
+};
+const multiCompareChange = (val) => {
+	if (!groupedValues) return;
+	if (val) {
+		handleMultiCompare();
+	} else {
+		setNewOption();
+	}
+};
+//#endregion
+
+defineExpose({
+	drawChart,
+	isMultiCompare,
+	handleMultiCompare,
+	handleData,
+});
+</script>
+<style scoped lang="scss"></style>
diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSet/RecordSetDialog.vue b/src/components/chat/chatComponents/summaryCom/components/recordSet/RecordSetDialog.vue
new file mode 100644
index 0000000..17e3a83
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/recordSet/RecordSetDialog.vue
@@ -0,0 +1,91 @@
+<template>
+	<el-dialog :destroy-on-close="true" v-model="isShow" draggable :close-on-click-modal="false" :title="chartValues?.title">
+		<RecordSet chartHeight="30rem" ref="recordSetRef" :data="chartValues">
+			<TimeRange ref="timeRangeRef" v-model="queryRange" class="flex-0 m-1" @change="timeRangeChange" />
+			<List class="flex-0 m-1" v-model="stepTime" :data="listData" @change="selectStepChange" />
+		</RecordSet>
+	</el-dialog>
+</template>
+
+<script setup lang="ts">
+import { nextTick, ref, watch } from 'vue';
+import RecordSet from './RecordSet.vue';
+import { queryScadaTimeValues } from '/@/api/ai/chat';
+import { useCompRef } from '/@/utils/types';
+import { getRecentDateRange } from '/@/utils/util';
+import { formatDate } from '/@/utils/formatTime';
+import TimeRange from './components/TimeRange.vue';
+import List from './components/List.vue';
+
+const props = defineProps(['otype', 'oname', 'indexName']);
+
+const isShow = defineModel({
+	type: Boolean,
+});
+
+const recordSetRef = useCompRef(RecordSet);
+const timeRangeRef = useCompRef(TimeRange);
+
+const listData = {
+	list: [
+		{ title: '5鍒嗛挓', value: '5 minutes' },
+		{ title: '10鍒嗛挓', value: '10 minutes' },
+		{ title: '鍗婂皬鏃�', value: '30 minutes' },
+		{ title: '1灏忔椂', value: '1 hours' },
+		{ title: '1澶�', value: '1 days' },
+	],
+	type: 'list',
+	title: '鏃堕暱',
+	value: '5 minutes',
+} as any;
+
+const queryRange = ref<string[]>(null);
+const timeRangeChange = (val) => {
+	setChartData();
+};
+
+const selectStepChange = (val) => {
+	setChartData();
+};
+const stepTime = ref('5 minutes');
+const chartValues = ref(null);
+const setChartData = async () => {
+	const res = await queryScadaTimeValues({
+		// 璁惧绫诲瀷
+		ptype: props.otype,
+		// 璁惧鍚嶇О
+		pname: props.oname,
+		otype: props.indexName,
+		start_time: timeRangeRef.value.formatDateValue[0],
+		end_time: timeRangeRef.value.formatDateValue[1],
+		step_time: stepTime.value,
+	});
+	chartValues.value = res.values;
+	chartValues.value.chart = 'single_line';
+	nextTick(() => {
+		setTimeout(() => {
+			if(recordSetRef.value.isMultiCompare){
+				recordSetRef.value.handleData();
+				recordSetRef.value.handleMultiCompare();
+			}else{
+				recordSetRef.value.drawChart();
+			}
+		}, 0);
+	});
+};
+
+watch(
+	() => isShow.value,
+	(val) => {
+		if (!val) {
+			return;
+		}
+		queryRange.value = getRecentDateRange(1).map((item) => formatDate(item));
+		nextTick(()=>{
+			setChartData();
+
+		})
+	}
+);
+</script>
+<style scoped lang="scss"></style>
diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSet/components/List.vue b/src/components/chat/chatComponents/summaryCom/components/recordSet/components/List.vue
new file mode 100644
index 0000000..9f6ac7d
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/recordSet/components/List.vue
@@ -0,0 +1,60 @@
+<template>
+	<el-select
+		class="w-32"
+		:style="{width:selectWidth}"
+		v-model="selectValue"
+		@change="changeValue"
+		:disabled="disabled"
+		:placeholder="data.title"
+	>
+		<el-option v-for="item in data.list" :key="item.value" :value="item.value" :label="item.title"></el-option>
+	</el-select>
+</template>
+
+<script setup lang="ts">
+import { ref, type PropType, computed } from 'vue';
+import type { ListParam } from '../types';
+import { getTextWidth } from '/@/utils/util';
+
+const props = defineProps({
+	data: {
+		type: Object as PropType<ListParam>,
+	},
+	disabled: {
+		type: Boolean,
+		default: false,
+	},
+});
+
+const emit = defineEmits(['change']);
+const SELECT_OFFSET = 47;
+const selectWidth = computed(() => {
+	if (props.data?.list?.length > 0) {
+		// 浠ユ渶澶у瓧闀夸负瀹藉害
+		const widthList = props.data.list.map((item) =>
+			getTextWidth(item.title, {
+				size: fontSize.value,
+			})
+		);
+		const maxWidth = Math.max(...widthList);
+		const realWidth = maxWidth + SELECT_OFFSET;
+		return realWidth + 'px';
+	} else {
+		return 0;
+	}
+});
+
+const fontSize = ref('14px');
+const selectValue = defineModel({
+	type: String,
+});
+
+const changeValue = (val) => {
+	emit('change', val);
+};
+</script>
+<style scoped lang="scss">
+:deep(.el-input) {
+	font-size: v-bind(fontSize);
+}
+</style>
diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSet/components/TimeRange.vue b/src/components/chat/chatComponents/summaryCom/components/recordSet/components/TimeRange.vue
new file mode 100644
index 0000000..674e9bd
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/recordSet/components/TimeRange.vue
@@ -0,0 +1,146 @@
+<template>
+	<div class="flex items-center">
+		<div class="flex items-center space-x-1">
+			<div
+				class="ywifont ywicon-pre"
+				:class="{ 'cursor-not-allowed': !offsetClickIsAllow, 'cursor-pointer': offsetClickIsAllow }"
+				@click="preDayClick"
+			></div>
+			<el-date-picker
+				style="width: 240px"
+				v-model="dateValue"
+				type="daterange"
+				:start-placeholder="START_PLACEHOLDER"
+				:end-placeholder="END_PLACEHOLDER"
+				:value-format="valueFormat"
+				:format="DEFAULT_FORMATS_DATE"
+				:disabled-date="disabledDate"
+				:clearable="false"
+				:disabled="disabled"
+				@change="datePickerChange"
+			>
+				<template v-for="(value, name) in $slots" #[name]="slotData">
+					<slot :name="name" v-bind="slotData || {}"></slot>
+				</template>
+			</el-date-picker>
+			<div
+				class="ywifont ywicon-next"
+				:class="{ 'cursor-not-allowed': !offsetClickIsAllow, 'cursor-pointer': offsetClickIsAllow }"
+				@click="nextDayClick"
+			></div>
+		</div>
+
+		<div class="ml-2 inline-flex items-center space-x-2 text-[14px]">
+			<div
+				@click="quickPickRangeClick(parseInt(item))"
+				class="border border-solid rounded-md px-2 cursor-pointer"
+				:class="{ 'bg-[#1677ff]': parseInt(item) === quickPickValue, 'text-white': parseInt(item) === quickPickValue }"
+				v-for="item in Object.keys(timeRangeEnumMapTitle)"
+				:key="item"
+			>
+				{{ timeRangeEnumMapTitle[item] }}
+			</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { ElDatePicker } from 'element-plus';
+import { definePropType } from 'element-plus/es/utils/vue/props/runtime';
+import { ref, type PropType, computed, watch } from 'vue';
+import type { TimeRangeParam } from '../types';
+import type { TimeRangeEnum } from './types';
+import { timeRangeEnumMapTitle, timeRangeEnumMapValue } from './types';
+import {
+	CURRENT_DAY,
+	DEFAULT_FORMATS_DATE,
+	DEFAULT_FORMATS_TIME,
+	END_PLACEHOLDER,
+	RANGE_SEPARATOR,
+	START_PLACEHOLDER,
+} from '/@/components/form/datepicker/constants';
+import { formatDate } from '/@/utils/formatTime';
+import moment from 'moment';
+
+const valueFormat = DEFAULT_FORMATS_DATE + ' ' + DEFAULT_FORMATS_TIME;
+const props = defineProps({
+	data: {
+		type: Object as PropType<TimeRangeParam>,
+	},
+	disabled: {
+		type: Boolean,
+		default: false,
+	},
+});
+const dateValue = defineModel({
+	type: definePropType<[string, string]>(Array),
+});
+const emit = defineEmits(['change']);
+
+/**
+ * 闇�瑕佸 dateValue 鏍煎紡鍖栵紝dataValue 缁撴潫鏃堕棿涓嶆槸23:59:59
+ */
+const formatDateValue = computed({
+	get: () => {
+		if (!dateValue.value) return null;
+		return [moment(dateValue.value[0]).format('YYYY-MM-DD 00:00:00'), moment(dateValue.value[1]).format('YYYY-MM-DD 23:59:59')] as [
+			string,
+			string
+		];
+	},
+	set: (value) => {
+		dateValue.value = value;
+	},
+});
+
+const disabledDate = (date: Date) => {
+	return date > CURRENT_DAY;
+};
+
+const resetQuickPickValue = () =>{
+	quickPickValue.value = null;
+}
+const quickPickValue = ref<TimeRangeEnum>(null);
+const quickPickRangeClick = (val: TimeRangeEnum) => {
+	if (quickPickValue.value === val) return;
+
+	quickPickValue.value = val;
+	formatDateValue.value = timeRangeEnumMapValue[val]().map((item) => formatDate(item)) as [string, string];
+};
+
+const offsetClickIsAllow = computed(() => !!dateValue.value && !props.disabled);
+const preDayClick = () => {
+	if (!dateValue.value) return;
+	dateValue.value[0] = moment(dateValue.value[0]).subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss');
+	dateValue.value = [...dateValue.value];
+	resetQuickPickValue();
+
+};
+
+const nextDayClick = () => {
+	if (!dateValue.value) return;
+	dateValue.value[1] = moment(dateValue.value[1]).add(1, 'day').format('YYYY-MM-DD HH:mm:ss');
+	dateValue.value = [...dateValue.value];
+	resetQuickPickValue();
+};
+
+const datePickerChange = (va) => {
+	resetQuickPickValue();
+};
+
+watch(
+	() => formatDateValue.value,
+	(val) => {
+		emit('change', val);
+	}
+);
+
+defineExpose({
+	formatDateValue
+})
+</script>
+<style scoped lang="scss">
+:deep(.el-date-editor .el-range__close-icon--hidden) {
+	display: none;
+}
+</style>
diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSet/components/Timestamp.vue b/src/components/chat/chatComponents/summaryCom/components/recordSet/components/Timestamp.vue
new file mode 100644
index 0000000..073dcba
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/recordSet/components/Timestamp.vue
@@ -0,0 +1,59 @@
+<template>
+	<el-date-picker
+		class="timestamp"
+		style="width: 130px"
+		@change="changeValue"
+		v-model="selectValue"
+		type="date"
+		:placeholder="props.data.title"
+		value-format="YYYY-MM-DD HH:mm:ss"
+		format="YYYY-MM-DD"
+		:shortcuts="shortcuts"
+		:disabled-date="disabledCurrentDate"
+		:clearable="false"
+		:disabled="disabled"
+	/>
+</template>
+
+<script setup lang="ts">
+import type { PropType } from 'vue';
+import type { TimestampParam } from '../types';
+import { getRecentDate } from '/@/utils/util';
+import { disabledCurrentDate } from '/@/components/form/datepicker/constants';
+const props = defineProps({
+	data: {
+		type: Object as PropType<TimestampParam>,
+	},
+	disabled: {
+		type: Boolean,
+		default: false,
+	},
+});
+const emit = defineEmits(['change']);
+
+const shortcuts = [
+	{
+		text: '浠婂ぉ',
+		value: () => getRecentDate(0),
+	},
+	{
+		text: '涓夊ぉ鍓�',
+		value: () => getRecentDate(3),
+	},
+	{
+		text: '涓冨ぉ鍓�',
+		value: () => getRecentDate(7),
+	},
+];
+const selectValue = defineModel({
+	type: String,
+});
+const changeValue = (val) => {
+	emit('change', val);
+};
+</script>
+<style scoped lang="scss">
+.timestamp {
+	width: 180px;
+}
+</style>
diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSet/components/YRange.vue b/src/components/chat/chatComponents/summaryCom/components/recordSet/components/YRange.vue
new file mode 100644
index 0000000..bbfbadc
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/recordSet/components/YRange.vue
@@ -0,0 +1,48 @@
+<template>
+	<div class="flex items-center">
+		<el-input-number
+			placeholder="鏈�灏忓��"
+			:style="{
+				width: inputWidth,
+			}"
+			class="rounded-full"
+			v-model="yRange.min"
+			@input="(val) => numInput(val, 'min')"
+			:controls="false"
+		></el-input-number>
+		<span class="bg-[#cdcdcd] h-[32px] inline-block flex-center">~</span>
+		<el-input-number
+			placeholder="鏈�澶у��"
+			:style="{
+				width: inputWidth,
+			}"
+			class="round"
+			v-model="yRange.max"
+			:controls="false"
+			@input="(val) => numInput(val, 'max')"
+		></el-input-number>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { watch, type PropType, ref } from 'vue';
+
+const emit = defineEmits(['input']);
+
+const yRange = ref({
+	min: null,
+	max: null,
+});
+
+const realYRange = ref({
+    min:null,
+    max:null
+})
+const inputWidth = '82px';
+
+const numInput = (val: any, type: 'min' | 'max') => {
+    realYRange.value[type] = val;
+	emit('input', realYRange.value);
+};
+</script>
+<style scoped lang="scss"></style>
diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSet/components/constants.ts b/src/components/chat/chatComponents/summaryCom/components/recordSet/components/constants.ts
new file mode 100644
index 0000000..5280b64
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/recordSet/components/constants.ts
@@ -0,0 +1,2 @@
+// 鏈�澶ч�夋嫨鍐呭瀹藉害
+export const MAX_SELECT_CONTENT_WIDTH = 12;
\ No newline at end of file
diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSet/components/types.ts b/src/components/chat/chatComponents/summaryCom/components/recordSet/components/types.ts
new file mode 100644
index 0000000..3945154
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/recordSet/components/types.ts
@@ -0,0 +1,18 @@
+import { getRecentDate, getRecentDateRange } from '/@/utils/util';
+
+export const enum TimeRangeEnum {
+	CurrentDay,
+	ThreeDay,
+	SevenDay,
+}
+
+export const timeRangeEnumMapTitle = {
+	[TimeRangeEnum.CurrentDay]: '褰撴棩',
+	[TimeRangeEnum.ThreeDay]: '杩戜笁鏃�',
+	[TimeRangeEnum.SevenDay]: '杩戜竷鏃�',
+};
+export const timeRangeEnumMapValue = {
+	[TimeRangeEnum.CurrentDay]: () => getRecentDateRange(1),
+	[TimeRangeEnum.ThreeDay]: () => getRecentDateRange(3),
+	[TimeRangeEnum.SevenDay]: () => getRecentDateRange(7),
+};
diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSet/types.ts b/src/components/chat/chatComponents/summaryCom/components/recordSet/types.ts
new file mode 100644
index 0000000..7bf7638
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/recordSet/types.ts
@@ -0,0 +1,64 @@
+import List from './components/List.vue';
+import Timestamp from './components/Timestamp.vue';
+import TimeRange from './components/TimeRange.vue';
+
+export const enum RecordSetParamsType {
+	List = 'list',
+	/** @description 鍚庣鏍煎紡 */
+	StartTime = 'start_time',
+	EndTime = 'end_time',
+	/** @description start 鍜� end 鍚堝苟涓轰竴涓� range */
+	TimeRange = 'time_range',
+}
+
+export type BaseParam = {
+	id: string;
+	title: string;
+	hide?: boolean;
+};
+
+export type ListParamListItem = {
+	title: string;
+	value: string;
+};
+export type ListParam = {
+	type: RecordSetParamsType.List;
+	value: string;
+	list: ListParamListItem[];
+} & BaseParam;
+
+//#region ====================== 鍚庣鏁版嵁鏍煎紡 ======================
+export type TimeRangeBackEndParamType = RecordSetParamsType.StartTime | RecordSetParamsType.EndTime;
+export type TimeRangeBackEndParam = {
+	type: TimeRangeBackEndParamType;
+	value: string;
+	// 灞炰簬鍚屼竴涓� group 閰嶅
+	group: string;
+} & BaseParam;
+//#endregion
+//#region ====================== 鏁村悎 start 鍜� end锛屽緱鍒板墠绔牸寮� ======================
+export  type  TimeRangeParamValue = [
+	string,
+	string
+]
+export type TimeRangeParam = {
+	type: RecordSetParamsType.TimeRange;
+	value: TimeRangeParamValue;
+	// 灞炰簬鍚屼竴涓� group 閰嶅
+	group: string;
+	range?: [TimeRangeBackEndParam, TimeRangeBackEndParam];
+} & BaseParam;
+//#endregion
+
+
+export type RecordSetParamsItem = ListParam | TimeRangeParam | TimeRangeBackEndParam;
+export type RecordSet = {
+	params?: RecordSetParamsItem[];
+} & Record<string, any>;
+
+export const recordSetMapCom = {
+	[RecordSetParamsType.List]: List,
+	[RecordSetParamsType.TimeRange]: TimeRange,
+	[RecordSetParamsType.StartTime]:Timestamp,
+	[RecordSetParamsType.EndTime]:Timestamp
+};
diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSetTable/RecordSetTable.vue b/src/components/chat/chatComponents/summaryCom/components/recordSetTable/RecordSetTable.vue
new file mode 100644
index 0000000..f1f59ae
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/recordSetTable/RecordSetTable.vue
@@ -0,0 +1,136 @@
+<!-- 鏌ヨ鏈�鏂拌鍛婁俊鎭� -->
+<template>
+	<div>
+		<span v-if="data?.title" class="text-base font-bold flex-center mb-5">{{ data?.title }}</span>
+		<div class="w-full" style="height: 70vh" ref="containerRef" v-resize="resizeHandler">
+			<el-table
+				ref="tableRef"
+				border
+				:cell-style="{ textAlign: 'center' }"
+				:header-cell-style="{ textAlign: 'center' }"
+				:data="data?.values"
+				highlight-current-row
+				class="w-full h-full"
+				cellClassName="text-sm"
+				headerCellClassName="text-sm"
+			>
+				<template v-if="data?.cols?.length > 0">
+					<el-table-column
+						v-for="(item, index) in colList"
+						:label="item.title"
+						:width="item.width"
+						:sortable="item.type === 'time'"
+						:key="index"
+						:prop="index + ''"
+						show-overflow-tooltip
+					/>
+				</template>
+			</el-table>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import type { TableInstance } from 'element-plus';
+import _ from 'lodash';
+import { onMounted, ref, type PropType } from 'vue';
+import { debounce, getTextWidth } from '/@/utils/util';
+
+const props = defineProps({
+	data: {
+		type: Object as PropType<any>,
+	},
+});
+
+const colList = ref([]);
+const containerRef = ref<HTMLDivElement>(null);
+const tableRef = ref<TableInstance>(null);
+const measureWidthOffset = 28;
+
+const resizeEvent = ({ width, height }) => {
+	if (width === 0 || height === 0) {
+		return;
+	}
+	if (props.data?.cols?.length > 0 && props.data?.values?.length > 0) {
+		const maxStrList = props.data.cols.map((item) => item.title);
+		const maxLenList = props.data.cols.map((item) => item.title.gblen());
+		for (const item of props.data.values) {
+			item.map((subItem, index) => {
+				const subItemLen = subItem?.gblen();
+				if (maxLenList[index] < subItemLen) {
+					maxLenList[index] = subItemLen;
+					maxStrList[index] = subItem;
+				}
+			});
+		}
+		// 鎬诲
+		let sumWidth = 0;
+		let maxWidthList = maxStrList.map((item, index) => {
+			const width =
+				getTextWidth(item, {
+					size: '0.875rem',
+				}) + measureWidthOffset;
+			sumWidth += width;
+			return {
+				index,
+				maxWidth: width,
+			};
+		});
+		if (sumWidth <= width) {
+			maxWidthList = maxWidthList.map((item) => ({
+				...item,
+				width: (item.maxWidth / sumWidth) * width,
+			}));
+			// 鍏堟弧瓒冲皬鐨勶紝鍓╀綑绌洪棿鎸夋瘮渚嬪潎鍒�
+		} else {
+			let curWidth = 0;
+			const sortedWidthList = _.sortBy(maxWidthList, 'maxWidth');
+			let restWidth = width;
+			let notFitStartIndex = 1;
+			for (let index = 0; index < sortedWidthList.length; index++) {
+				const item = sortedWidthList[index];
+				curWidth += item.maxWidth;
+				if (curWidth < width || index === 0) {
+					maxWidthList[item.index].width = item.maxWidth;
+					continue;
+				}
+				restWidth = width - (curWidth - item.maxWidth);
+				notFitStartIndex = index;
+				break;
+			}
+			let sumRestMaxWidth = 0;
+			const notFitIndexList = sortedWidthList.slice(notFitStartIndex).map((item) => {
+				sumRestMaxWidth += item.maxWidth;
+				return item.index;
+			});
+
+			// 骞冲潎鍒嗛厤鍓╀綑绌洪棿
+			for (const item of notFitIndexList) {
+				maxWidthList[item].width = restWidth * (maxWidthList[item].maxWidth / sumRestMaxWidth);
+				if (maxWidthList[item].maxWidth <= restWidth) {
+					maxWidthList[item].width = restWidth;
+				}
+
+				// maxWidthList[item].width = undefined;
+			}
+		}
+		colList.value = props.data.cols.map((item, index) => ({
+			...item,
+			width: maxWidthList[index].width,
+		}));
+	} else {
+		colList.value = [];
+	}
+	tableRef.value.doLayout();
+};
+
+const resizeHandler = debounce(resizeEvent);
+
+onMounted(() => {
+	resizeEvent({
+		width: containerRef.value.clientWidth,
+		height: 0.7 * document.body.clientHeight,
+	});
+});
+</script>
+<style scoped lang="scss"></style>
diff --git a/src/components/chat/chatComponents/summaryCom/components/summary/Summary.vue b/src/components/chat/chatComponents/summaryCom/components/summary/Summary.vue
new file mode 100644
index 0000000..2a32be0
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/summary/Summary.vue
@@ -0,0 +1,32 @@
+<!-- 鏄ㄦ棩渚涙按绠$綉姒傚喌 -->
+<template>
+	<div class="w-full">
+		<span class="text-base font-bold">{{ data.title }}</span>
+		<el-table ref="tableRefList" class="w-full mt-5" :data="[{}]" cellClassName="text-sm" headerCellClassName="text-sm">
+			<el-table-column v-for="(col, index) in data.values" :label="col.title" :key="index">
+				<template #default="scope">
+					{{ col?.value }}
+				</template>
+			</el-table-column>
+		</el-table>
+	</div>
+</template>
+
+<script setup lang="ts">
+import type { TableInstance } from 'element-plus';
+import { onMounted, ref } from 'vue';
+import { chatComProps } from '../../../common';
+
+const props = defineProps(chatComProps);
+
+const tableRef = ref<TableInstance>();
+const doTableLayout = () => {
+	tableRef.value?.doLayout();
+};
+onMounted(() => {
+	setTimeout(() => {
+		doTableLayout();
+	}, 300);
+});
+</script>
+<style scoped lang="scss"></style>
diff --git a/src/components/chat/chatComponents/summaryCom/components/types.ts b/src/components/chat/chatComponents/summaryCom/components/types.ts
new file mode 100644
index 0000000..0113c5f
--- /dev/null
+++ b/src/components/chat/chatComponents/summaryCom/components/types.ts
@@ -0,0 +1,31 @@
+import HTMLCom from '../../htmlCom/HTMLCom.vue';
+import MapCom from '../../mapCom/MapCom.vue';
+import RecordSet from './recordSet/RecordSet.vue';
+import RecordSetTable from './recordSetTable/RecordSetTable.vue';
+import AmisPage from './amisPage/AmisPage.vue';
+
+
+import Summary from './summary/Summary.vue';
+import DeviceLastValueCom from './deviceLastValue/DeviceLastValueCom.vue';
+
+export const enum SummaryAnswerType {
+	RecordSet = 'recordset',
+	Summary = 'summary',
+	Url = 'url',
+	Map = 'map',
+	DeviceLastValue='device_last_value',
+	/** @description 鍚庣骞舵病鏈夊鍔犱竴涓柊鐨� table 绫诲瀷锛岃�屾槸褰撴垚 recordset 鐨勪竴绉嶇壒鍒� */
+	RecordSetTable = 'recordsetTable',
+	AmisPage="amis_page"
+}
+
+export const summaryAnswerTypeMapCom = {
+	[SummaryAnswerType.RecordSet]: RecordSet,
+	[SummaryAnswerType.Summary]: Summary,
+	[SummaryAnswerType.Url]: HTMLCom,
+	[SummaryAnswerType.Map]: MapCom,
+	[SummaryAnswerType.DeviceLastValue]:DeviceLastValueCom,
+	[SummaryAnswerType.RecordSetTable]:RecordSetTable,
+	[SummaryAnswerType.AmisPage]:AmisPage
+	
+};
diff --git a/src/components/chat/chatComponents/types.ts b/src/components/chat/chatComponents/types.ts
new file mode 100644
index 0000000..a98a373
--- /dev/null
+++ b/src/components/chat/chatComponents/types.ts
@@ -0,0 +1,18 @@
+
+export const enum ChartTypeEnum {
+	Scatter,
+	Line,
+	Bar,
+}
+
+export const chartTypeMapName = {
+	[ChartTypeEnum.Line]: '鎶樼嚎鍥�',
+	[ChartTypeEnum.Scatter]: '鏁g偣鍥�',
+	[ChartTypeEnum.Bar]: '鏌辩姸鍥�',
+};
+
+export const chartTypeMapEchart = {
+	[ChartTypeEnum.Scatter]: 'scatter',
+	[ChartTypeEnum.Line]: 'line',
+	[ChartTypeEnum.Bar]: 'bar',
+};
diff --git a/src/components/chat/components/Loading/Loading.vue b/src/components/chat/components/Loading/Loading.vue
new file mode 100644
index 0000000..80d4286
--- /dev/null
+++ b/src/components/chat/components/Loading/Loading.vue
@@ -0,0 +1,114 @@
+<template>
+	<div class="com__box flex-center">
+	  <div class="loading">
+		<div></div>
+		<div></div>
+		<div></div>
+		<div></div>
+		<div></div>
+		<div></div>
+		<div></div>
+		<div></div>
+		<div></div>
+	  </div>
+	</div>
+  </template>
+  
+  <style scoped>
+  .loading,
+  .loading > div {
+	position: relative;
+	box-sizing: border-box;
+  }
+  
+  .loading {
+	display: block;
+	font-size: 0;
+	color: #000;
+  }
+  
+  .loading.la-dark {
+	color: #333;
+  }
+  
+  .loading > div {
+	display: inline-block;
+	float: none;
+	background-color: currentColor;
+	border: 0 solid currentColor;
+  }
+  
+  .loading {
+	width: 17px;
+	height: 17px;
+  }
+  
+  .loading > div {
+	width: 3px;
+	height: 3px;
+	margin: 1px;
+	border-radius: 100%;
+	animation-name: ball-grid-beat;
+	animation-iteration-count: infinite;
+  }
+  
+  .loading > div:nth-child(1) {
+	animation-duration: 0.65s;
+	animation-delay: 0.03s;
+  }
+  
+  .loading > div:nth-child(2) {
+	animation-duration: 1.02s;
+	animation-delay: 0.09s;
+  }
+  
+  .loading > div:nth-child(3) {
+	animation-duration: 1.06s;
+	animation-delay: -0.69s;
+  }
+  
+  .loading > div:nth-child(4) {
+	animation-duration: 1.5s;
+	animation-delay: -0.41s;
+  }
+  
+  .loading > div:nth-child(5) {
+	animation-duration: 1.6s;
+	animation-delay: 0.04s;
+  }
+  
+  .loading > div:nth-child(6) {
+	animation-duration: 0.84s;
+	animation-delay: 0.07s;
+  }
+  
+  .loading > div:nth-child(7) {
+	animation-duration: 0.68s;
+	animation-delay: -0.66s;
+  }
+  
+  .loading > div:nth-child(8) {
+	animation-duration: 0.93s;
+	animation-delay: -0.76s;
+  }
+  
+  .loading > div:nth-child(9) {
+	animation-duration: 1.24s;
+	animation-delay: -0.76s;
+  }
+  
+  @keyframes ball-grid-beat {
+	0% {
+	  opacity: 1;
+	}
+  
+	50% {
+	  opacity: 0.35;
+	}
+  
+	100% {
+	  opacity: 1;
+	}
+  }
+  </style>
+  
\ No newline at end of file
diff --git a/src/components/chat/components/model/Record.ts b/src/components/chat/components/model/Record.ts
new file mode 100644
index 0000000..818c5a4
--- /dev/null
+++ b/src/components/chat/components/model/Record.ts
@@ -0,0 +1,68 @@
+import type {  RecordSetValues } from '/@/api/ai/chat';
+
+export class RecordSet {
+	names: string[];
+	values: Array<any[]>;
+	title: string;
+	constructor(data: RecordSetValues) {
+		this.names = data.names ?? [];
+		this.values = data.values ?? [];
+		this.title = data.title;
+	}
+
+	generateHTML() {
+		const thList = this.names.map((item) => {
+			return ` <th class="text-left py-3 px-4 uppercase font-semibold text-sm">${item}</th>`;
+		});
+		const trList = this.values.map((item) => {
+			const tdList = ((item??[])).map((subItem) => ` <td class="text-left py-3 px-4">${subItem}</td>`);
+			return ` <tr>
+          ${tdList}
+          </tr>`;
+		});
+		return `
+        <div class="md:px-32 py-8 w-full">
+          <div class="shadow overflow-hidden rounded border-b border-gray-200">
+            <table class="min-w-full bg-white">
+              <thead class="bg-gray-800 text-white">
+                <tr>
+                ${thList}
+                </tr>
+              </thead>
+            <tbody class="text-gray-700">
+             ${trList}
+            </tbody>
+            </table>
+          </div>
+        </div>`;
+	}
+}
+
+
+
+const record = new RecordSet({
+    "json_ok": true,
+    "question": "鏄ㄦ棩浜斾竴骞垮満鍘嬪姏",
+    "answer_type": "recordset",
+    "values": {
+      "names": [
+        "yesterday",
+        "max_pressure"
+      ],
+      "values": [
+        [
+          "2024-06-28 00:00:00",
+          24.378
+        ],
+        [
+          "2024-06-29 00:00:00",
+          24.276
+        ]
+      ],
+      "type": "records",
+      "title": "鏄ㄦ棩浜斾竴骞垮満(D_GW_04)鐨勬渶澶у帇鍔涘��"
+    }
+  }.values)
+
+  const html = record.generateHTML();
+
diff --git a/src/components/chat/components/model/types.ts b/src/components/chat/components/model/types.ts
new file mode 100644
index 0000000..02a53ce
--- /dev/null
+++ b/src/components/chat/components/model/types.ts
@@ -0,0 +1,61 @@
+import RecordSetCom from '../chatComponents/recordSetCom/RecordSetCom.vue';
+import NormalTextCom from '../chatComponents/normalTextCom/NormalTextCom.vue';
+import knowledgeCom from '../chatComponents/knowledgeCom/KnowledgeCom.vue';
+import SummaryCom from '../chatComponents/summaryCom/SummaryCom.vue';
+
+import assistantPic from '/static/images/role/assistant-200x192.png';
+import userPic from '/static/images/role/user-200x206.png';
+export const enum AnswerType {
+	Knowledge = 'knowledge',
+	RecordSet = 'recordset',
+	Text = 'text',
+	Summary = 'summary',
+	Url = 'url',
+	Map = 'map',
+}
+
+export const answerTypeMapCom = {
+	[AnswerType.Knowledge]: knowledgeCom,
+	[AnswerType.RecordSet]: RecordSetCom,
+	[AnswerType.Text]: NormalTextCom,
+	[AnswerType.Summary]: SummaryCom,
+};
+
+export const enum RoleEnum {
+	user = 'user',
+	assistant = 'assistant',
+}
+export const AnswerState = {
+	Null: null,
+	Like: '1',
+	Unlike: '0',
+};
+
+export type AnswerStateType = typeof AnswerState;
+export type ContextHistory = {
+	/** @description 鏁板瓧瀛楃涓� */
+	ratio: string;
+	history_id: string;
+	question: string;
+};
+
+export type ChatContent = {
+	type: AnswerType;
+	values: any;
+	askMoreList?: ContextHistory[];
+	errCode?: string;
+	errMsg?: string;
+	origin?: any;
+};
+
+export interface ChatMessage {
+	historyId: string;
+	role: RoleEnum;
+	content?: ChatContent;
+	state?: null | '1' | '0';
+}
+
+export const roleImageMap = {
+	[RoleEnum.user]: userPic,
+	[RoleEnum.assistant]: assistantPic,
+};
diff --git a/src/components/chat/hooks/useAssistantContentOpt.ts b/src/components/chat/hooks/useAssistantContentOpt.ts
new file mode 100644
index 0000000..3617953
--- /dev/null
+++ b/src/components/chat/hooks/useAssistantContentOpt.ts
@@ -0,0 +1,144 @@
+import { ElMessage } from 'element-plus';
+import type { ComputedRef, Ref } from 'vue';
+import { computed, nextTick, ref } from 'vue';
+import useClipboard from 'vue-clipboard3';
+import type { ChatMessage } from '../model/types';
+import { AnswerState, AnswerType, RoleEnum } from '../model/types';
+import { SetHistoryAnswerState } from '/@/api/ai/chat';
+import { onClickOutside } from '@vueuse/core';
+
+export type AssistantContentOptOption = {
+	forbidScroll: Ref<boolean>;
+	sendChatMessage: any;
+	displayMessageList: ComputedRef<ChatMessage[]>;
+};
+
+export const useAssistantContentOpt = (option: AssistantContentOptOption) => {
+	const { forbidScroll, sendChatMessage, displayMessageList } = option;
+	const { toClipboard } = useClipboard();
+	const preQuestion = ref(null);
+
+	const copyClick = (item) => {
+		const type = item.content.type;
+		let text = '';
+		if (type === AnswerType.Knowledge) {
+			text = item.content.values?.map((item) => item.answer).join('\n\n') ?? '';
+		} else {
+			text = item.content.values;
+		}
+		ElMessage.success('澶嶅埗鎴愬姛');
+		toClipboard(text);
+	};
+
+	const likeClick = async (item) => {
+		const toSetState = item.state === AnswerState.Like ? AnswerState.Null : AnswerState.Like;
+		const res = await SetHistoryAnswerState({
+			history_id: item.historyId,
+			answer_state: toSetState,
+		});
+		item.state = toSetState;
+		forbidScroll.value = true;
+		nextTick(() => {
+			forbidScroll.value = false;
+		});
+	};
+
+	const unLikeClick = async (item) => {
+		const toSetState = item.state === AnswerState.Unlike ? AnswerState.Null : AnswerState.Unlike;
+		const res = await SetHistoryAnswerState({
+			history_id: item.historyId,
+			answer_state: toSetState,
+		});
+		item.state = toSetState;
+
+		forbidScroll.value = true;
+		nextTick(() => {
+			forbidScroll.value = false;
+		});
+	};
+	const feedbackPosition = ref({
+		x: 0,
+		y: 0,
+	});
+
+	const feedbackIsShow = ref(false);
+	const feedbackContent = ref('');
+	const feedbackPanelRef = ref<HTMLDivElement>(null);
+	const currentFeedbackMapItem = ref(null);
+	const curFeedbackIndex = ref(0);
+	const feedbackClick = async (e, item, index) => {
+		currentFeedbackMapItem.value = item;
+		curFeedbackIndex.value = index;
+		const offsetX = -4;
+		const offsetY = -8;
+		feedbackIsShow.value = true;
+		nextTick(() => {
+			feedbackPosition.value = {
+				x: -feedbackPanelRef.value[index].$el.clientWidth + offsetX,
+				y: -feedbackPanelRef.value[index].$el.clientHeight + offsetY,
+			};
+		});
+	};
+
+	onClickOutside(
+		computed(() => feedbackPanelRef.value?.[curFeedbackIndex.value]),
+		(e) => {
+			feedbackIsShow.value = false;
+			feedbackContent.value = '';
+		}
+	);
+	// useClickOther(
+	// 	computed(() => feedbackPanelRef.value?.[curFeedbackIndex.value]),
+	// 	feedbackIsShow,
+	// 	() => {
+	// 		feedbackIsShow.value = false;
+	// 		feedbackContent.value = '';
+	// 	}
+	// );
+	const showAskMore = computed(() => {
+		if (!displayMessageList.value || displayMessageList.value.length === 0) return false;
+		const last = displayMessageList.value.at(-1);
+		const isShow = last?.role === RoleEnum.assistant && last?.content?.values && last.content?.askMoreList?.length > 0;
+		return isShow;
+	});
+
+	const showFixQuestion = (item) => {
+		const isShow = item?.role === RoleEnum.assistant && item?.content?.values && item.content?.origin?.err_json?.fix_question;
+		return isShow;
+	};
+	const askMoreClick = (item) => {
+		if (!item.question) return;
+		sendChatMessage({ type: AnswerType.Text, values: item.question });
+	};
+
+	const fixQuestionClick = (item, originData) => {
+		if (!item.question) return;
+		preQuestion.value = originData?.question;
+		try {
+			sendChatMessage({
+				type: AnswerType.Text,
+				values: item.question,
+			});
+		} finally {
+			preQuestion.value = null;
+		}
+	};
+
+	return {
+		copyClick,
+		likeClick,
+		unLikeClick,
+		feedbackPosition,
+		feedbackIsShow,
+		feedbackContent,
+		feedbackPanelRef,
+		currentFeedbackMapItem,
+		curFeedbackIndex,
+		feedbackClick,
+		askMoreClick,
+		fixQuestionClick,
+		preQuestion,
+		showAskMore,
+		showFixQuestion,
+	};
+};
diff --git a/src/components/chat/hooks/useQueryProcess.ts b/src/components/chat/hooks/useQueryProcess.ts
new file mode 100644
index 0000000..00d970d
--- /dev/null
+++ b/src/components/chat/hooks/useQueryProcess.ts
@@ -0,0 +1,41 @@
+import { ref } from 'vue';
+import { getQuestionProcess } from '/@/api/ai/chat';
+
+export const useQueryProcess = () => {
+	const processId = ref('');
+	const QUERY_PROCESS_INTERVAL = 1000;
+	const process = ref('');
+	let processTimer = null;
+	let finishProcess = true;
+
+	const queryProcessApi = async () => {
+		const res = await getQuestionProcess({
+			process_id: processId.value,
+		}).catch((err) => {
+			process.value = err;
+		});
+
+		process.value = res.process;
+		finishProcess = true;
+	};
+
+	const queryProcess = () => {
+		processTimer = setInterval(() => {
+			if (!finishProcess) return;
+			finishProcess = false;
+			queryProcessApi();
+		}, QUERY_PROCESS_INTERVAL);
+	};
+
+	const clearQueryProcess = () => {
+		process.value = '';
+		clearInterval(processTimer);
+	};
+
+	return {
+        processId,
+        process,
+        queryProcess,
+        clearQueryProcess
+    };
+};
diff --git a/src/components/chat/hooks/useScrollToBottom.ts b/src/components/chat/hooks/useScrollToBottom.ts
new file mode 100644
index 0000000..b23e8a4
--- /dev/null
+++ b/src/components/chat/hooks/useScrollToBottom.ts
@@ -0,0 +1,37 @@
+import type { ComputedRef, Ref } from 'vue';
+import { nextTick, onActivated, ref, watch } from 'vue';
+import type { ChatMessage } from '../model/types';
+
+export type UseScrollToBottomOption = {
+	chatListDom: Ref<HTMLDivElement>;
+	displayMessageList: ComputedRef<ChatMessage[]>;
+};
+
+export const useScrollToBottom = (option:UseScrollToBottomOption) => {
+    const {chatListDom,displayMessageList} = option;
+
+    const scrollToBottom = () => {
+        if (!chatListDom.value) return;
+        chatListDom.value.lastElementChild?.scrollIntoView();
+    };
+    const forbidScroll = ref(false);
+	watch(
+		displayMessageList,
+		() => {
+			if (forbidScroll.value) return;
+			nextTick(() => scrollToBottom());
+		},
+		{
+			deep: true,
+		}
+	);
+
+	onActivated(() => {
+		if (forbidScroll.value) return;
+		nextTick(() => scrollToBottom());
+	});
+
+	return {
+        forbidScroll
+    };
+};
diff --git a/src/components/chat/libs/gpt.ts b/src/components/chat/libs/gpt.ts
new file mode 100644
index 0000000..2a90398
--- /dev/null
+++ b/src/components/chat/libs/gpt.ts
@@ -0,0 +1,21 @@
+import type { ChatMessage } from '../types';
+
+export async function chat(messageList: ChatMessage[], apiKey: string) {
+	const result = await fetch('https://api.openai.com/v1/chat/completions', {
+		method: 'post',
+		// signal: AbortSignal.timeout(8000),
+		// 寮�鍚悗鍒拌揪璁惧畾鏃堕棿浼氫腑鏂祦寮忚緭鍑�
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${apiKey}`,
+		},
+		body: JSON.stringify({
+			model: 'gpt-3.5-turbo',
+			stream: true,
+			messages: messageList,
+		}),
+	}).catch(error=>{
+    throw error
+  });
+	return result;
+}
diff --git a/src/components/chat/libs/markdown.ts b/src/components/chat/libs/markdown.ts
new file mode 100644
index 0000000..0a002fe
--- /dev/null
+++ b/src/components/chat/libs/markdown.ts
@@ -0,0 +1,21 @@
+import highlight from 'highlight.js';
+import Markdown from 'markdown-it';
+
+const mdOptions: Markdown.Options = {
+	linkify: true,
+	typographer: true,
+	breaks: true,
+	langPrefix: 'language-',
+	// 浠g爜楂樹寒
+	highlight(str, lang) {
+		if (lang && highlight.getLanguage(lang)) {
+			try {
+				return '<pre class="hljs"><code>' + highlight.highlight(lang, str, true).value + '</code></pre>';
+			} catch (__) {
+      }
+		}
+		return '';
+	},
+};
+
+export const md = new Markdown(mdOptions);
diff --git a/src/components/chat/model/Record.ts b/src/components/chat/model/Record.ts
new file mode 100644
index 0000000..818c5a4
--- /dev/null
+++ b/src/components/chat/model/Record.ts
@@ -0,0 +1,68 @@
+import type {  RecordSetValues } from '/@/api/ai/chat';
+
+export class RecordSet {
+	names: string[];
+	values: Array<any[]>;
+	title: string;
+	constructor(data: RecordSetValues) {
+		this.names = data.names ?? [];
+		this.values = data.values ?? [];
+		this.title = data.title;
+	}
+
+	generateHTML() {
+		const thList = this.names.map((item) => {
+			return ` <th class="text-left py-3 px-4 uppercase font-semibold text-sm">${item}</th>`;
+		});
+		const trList = this.values.map((item) => {
+			const tdList = ((item??[])).map((subItem) => ` <td class="text-left py-3 px-4">${subItem}</td>`);
+			return ` <tr>
+          ${tdList}
+          </tr>`;
+		});
+		return `
+        <div class="md:px-32 py-8 w-full">
+          <div class="shadow overflow-hidden rounded border-b border-gray-200">
+            <table class="min-w-full bg-white">
+              <thead class="bg-gray-800 text-white">
+                <tr>
+                ${thList}
+                </tr>
+              </thead>
+            <tbody class="text-gray-700">
+             ${trList}
+            </tbody>
+            </table>
+          </div>
+        </div>`;
+	}
+}
+
+
+
+const record = new RecordSet({
+    "json_ok": true,
+    "question": "鏄ㄦ棩浜斾竴骞垮満鍘嬪姏",
+    "answer_type": "recordset",
+    "values": {
+      "names": [
+        "yesterday",
+        "max_pressure"
+      ],
+      "values": [
+        [
+          "2024-06-28 00:00:00",
+          24.378
+        ],
+        [
+          "2024-06-29 00:00:00",
+          24.276
+        ]
+      ],
+      "type": "records",
+      "title": "鏄ㄦ棩浜斾竴骞垮満(D_GW_04)鐨勬渶澶у帇鍔涘��"
+    }
+  }.values)
+
+  const html = record.generateHTML();
+
diff --git a/src/components/chat/model/types.ts b/src/components/chat/model/types.ts
new file mode 100644
index 0000000..02a53ce
--- /dev/null
+++ b/src/components/chat/model/types.ts
@@ -0,0 +1,61 @@
+import RecordSetCom from '../chatComponents/recordSetCom/RecordSetCom.vue';
+import NormalTextCom from '../chatComponents/normalTextCom/NormalTextCom.vue';
+import knowledgeCom from '../chatComponents/knowledgeCom/KnowledgeCom.vue';
+import SummaryCom from '../chatComponents/summaryCom/SummaryCom.vue';
+
+import assistantPic from '/static/images/role/assistant-200x192.png';
+import userPic from '/static/images/role/user-200x206.png';
+export const enum AnswerType {
+	Knowledge = 'knowledge',
+	RecordSet = 'recordset',
+	Text = 'text',
+	Summary = 'summary',
+	Url = 'url',
+	Map = 'map',
+}
+
+export const answerTypeMapCom = {
+	[AnswerType.Knowledge]: knowledgeCom,
+	[AnswerType.RecordSet]: RecordSetCom,
+	[AnswerType.Text]: NormalTextCom,
+	[AnswerType.Summary]: SummaryCom,
+};
+
+export const enum RoleEnum {
+	user = 'user',
+	assistant = 'assistant',
+}
+export const AnswerState = {
+	Null: null,
+	Like: '1',
+	Unlike: '0',
+};
+
+export type AnswerStateType = typeof AnswerState;
+export type ContextHistory = {
+	/** @description 鏁板瓧瀛楃涓� */
+	ratio: string;
+	history_id: string;
+	question: string;
+};
+
+export type ChatContent = {
+	type: AnswerType;
+	values: any;
+	askMoreList?: ContextHistory[];
+	errCode?: string;
+	errMsg?: string;
+	origin?: any;
+};
+
+export interface ChatMessage {
+	historyId: string;
+	role: RoleEnum;
+	content?: ChatContent;
+	state?: null | '1' | '0';
+}
+
+export const roleImageMap = {
+	[RoleEnum.user]: userPic,
+	[RoleEnum.assistant]: assistantPic,
+};
diff --git a/src/components/form/datepicker/constants.ts b/src/components/form/datepicker/constants.ts
index 5f5d2af..24d77ce 100644
--- a/src/components/form/datepicker/constants.ts
+++ b/src/components/form/datepicker/constants.ts
@@ -35,3 +35,6 @@
 		value: () => getRecentDate(2),
 	},
 ];
+export const disabledCurrentDate = (date: Date) => {
+	return date > CURRENT_DAY;
+};
\ No newline at end of file
diff --git a/src/utils/util.ts b/src/utils/util.ts
index 5e60f34..72d1e41 100644
--- a/src/utils/util.ts
+++ b/src/utils/util.ts
@@ -708,3 +708,29 @@
 	if (num == null) return '';
 	return num.toFixed(precision).replace(/\.?0+$/, '');
 };
+
+
+type GetTextWidthOption = {
+	size?: string;
+	family?: string;
+};
+
+export function getTextWidth(text: string, option: GetTextWidthOption) {
+	if (!text) return 0;
+	const { size = '14px', family = 'Microsoft YaHei' } = option;
+	const spanEle = document.createElement('span');
+	document.body.appendChild(spanEle);
+
+	spanEle.style.font = 'times new roman';
+	spanEle.style.fontSize = size;
+	spanEle.style.height = 'auto';
+	spanEle.style.width = 'auto';
+	spanEle.style.position = 'absolute';
+	spanEle.style.whiteSpace = 'no-wrap';
+	spanEle.innerHTML = text;
+
+	const width = spanEle.clientWidth;
+
+	document.body.removeChild(spanEle);
+	return width;
+}
diff --git a/src/views/project/yw/lowCode/sqlAmis/SqlAmis.vue b/src/views/project/yw/lowCode/sqlAmis/SqlAmis.vue
index fe812ca..40b150f 100644
--- a/src/views/project/yw/lowCode/sqlAmis/SqlAmis.vue
+++ b/src/views/project/yw/lowCode/sqlAmis/SqlAmis.vue
@@ -35,6 +35,7 @@
 						</div>
 					</template>
 				</el-table-column> -->
+				<el-table-column prop="id" label="id" width="130" fixed="left" show-overflow-tooltip> </el-table-column>
 
 				<el-table-column prop="title" label="鏍囬" width="300" fixed="left" show-overflow-tooltip> </el-table-column>
 				<el-table-column prop="prompt" label="鎻愮ず璇�" show-overflow-tooltip> </el-table-column>

--
Gitblit v1.9.3