Browse Source

侧边栏支持拖拽大小

master
xsx 6 months ago
parent
commit
3d9e9970e0
  1. 131
      im-web/src/components/common/ResizableAside.vue
  2. 23
      im-web/src/view/Chat.vue
  3. 23
      im-web/src/view/Friend.vue
  4. 22
      im-web/src/view/Group.vue

131
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
},
// localStoragekey
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
im-web/src/view/Chat.vue

@ -1,6 +1,6 @@
<template> <template>
<el-container class="chat-page"> <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"> <div class="header">
<el-input class="search-text" size="small" placeholder="搜索" v-model="searchText"> <el-input class="search-text" size="small" placeholder="搜索" v-model="searchText">
<i class="el-icon-search el-input__icon" slot="prefix"> </i> <i class="el-icon-search el-input__icon" slot="prefix"> </i>
@ -16,7 +16,7 @@
@dnd="onDnd(chat)" :active="chat === chatStore.activeChat"></chat-item> @dnd="onDnd(chat)" :active="chat === chatStore.activeChat"></chat-item>
</div> </div>
</el-scrollbar> </el-scrollbar>
</el-aside> </resizable-aside>
<el-container> <el-container>
<chat-box v-if="chatStore.activeChat" :chat="chatStore.activeChat"></chat-box> <chat-box v-if="chatStore.activeChat" :chat="chatStore.activeChat"></chat-box>
</el-container> </el-container>
@ -26,12 +26,14 @@
<script> <script>
import ChatItem from "../components/chat/ChatItem.vue"; import ChatItem from "../components/chat/ChatItem.vue";
import ChatBox from "../components/chat/ChatBox.vue"; import ChatBox from "../components/chat/ChatBox.vue";
import ResizableAside from "../components/common/ResizableAside.vue";
export default { export default {
name: "chat", name: "chat",
components: { components: {
ChatItem, ChatItem,
ChatBox ChatBox,
ResizableAside
}, },
data() { data() {
return { return {
@ -97,19 +99,6 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
.chat-page { .chat-page {
.aside {
display: flex;
flex-direction: column;
background: white;
border-right: 1px solid #eee;
&.fullscreen {
width: 260px !important;
@media (min-width: 1200px) {
width: 290px !important;
}
}
.header { .header {
height: 50px; height: 50px;
@ -135,6 +124,6 @@ export default {
.chat-items { .chat-items {
flex: 1; flex: 1;
} }
}
} }
</style> </style>

23
im-web/src/view/Friend.vue

@ -1,6 +1,6 @@
<template> <template>
<el-container class="friend-page"> <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"> <div class="header">
<el-input class="search-text" size="small" placeholder="搜索" v-model="searchText"> <el-input class="search-text" size="small" placeholder="搜索" v-model="searchText">
<i class="el-icon-search el-input__icon" slot="prefix"> </i> <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 v-if="i < friendValues.length - 1" class="divider"></div>
</div> </div>
</el-scrollbar> </el-scrollbar>
</el-aside> </resizable-aside>
<el-container class="container"> <el-container class="container">
<div class="header" v-show="userInfo.id"> <div class="header" v-show="userInfo.id">
{{ userInfo.nickName }} {{ userInfo.nickName }}
@ -61,6 +61,7 @@
import FriendItem from "../components/friend/FriendItem.vue"; import FriendItem from "../components/friend/FriendItem.vue";
import AddFriend from "../components/friend/AddFriend.vue"; import AddFriend from "../components/friend/AddFriend.vue";
import HeadImage from "../components/common/HeadImage.vue"; import HeadImage from "../components/common/HeadImage.vue";
import ResizableAside from "../components/common/ResizableAside.vue";
import { pinyin } from 'pinyin-pro'; import { pinyin } from 'pinyin-pro';
export default { export default {
@ -68,8 +69,8 @@ export default {
components: { components: {
FriendItem, FriendItem,
AddFriend, AddFriend,
HeadImage HeadImage,
ResizableAside
}, },
data() { data() {
return { return {
@ -223,19 +224,6 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
.friend-page { .friend-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 { .header {
height: 50px; height: 50px;
@ -299,5 +287,4 @@ export default {
padding: 20px; padding: 20px;
} }
} }
}
</style> </style>

22
im-web/src/view/Group.vue

@ -1,6 +1,6 @@
<template> <template>
<el-container class="group-page"> <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"> <div class="header">
<el-input class="search-text" size="small" placeholder="搜索" v-model="searchText"> <el-input class="search-text" size="small" placeholder="搜索" v-model="searchText">
<i class="el-icon-search el-input__icon" slot="prefix"> </i> <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 v-if="i < groupValues.length - 1" class="divider"></div>
</div> </div>
</el-scrollbar> </el-scrollbar>
</el-aside> </resizable-aside>
<el-container class="container"> <el-container class="container">
<div class="header" v-show="activeGroup.id">{{ activeGroup.showGroupName }}({{ showMembers.length }})</div> <div class="header" v-show="activeGroup.id">{{ activeGroup.showGroupName }}({{ showMembers.length }})</div>
<div class="container-box" v-show="activeGroup.id"> <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 AddGroupMember from '../components/group/AddGroupMember.vue';
import GroupMemberSelector from '../components/group/GroupMemberSelector.vue'; import GroupMemberSelector from '../components/group/GroupMemberSelector.vue';
import HeadImage from '../components/common/HeadImage.vue'; import HeadImage from '../components/common/HeadImage.vue';
import ResizableAside from "../components/common/ResizableAside.vue";
import { pinyin } from 'pinyin-pro'; import { pinyin } from 'pinyin-pro';
export default { export default {
@ -111,7 +112,8 @@ export default {
FileUpload, FileUpload,
AddGroupMember, AddGroupMember,
GroupMemberSelector, GroupMemberSelector,
HeadImage HeadImage,
ResizableAside
}, },
data() { data() {
return { return {
@ -349,19 +351,6 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
.group-page { .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 { .header {
height: 50px; height: 50px;
@ -387,7 +376,6 @@ export default {
color: var(--im-text-color-light); color: var(--im-text-color-light);
} }
} }
}
.container { .container {
display: flex; display: flex;

Loading…
Cancel
Save