kagg886 1 miesiąc temu
rodzic
commit
bf72c466e2
4 zmienionych plików z 164 dodań i 9 usunięć
  1. 1 0
      package.json
  2. 16 9
      src/api/assist/type.ts
  3. 120 0
      src/views/assistant/index.vue
  4. 27 0
      yarn.lock

+ 1 - 0
package.json

@@ -26,6 +26,7 @@
     "@antv/g2plot": "2.4.20",
     "@element-plus/icons-vue": "2.0.9",
     "@guolao/vue-monaco-editor": "^1.5.5",
+    "@vueuse/core": "9.0.1",
     "axios": "0.26.0",
     "clipboard": "2.0.11",
     "codemirror": "5.65.16",

+ 16 - 9
src/api/assist/type.ts

@@ -24,21 +24,29 @@ export type Message = {
 	content: string
 	timestamp: number
 
-
 	//仅role为tool时需要
-	name?: string,
+	name?: string
 	tool_call_id?: string
 
 	//仅role为assistant时需要
 	like?: boolean
 	tool_calls?: FunctionCall[]
+
+	//仅role为user时需要
+	files?: {
+		size: number
+		path: string
+		name: string
+		type: string
+		full_path: string
+	}
 }
 
 export type FunctionCall = {
-	id: string,
-	type: 'function',
+	id: string
+	type: 'function'
 	function: {
-		name: string,
+		name: string
 		arguments: string
 	}
 }
