فهرست منبع

add agent sider and chat sider

Gudge-ZL 2 سال پیش
والد
کامیت
bc7e64f9fc

+ 0 - 6
.idea/inspectionProfiles/Project_Default.xml

@@ -1,6 +0,0 @@
-<component name="InspectionProjectProfileManager">
-  <profile version="1.0">
-    <option name="myName" value="Project Default" />
-    <inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
-  </profile>
-</component>

+ 2 - 0
SEChat-frontend/src/locales/zh-CN.ts

@@ -31,6 +31,8 @@ export default {
   chat: {
     newChatButton: '新建聊天',
     newChatTitle: '新建聊天',
+		newAgentButton: '新建助手',
+		newAgentTitle: '新建助手',
     placeholder: '来说点什么吧...(Shift + Enter = 换行,"/" 触发提示词)',
     placeholderMobile: '来说点什么...',
     copy: '复制',

+ 3 - 2
SEChat-frontend/src/store/modules/app/helper.ts

@@ -23,14 +23,15 @@ const languageMap: { [key: string]: Language } = {
 }
 
 export interface AppState {
-  siderCollapsed: boolean
+  agentSiderCollapsed: boolean
+  chatSiderCollapsed: boolean
   theme: Theme
   language: Language
 }
 
 export function defaultSetting(): AppState {
   const language = languageMap[navigator.language]
-  return { siderCollapsed: false, theme: 'light', language }
+  return { chatSiderCollapsed: false, agentSiderCollapsed: false, theme: 'light', language }
 }
 
 export function getLocalSetting(): AppState {

+ 7 - 2
SEChat-frontend/src/store/modules/app/index.ts

@@ -6,8 +6,13 @@ import { store } from '@/store/helper'
 export const useAppStore = defineStore('app-store', {
   state: (): AppState => getLocalSetting(),
   actions: {
-    setSiderCollapsed(collapsed: boolean) {
-      this.siderCollapsed = collapsed
+		setChatSiderCollapsed(collapsed: boolean) {
+			this.chatSiderCollapsed = collapsed
+			this.recordState()
+		},
+
+    setAgentSiderCollapsed(collapsed: boolean) {
+      this.agentSiderCollapsed = collapsed
       this.recordState()
     },
 

+ 22 - 12
SEChat-frontend/src/views/chat/layout/Layout.vue

@@ -2,7 +2,8 @@
 import { computed } from 'vue'
 import { NLayout, NLayoutContent } from 'naive-ui'
 import { useRouter } from 'vue-router'
-import Sider from './sider/index.vue'
+import ChatSider from './sider/Chat.vue'
+import AgentSider from './sider/Agent.vue'
 import Permission from './Permission.vue'
 import { useBasicLayout } from '@/hooks/useBasicLayout'
 import { useAppStore, useAuthStore, useChatStore } from '@/store'
@@ -16,7 +17,8 @@ router.replace({ name: 'Chat', params: { uuid: chatStore.active } })
 
 const { isMobile } = useBasicLayout()
 
-const collapsed = computed(() => appStore.siderCollapsed)
+const chatCollapsed = computed(() => appStore.chatSiderCollapsed)
+const agentCollapsed = computed(() => appStore.agentSiderCollapsed)
 
 const needPermission = computed(() => !!authStore.session?.auth && !authStore.token)
 
@@ -26,25 +28,33 @@ const getMobileClass = computed(() => {
   return ['border', 'rounded-md', 'shadow-md', 'dark:border-neutral-800']
 })
 
-const getContainerClass = computed(() => {
+const getChatContainerClass = computed(() => {
   return [
     'h-full',
-    { 'pl-[260px]': !isMobile.value && !collapsed.value },
   ]
 })
+
+const getAgentContainerClass = computed(() => {
+	return [
+		'h-full',
+	]
+})
 </script>
 
 <template>
   <div class="h-full dark:bg-[#24272e] transition-all" :class="[isMobile ? 'p-0' : 'p-4']">
     <div class="h-full overflow-hidden" :class="getMobileClass">
-      <NLayout class="z-40 transition" :class="getContainerClass" has-sider>
-        <Sider />
-        <NLayoutContent class="h-full">
-          <RouterView v-slot="{ Component, route }">
-            <component :is="Component" :key="route.fullPath" />
-          </RouterView>
-        </NLayoutContent>
-      </NLayout>
+			<NLayout class="z-40 transition" :class="getChatContainerClass" has-sider sider-placement="left">
+				<ChatSider />
+				<NLayout class="z-40 transition" :class="getAgentContainerClass" has-sider sider-placement="right">
+					<NLayoutContent class="h-full">
+						<RouterView v-slot="{ Component, route }">
+							<component :is="Component" :key="route.fullPath" />
+						</RouterView>
+					</NLayoutContent>
+					<AgentSider />
+				</NLayout>
+			</NLayout>
     </div>
     <Permission :visible="needPermission" />
   </div>

+ 102 - 0
SEChat-frontend/src/views/chat/layout/sider/Agent.vue

@@ -0,0 +1,102 @@
+<script setup lang='ts'>
+import type { CSSProperties } from 'vue'
+import { computed, ref, watch } from 'vue'
+import { NButton, NLayoutSider, useDialog } from 'naive-ui'
+import List from './List.vue'
+import { useAppStore, useChatStore } from '@/store'
+import { useBasicLayout } from '@/hooks/useBasicLayout'
+import { t } from '@/locales'
+
+const appStore = useAppStore()
+const chatStore = useChatStore()
+
+const dialog = useDialog()
+
+const { isMobile } = useBasicLayout()
+const show = ref(false)
+
+const collapsed = computed(() => appStore.agentSiderCollapsed)
+
+function handleAdd() {
+  //chatStore.addHistory({ title: t('chat.newAgentTitle'), uuid: Date.now(), isEdit: false })
+  //if (isMobile.value)
+    //appStore.setAgentSiderCollapsed(true)
+}
+
+function handleUpdateCollapsed() {
+  appStore.setAgentSiderCollapsed(!collapsed.value)
+}
+
+function handleClearAll() {
+  dialog.warning({
+    title: t('chat.deleteMessage'),
+    content: t('chat.clearHistoryConfirm'),
+    positiveText: t('common.yes'),
+    negativeText: t('common.no'),
+    onPositiveClick: () => {
+      chatStore.clearHistory()
+      if (isMobile.value)
+        appStore.setAgentSiderCollapsed(true)
+    },
+  })
+}
+
+const getMobileClass = computed<CSSProperties>(() => {
+  if (isMobile.value) {
+    return {
+      position: 'fixed',
+      zIndex: 50,
+    }
+  }
+  return {}
+})
+
+const mobileSafeArea = computed(() => {
+  if (isMobile.value) {
+    return {
+      paddingBottom: 'env(safe-area-inset-bottom)',
+    }
+  }
+  return {}
+})
+
+watch(
+  isMobile,
+  (val) => {
+    appStore.setAgentSiderCollapsed(val)
+  },
+  {
+    immediate: true,
+    flush: 'post',
+  },
+)
+</script>
+
+<template>
+  <NLayoutSider
+    :collapsed="collapsed"
+    :collapsed-width="0"
+    :width="260"
+    :show-trigger="isMobile ? false : 'arrow-circle'"
+    collapse-mode="transform"
+    bordered
+    :style="getMobileClass"
+    @update-collapsed="handleUpdateCollapsed"
+  >
+    <div class="flex flex-col h-full" :style="mobileSafeArea">
+      <main class="flex flex-col flex-1 min-h-0">
+        <div class="p-4">
+          <NButton dashed block @click="handleAdd">
+            {{ $t('chat.newAgentButton') }}
+          </NButton>
+        </div>
+        <div class="flex-1 min-h-0 pb-4 overflow-hidden">
+          <List style="display: none" />
+        </div>
+      </main>
+    </div>
+  </NLayoutSider>
+  <template v-if="isMobile">
+    <div v-show="!collapsed" class="fixed inset-0 z-40 w-full h-full bg-black/40" @click="handleUpdateCollapsed" />
+  </template>
+</template>

+ 116 - 0
SEChat-frontend/src/views/chat/layout/sider/Chat.vue

@@ -0,0 +1,116 @@
+<script setup lang='ts'>
+import type { CSSProperties } from 'vue'
+import { computed, ref, watch } from 'vue'
+import { NButton, NLayoutSider, useDialog } from 'naive-ui'
+import List from './List.vue'
+import Footer from './Footer.vue'
+import { useAppStore, useChatStore } from '@/store'
+import { useBasicLayout } from '@/hooks/useBasicLayout'
+import { PromptStore, SvgIcon } from '@/components/common'
+import { t } from '@/locales'
+
+const appStore = useAppStore()
+const chatStore = useChatStore()
+
+const dialog = useDialog()
+
+const { isMobile } = useBasicLayout()
+const show = ref(false)
+
+const collapsed = computed(() => appStore.chatSiderCollapsed)
+
+function handleAdd() {
+  chatStore.addHistory({ title: t('chat.newChatTitle'), uuid: Date.now(), isEdit: false })
+  if (isMobile.value)
+    appStore.setChatSiderCollapsed(true)
+}
+
+function handleUpdateCollapsed() {
+  appStore.setChatSiderCollapsed(!collapsed.value)
+}
+
+function handleClearAll() {
+  dialog.warning({
+    title: t('chat.deleteMessage'),
+    content: t('chat.clearHistoryConfirm'),
+    positiveText: t('common.yes'),
+    negativeText: t('common.no'),
+    onPositiveClick: () => {
+      chatStore.clearHistory()
+      if (isMobile.value)
+        appStore.setChatSiderCollapsed(true)
+    },
+  })
+}
+
+const getMobileClass = computed<CSSProperties>(() => {
+  if (isMobile.value) {
+    return {
+      position: 'fixed',
+      zIndex: 50,
+    }
+  }
+  return {}
+})
+
+const mobileSafeArea = computed(() => {
+  if (isMobile.value) {
+    return {
+      paddingBottom: 'env(safe-area-inset-bottom)',
+    }
+  }
+  return {}
+})
+
+watch(
+  isMobile,
+  (val) => {
+    appStore.setChatSiderCollapsed(val)
+  },
+  {
+    immediate: true,
+    flush: 'post',
+  },
+)
+</script>
+
+<template>
+  <NLayoutSider
+    :collapsed="collapsed"
+    :collapsed-width="0"
+    :width="260"
+    :show-trigger="isMobile ? false : 'arrow-circle'"
+    collapse-mode="transform"
+    bordered
+    :style="getMobileClass"
+    @update-collapsed="handleUpdateCollapsed"
+  >
+    <div class="flex flex-col h-full" :style="mobileSafeArea">
+      <main class="flex flex-col flex-1 min-h-0">
+        <div class="p-4">
+          <NButton dashed block @click="handleAdd">
+            {{ $t('chat.newChatButton') }}
+          </NButton>
+        </div>
+        <div class="flex-1 min-h-0 pb-4 overflow-hidden">
+          <List />
+        </div>
+        <div class="flex items-center p-4 space-x-4">
+          <div class="flex-1">
+            <NButton block @click="show = true">
+              {{ $t('store.siderButton') }}
+            </NButton>
+          </div>
+          <NButton @click="handleClearAll">
+            <SvgIcon icon="ri:close-circle-line" />
+          </NButton>
+        </div>
+      </main>
+      <Footer />
+    </div>
+  </NLayoutSider>
+  <template v-if="isMobile">
+    <div v-show="!collapsed" class="fixed inset-0 z-40 w-full h-full bg-black/40" @click="handleUpdateCollapsed" />
+  </template>
+  <PromptStore v-model:visible="show" />
+</template>

+ 0 - 1
SEChat-frontend/src/views/chat/layout/sider/index.vue

@@ -81,7 +81,6 @@ watch(
     :width="260"
     :show-trigger="isMobile ? false : 'arrow-circle'"
     collapse-mode="transform"
-    position="absolute"
     bordered
     :style="getMobileClass"
     @update-collapsed="handleUpdateCollapsed"