Skip to content

Commit 76f5d6b

Browse files
authored
Merge pull request #4 from FreecellRaid/refactor/selection
Refactor/selection
2 parents bdec7f0 + b5701cf commit 76f5d6b

12 files changed

Lines changed: 511 additions & 265 deletions

File tree

src/assets/styles/style.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
--active-accent: #007acc;
2020
--inactive-accent: #c9cbcc;
2121
--selection-bg: #e6f7ff;
22+
--inactive-selection-bg:#e8e8e8;
2223
--color-warning: #ff4d4f;
2324

2425
/* 其他状态 */
@@ -43,6 +44,7 @@
4344
--active-accent: #2fa5f4;
4445
--inactive-accent: #575859;
4546
--selection-bg: #264f78;
47+
--inactive-selection-bg: #444444;
4648
--color-warning: #ff4d4f;
4749

4850
--icon-color: #a3a3a3;

src/components/common/MessageItem.vue

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
class="message-item"
55
:class="{
66
'is-selected': isSelected,
7+
'is-active': isActive,
78
'is-hidden-active': isHidden,
89
}"
910
draggable="true"
@@ -80,7 +81,8 @@ const props = defineProps<{
8081
message: Message;
8182
chunkId: string;
8283
index: number;
83-
isSelected: boolean; // 改由父容器传入,彻底解决选择状态隔离问题
84+
isSelected: boolean;
85+
isActive: boolean;
8486
}>();
8587
8688
// 向上事件传递
@@ -164,10 +166,14 @@ const isHidden = computed(() => {
164166
border-color: var(--active-accent);
165167
}
166168
167-
.message-item.is-selected {
169+
.message-item.is-selected.is-active {
168170
background-color: var(--selection-bg);
169171
}
170172
173+
.message-item.is-selected:not(.is-active) {
174+
background-color: var(--inactive-selection-bg);
175+
}
176+
171177
.message-header {
172178
display: flex;
173179
align-items: center;

src/components/panels/ChunkListPanel.vue

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div class="panel">
2+
<div class="panel" @pointerdown="windowStore.setFocus('chunkList')">
33
<div class="panel-header">
44
<div class="header-title">
55
<input
@@ -103,11 +103,15 @@
103103
<div
104104
class="chunk-item"
105105
:class="{
106-
// windowStore的焦点判断
106+
// 对自身的焦点判断
107107
'is-active':
108+
windowStore.currentActiveWindow.windowId ===
109+
'chunkList',
110+
// 对view的焦点判断
111+
'is-active-chunk':
108112
windowStore.currentActiveView.windowId ===
109113
chunk.chunkId,
110-
// useFilter的选中判断
114+
// 选中判断
111115
'is-selected':
112116
filterTool.selectedChunkIds.value.has(
113117
chunk.chunkId,
@@ -503,20 +507,27 @@ function handleChunkDragEnd() {
503507
}
504508
505509
.chunk-item:hover {
506-
background-color: var(--hover-bg);
510+
outline: 1px solid var(--active-accent);
511+
outline-offset: -1px;
507512
}
508513
509-
.chunk-item.is-active {
514+
.chunk-item.is-active.is-selected {
510515
background-color: var(--selection-bg);
511-
outline: 1px solid var(--active-accent);
512-
outline-offset: -1px;
513516
color: var(--active-accent);
514517
}
515518
516-
/* 被选中但不是当前活跃编辑的块(淡色背景,预留给未来多选) */
517519
.chunk-item.is-selected:not(.is-active) {
518-
background-color: var(--hover-bg);
519-
opacity: 0.8;
520+
background-color: var(--inactive-selection-bg);
521+
}
522+
523+
.chunk-item.is-active-chunk {
524+
outline: 1px solid var(--active-accent);
525+
outline-offset: -1px;
526+
}
527+
528+
.chunk-item.is-active-chunk:not(.is-active) {
529+
outline: 1px solid var(--border-color);
530+
outline-offset: -1px;
520531
}
521532
522533
.chunk-name {

src/components/panels/ExportFormatPanel.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div class="panel">
2+
<div class="panel" @pointerdown="windowStore.setFocus('exportFormat')">
33
<div class="panel-header">
44
<div class="header-title">
55
<h3>导出模板</h3>

src/components/panels/IdentityPanel.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div class="panel">
2+
<div class="panel" @pointerdown="windowStore.setFocus('identity')">
33
<div class="panel-header">
44
<div class="header-title">
55
<h3>{{ isPlayerMode ? '角色管理' : '账号管理' }}</h3>
@@ -115,6 +115,7 @@ import { useHistoryStore } from '@/stores/historyStore';
115115
import { useMessageEditorStore } from '@/stores/editorStore/messageStore';
116116
import type { RoleType } from '@/types/log';
117117
import type { ColorMode, ColorRule } from '@/types/config';
118+
import { useWindowStore } from '@/stores/windowStore';
118119
import {
119120
buildIdentityStats,
120121
collectMessageIdsByIdentity,
@@ -133,6 +134,7 @@ const logStore = useLogStore();
133134
const uiStore = useUiStore();
134135
const historyStore = useHistoryStore();
135136
const messageEditorStore = useMessageEditorStore();
137+
const windowStore = useWindowStore();
136138
const localDisplayMode = ref<ColorMode>(styleStore.viewSettings.colorMode); // 局部显示模式
137139
const editingId = ref<string | null>(null); // 当前正在编辑的 ID
138140
const editBuffer = ref(''); // 编辑缓冲区

src/components/panels/RuleEditorPanel.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div class="panel">
2+
<div class="panel" @pointerdown="windowStore.setFocus('ruleEditor')">
33
<div class="panel-header">
44
<div class="header-title">
55
<h3>自定义染色规则</h3>
@@ -260,10 +260,12 @@ import { useFilter } from '@/composables/useFilter';
260260
import type { ColorRule } from '@/types/config';
261261
import type { MessageFilter } from '@/types/log';
262262
import { matchesMessageFilter } from '@/editor/filter';
263+
import { useWindowStore } from '@/stores/windowStore';
263264
264265
const styleStore = useStyleStore();
265266
const logStore = useLogStore();
266267
const filterStore = useFilter();
268+
const windowStore = useWindowStore();
267269
const expandedRules = ref<Set<string>>(new Set());
268270
269271
function toggleExpand(ruleId: string) {

src/components/panels/SearchPanel.vue

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div class="panel">
2+
<div class="panel" @pointerdown="windowStore.setFocus('search')">
33
<div class="panel-header">
44
<div class="header-title">
55
<h3>搜索与筛选</h3>
@@ -108,7 +108,7 @@
108108
找到 {{ searchResults.length }} 条结果
109109
</span>
110110
<button
111-
class="btn-primary btn-sm"
111+
class="btn-primary"
112112
:disabled="searchResults.length === 0"
113113
@click="selectAllMatches"
114114
>
@@ -130,8 +130,10 @@
130130
'is-selected': filterStore.selectedMessageIds.value.has(
131131
msg.messageId,
132132
),
133+
'is-active':
134+
windowStore.currentActiveWindow.windowId === 'search',
133135
}"
134-
@click="filterStore.toggleMessageSelection(msg.messageId)"
136+
@click="handleItemClick($event, msg.messageId)"
135137
>
136138
<div class="result-meta">
137139
<span class="result-name">
@@ -149,15 +151,19 @@
149151

150152
<script setup lang="ts">
151153
import { FunnelIcon, FunnelXIcon, RefreshCcw } from '@lucide/vue';
152-
import { ref, computed, reactive } from 'vue';
154+
import { ref, computed, reactive, onMounted, onUnmounted } from 'vue';
153155
import { useLogStore } from '@/stores/logStore';
154156
import { useFilter } from '@/composables/useFilter';
155157
import { matchesMessageFilter } from '@/editor/filter';
156158
import type { MessageFilter } from '@/types/log';
157159
import { formatDate } from '@/utils/date';
160+
import { useCommandDispatcher } from '@/composables/useCommandDispatcher';
161+
import { useWindowStore } from '@/stores/windowStore';
158162
159163
const logStore = useLogStore();
164+
const windowStore = useWindowStore();
160165
const filterStore = useFilter('search');
166+
const { dispatch } = useCommandDispatcher();
161167
162168
// 状态控制
163169
const quickSearch = ref('');
@@ -218,15 +224,34 @@ function handleSearch() {
218224
quickSearch.value = quickSearch.value.trim();
219225
}
220226
227+
function handleItemClick(event: MouseEvent, msgId: string) {
228+
windowStore.setFocus('search');
229+
230+
dispatch('messageSelect', {
231+
event,
232+
msgId,
233+
messages: searchResults.value, // 将当前计算出的搜索结果快照传给调度器
234+
});
235+
}
236+
221237
function selectAllMatches() {
222-
const ids = searchResults.value.map((m) => m.messageId);
223-
ids.forEach((id) => {
224-
if (!filterStore.selectedMessageIds.value.has(id)) {
225-
filterStore.toggleMessageSelection(id);
226-
}
238+
dispatch('selectAll', {
239+
messages: searchResults.value,
227240
});
228241
}
229242
243+
const handleKeyDown = (e: KeyboardEvent) => {
244+
if (windowStore.activeFocus !== 'search') return;
245+
246+
if ((e.ctrlKey || e.metaKey) && e.key === 'a') {
247+
e.preventDefault();
248+
selectAllMatches();
249+
}
250+
};
251+
252+
onMounted(() => window.addEventListener('keydown', handleKeyDown));
253+
onUnmounted(() => window.removeEventListener('keydown', handleKeyDown));
254+
230255
function clearAllFilters() {
231256
quickSearch.value = '';
232257
filter.playerName = '';
@@ -351,14 +376,24 @@ const truncate = (str: string, len: number) => {
351376
352377
.search-summary {
353378
flex-shrink: 0;
354-
padding: 8px 12px;
379+
padding: 4px 8px;
355380
display: flex;
356381
justify-content: space-between;
357382
align-items: center;
358383
background: var(--bg-secondary);
359-
border-bottom: 1px solid var(--border-color);
360384
}
361385
386+
.btn-primary {
387+
font-size: 12px;
388+
color: var(--text-muted);
389+
background-color: var(--bg-secondary);
390+
border: 1px solid var(--border-color);
391+
border-radius: 4px;
392+
}
393+
394+
.btn-primary:hover {
395+
background-color: var(--hover-bg);
396+
}
362397
.count-text {
363398
font-size: 12px;
364399
color: var(--text-secondary);
@@ -374,17 +409,20 @@ const truncate = (str: string, len: number) => {
374409
.result-item {
375410
padding: 10px 10px 10px 8px;
376411
cursor: pointer;
377-
border-bottom: 1px solid var(--border-color);
378412
}
379413
380414
.result-item:hover {
381415
background: var(--bg-secondary);
416+
outline-offset: -1px;
417+
outline: 1px solid var(--active-accent);
382418
}
383419
384-
.result-item.is-selected {
420+
.result-item.is-selected.is-active {
385421
background: var(--selection-bg);
386-
outline: 1px solid var(--active-accent);
387-
outline-offset: -1px;
422+
}
423+
424+
.result-item.is-selected:not(.is-active) {
425+
background: var(--inactive-selection-bg);
388426
}
389427
390428
.result-meta {

src/components/workspace/ChunkView.vue

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
msg.messageId,
4141
)
4242
"
43+
:is-active="isViewFocused"
4344
@select="handleMessageSelect"
4445
@dragstart="handleMessageDragStart"
4546
@dragover="handleMessageDragOver($event, index)"
@@ -95,6 +96,8 @@ onMounted(() => {
9596
});
9697
});
9798
99+
const isViewFocused = computed(() => windowStore.activeFocus === props.chunkId);
100+
98101
const currentChunk = computed(function () {
99102
return logStore.findChunkById(props.chunkId) || undefined;
100103
});
@@ -175,17 +178,16 @@ function handleActionInsert(msg: Message, index: number) {
175178
function handleActionMerge(msg: Message, index: number) {
176179
const selectedIds = filterTool.selectedMessageIds.value;
177180
if (selectedIds.has(msg.messageId) && selectedIds.size > 1) {
178-
// 多选状态:将选中的所有消息合并到当前被点击项
181+
// 多选状态合并
179182
messageEditorStore.mergeMessages(
180183
props.chunkId,
181184
Array.from(selectedIds),
182185
msg.messageId,
183186
);
184-
selectedIds.clear(); // 合并后清空多选状态
187+
filterTool.clearMessageSelection();
185188
} else {
186-
// 单选状态/未处于多选中:与 index+1 往下合并
189+
// 常规向下合并
187190
if (index < messages.value.length - 1) {
188-
// 已经是最后一条则不合并
189191
const nextMsgId = messages.value[index + 1].messageId;
190192
messageEditorStore.mergeMessages(
191193
props.chunkId,
@@ -205,7 +207,7 @@ function handleActionDelete(msgId: string) {
205207
const selectedIds = filterTool.selectedMessageIds.value;
206208
if (selectedIds.has(msgId)) {
207209
messageEditorStore.batchDeleteMessages(selectedIds);
208-
selectedIds.clear();
210+
filterTool.clearMessageSelection();
209211
} else {
210212
messageEditorStore.deleteMessage(props.chunkId, msgId);
211213
}

0 commit comments

Comments
 (0)