From c89d1d1fe4e820bc9d1a942467a3f1e017b40dac Mon Sep 17 00:00:00 2001
From: wujingjing <gersonwu@qq.com>
Date: 星期三, 06 十一月 2024 16:33:36 +0800
Subject: [PATCH] 无关代码删除

---
 src/components/chat/components/playBar/PlayBar.vue |  342 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 314 insertions(+), 28 deletions(-)

diff --git a/src/components/chat/components/playBar/PlayBar.vue b/src/components/chat/components/playBar/PlayBar.vue
index 663f339..e71d240 100644
--- a/src/components/chat/components/playBar/PlayBar.vue
+++ b/src/components/chat/components/playBar/PlayBar.vue
@@ -1,22 +1,58 @@
 <template>
-	<div class="playInput hl_input">
+	<div class="playInput hl_input rounded-[22px] input-border input-shadow">
+		<!-- 搴旂敤鍦烘櫙 -->
+		<div class="application-scenarios absolute bottom-[114%] left-4">
+			<div class="flex-items-center space-x-2">
+				<div
+					class="border border-gray-400 border-solid h-8 flex-items-center px-3 py-2 rounded-2xl cursor-pointer space-x-1 hover:bg-[#cae3ff]"
+					:class="{ 'bg-[#c5e0ff]': activeGroupType === item, '!text-[#1c86ff]': activeGroupType === item }"
+					v-for="item in groupTypeList"
+					@click="groupTypeClick(item)"
+				>
+					<span :class="['ywifont', groupTypeMapIcon[item]]"></span>
+					<div class="">{{ item }}</div>
+				</div>
+			</div>
+		</div>
+		<!-- 褰撳墠搴旂敤鍦烘櫙 -->
+		<!-- <div
+			class="bg-[#f9fafb] rounded-t-[22px] absolute bottom-[100%] left-0 w-full input-border h-11 flex-items-center justify-between text-[14px]"
+			style="padding: 4px 4px 4px 18px; border-bottom: none"
+		>
+			<div class="flex-items-center">
+				<span :class="[groupTypeMapIcon[activeGroupType]]" class="ywifont mr-2 !text-[14px]"></span>
+				<div>{{ activeGroupType }}</div>
+			</div>
+			<el-tooltip content="閫�鍑哄簲鐢ㄥ満鏅�" placement="top">
+				<span
+					@click="closeCurrentGroupType"
+					class="ywifont ywicon-guanbi mr-3 rounded-sm p-1 hover:bg-[#eaebec] cursor-pointer"
+				></span>
+			</el-tooltip>
+		</div> -->
 		<div class="assembly flex">
-			<el-button title="鎻掍欢鑿滃崟" class="label flex items-center cursor-pointer" link>
+			<el-button
+				title="杈撳叆甯哥敤璇爣棰橈紝鍙揩鎹疯皟鐢ㄥ父鐢ㄨ"
+				class="label flex items-center cursor-pointer"
+				link
+				@click="commonPhrasesClick"
+			>
 				<img src="/static/images/wave/PlugIn.png" class="set-icon box-border" />
 			</el-button>
 		</div>
-		<InfoDetail class="text-base" v-model="infoDetailIsShow" :item="detailMapRow"  />
+		<InfoDetail class="text-base" v-model="infoDetailIsShow" :item="detailMapRow" />
 
 		<div class="set-input">
 			<!-- @input="inputText" -->
-
 			<el-input
 				ref="inputRef"
 				class="relative align-bottom set-inputAnswer"
 				type="textarea"
 				resize="none"
+				maxlength="1024"
 				:autosize="{ minRows: 1, maxRows: 8 }"
 				v-elInputFocus
+				show-word-limit
 				@keydown="keydownInput"
 				@input="inputText"
 				v-model="inputValue"
@@ -39,8 +75,12 @@
 						@click="similarClick(item)"
 					>
 						<span class="text-sm text-gray-500 pr-1.5">{{ index + 1 }}</span>
-						<span> {{ item?.question }} </span>
-						<!-- <span class="text-blue-400 font-bold cursor-pointer hover:underline" @click.stop="tipMetricsClick">娴嬭瘯鏄�</span> -->
+						<template v-if="sentenceSplitMap?.[item.question]">
+							<template v-for="part in sentenceSplitMap[item.question]">
+								<span v-if="part.isKeyword" class="text-blue-400 font-bold cursor-pointer">{{ part.partStr }}</span>
+								<span v-else>{{ part.partStr }}</span>
+							</template>
+						</template>
 					</div>
 				</div>
 			</div>
@@ -49,11 +89,11 @@
 			<div class="upload_img space-y">
 				<div class="imgbox cursor-pointer flex items-center">
 					<!-- <el-button title="AI鐪嬪浘" class="cursor-pointer" link style="margin-left: unset">
-						<img src="/static/images/wave/LookImg.png" class="set-img-icon box-border" />
-					</el-button> -->
+				<img src="/static/images/wave/LookImg.png" class="set-img-icon box-border" />
+			</el-button> -->
 					<!-- <el-button title="AI璇煶瀵硅瘽" class="cursor-pointer" link style="margin-left: unset" @click="audioChangeWord">
-						<img src="/static/images/wave/HeadImg.png" class="set-img-icon box-border" />
-					</el-button> -->
+				<img src="/static/images/wave/HeadImg.png" class="set-img-icon box-border" />
+			</el-button> -->
 
 					<el-button title="鍙戦��" :disabled="isTalking" class="cursor-pointer" link @click="emits('sendClick')">
 						<div class="send">
@@ -70,23 +110,44 @@
 			@updateInputValue="updateInputValue"
 			:isHome="isHome"
 		/>
+		<CommonPhrases
+			v-model:isShow="isShowPhrase"
+			v-show="isShowPhrase"
+			:isHome="isHome"
+			ref="commonPhrasesRef"
+			@updateCommonChatInput="updateCommonChatInput"
+		/>
 	</div>
 </template>
 
 <script setup lang="ts">
+import { onClickOutside } from '@vueuse/core';
 import type { InputInstance } from 'element-plus';
 import { ElMessage } from 'element-plus';
+import _ from 'lodash';
 import getCaretCoordinates from 'textarea-caret';
-import { computed, nextTick, ref } from 'vue';
-import VoicePage from './voicePage/VoicePage.vue';
-import { querySimilarityHistory } from '/@/api/ai/chat';
-import { useClickOther } from '/@/hooks/useClickOther';
+import { computed, nextTick, ref, toRefs, watch } from 'vue';
 import InfoDetail from './InfoDetail.vue';
+import CommonPhrases from './phrase/CommonPhrases.vue';
+import VoicePage from './voicePage/VoicePage.vue';
+import { getMetricsNames, querySimilarityHistory } from '/@/api/ai/chat';
+import { activeGroupType, groupTypeList, groupTypeMapIcon } from '/@/stores/chatRoom';
+const emits = defineEmits(['sendClick', 'showUpChatClick', 'showDownChatClick']);
+const props = defineProps({
+	isTalking: Boolean,
+	isHome: Boolean,
+	setCommonQuestionInfo: {
+		type: Object,
+		default: {},
+	},
+});
 
-import { onClickOutside } from '@vueuse/core';
-const emits = defineEmits(['sendClick']);
-const props = defineProps(['isTalking', 'isHome']);
+const { setCommonQuestionInfo } = toRefs(props);
 const voicePageIsShow = defineModel('voicePageIsShow', {
+	type: Boolean,
+	default: false,
+});
+const isShowPhrase = defineModel('isShowPhrase', {
 	type: Boolean,
 	default: false,
 });
@@ -105,7 +166,9 @@
 	if (props.isTalking) return;
 	const isEnterInput = !e.shiftKey && e.key == 'Enter';
 	const isDigitalInput = e.ctrlKey && e.code.startsWith('Digit') && tipIsShow.value;
-	if (isEnterInput || isDigitalInput) {
+	const arrowUp = e.key === 'ArrowUp';
+	const arrowDown = e.key === 'ArrowDown';
+	if (isEnterInput || isDigitalInput || arrowUp || arrowDown) {
 		e.cancelBubble = true; //ie闃绘鍐掓场琛屼负
 		e.stopPropagation(); //Firefox闃绘鍐掓场琛屼负
 		e.preventDefault(); //鍙栨秷浜嬩欢鐨勯粯璁ゅ姩浣�*鎹㈣
@@ -119,6 +182,10 @@
 				inputValue.value = mapValue;
 				triggerShow.value = false;
 			}
+		} else if (arrowUp) {
+			emits('showUpChatClick');
+		} else if (arrowDown) {
+			emits('showDownChatClick');
 		}
 	}
 };
@@ -140,6 +207,7 @@
 onClickOutside(tipEleRef, () => {
 	triggerShow.value = false;
 });
+
 const inputText = (text) => {
 	nextTick(() => {
 		setTimeout(() => {
@@ -153,13 +221,25 @@
 			popUpPosition.value.left = caret.left + leftOffset;
 			popUpPosition.value.bottom = container.offsetHeight + bottomOffset;
 			triggerShow.value = true;
+
 			if (lastIsFinish) {
 				querySimilarityApi(text);
 			}
 		}, 0);
 	});
 };
