From 27cd042df9aca0383a49f3514bc21958dd890912 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期一, 29 六月 2026 15:42:23 +0800
Subject: [PATCH] 银川 1.联调产品维护页面 2.添加IM即时通讯模块
---
src/views/im/home/components/resizable-aside.vue | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 111 insertions(+), 0 deletions(-)
diff --git a/src/views/im/home/components/resizable-aside.vue b/src/views/im/home/components/resizable-aside.vue
new file mode 100644
index 0000000..e2785c1
--- /dev/null
+++ b/src/views/im/home/components/resizable-aside.vue
@@ -0,0 +1,111 @@
+<script lang="ts" setup>
+import { onBeforeUnmount, onMounted, ref } from 'vue'
+
+defineOptions({ name: 'ImResizableAside' })
+
+const props = withDefaults(
+ defineProps<{
+ defaultWidth?: number // 榛樿瀹藉害
+ maxWidth?: number // 鏈�澶у搴�
+ minWidth?: number // 鏈�灏忓搴�
+ storageKey: string // localStorage 瀛樺偍 key锛屽繀濉紱璋冪敤鏂逛紶 StorageKeys.localStorage.asideWidth
+ }>(),
+ {
+ defaultWidth: 260,
+ minWidth: 200,
+ maxWidth: 500
+ }
+)
+
+const asideWidth = ref<number>(props.defaultWidth)
+const isResizing = ref(false)
+let startX = 0
+let startWidth = 0
+
+onMounted(() => {
+ const saved = localStorage.getItem(props.storageKey)
+ if (saved) {
+ const w = Number.parseInt(saved, 10)
+ if (!Number.isNaN(w)) {
+ asideWidth.value = clamp(w)
+ }
+ }
+ document.addEventListener('mousemove', handleResize)
+ document.addEventListener('mouseup', stopResize)
+})
+
+onBeforeUnmount(() => {
+ // 鎷栨嫿涓嵏杞斤細澶嶇敤 stopResize 澶嶄綅 body cursor/userSelect 骞跺啓鍥炲綋鍓嶅搴︼紝閬垮厤鍏ㄥ眬鐘舵�佹硠婕�
+ if (isResizing.value) {
+ stopResize()
+ }
+ document.removeEventListener('mousemove', handleResize)
+ document.removeEventListener('mouseup', stopResize)
+})
+
+/** 鎶婂搴﹀す鍒� [minWidth, maxWidth] 鍖洪棿锛屾仮澶� / 鎷栨嫿璺緞閮借蛋瀹冨厹搴� */
+function clamp(w: number) {
+ return Math.max(props.minWidth, Math.min(props.maxWidth, w))
+}
+
+/** 鎸変笅鎷栨嫿鎵嬫焺锛氳褰曡捣濮嬩綅缃� + 閿佸畾 body cursor/userSelect锛岄伩鍏嶆嫋鎷戒腑璇�夋枃鏈� */
+function startResize(e: MouseEvent) {
+ isResizing.value = true
+ startX = e.clientX
+ startWidth = asideWidth.value
+ document.body.style.cursor = 'col-resize'
+ document.body.style.userSelect = 'none'
+ e.preventDefault()
+}
+
+/** 鎷栨嫿涓細鎸夐紶鏍囦綅绉昏绠楁柊瀹藉害骞� clamp 鍒板厑璁稿尯闂� */
+function handleResize(e: MouseEvent) {
+ if (!isResizing.value) {
+ return
+ }
+ const deltaX = e.clientX - startX
+ asideWidth.value = clamp(startWidth + deltaX)
+}
+
+/** 鏉惧紑榧犳爣锛氳В閿� body 鍏ㄥ眬鎬佸苟鎶婂綋鍓嶅搴﹀啓鍏� localStorage 鎸佷箙鍖� */
+function stopResize() {
+ if (!isResizing.value) {
+ return
+ }
+ isResizing.value = false
+ document.body.style.cursor = ''
+ document.body.style.userSelect = ''
+ localStorage.setItem(props.storageKey, String(asideWidth.value))
+}
+</script>
+
+<template>
+ <!--
+ 鍙嫋鎷藉搴︾殑宸︿晶 Aside
+ - 浣跨敤 localStorage 璁颁綇鐢ㄦ埛涓婃璋冩暣鐨勫搴︼紙storageKey 蹇呭~锛�
+ - 鎷栨嫿鍖哄湪鍙宠竟缂橈紝榧犳爣鍙� col-resize
+ -->
+ <aside
+ class="relative flex flex-col shrink-0 bg-[var(--ant-color-fill-secondary)] border-r border-r-solid border-[var(--im-border-color-lighter)] shadow-[2px_0_8px_rgba(0,0,0,0.05)]"
+ :style="{ width: `${asideWidth }px` }"
+ >
+ <slot></slot>
+ <div
+ class="im-resizable-aside__handle absolute top-0 right--0.75 z-10 flex items-center justify-center w-1.5 h-full cursor-col-resize transition-colors"
+ :class="{ 'is-resizing': isResizing }"
+ title="鎷栨嫿璋冩暣瀹藉害"
+ @mousedown="startResize"
+ >
+ <div class="im-resizable-aside__line w-0.5 h-full rounded-0.5 bg-transparent transition-all"></div>
+ </div>
+ </aside>
+</template>
+
+<style scoped>
+/* hover / 鎷栨嫿涓� 鎶婂唴閮� line 鍔犵矖鍙樻繁锛屾彁绀烘墜鏌勫彲鎷栵紱鐘舵�佸湪鐖� handle 涓� 鈫� 閫氳繃鍚庝唬閫夋嫨鍣ㄨ仈鍔ㄥ瓙 line */
+.im-resizable-aside__handle:hover .im-resizable-aside__line,
+.im-resizable-aside__handle.is-resizing .im-resizable-aside__line {
+ width: 3px;
+ background-color: var(--im-resize-line-color);
+}
+</style>
--
Gitblit v1.9.3