-
@@ -257,6 +300,9 @@ import { listObjectLog, rollbackObjectLog } from '@/api/system/objectLog'
const STORAGE_KEY_PREFIX = 'onlineMembersPanel_'
const OP_TYPE_MAP = { 1: '新增', 2: '修改', 3: '删除', 4: '选择', 5: '回滚' }
+/** 固定外框尺寸;小屏时由 panelStyle 收缩以不超出视口 */
+const PANEL_FIXED_WIDTH = 960
+const PANEL_FIXED_HEIGHT = 720
export default {
name: 'OnlineMembersDialog',
@@ -303,25 +349,20 @@ export default {
activeTab: 'members',
panelLeft: null,
panelTop: null,
- panelWidth: 700,
- panelHeight: 520,
+ panelWidth: PANEL_FIXED_WIDTH,
+ panelHeight: PANEL_FIXED_HEIGHT,
isDragging: false,
dragStartX: 0,
dragStartY: 0,
- isResizing: false,
- resizeStartX: 0,
- resizeStartY: 0,
- resizeStartW: 0,
- resizeStartH: 0,
showRollbackDialog: false,
// 在线成员数据(当 props.onlineMembers 为空时使用 mock)
_mockOnlineMembers: [
- { id: 1, name: '张三', role: '指挥官', status: '在线', isEditing: true, avatar: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png' },
- { id: 2, name: '李四', role: '参谋', status: '在线', isEditing: false, avatar: 'https://cube.elemecdn.com/1/88/03b0d39583f48206768a7534e55bcpng.png' },
- { id: 3, name: '王五', role: '操作员', status: '在线', isEditing: false, avatar: 'https://cube.elemecdn.com/2/88/03b0d39583f48206768a7534e55bcpng.png' },
- { id: 4, name: '赵六', role: '观察员', status: '在线', isEditing: false, avatar: 'https://cube.elemecdn.com/3/88/03b0d39583f48206768a7534e55bcpng.png' },
- { id: 5, name: '孙七', role: '分析师', status: '在线', isEditing: false, avatar: 'https://cube.elemecdn.com/4/88/03b0d39583f48206768a7534e55bcpng.png' }
+ { id: 1, userId: 1, userName: 'zhangsan', name: '张三', role: '指挥官', status: '在线', isEditing: true, avatar: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png' },
+ { id: 2, userId: 2, userName: 'lisi', name: '李四', role: '参谋', status: '在线', isEditing: false, avatar: 'https://cube.elemecdn.com/1/88/03b0d39583f48206768a7534e55bcpng.png' },
+ { id: 3, userId: 3, userName: 'wangwu', name: '王五', role: '操作员', status: '在线', isEditing: false, avatar: 'https://cube.elemecdn.com/2/88/03b0d39583f48206768a7534e55bcpng.png' },
+ { id: 4, userId: 4, userName: 'zhaoliu', name: '赵六', role: '观察员', status: '离线', isEditing: false, avatar: 'https://cube.elemecdn.com/3/88/03b0d39583f48206768a7534e55bcpng.png' },
+ { id: 5, userId: 5, userName: 'sunqi', name: '孙七', role: '分析师', status: '在线', isEditing: false, avatar: 'https://cube.elemecdn.com/4/88/03b0d39583f48206768a7534e55bcpng.png' }
],
// 当前操作状态
@@ -343,7 +384,8 @@ export default {
newMessage: '',
chatMode: 'group',
- privateChatTarget: null
+ privateChatTarget: null,
+ privateContactPending: null
};
},
computed: {
@@ -368,15 +410,25 @@ export default {
if (!this.privateChatTarget) return null;
return this.chatableMembers.find(m => Number(m.userId) === Number(this.privateChatTarget));
},
+ privateChatTargetName() {
+ const m = this.selectedPrivateMember;
+ if (m) return m.name || m.userName || '';
+ return this.$t('onlineMembersDialog.selectMemberToChat');
+ },
panelStyle() {
- const left = this.panelLeft != null ? this.panelLeft : (window.innerWidth - this.panelWidth) / 2 - 20;
- const top = this.panelTop != null ? this.panelTop : (window.innerHeight - this.panelHeight) / 2 - 40;
+ const pad = 16
+ const effW = Math.min(this.panelWidth, Math.max(320, window.innerWidth - pad))
+ const effH = Math.min(this.panelHeight, Math.max(400, window.innerHeight - pad))
+ let left = this.panelLeft != null ? this.panelLeft : (window.innerWidth - effW) / 2
+ let top = this.panelTop != null ? this.panelTop : (window.innerHeight - effH) / 2
+ left = Math.max(0, Math.min(left, window.innerWidth - effW))
+ top = Math.max(0, Math.min(top, window.innerHeight - effH))
return {
left: `${left}px`,
top: `${top}px`,
- width: `${this.panelWidth}px`,
- height: `${this.panelHeight}px`
- };
+ width: `${effW}px`,
+ height: `${effH}px`
+ }
}
},
methods: {
@@ -386,90 +438,68 @@ export default {
getStorageKey() {
return STORAGE_KEY_PREFIX + (this.roomId || 'default');
},
+ effectivePanelSize() {
+ const pad = 16
+ const effW = Math.min(this.panelWidth, Math.max(320, window.innerWidth - pad))
+ const effH = Math.min(this.panelHeight, Math.max(400, window.innerHeight - pad))
+ return { effW, effH }
+ },
loadPosition() {
try {
- const key = this.getStorageKey();
- const raw = localStorage.getItem(key);
+ const key = this.getStorageKey()
+ const raw = localStorage.getItem(key)
if (raw) {
- const d = JSON.parse(raw);
+ const d = JSON.parse(raw)
if (d.panelPosition) {
- const left = Number(d.panelPosition.left);
- const top = Number(d.panelPosition.top);
- if (!isNaN(left) && left >= 0) this.panelLeft = Math.min(left, window.innerWidth - this.panelWidth);
- if (!isNaN(top) && top >= 0) this.panelTop = Math.min(top, window.innerHeight - this.panelHeight);
- }
- if (d.panelSize) {
- const w = Number(d.panelSize.width);
- const h = Number(d.panelSize.height);
- if (!isNaN(w) && w >= 400 && w <= 1000) this.panelWidth = w;
- if (!isNaN(h) && h >= 300 && h <= window.innerHeight - 60) this.panelHeight = h;
+ const left = Number(d.panelPosition.left)
+ const top = Number(d.panelPosition.top)
+ const { effW, effH } = this.effectivePanelSize()
+ if (!isNaN(left)) this.panelLeft = Math.max(0, Math.min(left, window.innerWidth - effW))
+ if (!isNaN(top)) this.panelTop = Math.max(0, Math.min(top, window.innerHeight - effH))
}
}
} catch (e) {
- console.warn('加载在线成员弹窗位置失败:', e);
+ console.warn('加载在线成员弹窗位置失败:', e)
}
},
savePosition() {
try {
- const payload = { panelSize: { width: this.panelWidth, height: this.panelHeight } };
+ const payload = {}
if (this.panelLeft != null && this.panelTop != null) {
- payload.panelPosition = { left: this.panelLeft, top: this.panelTop };
+ payload.panelPosition = { left: this.panelLeft, top: this.panelTop }
}
- localStorage.setItem(this.getStorageKey(), JSON.stringify(payload));
+ localStorage.setItem(this.getStorageKey(), JSON.stringify(payload))
} catch (e) {
- console.warn('保存在线成员弹窗位置失败:', e);
+ console.warn('保存在线成员弹窗位置失败:', e)
}
},
onDragStart(e) {
- e.preventDefault();
- this.isDragging = true;
- const currentLeft = this.panelLeft != null ? this.panelLeft : (window.innerWidth - this.panelWidth) / 2 - 20;
- const currentTop = this.panelTop != null ? this.panelTop : (window.innerHeight - this.panelHeight) / 2 - 40;
- this.dragStartX = e.clientX - currentLeft;
- this.dragStartY = e.clientY - currentTop;
- document.addEventListener('mousemove', this.onDragMove);
- document.addEventListener('mouseup', this.onDragEnd);
+ e.preventDefault()
+ this.isDragging = true
+ const { effW, effH } = this.effectivePanelSize()
+ const currentLeft = this.panelLeft != null ? this.panelLeft : (window.innerWidth - effW) / 2
+ const currentTop = this.panelTop != null ? this.panelTop : (window.innerHeight - effH) / 2
+ this.dragStartX = e.clientX - currentLeft
+ this.dragStartY = e.clientY - currentTop
+ document.addEventListener('mousemove', this.onDragMove)
+ document.addEventListener('mouseup', this.onDragEnd)
},
onDragMove(e) {
- if (!this.isDragging) return;
- e.preventDefault();
- let left = e.clientX - this.dragStartX;
- let top = e.clientY - this.dragStartY;
- left = Math.max(0, Math.min(window.innerWidth - this.panelWidth, left));
- top = Math.max(0, Math.min(window.innerHeight - this.panelHeight, top));
- this.panelLeft = left;
- this.panelTop = top;
+ if (!this.isDragging) return
+ e.preventDefault()
+ const { effW, effH } = this.effectivePanelSize()
+ let left = e.clientX - this.dragStartX
+ let top = e.clientY - this.dragStartY
+ left = Math.max(0, Math.min(window.innerWidth - effW, left))
+ top = Math.max(0, Math.min(window.innerHeight - effH, top))
+ this.panelLeft = left
+ this.panelTop = top
},
onDragEnd() {
- this.isDragging = false;
- document.removeEventListener('mousemove', this.onDragMove);
- document.removeEventListener('mouseup', this.onDragEnd);
- this.savePosition();
- },
- onResizeStart(e) {
- e.preventDefault();
- e.stopPropagation();
- this.isResizing = true;
- this.resizeStartX = e.clientX;
- this.resizeStartY = e.clientY;
- this.resizeStartW = this.panelWidth;
- this.resizeStartH = this.panelHeight;
- document.addEventListener('mousemove', this.onResizeMove);
- document.addEventListener('mouseup', this.onResizeEnd);
- },
- onResizeMove(e) {
- if (!this.isResizing) return;
- e.preventDefault();
- const dx = e.clientX - this.resizeStartX;
- const dy = e.clientY - this.resizeStartY;
- this.panelWidth = Math.max(400, Math.min(1000, this.resizeStartW + dx));
- this.panelHeight = Math.max(300, Math.min(window.innerHeight - 60, this.resizeStartH + dy));
- },
- onResizeEnd() {
- this.isResizing = false;
- document.removeEventListener('mousemove', this.onResizeMove);
- document.removeEventListener('mouseup', this.onResizeEnd);
- this.savePosition();
+ this.isDragging = false
+ document.removeEventListener('mousemove', this.onDragMove)
+ document.removeEventListener('mouseup', this.onDragEnd)
+ this.savePosition()
},
fetchOperationLogs() {
@@ -711,6 +741,44 @@ export default {
if (av.startsWith('http')) return av;
const base = process.env.VUE_APP_BACKEND_URL || (window.location.origin + (process.env.VUE_APP_BASE_API || '/dev-api'));
return base + av;
+ },
+ contactAvatarColor(index) {
+ const colors = ['#1877f2', '#10b981', '#f59e0b', '#7239ea', '#86909c', '#ea4c89'];
+ return colors[index % colors.length];
+ },
+ memberInitial(m) {
+ const n = (m && (m.name || m.userName)) || '';
+ return n ? String(n).charAt(0) : '?';
+ },
+ isMemberOnline(m) {
+ if (!m || !m.status) return true;
+ return m.status !== '离线' && String(m.status).toLowerCase() !== 'offline';
+ },
+ memberOnlineLabel(m) {
+ return this.isMemberOnline(m)
+ ? this.$t('onlineMembersDialog.memberStatusOnline')
+ : this.$t('onlineMembersDialog.memberStatusOffline');
+ },
+ isPrivateContactRowActive(m) {
+ if (this.privateContactPending == null || !m || m.userId == null) return false;
+ return Number(this.privateContactPending) === Number(m.userId);
+ },
+ cancelPrivateContactPick() {
+ this.privateContactPending = null;
+ this.chatMode = 'group';
+ },
+ confirmPrivateContactPick() {
+ if (this.privateContactPending == null || this.privateContactPending === '') {
+ this.$message.warning(this.$t('onlineMembersDialog.selectMemberToChat'));
+ return;
+ }
+ this.privateChatTarget = this.privateContactPending;
+ this.privateContactPending = null;
+ this.$nextTick(() => this.scrollChatToBottom());
+ },
+ clearPrivateChatTarget() {
+ this.privateChatTarget = null;
+ this.privateContactPending = null;
}
},
watch: {
@@ -742,6 +810,7 @@ export default {
handler(members) {
if (this.privateChatTarget && !members.some(m => Number(m.userId) === Number(this.privateChatTarget))) {
this.privateChatTarget = null;
+ this.privateContactPending = null;
}
}
},
@@ -749,6 +818,9 @@ export default {
if (val && this.sendPrivateChatHistoryRequest) {
this.sendPrivateChatHistoryRequest(val);
}
+ },
+ chatMode() {
+ this.privateContactPending = null;
}
}
};
@@ -766,9 +838,9 @@ export default {
.panel-container {
position: fixed;
- background: white;
- border-radius: 8px;
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
+ background: #ffffff;
+ border-radius: 10px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
overflow: hidden;
z-index: 1001;
pointer-events: auto;
@@ -792,16 +864,18 @@ export default {
display: flex;
align-items: center;
justify-content: space-between;
- padding: 16px 20px;
- border-bottom: 1px solid #e8e8e8;
+ padding: 14px 18px;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06);
cursor: move;
+ flex-shrink: 0;
+ background: #ffffff;
}
.dialog-header h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
- color: #333;
+ color: #1d2129;
}
.close-btn {
@@ -823,56 +897,131 @@ export default {
}
.dialog-body {
- padding: 20px;
+ padding: 0;
+ flex: 1;
+ min-height: 0;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ background: #f9fafb;
+}
+
+/* Tab 区域撑满弹窗余项,各 Tab 内容在 tab-pane-fill 内再滚动 */
+.online-members-el-tabs {
+ --tab-accent: #1877f2;
+ flex: 1;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+}
+
+.online-members-el-tabs ::v-deep .el-tabs__header {
+ flex-shrink: 0;
+ margin: 0;
+ padding: 0 14px 8px;
+ background: #ffffff;
+}
+
+.online-members-el-tabs ::v-deep .el-tabs__nav-wrap {
+ background: #ffffff;
+}
+
+.online-members-el-tabs ::v-deep .el-tabs__nav-wrap::after {
+ height: 1px;
+ background-color: rgba(0, 0, 0, 0.06);
+}
+
+.online-members-el-tabs ::v-deep .el-tabs__item {
+ height: 44px;
+ line-height: 44px;
+ padding: 0 20px;
+ font-size: 15px;
+ font-weight: 500;
+ color: #86909c;
+}
+
+.online-members-el-tabs ::v-deep .el-tabs__item:hover {
+ color: #4e5969;
+}
+
+.online-members-el-tabs ::v-deep .el-tabs__item.is-active {
+ color: var(--tab-accent);
+ font-weight: 600;
+}
+
+.online-members-el-tabs ::v-deep .el-tabs__active-bar {
+ height: 3px;
+ border-radius: 3px 3px 0 0;
+ background-color: var(--tab-accent);
+}
+
+.online-members-el-tabs ::v-deep .el-tabs__content {
flex: 1;
- overflow-y: auto;
min-height: 0;
+ height: 0;
+ overflow: hidden;
+ background: transparent;
}
-.resize-handle {
- position: absolute;
- right: 0;
- bottom: 0;
- width: 20px;
- height: 20px;
- cursor: nwse-resize;
- user-select: none;
- z-index: 10;
- background: linear-gradient(to top left, transparent 50%, rgba(0, 138, 255, 0.2) 50%);
+.online-members-el-tabs ::v-deep .el-tab-pane {
+ height: 100%;
+ overflow: hidden;
+ background: transparent;
+}
+
+.tab-pane-fill {
+ height: 100%;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ background: transparent;
+ padding: 8px 14px 14px;
+ box-sizing: border-box;
}
-.resize-handle:hover {
- background: linear-gradient(to top left, transparent 50%, rgba(0, 138, 255, 0.4) 50%);
+/* 聊天 Tab:与 body 同色铺满,无额外白边 */
+.tab-pane-fill--chat {
+ margin: 0;
+ width: 100%;
+ padding: 0;
+ background: transparent;
+ border-radius: 0;
+ overflow: hidden;
}
/* 在线成员样式 */
.members-list {
display: flex;
flex-direction: column;
- gap: 12px;
- height: 400px;
+ gap: 0;
+ flex: 1;
+ min-height: 0;
+ overflow-x: hidden;
overflow-y: auto;
}
.member-item {
display: flex;
align-items: center;
- padding: 12px;
- border-radius: 8px;
- background: rgba(240, 242, 245, 0.8);
- transition: all 0.3s;
+ padding: 14px 6px;
+ border-radius: 0;
+ background: transparent;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06);
+ transition: background 0.2s ease;
+}
+
+.member-item:last-child {
+ border-bottom: none;
}
.member-item:hover {
- background: rgba(220, 233, 255, 0.8);
- transform: translateY(-1px);
- box-shadow: 0 2px 8px rgba(0, 138, 255, 0.15);
+ background: rgba(255, 255, 255, 0.35);
}
.member-item.active {
- background: rgba(190, 220, 255, 0.8);
- border: 1px solid rgba(0, 138, 255, 0.3);
- box-shadow: 0 2px 10px rgba(0, 138, 255, 0.2);
+ background: rgba(24, 119, 242, 0.1);
+ box-shadow: inset 3px 0 0 #1877f2;
}
.member-avatar {
@@ -929,15 +1078,22 @@ export default {
.current-operation {
display: flex;
flex-direction: column;
- gap: 20px;
- height: 400px;
+ gap: 0;
+ flex: 1;
+ min-height: 0;
+ overflow-x: hidden;
overflow-y: auto;
}
.operation-section {
- background: rgba(240, 242, 245, 0.8);
- padding: 16px;
- border-radius: 8px;
+ background: transparent;
+ padding: 14px 4px;
+ border-radius: 0;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06);
+}
+
+.operation-section:last-child {
+ border-bottom: none;
}
.operation-section h4 {
@@ -967,9 +1123,11 @@ export default {
/* 操作日志样式 */
.operation-logs {
position: relative;
- max-height: 480px;
+ flex: 1;
+ min-height: 0;
display: flex;
flex-direction: column;
+ overflow: hidden;
}
.logs-header {
@@ -977,6 +1135,7 @@ export default {
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
+ flex-shrink: 0;
}
.logs-header h4 {
@@ -988,8 +1147,9 @@ export default {
.logs-body {
flex: 1;
+ min-height: 0;
+ overflow-x: hidden;
overflow-y: auto;
- min-height: 200px;
}
.logs-empty {
@@ -1003,24 +1163,26 @@ export default {
margin-top: 12px;
padding: 8px 0;
text-align: right;
+ flex-shrink: 0;
}
.log-content {
- background: rgba(240, 242, 245, 0.8);
- padding: 12px;
- border-radius: 6px;
- margin-left: 16px;
+ background: transparent;
+ padding: 10px 8px 10px 12px;
+ border-radius: 0;
+ margin-left: 8px;
cursor: pointer;
- border: 2px solid transparent;
+ border: none;
+ border-left: 3px solid rgba(24, 119, 242, 0.35);
}
.log-content:hover {
- background: rgba(230, 235, 242, 0.9);
+ background: rgba(255, 255, 255, 0.35);
}
.log-content.selected {
- border-color: #e6a23c;
- background: rgba(253, 246, 236, 0.95);
+ border-left-color: #e6a23c;
+ background: rgba(255, 255, 255, 0.45);
}
.log-user {
@@ -1037,7 +1199,7 @@ export default {
.log-object {
font-size: 12px;
- color: #008aff;
+ color: #165dff;
margin-bottom: 2px;
}
@@ -1055,132 +1217,493 @@ export default {
color: #fa8c16;
}
-/* 新增:聊天室样式 */
+/* 聊天室:整块 #f9fafb(含群聊/私聊工具条)| 底部白底悬浮输入条 */
.chat-room {
+ --chat-primary: #1877f2;
+ --chat-primary-dark: #166fe5;
+ --chat-surface-bg: #f9fafb;
display: flex;
flex-direction: column;
- height: 400px;
+ flex: 1;
+ min-height: 0;
+ font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}
-.chat-header {
+.chat-dark-wrap {
+ flex: 1;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ background: var(--chat-surface-bg);
+ border-radius: 0 0 10px 10px;
+ overflow: hidden;
+ position: relative;
+}
+
+.chat-top-bar {
display: flex;
- align-items: center;
justify-content: space-between;
- margin-bottom: 12px;
+ align-items: center;
+ padding: 12px 14px;
+ flex-shrink: 0;
+ background: var(--chat-surface-bg);
+ border-bottom: none;
}
-.chat-header h4 {
- margin: 0;
+.chat-status-tips {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ flex-wrap: wrap;
+ gap: 8px;
+ min-height: 32px;
+}
+
+.change-private-contact-btn {
+ border: none;
+ background: #f0f7ff;
+ color: var(--chat-primary);
+ font-size: 13px;
+ font-weight: 500;
+ padding: 6px 12px;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: background 0.2s ease, color 0.2s ease;
+}
+
+.change-private-contact-btn:hover {
+ background: #e8f4ff;
+ color: var(--chat-primary-dark);
+}
+
+.online-tip {
+ font-size: 14px;
+ color: var(--chat-primary);
+ font-weight: 500;
+ background: #f0f7ff;
+ padding: 6px 12px;
+ border-radius: 12px;
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.online-tip::before {
+ content: '';
+ width: 8px;
+ height: 8px;
+ background: var(--chat-primary);
+ border-radius: 50%;
+ flex-shrink: 0;
+}
+
+.private-chat-tip {
font-size: 14px;
+ color: var(--chat-primary);
+ font-weight: 500;
+ background: #f0f7ff;
+ padding: 6px 12px;
+ border-radius: 12px;
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ max-width: min(280px, 42vw);
+ overflow: hidden;
+}
+
+.private-chat-tip::before {
+ content: '';
+ width: 8px;
+ height: 8px;
+ background: var(--chat-primary);
+ border-radius: 50%;
+ flex-shrink: 0;
+}
+
+.private-contact-name {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
font-weight: 600;
- color: #333;
}
-.private-target {
+/* 群聊/私聊切换 → 胶囊按钮外观 */
+.chat-room .chat-mode-switch {
+ display: inline-flex;
+ gap: 8px;
+}
+
+.chat-room .chat-mode-switch ::v-deep .el-radio-button__inner {
+ padding: 8px 20px;
+ border: 1px solid #dcdfe6;
+ border-radius: 6px !important;
+ font-size: 14px;
+ font-weight: 500;
+ color: #4e5969;
+ background: #ffffff;
+ box-shadow: none;
+ transition: all 0.2s ease;
+}
+
+.chat-room .chat-mode-switch ::v-deep .el-radio-button:first-child .el-radio-button__inner {
+ border-radius: 6px !important;
+}
+
+.chat-room .chat-mode-switch ::v-deep .el-radio-button:last-child .el-radio-button__inner {
+ border-radius: 6px !important;
+}
+
+.chat-room .chat-mode-switch ::v-deep .el-radio-button__orig-radio:checked + .el-radio-button__inner {
+ background: var(--chat-primary);
+ color: #ffffff;
+ border-color: var(--chat-primary);
+ box-shadow: 0 2px 8px rgba(24, 119, 242, 0.2);
+}
+
+.contact-selector-panel {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ min-height: 0;
+ background: transparent;
+ border-radius: 0;
+ box-shadow: none;
+ border: none;
+ overflow: hidden;
+}
+
+.contact-selector-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #1d2129;
+ padding: 4px 2px 12px;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06);
+ flex-shrink: 0;
+}
+
+.contact-list-empty {
+ flex: 1;
display: flex;
align-items: center;
- margin-bottom: 10px;
+ justify-content: center;
+ padding: 32px 20px;
+ color: #86909c;
+ font-size: 14px;
}
-.target-label {
- font-size: 13px;
- color: #666;
- margin-right: 8px;
- white-space: nowrap;
+.contact-list-scroll {
+ flex: 1;
+ min-height: 0;
+ overflow-y: auto;
+ padding: 8px 0;
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+}
+
+.contact-row {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 12px 10px;
+ border-radius: 0;
+ cursor: pointer;
+ transition: background 0.2s ease, box-shadow 0.2s ease;
+ border: none;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.05);
+}
+
+.contact-row:last-of-type {
+ border-bottom: none;
}
-.target-select {
+.contact-row:hover {
+ background: rgba(255, 255, 255, 0.85);
+}
+
+.contact-row.active {
+ background: rgba(24, 119, 242, 0.12);
+ border-color: transparent;
+ box-shadow: inset 3px 0 0 #1877f2;
+}
+
+.contact-row-avatar {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #fff;
+ font-size: 14px;
+ font-weight: 600;
+ flex-shrink: 0;
+}
+
+.contact-row-info {
flex: 1;
- min-width: 120px;
+ min-width: 0;
+}
+
+.contact-row-name {
+ font-size: 14px;
+ font-weight: 500;
+ color: #1d2129;
}
-.target-role {
+.contact-row-status {
font-size: 12px;
- color: #999;
- margin-left: 4px;
+ margin-top: 2px;
+ color: #86909c;
+}
+
+.contact-row-status.online {
+ color: #10b981;
+}
+
+.contact-row-status.offline {
+ color: #86909c;
+}
+
+.contact-selector-footer {
+ flex-shrink: 0;
+ display: flex;
+ justify-content: flex-end;
+ gap: 12px;
+ padding: 12px 4px 4px;
+ border-top: 1px solid rgba(0, 0, 0, 0.06);
+ background: transparent;
}
.chat-empty {
- color: #999;
+ color: #86909c;
font-size: 14px;
text-align: center;
- padding: 40px 0;
-}
-
-.online-count {
- font-size: 12px;
- color: #008aff;
+ padding: 48px 16px;
+ line-height: 1.6;
}
.chat-content {
flex: 1;
+ display: flex;
+ flex-direction: column;
+ min-height: 0;
+ overflow-x: hidden;
overflow-y: auto;
- padding: 12px;
- background: rgba(240, 242, 245, 0.5);
- border-radius: 8px;
- margin-bottom: 12px;
+ padding: 14px 12px 20px;
+ margin-bottom: 0;
+ background: var(--chat-surface-bg);
+ border-radius: 0;
+ scroll-behavior: smooth;
+}
+
+.chat-content > .contact-selector-panel {
+ flex: 1;
+ min-height: 0;
+}
+
+.chat-content::-webkit-scrollbar {
+ width: 8px;
+}
+
+.chat-content::-webkit-scrollbar-track {
+ background: transparent;
+ border-radius: 4px;
+}
+
+.chat-content::-webkit-scrollbar-thumb {
+ background: #d0d3d9;
+ border-radius: 4px;
+}
+
+.chat-content::-webkit-scrollbar-thumb:hover {
+ background: #b1b5c0;
}
.chat-message {
display: flex;
- margin-bottom: 16px;
- max-width: 80%;
+ flex-direction: column;
+ margin-bottom: 22px;
+ width: 100%;
+ max-width: 100%;
+ animation: chat-msg-fade-in 0.3s ease-out;
+}
+
+@keyframes chat-msg-fade-in {
+ from {
+ opacity: 0;
+ transform: translateY(8px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.other-message {
+ align-items: flex-start;
+ align-self: flex-start;
+ max-width: 78%;
}
.self-message {
- flex-direction: row-reverse;
+ align-items: flex-end;
+ align-self: flex-end;
+ max-width: 78%;
margin-left: auto;
}
-.other-message {
- margin-right: auto;
+.message-header-row {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 8px;
+}
+
+.self-message .message-header-row {
+ flex-direction: row-reverse;
+}
+
+.message-meta {
+ display: flex;
+ align-items: baseline;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.self-message .message-time {
+ margin-left: 0;
}
.message-avatar {
- margin: 0 8px;
+ flex-shrink: 0;
}
-.message-content {
- background: white;
- padding: 8px 12px;
- border-radius: 8px;
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+.message-avatar ::v-deep .el-avatar {
+ border: 2px solid #fff;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
-.self-message .message-content {
- background: #e6f7ff;
+.other-message .message-avatar ::v-deep .el-avatar {
+ background: linear-gradient(135deg, #e8eaed, #d0d3d9) !important;
+ color: #4e5969 !important;
}
-.message-sender {
- font-size: 12px;
- font-weight: 600;
- color: #333;
- margin-bottom: 4px;
+.self-message .message-avatar ::v-deep .el-avatar {
+ background: linear-gradient(135deg, var(--chat-primary), var(--chat-primary-dark)) !important;
+ color: #fff !important;
+ box-shadow: 0 2px 8px rgba(24, 119, 242, 0.3);
}
-.message-text {
+.message-sender {
font-size: 13px;
- color: #333;
- line-height: 1.4;
- margin-bottom: 4px;
+ font-weight: 500;
+}
+
+.other-message .message-sender {
+ color: #4e5969;
+}
+
+.self-message .message-sender {
+ color: var(--chat-primary);
}
.message-time {
font-size: 11px;
- color: #999;
- text-align: right;
+ color: #86909c;
+}
+
+.message-bubble {
+ padding: 14px 18px;
+ border-radius: 10px;
+ max-width: 100%;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
+ font-size: 14px;
+ line-height: 1.5;
+ word-wrap: break-word;
+ word-break: break-word;
+}
+
+.other-message .message-bubble {
+ background: #ffffff;
+ color: #1d2129;
+ border: 1px solid #f0f2f5;
+ border-radius: 0 10px 10px 10px;
+}
+
+.self-message .message-bubble {
+ background: linear-gradient(135deg, var(--chat-primary), var(--chat-primary-dark));
+ color: #ffffff;
+ border-radius: 10px 0 10px 10px;
+ box-shadow: 0 2px 8px rgba(24, 119, 242, 0.15);
}
-.chat-input {
+.chat-input-area {
display: flex;
- gap: 8px;
+ gap: 12px;
+ align-items: flex-end;
+ flex-shrink: 0;
+ position: relative;
+ z-index: 2;
+ padding: 12px 14px;
+ margin: -8px 14px 14px;
+ background: #ffffff;
+ border: none;
+ border-radius: 12px;
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.04);
}
-.chat-input .el-input {
+.chat-input-field {
flex: 1;
+ min-width: 0;
+}
+
+.chat-input-field ::v-deep .el-input__inner {
+ min-height: 44px;
+ line-height: 1.5;
+ padding: 12px 16px;
+ border-radius: 8px;
+ border: 1px solid #e8eaed;
+ background: #f9fafb;
+ font-size: 14px;
+ transition: all 0.2s ease;
+}
+
+.chat-input-field ::v-deep .el-input__inner:focus {
+ border-color: var(--chat-primary);
+ box-shadow: 0 0 0 3px rgba(24, 119, 242, 0.1);
+ background: #fff;
+}
+
+.chat-input-field ::v-deep .el-input.is-disabled .el-input__inner {
+ background: #f5f7fa;
+ color: #c0c4cc;
+}
+
+.chat-send-btn {
+ height: 44px;
+ padding: 0 24px;
+ flex-shrink: 0;
+ border: none;
+ border-radius: 8px;
+ font-size: 14px;
+ font-weight: 500;
+ background: var(--chat-primary) !important;
+ box-shadow: 0 2px 8px rgba(24, 119, 242, 0.15);
+ transition: all 0.2s ease;
+}
+
+.chat-send-btn:hover:not(.is-disabled),
+.chat-send-btn:focus:not(.is-disabled) {
+ background: var(--chat-primary-dark) !important;
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(24, 119, 242, 0.2);
}
-.send-btn {
- width: 80px;
+.chat-send-btn.is-disabled,
+.chat-send-btn.is-disabled:hover {
+ transform: none;
+ box-shadow: none;
+ opacity: 0.55;
}
\ No newline at end of file
diff --git a/ruoyi-ui/src/views/dialogs/PageLayoutDialog.vue b/ruoyi-ui/src/views/dialogs/PageLayoutDialog.vue
index e61ba49..4f8f0fb 100644
--- a/ruoyi-ui/src/views/dialogs/PageLayoutDialog.vue
+++ b/ruoyi-ui/src/views/dialogs/PageLayoutDialog.vue
@@ -160,15 +160,15 @@ export default {
}
.position-option:hover {
- border-color: #008aff;
- background: rgba(0, 138, 255, 0.05);
+ border-color: #165dff;
+ background: rgba(22, 93, 255, 0.05);
transform: translateY(-2px);
- box-shadow: 0 2px 8px rgba(0, 138, 255, 0.15);
+ box-shadow: 0 2px 8px rgba(22, 93, 255, 0.15);
}
.position-option.active {
- border-color: #008aff;
- background: rgba(0, 138, 255, 0.1);
+ border-color: #165dff;
+ background: rgba(22, 93, 255, 0.1);
}
.position-option i {
@@ -180,7 +180,7 @@ export default {
.position-option:hover i,
.position-option.active i {
- color: #008aff;
+ color: #165dff;
}
.position-option span {
diff --git a/ruoyi-ui/src/views/dialogs/PlatformEditDialog.vue b/ruoyi-ui/src/views/dialogs/PlatformEditDialog.vue
index 0e136da..f198820 100644
--- a/ruoyi-ui/src/views/dialogs/PlatformEditDialog.vue
+++ b/ruoyi-ui/src/views/dialogs/PlatformEditDialog.vue
@@ -453,7 +453,7 @@ export default {
border-radius: 4px;
display: inline-block;
margin: 15px 0 10px;
- border-left: 3px solid #1890ff;
+ border-left: 3px solid #165dff;
}
/* 5. 图标预览:强制左对齐 */
@@ -476,7 +476,7 @@ export default {
}
.upload-preview-box:hover {
- border-color: #409EFF;
+ border-color: #165dff;
}
.avatar-img {
@@ -571,10 +571,10 @@ export default {
cursor: nwse-resize;
user-select: none;
z-index: 10;
- background: linear-gradient(to top left, transparent 50%, rgba(24, 144, 255, 0.2) 50%);
+ background: linear-gradient(to top left, transparent 50%, rgba(22, 93, 255, 0.2) 50%);
}
.resize-handle:hover {
- background: linear-gradient(to top left, transparent 50%, rgba(24, 144, 255, 0.4) 50%);
+ background: linear-gradient(to top left, transparent 50%, rgba(22, 93, 255, 0.4) 50%);
}
diff --git a/ruoyi-ui/src/views/dialogs/PlatformImportDialog.vue b/ruoyi-ui/src/views/dialogs/PlatformImportDialog.vue
index 0efc954..a88b407 100644
--- a/ruoyi-ui/src/views/dialogs/PlatformImportDialog.vue
+++ b/ruoyi-ui/src/views/dialogs/PlatformImportDialog.vue
@@ -101,7 +101,7 @@ export default {
diff --git a/ruoyi-ui/src/views/childRoom/WhiteboardPanel.vue b/ruoyi-ui/src/views/childRoom/WhiteboardPanel.vue
index e5ac70e..f612d5e 100644
--- a/ruoyi-ui/src/views/childRoom/WhiteboardPanel.vue
+++ b/ruoyi-ui/src/views/childRoom/WhiteboardPanel.vue
@@ -29,12 +29,15 @@
-
+