@@ -0,0 +1,152 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import apiClient from '../utils/apiClient';
|
||||
import { useUiNotificationsStore } from './uiNotifications.store';
|
||||
|
||||
// 定义快捷指令标签接口 (与后端 QuickCommandTag 对应)
|
||||
export interface QuickCommandTag {
|
||||
id: number;
|
||||
name: string;
|
||||
created_at: number;
|
||||
updated_at: number;
|
||||
}
|
||||
|
||||
export const useQuickCommandTagsStore = defineStore('quickCommandTags', () => {
|
||||
const tags = ref<QuickCommandTag[]>([]);
|
||||
const isLoading = ref(false);
|
||||
const error = ref<string | null>(null);
|
||||
const uiNotificationsStore = useUiNotificationsStore();
|
||||
|
||||
// 获取快捷指令标签列表 (带缓存)
|
||||
async function fetchTags() {
|
||||
const cacheKey = 'quickCommandTagsCache';
|
||||
error.value = null;
|
||||
|
||||
// 1. 尝试从 localStorage 加载缓存
|
||||
try {
|
||||
const cachedData = localStorage.getItem(cacheKey);
|
||||
if (cachedData) {
|
||||
console.log('[QuickCmdTagStore] Loading quick command tags from cache.');
|
||||
tags.value = JSON.parse(cachedData);
|
||||
isLoading.value = false;
|
||||
} else {
|
||||
isLoading.value = true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[QuickCmdTagStore] Failed to load or parse cache:', e);
|
||||
localStorage.removeItem(cacheKey);
|
||||
isLoading.value = true;
|
||||
}
|
||||
|
||||
// 2. 后台获取最新数据
|
||||
isLoading.value = true;
|
||||
try {
|
||||
console.log('[QuickCmdTagStore] Fetching latest quick command tags from server...');
|
||||
// 使用新的 API 端点
|
||||
const response = await apiClient.get<QuickCommandTag[]>('/quick-command-tags');
|
||||
const freshData = response.data;
|
||||
const freshDataString = JSON.stringify(freshData);
|
||||
|
||||
// 3. 对比并更新
|
||||
const currentDataString = JSON.stringify(tags.value);
|
||||
if (currentDataString !== freshDataString) {
|
||||
console.log('[QuickCmdTagStore] Tags data changed, updating state and cache.');
|
||||
tags.value = freshData;
|
||||
localStorage.setItem(cacheKey, freshDataString);
|
||||
} else {
|
||||
console.log('[QuickCmdTagStore] Tags data is up-to-date.');
|
||||
}
|
||||
error.value = null;
|
||||
return true;
|
||||
} catch (err: any) {
|
||||
console.error('[QuickCmdTagStore] Failed to fetch tags:', err);
|
||||
error.value = err.response?.data?.message || err.message || '获取快捷指令标签列表失败';
|
||||
if (error.value) { // Check if error.value is not null
|
||||
uiNotificationsStore.showError(error.value); // 显示错误通知
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加新快捷指令标签 (添加后清除缓存)
|
||||
async function addTag(name: string): Promise<QuickCommandTag | null> {
|
||||
isLoading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
// 使用新的 API 端点
|
||||
const response = await apiClient.post<{ message: string, tag: QuickCommandTag }>('/quick-command-tags', { name });
|
||||
const newTag = response.data.tag;
|
||||
localStorage.removeItem('quickCommandTagsCache'); // 清除缓存
|
||||
await fetchTags(); // 重新获取以更新列表
|
||||
uiNotificationsStore.showSuccess('快捷指令标签已添加');
|
||||
return newTag;
|
||||
} catch (err: any) {
|
||||
console.error('[QuickCmdTagStore] Failed to add tag:', err);
|
||||
error.value = err.response?.data?.message || err.message || '添加快捷指令标签失败';
|
||||
if (error.value) { // Check if error.value is not null
|
||||
uiNotificationsStore.showError(error.value);
|
||||
}
|
||||
return null;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新快捷指令标签
|
||||
async function updateTag(id: number, name: string): Promise<boolean> {
|
||||
isLoading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
// 使用新的 API 端点
|
||||
await apiClient.put(`/quick-command-tags/${id}`, { name });
|
||||
localStorage.removeItem('quickCommandTagsCache');
|
||||
await fetchTags();
|
||||
uiNotificationsStore.showSuccess('快捷指令标签已更新');
|
||||
return true;
|
||||
} catch (err: any) {
|
||||
console.error('[QuickCmdTagStore] Failed to update tag:', err);
|
||||
error.value = err.response?.data?.message || err.message || '更新快捷指令标签失败';
|
||||
if (error.value) { // Check if error.value is not null
|
||||
uiNotificationsStore.showError(error.value);
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 删除快捷指令标签
|
||||
async function deleteTag(id: number): Promise<boolean> {
|
||||
isLoading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
// 使用新的 API 端点
|
||||
await apiClient.delete(`/quick-command-tags/${id}`);
|
||||
localStorage.removeItem('quickCommandTagsCache');
|
||||
await fetchTags();
|
||||
uiNotificationsStore.showSuccess('快捷指令标签已删除');
|
||||
return true;
|
||||
} catch (err: any) {
|
||||
console.error('[QuickCmdTagStore] Failed to delete tag:', err);
|
||||
error.value = err.response?.data?.message || err.message || '删除快捷指令标签失败';
|
||||
if (error.value) { // Check if error.value is not null
|
||||
uiNotificationsStore.showError(error.value);
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
tags,
|
||||
isLoading,
|
||||
error,
|
||||
fetchTags,
|
||||
addTag,
|
||||
updateTag,
|
||||
deleteTag,
|
||||
};
|
||||
});
|
||||
@@ -1,62 +1,224 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import apiClient from '../utils/apiClient'; // 使用统一的 apiClient
|
||||
import { ref, computed } from 'vue';
|
||||
import { ref, computed, watch } from 'vue'; // Import watch
|
||||
import { useUiNotificationsStore } from './uiNotifications.store';
|
||||
import type { QuickCommand } from '../types/quick-commands.types'; // 引入本地 QuickCommand 类型
|
||||
import { useQuickCommandTagsStore, type QuickCommandTag } from './quickCommandTags.store'; // +++ Import new tag store +++
|
||||
import { useI18n } from 'vue-i18n'; // +++ Import i18n for "Untagged" +++
|
||||
// Assuming QuickCommand type in types includes tagIds now, or define it here
|
||||
// import type { QuickCommand } from '../types/quick-commands.types';
|
||||
|
||||
// 定义前端使用的快捷指令接口 (可以与后端一致)
|
||||
export type QuickCommandFE = QuickCommand;
|
||||
// 定义前端使用的快捷指令接口 (包含 tagIds)
|
||||
export interface QuickCommandFE { // Renamed from QuickCommand if necessary
|
||||
id: number;
|
||||
name: string | null;
|
||||
command: string;
|
||||
usage_count: number;
|
||||
created_at: number;
|
||||
updated_at: number;
|
||||
tagIds: number[]; // +++ Add tagIds +++
|
||||
}
|
||||
|
||||
// 定义排序类型
|
||||
export type QuickCommandSortByType = 'name' | 'usage_count';
|
||||
|
||||
// 定义分组后的数据结构
|
||||
export interface GroupedQuickCommands {
|
||||
groupName: string;
|
||||
tagId: number | null; // null for "Untagged" group
|
||||
commands: QuickCommandFE[];
|
||||
}
|
||||
|
||||
// +++ localStorage key for expanded groups +++
|
||||
const EXPANDED_GROUPS_STORAGE_KEY = 'quickCommandsExpandedGroups';
|
||||
|
||||
export const useQuickCommandsStore = defineStore('quickCommands', () => {
|
||||
const quickCommandsList = ref<QuickCommandFE[]>([]);
|
||||
const quickCommandsList = ref<QuickCommandFE[]>([]); // Should now contain QuickCommandFE with tagIds
|
||||
const searchTerm = ref('');
|
||||
const sortBy = ref<QuickCommandSortByType>('name'); // 默认按名称排序
|
||||
const isLoading = ref(false);
|
||||
const error = ref<string | null>(null);
|
||||
const uiNotificationsStore = useUiNotificationsStore();
|
||||
const selectedIndex = ref<number>(-1); // NEW: Index of the selected command in the filtered list
|
||||
const quickCommandTagsStore = useQuickCommandTagsStore(); // +++ Inject new tag store +++
|
||||
const { t } = useI18n(); // +++ For "Untagged" translation +++
|
||||
const selectedIndex = ref<number>(-1); // Index in the flatVisibleCommands list
|
||||
|
||||
// +++ State for expanded groups +++
|
||||
const expandedGroups = ref<Record<string, boolean>>({});
|
||||
|
||||
// --- Getters ---
|
||||
|
||||
// 计算属性:根据搜索词过滤和排序指令
|
||||
const filteredAndSortedCommands = computed(() => {
|
||||
// +++ 重写 Getter: 过滤、分组、排序指令 +++
|
||||
const filteredAndGroupedCommands = computed((): GroupedQuickCommands[] => {
|
||||
const term = searchTerm.value.toLowerCase().trim();
|
||||
let filtered = quickCommandsList.value;
|
||||
const allTags = quickCommandTagsStore.tags; // 获取快捷指令专属标签
|
||||
const tagMap = new Map(allTags.map(tag => [tag.id, tag.name]));
|
||||
const untaggedGroupName = t('quickCommands.untagged', '未标记'); // 获取 "未标记" 的翻译
|
||||
|
||||
// 1. 过滤 (New logic: filter by command name, command content, OR tag name)
|
||||
let filtered = quickCommandsList.value;
|
||||
if (term) {
|
||||
filtered = filtered.filter(cmd =>
|
||||
(cmd.name && cmd.name.toLowerCase().includes(term)) ||
|
||||
cmd.command.toLowerCase().includes(term)
|
||||
);
|
||||
filtered = filtered.filter(cmd => {
|
||||
// Check command name
|
||||
if (cmd.name && cmd.name.toLowerCase().includes(term)) {
|
||||
return true;
|
||||
}
|
||||
// Check command content
|
||||
if (cmd.command.toLowerCase().includes(term)) {
|
||||
return true;
|
||||
}
|
||||
// Check associated tag names
|
||||
if (cmd.tagIds && cmd.tagIds.length > 0) {
|
||||
for (const tagId of cmd.tagIds) {
|
||||
const tagName = tagMap.get(tagId);
|
||||
if (tagName && tagName.toLowerCase().includes(term)) {
|
||||
return true; // Match found in tag name
|
||||
}
|
||||
}
|
||||
}
|
||||
// No match found
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Pinia store getter 中直接排序可能不是最佳实践,但这里为了简单起见先这样实现
|
||||
// 更好的方式可能是在 fetch 时就按需排序,或者在组件层排序
|
||||
// 注意:这里直接修改 ref 数组的顺序,如果需要在多处使用不同排序,需要创建副本
|
||||
// return [...filtered].sort((a, b) => {
|
||||
// if (sortBy.value === 'usage_count') {
|
||||
// // 按使用次数降序,次数相同按名称升序
|
||||
// if (b.usage_count !== a.usage_count) {
|
||||
// return b.usage_count - a.usage_count;
|
||||
// }
|
||||
// }
|
||||
// // 默认或次数相同时按名称升序 (null 名称排在前面)
|
||||
// const nameA = a.name ?? '';
|
||||
// const nameB = b.name ?? '';
|
||||
// return nameA.localeCompare(nameB);
|
||||
// });
|
||||
// **修正:Getter 不应修改原始数组,返回过滤后的即可,排序由 fetch 控制**
|
||||
return filtered;
|
||||
// 2. 分组
|
||||
const groups: Record<string, { commands: QuickCommandFE[], tagId: number | null }> = {};
|
||||
const untaggedCommands: QuickCommandFE[] = [];
|
||||
|
||||
filtered.forEach(cmd => {
|
||||
let isTagged = false;
|
||||
if (cmd.tagIds && cmd.tagIds.length > 0) {
|
||||
cmd.tagIds.forEach(tagId => {
|
||||
const tagName = tagMap.get(tagId);
|
||||
if (tagName) {
|
||||
if (!groups[tagName]) {
|
||||
groups[tagName] = { commands: [], tagId: tagId };
|
||||
// 初始化展开状态 (如果未定义,默认为 true)
|
||||
if (expandedGroups.value[tagName] === undefined) {
|
||||
expandedGroups.value[tagName] = true;
|
||||
}
|
||||
}
|
||||
// 避免重复添加(如果一个指令有多个相同标签ID? 不太可能但做个防御)
|
||||
if (!groups[tagName].commands.some(c => c.id === cmd.id)) {
|
||||
groups[tagName].commands.push(cmd);
|
||||
}
|
||||
isTagged = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!isTagged) {
|
||||
untaggedCommands.push(cmd);
|
||||
}
|
||||
});
|
||||
|
||||
// 3. 排序分组内指令 & 格式化输出
|
||||
const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b));
|
||||
const result: GroupedQuickCommands[] = sortedGroupNames.map(groupName => {
|
||||
const groupData = groups[groupName];
|
||||
// 组内排序
|
||||
groupData.commands.sort((a, b) => {
|
||||
if (sortBy.value === 'usage_count') {
|
||||
if (b.usage_count !== a.usage_count) return b.usage_count - a.usage_count;
|
||||
}
|
||||
const nameA = a.name ?? a.command; // Fallback to command if name is null
|
||||
const nameB = b.name ?? b.command;
|
||||
return nameA.localeCompare(nameB);
|
||||
});
|
||||
return {
|
||||
groupName: groupName,
|
||||
tagId: groupData.tagId,
|
||||
commands: groupData.commands
|
||||
};
|
||||
});
|
||||
|
||||
// 4. 处理未标记的分组
|
||||
if (untaggedCommands.length > 0) {
|
||||
// 初始化展开状态 (如果未定义,默认为 true)
|
||||
if (expandedGroups.value[untaggedGroupName] === undefined) {
|
||||
expandedGroups.value[untaggedGroupName] = true;
|
||||
}
|
||||
// 组内排序
|
||||
untaggedCommands.sort((a, b) => {
|
||||
if (sortBy.value === 'usage_count') {
|
||||
if (b.usage_count !== a.usage_count) return b.usage_count - a.usage_count;
|
||||
}
|
||||
const nameA = a.name ?? a.command;
|
||||
const nameB = b.name ?? b.command;
|
||||
return nameA.localeCompare(nameB);
|
||||
});
|
||||
result.push({
|
||||
groupName: untaggedGroupName,
|
||||
tagId: null,
|
||||
commands: untaggedCommands
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
// +++ 新增 Getter: 获取当前可见的扁平指令列表 (用于键盘导航) +++
|
||||
const flatVisibleCommands = computed((): QuickCommandFE[] => {
|
||||
const flatList: QuickCommandFE[] = [];
|
||||
filteredAndGroupedCommands.value.forEach(group => {
|
||||
// 只添加已展开分组中的指令
|
||||
if (expandedGroups.value[group.groupName]) {
|
||||
flatList.push(...group.commands);
|
||||
}
|
||||
});
|
||||
return flatList;
|
||||
});
|
||||
|
||||
|
||||
// --- Actions ---
|
||||
|
||||
// NEW: Action to select the next command in the filtered list
|
||||
// +++ Load initial expanded groups state from localStorage +++
|
||||
const loadExpandedGroups = () => {
|
||||
try {
|
||||
const storedState = localStorage.getItem(EXPANDED_GROUPS_STORAGE_KEY);
|
||||
if (storedState) {
|
||||
const parsedState = JSON.parse(storedState);
|
||||
if (typeof parsedState === 'object' && parsedState !== null) {
|
||||
expandedGroups.value = parsedState;
|
||||
console.log('[QuickCmdStore] Loaded expanded groups state from localStorage.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[QuickCmdStore] Failed to load or parse expanded groups state:', e);
|
||||
localStorage.removeItem(EXPANDED_GROUPS_STORAGE_KEY);
|
||||
}
|
||||
// Default to empty object if no valid state found
|
||||
expandedGroups.value = {};
|
||||
};
|
||||
|
||||
// +++ Save expanded groups state to localStorage +++
|
||||
const saveExpandedGroups = () => {
|
||||
try {
|
||||
localStorage.setItem(EXPANDED_GROUPS_STORAGE_KEY, JSON.stringify(expandedGroups.value));
|
||||
} catch (e) {
|
||||
console.error('[QuickCmdStore] Failed to save expanded groups state:', e);
|
||||
}
|
||||
};
|
||||
|
||||
// +++ Watch for changes and save +++
|
||||
watch(expandedGroups, saveExpandedGroups, { deep: true });
|
||||
|
||||
// +++ Action to toggle group expansion +++
|
||||
const toggleGroup = (groupName: string) => {
|
||||
// Ensure the group exists in the state before toggling
|
||||
if (expandedGroups.value[groupName] === undefined) {
|
||||
// Default to true if toggling a group that wasn't explicitly set (e.g., newly appeared group)
|
||||
expandedGroups.value[groupName] = false; // Start collapsed if toggled first time? Or true? Let's start true.
|
||||
} else {
|
||||
expandedGroups.value[groupName] = !expandedGroups.value[groupName];
|
||||
}
|
||||
// The watcher will automatically save the state
|
||||
// Reset selection when a group is toggled? Maybe not necessary.
|
||||
// selectedIndex.value = -1;
|
||||
};
|
||||
|
||||
// Action to select the next command in the *visible* flat list
|
||||
const selectNextCommand = () => {
|
||||
const commands = filteredAndSortedCommands.value;
|
||||
const commands = flatVisibleCommands.value; // Use the flat visible list
|
||||
if (commands.length === 0) {
|
||||
selectedIndex.value = -1;
|
||||
return;
|
||||
@@ -64,9 +226,9 @@ export const useQuickCommandsStore = defineStore('quickCommands', () => {
|
||||
selectedIndex.value = (selectedIndex.value + 1) % commands.length;
|
||||
};
|
||||
|
||||
// NEW: Action to select the previous command in the filtered list
|
||||
// Action to select the previous command in the *visible* flat list
|
||||
const selectPreviousCommand = () => {
|
||||
const commands = filteredAndSortedCommands.value;
|
||||
const commands = flatVisibleCommands.value; // Use the flat visible list
|
||||
if (commands.length === 0) {
|
||||
selectedIndex.value = -1;
|
||||
return;
|
||||
@@ -74,37 +236,48 @@ export const useQuickCommandsStore = defineStore('quickCommands', () => {
|
||||
selectedIndex.value = (selectedIndex.value - 1 + commands.length) % commands.length;
|
||||
};
|
||||
|
||||
// 从后端获取快捷指令 (带缓存和排序)
|
||||
// 从后端获取快捷指令 (包含 tagIds,不再发送 sortBy)
|
||||
const fetchQuickCommands = async () => {
|
||||
const cacheKey = 'quickCommandsCache';
|
||||
// 将排序方式加入缓存键,确保不同排序有不同缓存
|
||||
const cacheKeyWithSort = `${cacheKey}_${sortBy.value}`;
|
||||
error.value = null; // 重置错误
|
||||
// 简化缓存:只缓存原始列表,不再区分排序
|
||||
const cacheKey = 'quickCommandsListCache';
|
||||
error.value = null;
|
||||
|
||||
// 1. 尝试从 localStorage 加载缓存
|
||||
try {
|
||||
const cachedData = localStorage.getItem(cacheKeyWithSort);
|
||||
const cachedData = localStorage.getItem(cacheKey);
|
||||
if (cachedData) {
|
||||
console.log(`[QuickCmdStore] Loading commands from cache (sort: ${sortBy.value}).`);
|
||||
quickCommandsList.value = JSON.parse(cachedData);
|
||||
isLoading.value = false; // 先显示缓存
|
||||
console.log(`[QuickCmdStore] Loading commands from cache.`);
|
||||
// 确保解析后的数据符合 QuickCommandFE 结构 (特别是 tagIds)
|
||||
const parsedData = JSON.parse(cachedData) as QuickCommandFE[];
|
||||
// 基本验证,确保 tagIds 是数组
|
||||
if (Array.isArray(parsedData) && parsedData.every(item => Array.isArray(item.tagIds))) {
|
||||
quickCommandsList.value = parsedData;
|
||||
isLoading.value = false;
|
||||
} else {
|
||||
console.warn('[QuickCmdStore] Cached data format invalid, ignoring cache.');
|
||||
localStorage.removeItem(cacheKey);
|
||||
isLoading.value = true;
|
||||
}
|
||||
} else {
|
||||
isLoading.value = true; // 无缓存,初始加载
|
||||
isLoading.value = true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[QuickCmdStore] Failed to load or parse commands cache:', e);
|
||||
localStorage.removeItem(cacheKeyWithSort); // 解析失败则移除缓存
|
||||
isLoading.value = true; // 缓存无效,需要加载
|
||||
localStorage.removeItem(cacheKey);
|
||||
isLoading.value = true;
|
||||
}
|
||||
|
||||
// 2. 后台获取最新数据
|
||||
isLoading.value = true; // 标记正在后台获取
|
||||
isLoading.value = true;
|
||||
try {
|
||||
console.log(`[QuickCmdStore] Fetching latest commands from server (sort: ${sortBy.value})...`);
|
||||
const response = await apiClient.get<QuickCommandFE[]>('/quick-commands', {
|
||||
params: { sortBy: sortBy.value }
|
||||
});
|
||||
const freshData = response.data;
|
||||
console.log(`[QuickCmdStore] Fetching latest commands from server...`);
|
||||
// 不再发送 sortBy 参数
|
||||
const response = await apiClient.get<QuickCommandFE[]>('/quick-commands');
|
||||
// 确保返回的数据包含 tagIds 数组
|
||||
const freshData = response.data.map(cmd => ({
|
||||
...cmd,
|
||||
tagIds: Array.isArray(cmd.tagIds) ? cmd.tagIds : [] // 确保 tagIds 是数组
|
||||
}));
|
||||
const freshDataString = JSON.stringify(freshData);
|
||||
|
||||
// 3. 对比并更新
|
||||
@@ -112,37 +285,37 @@ export const useQuickCommandsStore = defineStore('quickCommands', () => {
|
||||
if (currentDataString !== freshDataString) {
|
||||
console.log('[QuickCmdStore] Commands data changed, updating state and cache.');
|
||||
quickCommandsList.value = freshData;
|
||||
localStorage.setItem(cacheKeyWithSort, freshDataString); // 更新对应排序的缓存
|
||||
localStorage.setItem(cacheKey, freshDataString); // 更新缓存
|
||||
} else {
|
||||
console.log('[QuickCmdStore] Commands data is up-to-date.');
|
||||
}
|
||||
error.value = null; // 清除错误
|
||||
error.value = null;
|
||||
} catch (err: any) {
|
||||
console.error('[QuickCmdStore] 获取快捷指令失败:', err);
|
||||
error.value = err.response?.data?.message || '获取快捷指令时发生错误';
|
||||
// 保留缓存数据,仅设置错误状态
|
||||
uiNotificationsStore.showError(error.value ?? '未知错误');
|
||||
if (error.value) {
|
||||
uiNotificationsStore.showError(error.value);
|
||||
}
|
||||
} finally {
|
||||
isLoading.value = false; // 加载完成
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 清除所有排序的快捷指令缓存
|
||||
// 清除快捷指令列表缓存
|
||||
const clearQuickCommandsCache = () => {
|
||||
const cacheKeyBase = 'quickCommandsCache';
|
||||
// 移除两种排序的缓存
|
||||
localStorage.removeItem(`${cacheKeyBase}_name`);
|
||||
localStorage.removeItem(`${cacheKeyBase}_usage_count`);
|
||||
console.log('[QuickCmdStore] Cleared all quick commands caches.');
|
||||
localStorage.removeItem('quickCommandsListCache');
|
||||
console.log('[QuickCmdStore] Cleared quick commands list cache.');
|
||||
};
|
||||
|
||||
|
||||
// 添加快捷指令 (添加后清除缓存)
|
||||
const addQuickCommand = async (name: string | null, command: string): Promise<boolean> => {
|
||||
// 添加快捷指令 (发送 tagIds)
|
||||
const addQuickCommand = async (name: string | null, command: string, tagIds?: number[]): Promise<boolean> => {
|
||||
try {
|
||||
await apiClient.post('/quick-commands', { name, command });
|
||||
clearQuickCommandsCache(); // 清除所有排序缓存
|
||||
await fetchQuickCommands(); // 刷新当前排序的列表和缓存
|
||||
// 在请求体中包含 tagIds
|
||||
const response = await apiClient.post<{ message: string, command: QuickCommandFE }>('/quick-commands', { name, command, tagIds });
|
||||
// 后端现在返回完整的 command 对象,可以直接使用或触发刷新
|
||||
clearQuickCommandsCache(); // 清除缓存
|
||||
await fetchQuickCommands(); // 重新获取以确保数据同步
|
||||
uiNotificationsStore.showSuccess('快捷指令已添加');
|
||||
return true;
|
||||
} catch (err: any) {
|
||||
@@ -153,12 +326,14 @@ export const useQuickCommandsStore = defineStore('quickCommands', () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 更新快捷指令
|
||||
const updateQuickCommand = async (id: number, name: string | null, command: string): Promise<boolean> => {
|
||||
// 更新快捷指令 (发送 tagIds)
|
||||
const updateQuickCommand = async (id: number, name: string | null, command: string, tagIds?: number[]): Promise<boolean> => {
|
||||
try {
|
||||
await apiClient.put(`/quick-commands/${id}`, { name, command });
|
||||
clearQuickCommandsCache(); // 清除所有排序缓存
|
||||
await fetchQuickCommands(); // 刷新当前排序的列表和缓存
|
||||
// 在请求体中包含 tagIds (即使是 undefined 也要发送,让后端知道是否要更新)
|
||||
const response = await apiClient.put<{ message: string, command: QuickCommandFE }>(`/quick-commands/${id}`, { name, command, tagIds });
|
||||
// 后端现在返回完整的 command 对象
|
||||
clearQuickCommandsCache(); // 清除缓存
|
||||
await fetchQuickCommands(); // 重新获取以确保数据同步
|
||||
uiNotificationsStore.showSuccess('快捷指令已更新');
|
||||
return true;
|
||||
} catch (err: any) {
|
||||
@@ -214,12 +389,12 @@ export const useQuickCommandsStore = defineStore('quickCommands', () => {
|
||||
selectedIndex.value = -1; // Reset selection when search term changes
|
||||
};
|
||||
|
||||
// 设置排序方式并重新获取数据
|
||||
const setSortBy = async (newSortBy: QuickCommandSortByType) => {
|
||||
// 设置排序方式 (只更新本地状态,不再重新获取数据)
|
||||
const setSortBy = (newSortBy: QuickCommandSortByType) => {
|
||||
if (sortBy.value !== newSortBy) {
|
||||
sortBy.value = newSortBy;
|
||||
// 排序方式改变,不需要清除缓存,fetchQuickCommands 会读取对应排序的缓存或重新获取
|
||||
await fetchQuickCommands();
|
||||
// 排序现在由 filteredAndGroupedCommands getter 处理,无需重新 fetch
|
||||
selectedIndex.value = -1; // Reset selection when sort changes
|
||||
}
|
||||
};
|
||||
|
||||
@@ -236,8 +411,10 @@ export const useQuickCommandsStore = defineStore('quickCommands', () => {
|
||||
sortBy,
|
||||
isLoading,
|
||||
error,
|
||||
filteredAndSortedCommands, // 使用计算属性
|
||||
selectedIndex, // NEW: Expose selected index
|
||||
filteredAndGroupedCommands, // Expose the grouped data
|
||||
flatVisibleCommands, // Expose the flat visible list for navigation logic if needed outside
|
||||
selectedIndex, // Index within flatVisibleCommands
|
||||
expandedGroups, // Expose expanded groups state
|
||||
fetchQuickCommands,
|
||||
addQuickCommand,
|
||||
updateQuickCommand,
|
||||
@@ -245,8 +422,64 @@ export const useQuickCommandsStore = defineStore('quickCommands', () => {
|
||||
incrementUsage,
|
||||
setSearchTerm,
|
||||
setSortBy,
|
||||
selectNextCommand, // NEW: Expose action
|
||||
selectPreviousCommand, // NEW: Expose action
|
||||
resetSelection, // Ensure resetSelection is exported
|
||||
selectNextCommand,
|
||||
selectPreviousCommand,
|
||||
resetSelection,
|
||||
toggleGroup, // +++ Expose toggleGroup action +++
|
||||
loadExpandedGroups, // +++ Expose load action +++
|
||||
|
||||
// +++ Action to assign a tag to multiple commands +++
|
||||
async assignCommandsToTagAction(commandIds: number[], tagId: number): Promise<boolean> {
|
||||
if (!commandIds || commandIds.length === 0) {
|
||||
console.warn('[Store] assignCommandsToTagAction: No command IDs provided.');
|
||||
return false;
|
||||
}
|
||||
isLoading.value = true; // Use the store's isLoading state
|
||||
error.value = null; // Use the store's error state
|
||||
try {
|
||||
const response = await apiClient.post('/quick-commands/bulk-assign-tag', { commandIds, tagId });
|
||||
if (response.data.success) {
|
||||
console.log(`[Store] Successfully assigned tag ${tagId} to ${commandIds.length} commands via API.`);
|
||||
|
||||
// --- Manual state update for immediate UI feedback ---
|
||||
let updatedCount = 0;
|
||||
commandIds.forEach(cmdId => {
|
||||
const commandIndex = quickCommandsList.value.findIndex(cmd => cmd.id === cmdId);
|
||||
if (commandIndex !== -1) {
|
||||
const command = quickCommandsList.value[commandIndex];
|
||||
// Ensure tagIds exists and add the new tagId if not already present
|
||||
if (!Array.isArray(command.tagIds)) {
|
||||
command.tagIds = [];
|
||||
}
|
||||
if (!command.tagIds.includes(tagId)) {
|
||||
command.tagIds.push(tagId);
|
||||
updatedCount++;
|
||||
}
|
||||
} else {
|
||||
console.warn(`[Store] assignCommandsToTagAction: Command ID ${cmdId} not found in local list for manual update.`);
|
||||
}
|
||||
});
|
||||
console.log(`[Store] Manually updated tagIds for ${updatedCount} commands in local state.`);
|
||||
// --- End manual state update ---
|
||||
|
||||
// Optionally, still fetch for full consistency, but UI should update based on manual change first.
|
||||
// clearQuickCommandsCache();
|
||||
// await fetchQuickCommands();
|
||||
return true;
|
||||
} else {
|
||||
// This case might not happen if backend throws errors instead
|
||||
error.value = response.data.message || '批量分配标签失败 (未知)';
|
||||
if (error.value) uiNotificationsStore.showError(error.value); // Check if error.value is not null
|
||||
return false;
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('[Store] Error assigning tag to commands:', err);
|
||||
error.value = err.response?.data?.message || err.message || '批量分配标签时发生网络或服务器错误';
|
||||
if (error.value) uiNotificationsStore.showError(error.value); // Check if error.value is not null
|
||||
return false;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user