feat: 添加快捷指令的标签管理系统

Related to #5
This commit is contained in:
Baobhan Sith
2025-05-03 15:18:51 +08:00
parent 430aac8512
commit 026ed949fb
22 changed files with 1828 additions and 296 deletions
@@ -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;
}
},
};
});