-
+// 瀵� question 杩涜鍒嗗壊
+const sentenceSplitMap = ref<
+	Record<
+		string,
+		{
+			partStr: string;
+			startIndex: number;
+			endIndex: number;
+			isKeyword: boolean;
+		}[]
+	>
+>({});
 const similarList = ref([]);
 let lastIsFinish = true;
 const querySimilarityApi = async (text: string) => {
@@ -167,11 +247,18 @@
 	lastIsFinish = false;
 	const res = await querySimilarityHistory({
 		question: text,
+		group_type: activeGroupType.value,
 	});
 	lastIsFinish = true;
 	const handleValues = res?.values ?? [];
 
 	similarList.value = props.isHome ? handleValues.slice(0, 3) : handleValues;
+	metricsNamesPromise.then((value) => {
+		sentenceSplitMap.value = getSentenceMatchMap(
+			similarList.value.map((item) => item.question),
+			value as string[]
+		);
+	});
 };
 const audioChangeWord = () => {
 	navigator.getUserMedia(
@@ -184,7 +271,161 @@
 		}
 	);
 };
+/**
+ * 鍒囧垎鍙ュ瓙锛屽尮閰嶈瘝鐢� isKeyword 鏍囪
+ * @param sentences
+ * @param keywords
+ */
+const getSentenceMatchMap = (sentences: string[], keywords: any[]) => {
+	if (!sentences || sentences.length === 0) return null;
+	if (!keywords || keywords.length === 0) {
+		return sentences.map((item) => ({
+			partStr: item,
+			startIndex: 0,
+			endIndex: item.length,
+			isKeyword: false,
+		}));
+	}
+	let sentenceMatchMap = {};
 
+	sentences.map((sentence) => {
+		if (!sentenceMatchMap[sentence]) {
+			let sentenceMatchList = [];
+			keywords.map((keyword) => {
+				const matchList = [...sentence.matchAll(keyword)].map((item) => {
+					return {
+						partStr: item[0],
+						startIndex: item.index,
+						endIndex: item.index + item[0].length,
+					};
+				});
+				sentenceMatchList = sentenceMatchList.concat(matchList);
+			});
+
+			let nextIsMerge = false;
+			const checkNextIsMerge = (value, index, array) => {
+				nextIsMerge = false;
+				if (index === array.length - 1) return;
+				const nextValue = array[index + 1];
+
+				// 閫氳繃 nextIsMerge 鎺у埗涓嬩竴鍏冪礌鏄惁闇�瑕佷娇鐢�
+				if (value.endIndex > nextValue.startIndex) {
+					nextIsMerge = true;
+				}
+			};
+
+			// 鎸� startIndex 鎺掑簭锛屾秷闄ゅ郊姝や箣闂撮噸鍚堝厓绱�
+			sentenceMatchList = _.sortBy(sentenceMatchList, (item) => item.startIndex).filter((value, index, array) => {
+				if (nextIsMerge) {
+					checkNextIsMerge(value, index, array);
+					return false;
+				}
+				checkNextIsMerge(value, index, array);
+				return true;
+			});
+
+			sentenceMatchMap[sentence] = sentenceMatchList;
+		}
+	});
+
+	for (const sentence of Object.keys(sentenceMatchMap)) {
+		const matchList = sentenceMatchMap[sentence];
+		const result = [];
+
+		if (matchList.length === 0) {
+			result.push({
+				partStr: sentence,
+				startIndex: 0,
+				endIndex: sentence.length,
+				isKeyword: false,
+			});
+
+			sentenceMatchMap[sentence] = result;
+
+			continue;
+		}
+
+		matchList.forEach((value, index, array) => {
+			// 鍖归厤璇嶆伆濂戒笉鏄綅浜庣粨鏉熶綅缃�
+			if (array.length - 1 === index && value.endIndex !== array.length) {
+				result.push({
+					...value,
+					isKeyword: true,
+				});
+
+				if (value.endIndex !== sentence.length) {
+					result.push({
+						partStr: sentence.slice(value.endIndex, sentence.length),
+						startIndex: value.endIndex,
+						endIndex: sentence.length,
+						isKeyword: false,
+					});
+				}
+
+				// 濡傛灉鏁扮粍鍙湁涓�涓厓绱狅紝鍓嶉潰鐨勪篃闇�瑕佸姞杩涘幓
+				if (array.length === 1 && value.startIndex !== 0) {
+					result.unshift({
+						partStr: sentence.slice(0, value.startIndex),
+						startIndex: 0,
+						endIndex: value.startIndex,
+						isKeyword: false,
+					});
+				}
+
+				return;
+			}
+
+			// 鍖归厤璇嶆伆濂戒笉鏄綅浜庤捣濮嬩綅缃�
+			if (value.startIndex !== 0 && index === 0) {
+				result.push({
+					...value,
+					isKeyword: true,
+				});
+				result.unshift({
+					partStr: sentence.slice(0, value.startIndex),
+					startIndex: 0,
+					endIndex: value.startIndex,
+					isKeyword: false,
+				});
+
+				return;
+			}
+
+			// 鎭板ソ浣嶄簬绗竴涓�
+			if (index === 0) {
+				result.push({
+					...value,
+					isKeyword: true,
+				});
+				return;
+			}
+
+			// 涓棿鏈夐潪鍏抽敭璇�
+			if (array[index - 1].endIndex !== value.startIndex) {
+				result.push({
+					partStr: sentence.slice(array[index - 1].endIndex, value.startIndex),
+					startIndex: array[index - 1].endIndex,
+					endIndex: value.startIndex,
+					isKeyword: false,
+				});
+
+				result.push({
+					...value,
+					isKeyword: true,
+				});
+			}
+		});
+
+		sentenceMatchMap[sentence] = result;
+	}
+
+	return sentenceMatchMap;
+};
+
+const metricsNamesPromise = new Promise(async (resolve, reject) => {
+	const metricNames = (await getMetricsNames())?.values ?? [];
+	resolve(metricNames);
+});
 
 //#region ======================  楂樹寒鎸囨爣鐐瑰嚮======================
 const infoDetailIsShow = ref(false);
