Browse Source

侧边栏支持拖拽大小

master
xsx 6 months ago
parent
commit
3d9e9970e0
  1. 131
      im-web/src/components/common/ResizableAside.vue
  2. 57
      im-web/src/view/Chat.vue
  3. 113
      im-web/src/view/Friend.vue
  4. 58
      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>

57
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>

113
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>

58
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);
}
}

Loading…
Cancel
Save