浏览代码

美化底部按钮,增加清空所有对话按钮

kagg886 2 月之前
父节点
当前提交
1c814f9e77
共有 1 个文件被更改,包括 209 次插入104 次删除
  1. 209 104
      src/views/assistant/index.vue

+ 209 - 104
src/views/assistant/index.vue

@@ -1,7 +1,25 @@
 <script setup lang="ts">
 import { ref, nextTick, onMounted, computed, onUnmounted, reactive, watch, isReactive } from 'vue'
 import { Local } from '/@/utils/storage'
-import { User, ChatDotRound, Delete, Edit, Check, Close, ArrowDown, Star, StarFilled, Search, Download, MoreFilled } from '@element-plus/icons-vue'
+import {
+	User,
+	ChatDotRound,
+	Delete,
+	Edit,
+	Check,
+	Close,
+	ArrowDown,
+	Star,
+	StarFilled,
+	Search,
+	Download,
+	MoreFilled,
+	Setting,
+	Document,
+	Loading,
+	Promotion,
+	VideoPause,
+} from '@element-plus/icons-vue'
 import { MarkdownPlugin } from '/@/components/markdown/type/markdown'
 import EChartsPlugin from '/@/components/markdown/plugins/echarts'
 import ToolsLoadingPlugin from '/@/components/markdown/plugins/tools-loading'
@@ -455,6 +473,31 @@ const { loading: loadingDeleteConversation, doLoading: deleteConversation } = us
 	await doLoadConversations()
 })
 