@@ -194,6 +435,44 @@
 	detailMapRow.value = row;
 	infoDetailIsShow.value = true;
 };
+//#endregion
+
+//#region ====================== 褰撳墠搴旂敤鍦烘櫙 ======================
+const currentGroupTypeIsShow = computed(() => !!activeGroupType.value);
+const groupTypeClick = (item) => {
+	activeGroupType.value = item;
+	inputRef.value.focus();
+	commonPhrasesRef.value.getCommonPhrases();
+};
+
+// 鍏抽棴褰撳墠 groupType 闈㈡澘
+const closeCurrentGroupType = () => {
+	activeGroupType.value = null;
+};
+//#endregion
+
+//#region ====================== 甯哥敤璇姛鑳� ======================
+const commonPhrasesRef = ref(null);
+const commonPhrasesClick = () => {
+	isShowPhrase.value = true;
+};
+onClickOutside(commonPhrasesRef, () => {
+	isShowPhrase.value = false;
+});
+const updateCommonChatInput = (val) => {
+	inputValue.value = val;
+	isShowPhrase.value = false;
+};
+watch(setCommonQuestionInfo, (val) => {
+	if (!props.isHome) {
+		let obj = {
+			id: val?.historyId,
+			question: val?.content.values,
+		};
+		commonPhrasesRef.value.commonChatByUser(obj);
+		isShowPhrase.value = true;
+	}
+});
 //#endregion
 </script>
 <style scoped lang="scss">
@@ -246,6 +525,21 @@
 	border-color: #1c86ff;
 	color: #fff;
 }
+
+.input-shadow {
+	-webkit-box-shadow: 0 0 0 1px transparent, 0 3px 16px 0 #dee0f3;
+	box-shadow: 0 0 0 1px transparent, 0 3px 16px 0 #dee0f3;
+}
+
+.input-border {
+	border: 1px solid #00000030;
+
+	-webkit-transition: border-color 0.1s ease-in-out, -webkit-box-shadow 0.1s ease-in-out;
+	transition: border-color 0.1s ease-in-out, -webkit-box-shadow 0.1s ease-in-out;
+	-o-transition: border-color 0.1s ease-in-out, box-shadow 0.1s ease-in-out;
+	transition: border-color 0.1s ease-in-out, box-shadow 0.1s ease-in-out;
+	transition: border-color 0.1s ease-in-out, box-shadow 0.1s ease-in-out, -webkit-box-shadow 0.1s ease-in-out;
+}
 .playInput {
 	align-items: flex-start;
 	width: 760px;
@@ -258,15 +552,7 @@
 	align-items: flex-end;
 	-webkit-box-sizing: border-box;
 	box-sizing: border-box;
-	border-radius: 22px;
-	border: 1px solid #00000030;
-	-webkit-box-shadow: 0 0 0 1px transparent, 0 3px 16px 0 #dee0f3;
-	box-shadow: 0 0 0 1px transparent, 0 3px 16px 0 #dee0f3;
-	-webkit-transition: border-color 0.1s ease-in-out, -webkit-box-shadow 0.1s ease-in-out;
-	transition: border-color 0.1s ease-in-out, -webkit-box-shadow 0.1s ease-in-out;
-	-o-transition: border-color 0.1s ease-in-out, box-shadow 0.1s ease-in-out;
-	transition: border-color 0.1s ease-in-out, box-shadow 0.1s ease-in-out;
-	transition: border-color 0.1s ease-in-out, box-shadow 0.1s ease-in-out, -webkit-box-shadow 0.1s ease-in-out;
+
 	background-color: #fff;
 	.assembly {
 		position: relative;

--
Gitblit v1.9.3