@@ -64,7 +72,7 @@ export type Text = ChatResponseBase<'message'> & {
 export type ToolCallRequest = ChatResponseBase<'toolcall'> & {
 	request: {
 		id: string
-		name: string,
+		name: string
 		data: string
 	}
 }
@@ -72,7 +80,7 @@ export type ToolCallRequest = ChatResponseBase<'toolcall'> & {
 export type ToolCallResponse = ChatResponseBase<'toolres'> & {
 	response: {
 		id: string
-		name: string,
+		name: string
 		data: string
 	}
 }
@@ -156,7 +164,6 @@ export type LmConfigGetParams = {
 	id: number
 }
 
-
 export type LmSession = {
 	session_id: number
 	title: string
@@ -188,7 +195,7 @@ export type Prompt = {
 	id: number
 	title: string
 	prompt: string
-	placeholder?:string
+	placeholder?: string
 	createdAt?: string // 创建时间
 	updatedAt?: string // 更新时间
 	createdBy?: number // 创建者ID

+ 120 - 0
src/views/assistant/index.vue

@@ -19,6 +19,7 @@ import {
 	Promotion,
 	VideoPause,
 	CopyDocument,
+	Plus,
 } from '@element-plus/icons-vue'
 import { MarkdownPlugin } from '/@/components/markdown/type/markdown'
 import EChartsPlugin from '/@/components/markdown/plugins/echarts'
@@ -32,6 +33,7 @@ import { Setting as EleSetting } from '@element-plus/icons-vue'
 import { useRouter } from 'vue-router'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import StructDataPlugin from '/@/components/markdown/plugins/struct-data'
+import { useFileDialog } from '@vueuse/core'
 
 const plugins: Array<MarkdownPlugin<any>> = [EChartsPlugin(), ToolsLoadingPlugin(), TablePlugin(), StructDataPlugin()]
 
@@ -41,8 +43,35 @@ const messages = ref<Message[]>([])
 
 // 输入框内容
 const inputMessage = ref('')
+const {open,reset,files: attachments} = useFileDialog({
+	multiple: true,
+})
 const messagesContainer = ref<HTMLElement>()
 
+// 附件管理:使用本地列表以支持单个移除
+const selectedFiles = ref<File[]>([])
+
+// 监听文件选择并合并到本地列表(按 name+size+lastModified 去重)
+watch(
+	attachments,
+	(newFiles) => {
+		const incoming = Array.from(newFiles ?? [])
+		const merged = [...selectedFiles.value, ...incoming]
+		const map = new Map<string, File>()
+		for (const f of merged) {
+			const key = `${f.name}_${f.size}_${(f as any).lastModified ?? ''}`
+			if (!map.has(key)) map.set(key, f)
+		}
+		selectedFiles.value = Array.from(map.values())
+	},
+	{ immediate: true }
+)
+
+const removeAttachment = (index: number) => {
+	if (index < 0 || index >= selectedFiles.value.length) return
+	selectedFiles.value.splice(index, 1)
+}
+
 // // 选中的工具和模型
 // const selectedTool = ref([])
 // // 工具选择
@@ -1114,9 +1143,36 @@ const isBlank = (str: string) => {
 				<div class="messages-spacer"></div>
 			</div>
 
+			<!-- 附件栏(内嵌到输入框上方) -->
+
 			<div class="input-container" v-if="activeConversationId !== -1">
 				<!-- 大输入框容器 -->
 				<div class="large-input-container">
+					<!-- 附件栏:紧贴输入框上方,位于容器内部 -->
+					<div class="attachments-inline">
+						<el-scrollbar>
+							<div class="attachments-inline-scroll">
+								<div
+									v-for="(file, fIdx) in selectedFiles"
+									:key="file.name + '_' + file.size + '_' + (file as any).lastModified"
+									class="attachment-card"
+									title=""
+								>
+									<button class="control-btn attachment-item" :title="`${file.name}`">
+										<el-icon :size="10"><CopyDocument /></el-icon>
+										<span class="attachment-name">{{ file.name }}</span>
+									</button>
+									<button class="remove-attachment-icon" @click="removeAttachment(fIdx)">
+										<el-icon><Close /></el-icon>
+									</button>
+								</div>
+							</div>
+						</el-scrollbar>
+						<button class="control-btn add-attachment-btn" @click="open">
+							<el-icon :size="10"><Plus /></el-icon>
+							<span>添加附件</span>
+						</button>
+					</div>
 					<!-- 输入框 -->
 					<textarea
 						v-model="inputMessage"
@@ -1873,6 +1929,70 @@ const isBlank = (str: string) => {
 	box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
 }
 
+/* 附件栏(内嵌)样式 */
+.attachments-inline {
+	width: 100%;
+	padding: 8px;
+	display: flex;
+	align-items: center;
+	gap: 8px;
+}
+
+.attachments-inline-scroll {
+	flex: 1;
+	//overflow-x: auto;
+	display: flex;
+	align-items: center;
+	gap: 8px;
+	padding: 6px;
+}
+
+.attachment-card {
+	position: relative;
+	display: inline-flex;
+	align-items: center;
+	gap: 0;
+}
+
+.attachment-item {
+	/* 对齐下方控制按钮的尺寸与风格 */
+	max-width: 260px;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+
+	font-size: 10px!important;
+}
+
+.attachment-name {
+	max-width: 200px;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+
+.add-attachment-btn {
+	flex-shrink: 0;
+	font-size: 10px!important;
+}
+
+.remove-attachment-icon {
+	position: absolute;
+	top: -6px;
+	right: -6px;
+	width: 18px;
+	height: 18px;
+	border-radius: 50%;
+	border: 1px solid var(--el-border-color-light);
+	background: var(--el-bg-color);
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	cursor: pointer;
+	color: var(--el-text-color-regular);
+	padding: 0;
+}
+
 .large-input-container {
 	position: relative;
 	width: 100%;

+ 27 - 0
yarn.lock

@@ -605,6 +605,11 @@
   resolved "https://registry.npmmirror.com/@types/sortablejs/-/sortablejs-1.10.7.tgz"
   integrity sha512-lGCwwgpj8zW/ZmaueoPVSP7nnc9t8VqVWXS+ASX3eoUUENmiazv0rlXyTRludXzuX9ALjPsMqBu85TgJNWbTOg==
 
+"@types/web-bluetooth@^0.0.15":
+  version "0.0.15"
+  resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.15.tgz#d60330046a6ed8a13b4a53df3813c44942ebdf72"
+  integrity sha512-w7hEHXnPMEZ+4nGKl/KDRVpxkwYxYExuHOYXyzIzCDzEZ9ZCGMAewulr9IqJu2LR4N37fcnb1XVeuZ09qgOxhA==
+
 "@types/web-bluetooth@^0.0.16":
   version "0.0.16"
   resolved "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz"
@@ -998,6 +1003,16 @@
   resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.3.tgz"
   integrity sha512-Jp2v8nylKBT+PlOUjun2Wp/f++TfJVFjshLzNtJDdmFJabJa7noGMncqXRM1vXGX+Yo2V7WykQFNxusSim8SCA==
 
+"@vueuse/core@9.0.1":
+  version "9.0.1"
+  resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-9.0.1.tgz#28584a0176d6cc7b2d739f3d986d18a640cb31db"
+  integrity sha512-1ChG/wR5Rz8R0Rixu22+PGywqE2z0EE2HRDCin2btCOZGmULJZBbos/JZkLi7ehJ/IXhfwmPBrNVBuUH8jlQKg==
+  dependencies:
+    "@types/web-bluetooth" "^0.0.15"
+    "@vueuse/metadata" "9.0.1"
+    "@vueuse/shared" "9.0.1"
+    vue-demi "*"
+
 "@vueuse/core@^9.1.0":
   version "9.13.0"
   resolved "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz"
@@ -1008,11 +1023,23 @@
     "@vueuse/shared" "9.13.0"
     vue-demi "*"
 
+"@vueuse/metadata@9.0.1":
+  version "9.0.1"
+  resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-9.0.1.tgz#99a1364029383a5dff7724a56e61066876a9c754"
+  integrity sha512-VJqMHDCvdAUYesxz5rjUAglbcrJzbKaENpM3WVtAljiM+FqUVB8PgsfrDaq6ZRQT5fBAcZhPoWNFSWxIPTfJww==
+
 "@vueuse/metadata@9.13.0":
   version "9.13.0"
   resolved "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz"
   integrity sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==
 
+"@vueuse/shared@9.0.1":
+  version "9.0.1"
+  resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-9.0.1.tgz#0ebc866770217ba4970c31140e5ecb74142a55a4"
+  integrity sha512-Szk39QSk0/AAy8GH5J6A3MJ1MMUWCqiDl6l9cA0NahNTHZVW7YYxTPod797vhjYtEkDG7oa8HR7/xI9oPTSspg==
+  dependencies:
+    vue-demi "*"
+
 "@vueuse/shared@9.13.0":
   version "9.13.0"
   resolved "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz"