xsx 5 месяцев назад
Родитель
Сommit
3d9e9970e0

+ 131 - 0
im-web/src/components/common/ResizableAside.vue

@@ -0,0 +1,131 @@
+<template>
+  <el-aside :style="{ width: asideWidth + 'px' }" class="resizable-aside">
+    <slot></slot>
+    <!-- 拖拽条 -->
+    <div class="resize-handle" @mousedown="startResize" :class="{ 'resizing': isResizing }" title="拖拽调整宽度">
+      <div class="resize-line"></div>
+    </div>
+  </el-aside>
+</template>
+
+<script>
+export default {
+  name: "ResizableAside",
+  props: {
+    // 默认宽度
+    defaultWidth: {
+      type: Number,
+      default: 260
+    },
+    // 最小宽度
+    minWidth: {
+      type: Number,
+      default: 200
+    },
+    // 最大宽度
+    maxWidth: {
+      type: Number,
+      default: 500
+    },
+    // localStorage存储key
+    storageKey: {
+      type: String,
+      required: true
+    }
+  },
+  data() {
+    return {
+      asideWidth: this.defaultWidth,
+      isResizing: false,
+      startX: 0,
+      startWidth: 0
+    }
+  },
+  mounted() {
+    // 从localStorage恢复宽度设置
+    const savedWidth = localStorage.getItem(this.storageKey);
+    if (savedWidth) {
+      this.asideWidth = parseInt(savedWidth);
+    }
+    
+    // 添加全局事件监听
+    document.addEventListener('mousemove', this.handleResize);
+    document.addEventListener('mouseup', this.stopResize);
+  },
+  beforeDestroy() {
+    // 清理事件监听
+    document.removeEventListener('mousemove', this.handleResize);
+    document.removeEventListener('mouseup', this.stopResize);
+  },
+  methods: {
+    // 拖拽相关方法
+    startResize(e) {
+      this.isResizing = true;
+      this.startX = e.clientX;
+      this.startWidth = this.asideWidth;
+      document.body.style.cursor = 'col-resize';
+      document.body.style.userSelect = 'none';
+      e.preventDefault();
+    },
+    handleResize(e) {
+      if (!this.isResizing) return;
+      
+      const deltaX = e.clientX - this.startX;
+      let newWidth = this.startWidth + deltaX;
+      
+      // 限制宽度范围
+      newWidth = Math.max(this.minWidth, Math.min(this.maxWidth, newWidth));
+      
+      this.asideWidth = newWidth;
+    },
+    stopResize() {
+      if (!this.isResizing) return;
+      
+      this.isResizing = false;
+      document.body.style.cursor = '';
+      document.body.style.userSelect = '';
+      
+      // 保存宽度到localStorage
+      localStorage.setItem(this.storageKey, this.asideWidth.toString());
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.resizable-aside {
+  display: flex;
+  flex-direction: column;
+  border-right: 1px solid rgba(0, 0, 0, 0.08);
+  box-shadow: 2px 0 8px rgba(0, 0, 0, 0.05);
+  position: relative;
+
+  // 拖拽条样式
+  .resize-handle {
+    position: absolute;
+    top: 0;
+    right: -3px;
+    width: 6px;
+    height: 100%;
+    cursor: col-resize;
+    z-index: 10;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    transition: background-color 0.2s ease;
+    
+    .resize-line {
+      width: 2px;
+      height: 100%;
+      background-color: var(--im-background-active-dark);
+      border-radius: 1px;
+      transition: all 0.2s ease;
+    }
+
+    &:hover .resize-line,
+    &.resizing .resize-line {
+      width: 3px;
+    }
+  }
+}
+</style>

+ 23 - 34
im-web/src/view/Chat.vue

@@ -1,6 +1,6 @@
 <template>
   <el-container class="chat-page">
-    <el-aside width="260px" class="aside" :class="{ fullscreen: configStore.fullScreen }">
+    <resizable-aside :default-width="260" :min-width="200" :max-width="500" storage-key="chat-aside-width">
       <div class="header">
         <el-input class="search-text" size="small" placeholder="搜索" v-model="searchText">
           <i class="el-icon-search el-input__icon" slot="prefix"> </i>
@@ -16,7 +16,7 @@
             @dnd="onDnd(chat)" :active="chat === chatStore.activeChat"></chat-item>
         </div>
       </el-scrollbar>
-    </el-aside>
+    </resizable-aside>
     <el-container>
       <chat-box v-if="chatStore.activeChat" :chat="chatStore.activeChat"></chat-box>
     </el-container>
@@ -26,12 +26,14 @@
 <script>
 import ChatItem from "../components/chat/ChatItem.vue";
 import ChatBox from "../components/chat/ChatBox.vue";
+import ResizableAside from "../components/common/ResizableAside.vue";
 
 export default {
   name: "chat",
   components: {
     ChatItem,
-    ChatBox
+    ChatBox,
+    ResizableAside
   },
   data() {
     return {
@@ -97,44 +99,31 @@ export default {
 
 <style lang="scss" scoped>
 .chat-page {
-  .aside {
+  
+  .header {
+    height: 50px;
     display: flex;
-    flex-direction: column;
-    background: white;
-    border-right: 1px solid #eee;
+    align-items: center;
+    padding: 0 8px;
+  }
 
-    &.fullscreen {
-      width: 260px !important;
+  .chat-loading {
+    height: 50px;
+    background-color: #eee;
 
-      @media (min-width: 1200px) {
-        width: 290px !important;
-      }
+    .el-icon-loading {
+      font-size: 24px;
+      color: var(--im-text-color-light);
     }
 
-    .header {
-      height: 50px;
-      display: flex;
-      align-items: center;
-      padding: 0 8px;
-    }
-
-    .chat-loading {
-      height: 50px;
-      background-color: #eee;
-
-      .el-icon-loading {
-        font-size: 24px;
-        color: var(--im-text-color-light);
-      }
-
-      .el-loading-text {
-        color: var(--im-text-color-light);
-      }
+    .el-loading-text {
+      color: var(--im-text-color-light);
     }
+  }
 
-    .chat-items {
-      flex: 1;
-    }
+  .chat-items {
+    flex: 1;
   }
+
 }
 </style>

+ 50 - 63
im-web/src/view/Friend.vue

@@ -1,6 +1,6 @@
 <template>
 	<el-container class="friend-page">
-		<el-aside width="260px" class="aside" :class="{ fullscreen: configStore.fullScreen }">
+		<resizable-aside :default-width="260" :min-width="200" :max-width="500" storage-key="friend-aside-width">
 			<div class="header">
 				<el-input class="search-text" size="small" placeholder="搜索" v-model="searchText">
 					<i class="el-icon-search el-input__icon" slot="prefix"> </i>
@@ -21,7 +21,7 @@
 					<div v-if="i < friendValues.length - 1" class="divider"></div>
 				</div>
 			</el-scrollbar>
-		</el-aside>
+		</resizable-aside>
 		<el-container class="container">
 			<div class="header" v-show="userInfo.id">
 				{{ userInfo.nickName }}
@@ -61,6 +61,7 @@
 import FriendItem from "../components/friend/FriendItem.vue";
 import AddFriend from "../components/friend/AddFriend.vue";
 import HeadImage from "../components/common/HeadImage.vue";
+import ResizableAside from "../components/common/ResizableAside.vue";
 import { pinyin } from 'pinyin-pro';
 
 export default {
@@ -68,8 +69,8 @@ export default {
 	components: {
 		FriendItem,
 		AddFriend,
-		HeadImage
-
+		HeadImage,
+		ResizableAside
 	},
 	data() {
 		return {
@@ -223,81 +224,67 @@ export default {
 
 <style lang="scss" scoped>
 .friend-page {
-	.aside {
-		display: flex;
-		flex-direction: column;
-		background: var(--im-background);
-		border-right: 1px solid #eee;
 
-		&.fullscreen {
-			width: 260px !important;
+	.header {
+		height: 50px;
+		display: flex;
+		align-items: center;
+		padding: 0 8px;
 
-			@media (min-width: 1200px) {
-				width: 290px !important;
-			}
+		.add-btn {
+			padding: 5px !important;
+			margin: 5px;
+			font-size: 16px;
+			border-radius: 50%;
 		}
+	}
 
-		.header {
-			height: 50px;
-			display: flex;
-			align-items: center;
-			padding: 0 8px;
+	.friend-items {
+		flex: 1;
 
-			.add-btn {
-				padding: 5px !important;
-				margin: 5px;
-				font-size: 16px;
-				border-radius: 50%;
-			}
+		.letter {
+			text-align: left;
+			font-size: var(--im-larger-size-larger);
+			padding: 5px 15px;
+			color: var(--im-text-color-light);
 		}
+	}
+}
 
-		.friend-items {
-			flex: 1;
+.container {
+	display: flex;
+	flex-direction: column;
 
-			.letter {
-				text-align: left;
-				font-size: var(--im-larger-size-larger);
-				padding: 5px 15px;
-				color: var(--im-text-color-light);
-			}
-		}
+	.header {
+		height: 50px;
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		padding: 0 12px;
+		font-size: var(--im-font-size-larger);
+		border-bottom: var(--im-border);
+		box-sizing: border-box;
 	}
 
-	.container {
+	.friend-info {
 		display: flex;
-		flex-direction: column;
+		padding: 50px 80px 20px 80px;
+		text-align: center;
 
-		.header {
-			height: 50px;
-			display: flex;
-			justify-content: space-between;
-			align-items: center;
-			padding: 0 12px;
-			font-size: var(--im-font-size-larger);
-			border-bottom: var(--im-border);
-			box-sizing: border-box;
+		.info-item {
+			margin-left: 20px;
+			background-color: #ffffff;
+			border: 1px #ddd solid;
 		}
 
-		.friend-info {
-			display: flex;
-			padding: 50px 80px 20px 80px;
-			text-align: center;
-
-			.info-item {
-				margin-left: 20px;
-				background-color: #ffffff;
-				border: 1px #ddd solid;
-			}
-
-			.description {
-				padding: 20px 20px 0 20px;
-			}
+		.description {
+			padding: 20px 20px 0 20px;
 		}
+	}
 
-		.btn-group {
-			text-align: left !important;
-			padding: 20px;
-		}
+	.btn-group {
+		text-align: left !important;
+		padding: 20px;
 	}
 }
 </style>

+ 23 - 35
im-web/src/view/Group.vue

@@ -1,6 +1,6 @@
 <template>
 	<el-container class="group-page">
-		<el-aside width="260px" class="aside" :class="{ fullscreen: configStore.fullScreen }">
+		<resizable-aside :default-width="260" :min-width="200" :max-width="500" storage-key="group-aside-width">
 			<div class="header">
 				<el-input class="search-text" size="small" placeholder="搜索" v-model="searchText">
 					<i class="el-icon-search el-input__icon" slot="prefix"> </i>
@@ -18,7 +18,7 @@
 					<div v-if="i < groupValues.length - 1" class="divider"></div>
 				</div>
 			</el-scrollbar>
-		</el-aside>
+		</resizable-aside>
 		<el-container class="container">
 			<div class="header" v-show="activeGroup.id">{{ activeGroup.showGroupName }}({{ showMembers.length }})</div>
 			<div class="container-box" v-show="activeGroup.id">
@@ -101,6 +101,7 @@ import GroupMember from '../components/group/GroupMember.vue';
 import AddGroupMember from '../components/group/AddGroupMember.vue';
 import GroupMemberSelector from '../components/group/GroupMemberSelector.vue';
 import HeadImage from '../components/common/HeadImage.vue';
+import ResizableAside from "../components/common/ResizableAside.vue";
 import { pinyin } from 'pinyin-pro';
 
 export default {
@@ -111,7 +112,8 @@ export default {
 		FileUpload,
 		AddGroupMember,
 		GroupMemberSelector,
-		HeadImage
+		HeadImage,
+		ResizableAside
 	},
 	data() {
 		return {
@@ -349,43 +351,29 @@ export default {
 
 <style lang="scss" scoped>
 .group-page {
-	.aside {
-		display: flex;
-		flex-direction: column;
-		background: var(--im-background);
-		border-right: 1px solid #eee;
 
-		&.fullscreen {
-			width: 260px !important;
-
-			@media (min-width: 1200px) {
-				width: 290px !important;
-			}
-		}
-
-		.header {
-			height: 50px;
-			display: flex;
-			align-items: center;
-			padding: 0 8px;
+	.header {
+		height: 50px;
+		display: flex;
+		align-items: center;
+		padding: 0 8px;
 
-			.add-btn {
-				padding: 5px !important;
-				margin: 5px;
-				font-size: 16px;
-				border-radius: 50%;
-			}
+		.add-btn {
+			padding: 5px !important;
+			margin: 5px;
+			font-size: 16px;
+			border-radius: 50%;
 		}
+	}
 
-		.group-items {
-			flex: 1;
+	.group-items {
+		flex: 1;
 
-			.letter {
-				text-align: left;
-				font-size: var(--im-larger-size-larger);
-				padding: 5px 15px;
-				color: var(--im-text-color-light);
-			}
+		.letter {
+			text-align: left;
+			font-size: var(--im-larger-size-larger);
+			padding: 5px 15px;
+			color: var(--im-text-color-light);
 		}
 	}