+const {loading: loadingDeleteAllConversion,doLoading: deleteAllConversion } = useLoading(async () => {
+	const confirm = await ElMessageBox.confirm(`确定要删除所有对话吗?此操作不可恢复!`, "警告", {
+		confirmButtonText: "确定",
+		cancelButtonText: "取消",
+		type: "error",
+	})
+
+	if (confirm !== 'confirm') {
+		return
+	}
+
+	const res = await assist.session
+		.del(conversations.value.map((it)=>it.session_id))
+		.then(() => true)
+		.catch(() => false)
+		.finally(() => (currentDeletingConversation.value = -1))
+	if (!res) {
+		return
+	}
+	ElMessage.success('删除成功')
+	activeConversationId.value = undefined
+	messages.value = []
+	conversations.value = []
+})
+
 // 创建新对话
 const { loading: creatingConversation, doLoading: createConversationAndSetItActive } = useLoading(async () => {
 	try {
@@ -693,11 +736,14 @@ const { loading: exportConversationLoading, doLoading: exportConversation } = us
 							<el-dropdown-item @click="redirectToModelManager">
 								<span class="settings-label">模型管理</span>
 							</el-dropdown-item>
+							<el-dropdown-item @click="deleteAllConversion">
+								<span class="settings-label">清空所有对话</span>
+							</el-dropdown-item>
 						</el-dropdown-menu>
 					</template>
 				</el-dropdown>
 			</div>
-			<el-scrollbar class="conversation-list" v-loading="loadConversations">
+			<el-scrollbar class="conversation-list" v-loading="loadConversations || loadingDeleteAllConversion">
 				<div
 					v-for="conv in conversations"
 					:key="conv.session_id"
@@ -729,7 +775,7 @@ const { loading: exportConversationLoading, doLoading: exportConversation } = us
 								<template #dropdown>
 									<el-dropdown-menu>
 										<el-dropdown-item @click="exportConversation(conv.session_id)">
-											<el-button v-if="conv.session_id === exportId && exportConversationLoading" size="small" loading circle plain/>
+											<el-button v-if="conv.session_id === exportId && exportConversationLoading" size="small" loading circle plain />
 											<el-icon v-else>
 												<Download />
 											</el-icon>
@@ -930,75 +976,67 @@ const { loading: exportConversationLoading, doLoading: exportConversation } = us
 			</div>
 
 			<div class="input-container" v-if="activeConversationId !== -1">
-				<!-- 工具和模型选择行 -->
-				<div class="selection-row">
-<!--					&lt;!&ndash; 工具选择 &ndash;&gt;-->
-<!--					<div class="tool-selector">-->
-<!--						<el-cascader-->
-<!--							v-model="selectedTool"-->
-<!--							:options="toolOptions"-->
-<!--							:show-all-levels="false"-->
-<!--							:props="{ multiple: true }"-->
-<!--							collapse-tags-->
-<!--							collapse-tags-tooltip-->
-<!--							clearable-->
-<!--							size="small"-->
-<!--							style="width: 200px"-->
-<!--						/>-->
-<!--					</div>-->
-
-					<!-- 模型选择 -->
-					<div class="model-selector">
-						<el-select :loading="loadingModels" v-model="selectedModel" placeholder="选择模型" size="small" style="width: 200px">
-							<el-option v-for="item in modelOptions" :key="item.id" :value="item.id" :label="item.modelName" />
-						</el-select>
-					</div>
-
-					<!-- 提示词输入框 -->
-					<div class="prompt-selector">
-						<el-input v-model="prompt" placeholder="点击设置提示词..." size="small" style="width: 200px" readonly @click="openPromptDialog = true" />
-					</div>
-				</div>
-
-				<!-- 输入框和按钮行 -->
-				<div class="input-row">
+				<!-- 大输入框容器 -->
+				<div class="large-input-container">
 					<!-- 输入框 -->
-					<div class="message-input-wrapper">
-						<el-input
-							v-model="inputMessage"
-							type="textarea"
-							placeholder="请输入您的问题..."
-							:rows="2"
-							resize="none"
-							@keydown.enter.ctrl="sendMessage"
-							@keydown.enter.meta="sendMessage"
-							:disabled="isConversationActive"
-						/>
-					</div>
+					<textarea
+						v-model="inputMessage"
+						class="large-textarea"
+						placeholder="请输入您的问题..."
+						@keydown.enter.ctrl="sendMessage"
+						@keydown.enter.meta="sendMessage"
+						:disabled="isConversationActive"
+						rows="3"
+					></textarea>
+
+					<!-- 内嵌按钮区域 -->
+					<div class="embedded-controls">
+						<!-- 左下角按钮组 -->
+						<div class="left-controls">
+							<!-- 模型选择按钮 -->
+							<el-dropdown trigger="click" placement="top-start">
+								<button class="control-btn model-btn">
+									<el-icon><Setting /></el-icon>
+									<span>模型</span>
+								</button>
+								<template #dropdown>
+									<el-dropdown-menu>
+										<el-dropdown-item
+											v-for="item in modelOptions"
+											:key="item.id"
+											@click="selectedModel = item.id"
+											:class="{ 'is-selected': selectedModel === item.id }"
+										>
+											{{ item.modelName }}
+										</el-dropdown-item>
+									</el-dropdown-menu>
+								</template>
+							</el-dropdown>
 
-					<!-- 按钮组 -->
-					<div class="button-group">
-						<el-button
-							v-show="messages.length !== 0"
-							type="warning"
-							size="small"
-							@click="clearMessage"
-							style="margin-left: 12px"
-							:loading="loadingClearMessage"
-						>
-							清空
-						</el-button>
-						<el-button
-							v-if="!isConversationActive"
-							type="primary"
-							size="small"
-							@click="sendMessage"
-							@keyup.ctrl.enter="sendMessage"
-							:disabled="canSendMessage"
-						>
-							发送
-						</el-button>
-						<el-button v-else type="danger" size="small" @click="stopConversation"> 终止</el-button>
+							<!-- 提示词选择按钮 -->
+							<button class="control-btn prompt-btn" @click="openPromptDialog = true">
+								<el-icon><Document /></el-icon>
+								<span v-if="prompt">提示词:{{ prompt.length }}字</span>
+								<span v-else>提示词</span>
+							</button>
+						</div>
+
+						<!-- 右下角按钮组 -->
+						<div class="right-controls">
+							<!-- 清空按钮 -->
+							<button v-show="messages.length !== 0" class="control-btn clear-btn" @click="clearMessage" :disabled="loadingClearMessage">
+								<el-icon v-if="loadingClearMessage"><Loading /></el-icon>
+								<el-icon v-else><Delete /></el-icon>
+							</button>
+
+							<!-- 发送按钮 -->
+							<button v-if="!isConversationActive" class="control-btn send-btn" @click="sendMessage" :disabled="canSendMessage">
+								<el-icon><Promotion /></el-icon>
+							</button>
+							<button v-else class="control-btn stop-btn" @click="stopConversation">
+								<el-icon><VideoPause /></el-icon>
+							</button>
+						</div>
 					</div>
 				</div>
 			</div>
@@ -1633,6 +1671,7 @@ const { loading: exportConversationLoading, doLoading: exportConversation } = us
 	}
 }
 
+/* 新的输入容器样式 */
 .input-container {
 	position: absolute;
 	width: 90%;
@@ -1641,63 +1680,126 @@ const { loading: exportConversationLoading, doLoading: exportConversation } = us
 	transform: translate(-50%, 0);
 	background: var(--el-bg-color);
 	border: 1px solid var(--el-border-color-light);
-	border-radius: 12px;
-	padding: 16px;
+	border-radius: 8px;
+	padding: 0;
 	box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
-	display: flex;
-	flex-direction: column;
-	gap: 12px;
 }
 
-.selection-row {
+.large-input-container {
+	position: relative;
+	width: 100%;
+}
+
+.large-textarea {
+	width: 100%;
+	min-height: 120px;
+	padding: 16px 16px 60px 16px;
+	border: none;
+	outline: none;
+	resize: none;
+	font-size: 14px;
+	line-height: 1.5;
+	background: transparent;
+	color: var(--el-text-color-primary);
+	border-radius: 8px;
+	font-family: inherit;
+}
+
+.large-textarea::placeholder {
+	color: var(--el-text-color-placeholder);
+}
+
+.large-textarea:disabled {
+	color: var(--el-text-color-disabled);
+	cursor: not-allowed;
+}
+
+.embedded-controls {
+	position: absolute;
+	bottom: 0;
+	left: 0;
+	right: 0;
 	display: flex;
+	justify-content: space-between;
 	align-items: center;
-	gap: 12px;
-	flex-wrap: wrap;
+	padding: 12px 16px;
+	border-top: 1px solid var(--el-border-color-lighter);
+	background: var(--el-fill-color-extra-light);
+	border-radius: 0 0 8px 8px;
 }
 
-.tool-selector,
-.model-selector,
-.prompt-selector {
-	flex-shrink: 0;
+.left-controls,
+.right-controls {
+	display: flex;
+	align-items: center;
+	gap: 8px;
 }
 
-.input-row {
+.control-btn {
 	display: flex;
-	align-items: flex-end;
-	gap: 12px;
+	align-items: center;
+	gap: 6px;
+	padding: 6px 12px;
+	border: 1px solid var(--el-border-color-light);
+	border-radius: 6px;
+	background: var(--el-bg-color);
+	color: var(--el-text-color-regular);
+	font-size: 12px;
+	cursor: pointer;
+	transition: all 0.2s ease;
+	white-space: nowrap;
 }
 
-.message-input-wrapper {
-	flex: 1;
-	min-width: 0;
+.control-btn:hover:not(:disabled) {
+	background: var(--el-fill-color-light);
+	border-color: var(--el-color-primary-light-7);
+	color: var(--el-color-primary);
 }
 
-.message-input-wrapper :deep(.el-textarea__inner) {
-	border-radius: 8px;
-	resize: none;
+.control-btn:disabled {
+	opacity: 0.5;
+	cursor: not-allowed;
+}
+
+.control-btn .el-icon {
 	font-size: 14px;
-	line-height: 1.4;
 }
 
-.button-group {
-	display: flex;
-	flex-direction: column;
-	gap: 6px;
-	flex-shrink: 0;
+/* 特定按钮样式 */
+.send-btn {
+	background: var(--el-color-primary);
+	color: white;
+	border-color: var(--el-color-primary);
 }
 
-.button-group .el-button {
-	min-width: 60px;
+.send-btn:hover:not(:disabled) {
+	background: var(--el-color-primary-light-3);
+	border-color: var(--el-color-primary-light-3);
 }
 
-/* 提示词输入框样式 */
-.prompt-selector .el-input {
-	cursor: pointer;
+.stop-btn {
+	background: var(--el-color-danger);
+	color: white;
+	border-color: var(--el-color-danger);
 }
 
-.prompt-selector .el-input__inner {
-	cursor: pointer;
+.stop-btn:hover:not(:disabled) {
+	background: var(--el-color-danger-light-3);
+	border-color: var(--el-color-danger-light-3);
+}
+
+.clear-btn:hover:not(:disabled) {
+	background: var(--el-color-warning-light-9);
+	border-color: var(--el-color-warning-light-7);
+	color: var(--el-color-warning);
+}
+
+/* 下拉菜单样式 */
+:deep(.el-dropdown-menu) {
+	.el-dropdown-menu__item.is-selected {
+		background: var(--el-color-primary-light-9);
+		color: var(--el-color-primary);
+	}
 }
 
 /* 对话框样式 */
@@ -1806,6 +1908,9 @@ const { loading: exportConversationLoading, doLoading: exportConversation } = us
 
 /* 收藏面板底部栏样式 */
 .bookmark-footer {
+	border-radius: 8px;
+	padding: 16px;
+
 	.bookmark-controls {
 		display: flex;
 		justify-content: space-between;