feat: 状态监视器添加IP地址显示
This commit is contained in:
@@ -59,8 +59,8 @@
|
||||
import { ref, reactive, computed, watch, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuickCommandsStore, type QuickCommandFE } from '../stores/quickCommands.store';
|
||||
import { useQuickCommandTagsStore } from '../stores/quickCommandTags.store'; // +++ Import new tag store +++
|
||||
import TagInput from './TagInput.vue'; // +++ Import TagInput component (assuming it exists) +++
|
||||
import { useQuickCommandTagsStore } from '../stores/quickCommandTags.store';
|
||||
import TagInput from './TagInput.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
commandToEdit?: QuickCommandFE | null; // 接收要编辑的指令对象 (should include tagIds)
|
||||
@@ -109,12 +109,12 @@ onMounted(() => {
|
||||
// --- Tag Creation Handling ---
|
||||
// Assuming TagInput emits 'create-tag' with the tag name
|
||||
const handleCreateTag = async (tagName: string) => {
|
||||
console.log(`[QuickCmdForm] Received create-tag event for: ${tagName}`); // +++ 添加日志 +++
|
||||
console.log(`[QuickCmdForm] Received create-tag event for: ${tagName}`);
|
||||
if (!tagName || tagName.trim().length === 0) return;
|
||||
console.log(`[QuickCmdForm] Calling quickCommandTagsStore.addTag...`); // +++ 添加日志 +++
|
||||
console.log(`[QuickCmdForm] Calling quickCommandTagsStore.addTag...`);
|
||||
const newTag = await quickCommandTagsStore.addTag(tagName.trim());
|
||||
if (newTag && !formData.tagIds.includes(newTag.id)) {
|
||||
console.log(`[QuickCmdForm] New tag created (ID: ${newTag.id}), adding to selection.`); // +++ 添加日志 +++
|
||||
console.log(`[QuickCmdForm] New tag created (ID: ${newTag.id}), adding to selection.`);
|
||||
// Add the new tag's ID to the selected list
|
||||
formData.tagIds.push(newTag.id);
|
||||
}
|
||||
@@ -122,12 +122,12 @@ const handleCreateTag = async (tagName: string) => {
|
||||
|
||||
// --- Tag Deletion Handling ---
|
||||
const handleDeleteTag = async (tagId: number) => {
|
||||
console.log(`[QuickCmdForm] Received delete-tag event for ID: ${tagId}`); // +++ 添加日志 +++
|
||||
console.log(`[QuickCmdForm] Received delete-tag event for ID: ${tagId}`);
|
||||
const tagToDelete = quickCommandTagsStore.tags.find(t => t.id === tagId);
|
||||
if (!tagToDelete) return;
|
||||
|
||||
if (confirm(t('tags.prompts.confirmDelete', { name: tagToDelete.name }))) {
|
||||
console.log(`[QuickCmdForm] Calling quickCommandTagsStore.deleteTag...`); // +++ 添加日志 +++
|
||||
console.log(`[QuickCmdForm] Calling quickCommandTagsStore.deleteTag...`);
|
||||
const success = await quickCommandTagsStore.deleteTag(tagId);
|
||||
if (success) {
|
||||
// If deletion is successful, TagInput's availableTags will update,
|
||||
@@ -135,7 +135,7 @@ const handleDeleteTag = async (tagId: number) => {
|
||||
// We also need to remove it from the local formData.tagIds if it was selected.
|
||||
const index = formData.tagIds.indexOf(tagId);
|
||||
if (index > -1) {
|
||||
console.log(`[QuickCmdForm] Removing deleted tag ID ${tagId} from selection.`); // +++ 添加日志 +++
|
||||
console.log(`[QuickCmdForm] Removing deleted tag ID ${tagId} from selection.`);
|
||||
formData.tagIds.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -46,12 +46,12 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { useCommandHistoryStore, CommandHistoryEntryFE } from '../stores/commandHistory.store';
|
||||
import { useUiNotificationsStore } from '../stores/uiNotifications.store'; // 引入 UI 通知 store
|
||||
import { useI18n } from 'vue-i18n'; // 引入 i18n
|
||||
import { useUiNotificationsStore } from '../stores/uiNotifications.store';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const commandHistoryStore = useCommandHistoryStore();
|
||||
const uiNotificationsStore = useUiNotificationsStore(); // 实例化 UI 通知 store
|
||||
const { t } = useI18n(); // 使用 i18n
|
||||
const uiNotificationsStore = useUiNotificationsStore();
|
||||
const { t } = useI18n();
|
||||
const hoveredItemId = ref<number | null>(null);
|
||||
const listContainer = ref<HTMLElement | null>(null);
|
||||
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
import { ref, watch, nextTick, onMounted, onBeforeUnmount, defineExpose, computed, defineOptions } from 'vue'; // Import defineOptions
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useSessionStore } from '../stores/session.store'; // +++ 导入 Session Store +++
|
||||
import { useSessionStore } from '../stores/session.store';
|
||||
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store';
|
||||
import { useSettingsStore } from '../stores/settings.store';
|
||||
import { useQuickCommandsStore } from '../stores/quickCommands.store';
|
||||
import { useCommandHistoryStore } from '../stores/commandHistory.store';
|
||||
import QuickCommandsModal from './QuickCommandsModal.vue'; // +++ Import the modal component +++
|
||||
import SuspendedSshSessionsModal from './SuspendedSshSessionsModal.vue'; // +++ Import the new modal +++
|
||||
import { useFileEditorStore } from '../stores/fileEditor.store'; // +++ Import File Editor Store +++
|
||||
import QuickCommandsModal from './QuickCommandsModal.vue';
|
||||
import SuspendedSshSessionsModal from './SuspendedSshSessionsModal.vue';
|
||||
import { useFileEditorStore } from '../stores/fileEditor.store';
|
||||
import { useWorkspaceEventEmitter } from '../composables/workspaceEvents';
|
||||
|
||||
// Disable attribute inheritance as this component has multiple root nodes (div + modal)
|
||||
|
||||
defineOptions({ inheritAttrs: false });
|
||||
|
||||
const emitWorkspaceEvent = useWorkspaceEventEmitter(); // +++ 获取事件发射器 +++
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, type PropType, ref, watch, defineExpose, onMounted, onBeforeUnmount, nextTick } from 'vue'; // 添加 nextTick
|
||||
import { useI18n } from 'vue-i18n';
|
||||
// import { storeToRefs } from 'pinia'; // 移除 storeToRefs
|
||||
import MonacoEditor from './MonacoEditor.vue'; // 导入 Monaco Editor 组件
|
||||
import FileEditorTabs from './FileEditorTabs.vue'; // 导入标签栏组件 (路径确认无误)
|
||||
// import { useFileEditorStore } from '../stores/fileEditor.store'; // 移除 Store 导入
|
||||
import type { FileTab } from '../stores/fileEditor.store'; // 保留类型导入
|
||||
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store'; // +++ 导入焦点切换 Store +++
|
||||
import { useSessionStore } from '../stores/session.store'; // +++ 导入会话 Store +++
|
||||
import { useSettingsStore } from '../stores/settings.store'; // +++ 导入设置 Store +++
|
||||
import { storeToRefs } from 'pinia'; // +++ 导入 storeToRefs +++
|
||||
import MonacoEditor from './MonacoEditor.vue';
|
||||
import FileEditorTabs from './FileEditorTabs.vue';
|
||||
import type { FileTab } from '../stores/fileEditor.store';
|
||||
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store';
|
||||
import { useSessionStore } from '../stores/session.store';
|
||||
import { useSettingsStore } from '../stores/settings.store';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useWorkspaceEventEmitter } from '../composables/workspaceEvents';
|
||||
|
||||
const { t } = useI18n();
|
||||
@@ -228,7 +226,7 @@ let unregisterFocusFn: (() => void) | null = null; // 保存注销函数
|
||||
onMounted(() => {
|
||||
// 注册动作并保存返回的注销函数
|
||||
unregisterFocusFn = focusSwitcherStore.registerFocusAction('fileEditorActive', focusActiveEditor);
|
||||
// +++ 添加键盘事件监听器 +++
|
||||
// +++ 键盘事件监听器 +++
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
});
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ const {
|
||||
closeOtherTabs, // 修正:移除 Global 后缀
|
||||
closeTabsToTheRight, // 修正:移除 Global 后缀
|
||||
closeTabsToTheLeft, // 修正:移除 Global 后缀
|
||||
changeEncoding: changeGlobalEncoding, // +++ 添加全局编码更改 action +++
|
||||
changeEncoding: changeGlobalEncoding, // +++ 全局编码更改 action +++
|
||||
} = fileEditorStore;
|
||||
|
||||
// 会话 Store Actions (用于非共享模式)
|
||||
@@ -58,7 +58,7 @@ const {
|
||||
closeOtherTabsInSession,
|
||||
closeTabsToTheRightInSession,
|
||||
closeTabsToTheLeftInSession,
|
||||
changeEncodingInSession, // +++ 添加会话编码更改 action +++
|
||||
changeEncodingInSession, // +++ 会话编码更改 action +++
|
||||
} = sessionStore;
|
||||
|
||||
// --- 移除本地文件状态 ---
|
||||
|
||||
@@ -646,7 +646,7 @@ const triggerDownloadDirectory = (item: FileListItem) => {
|
||||
|
||||
|
||||
|
||||
// +++ 添加压缩/解压处理函数 +++
|
||||
// +++ 压缩/解压处理函数 +++
|
||||
const handleCompress = (items: FileListItem[], format: CompressFormat) => {
|
||||
if (!currentSftpManager.value) {
|
||||
console.error(`[FileManager ${props.sessionId}-${props.instanceId}] Cannot compress: SFTP manager not available.`);
|
||||
@@ -800,7 +800,7 @@ watch(sortDirection, () => {
|
||||
const saveLayoutSettings = () => {
|
||||
// 确保 colWidths.value 是普通对象,而不是 Proxy
|
||||
const widthsToSave = JSON.parse(JSON.stringify(colWidths.value));
|
||||
// +++ 添加日志:记录保存的值 +++
|
||||
// +++ 日志:记录保存的值 +++
|
||||
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] Triggering saveLayoutSettings: multiplier=${rowSizeMultiplier.value}, widths=${JSON.stringify(widthsToSave)}`);
|
||||
settingsStore.updateFileManagerLayoutSettings(rowSizeMultiplier.value, widthsToSave);
|
||||
};
|
||||
@@ -819,7 +819,7 @@ watchEffect(() => {
|
||||
const storeMultiplier = fileManagerRowSizeMultiplierNumber.value;
|
||||
const storeWidths = fileManagerColWidthsObject.value;
|
||||
|
||||
// +++ 添加日志:记录从 store 获取的值 +++
|
||||
// +++ 日志:记录从 store 获取的值 +++
|
||||
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] watchEffect triggered. Store values: multiplier=${storeMultiplier}, widths=${JSON.stringify(storeWidths)}`);
|
||||
|
||||
// 只有当 store 加载完成并提供了有效值时才更新
|
||||
@@ -829,7 +829,7 @@ watchEffect(() => {
|
||||
const currentWidthsString = JSON.stringify(colWidths.value);
|
||||
const storeWidthsString = JSON.stringify(storeWidths);
|
||||
|
||||
// +++ 添加日志:记录当前值和 store 值,以及是否更新 +++
|
||||
// +++ 日志:记录当前值和 store 值,以及是否更新 +++
|
||||
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] Comparing values: Current Multiplier=${currentMultiplier}, Store Multiplier=${storeMultiplier}. Update needed: ${storeMultiplier !== currentMultiplier}`);
|
||||
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] Comparing values: Current Widths=${currentWidthsString}, Store Widths=${storeWidthsString}. Update needed: ${storeWidthsString !== currentWidthsString}`);
|
||||
|
||||
@@ -851,7 +851,7 @@ watchEffect(() => {
|
||||
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] Column widths updated from store: ${JSON.stringify(updatedWidths)}`);
|
||||
}
|
||||
} else {
|
||||
// +++ 添加日志:记录等待 store 加载 +++
|
||||
// +++ 日志:记录等待 store 加载 +++
|
||||
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] Waiting for valid layout settings from store... Store Multiplier=${storeMultiplier}, Store Widths Keys=${Object.keys(storeWidths).length}`);
|
||||
}
|
||||
});
|
||||
@@ -1103,7 +1103,7 @@ const stopResize = () => {
|
||||
document.body.style.cursor = '';
|
||||
document.body.style.userSelect = '';
|
||||
// +++ 在调整结束后保存列宽 +++
|
||||
// +++ 添加日志:记录触发保存 +++
|
||||
// +++ 日志:记录触发保存 +++
|
||||
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] stopResize triggered saveLayoutSettings.`);
|
||||
saveLayoutSettings();
|
||||
}
|
||||
@@ -1240,7 +1240,7 @@ const handleWheel = (event: WheelEvent) => {
|
||||
// console.log(`Row size multiplier: ${rowSizeMultiplier.value}`); // 调试日志
|
||||
// +++ 在行大小变化后保存设置 +++
|
||||
if (rowSizeMultiplier.value !== oldMultiplier) {
|
||||
// +++ 添加日志:记录触发保存 +++
|
||||
// +++ 日志:记录触发保存 +++
|
||||
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] handleWheel triggered saveLayoutSettings.`);
|
||||
saveLayoutSettings();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, type PropType } from 'vue';
|
||||
import type { ContextMenuItem } from '../composables/file-manager/useFileManagerContextMenu'; // 导入菜单项类型
|
||||
import type { ContextMenuItem } from '../composables/file-manager/useFileManagerContextMenu';
|
||||
import { onUnmounted } from 'vue';
|
||||
import { useDeviceDetection } from '../composables/useDeviceDetection';
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, type Ref, nextTick } from 'vue'; // Import nextTick
|
||||
import { ref, computed, watch, type Ref, nextTick } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useLayoutStore, type LayoutNode, type PaneName } from '../stores/layout.store';
|
||||
import { useSettingsStore } from '../stores/settings.store'; // +++ Import settings store +++
|
||||
import { storeToRefs } from 'pinia'; // +++ Import storeToRefs +++
|
||||
import { useSettingsStore } from '../stores/settings.store';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import draggable from 'vuedraggable';
|
||||
import LayoutNodeEditor from './LayoutNodeEditor.vue';
|
||||
// +++ Import a switch component if available, otherwise use checkbox +++
|
||||
// Assuming a simple checkbox for now
|
||||
|
||||
|
||||
|
||||
// --- Props ---
|
||||
const props = defineProps({
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import type { ConnectionInfo } from '../stores/connections.store'; // +++ 导入 ConnectionInfo 类型 +++
|
||||
import type { ConnectionInfo } from '../stores/connections.store';
|
||||
import { computed, defineAsyncComponent, type PropType, type Component, ref, watch, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n'; // <-- Import useI18n
|
||||
// 添加依赖 font-awesome
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import '@fortawesome/fontawesome-free/css/all.min.css';
|
||||
import { Splitpanes, Pane } from 'splitpanes';
|
||||
import { useLayoutStore, type LayoutNode, type PaneName } from '../stores/layout.store';
|
||||
import { useSessionStore } from '../stores/session.store';
|
||||
import { useFileEditorStore } from '../stores/fileEditor.store'; // <-- Import FileEditorStore
|
||||
import { useSettingsStore } from '../stores/settings.store'; // +++ Import SettingsStore +++
|
||||
import { useSidebarResize } from '../composables/useSidebarResize'; // +++ Import useSidebarResize +++
|
||||
import { useFileEditorStore } from '../stores/fileEditor.store';
|
||||
import { useSettingsStore } from '../stores/settings.store';
|
||||
import { useSidebarResize } from '../composables/useSidebarResize';
|
||||
import { storeToRefs } from 'pinia';
|
||||
// import { defineEmits } from 'vue'; // --- 移除 ---
|
||||
|
||||
|
||||
// --- Props ---
|
||||
const props = defineProps({
|
||||
@@ -267,7 +267,7 @@ const sidebarProps = computed(() => (paneName: PaneName | null, side: 'left' | '
|
||||
// --- Methods ---
|
||||
// 处理 Splitpanes 大小调整事件
|
||||
const handlePaneResize = (eventData: { panes: Array<{ size: number; [key: string]: any }> }) => {
|
||||
// +++ 添加更详细的日志 +++
|
||||
// +++ 更详细的日志 +++
|
||||
// +++ Log the entire layoutNode object if ID is undefined +++
|
||||
if (props.layoutNode && typeof props.layoutNode.id === 'undefined') {
|
||||
console.warn(`[LayoutRenderer DEBUG] handlePaneResize triggered but props.layoutNode.id is undefined. Full layoutNode prop:`, JSON.parse(JSON.stringify(props.layoutNode)));
|
||||
@@ -291,7 +291,7 @@ const handlePaneResize = (eventData: { panes: Array<{ size: number; [key: string
|
||||
size: paneInfo.size
|
||||
}));
|
||||
|
||||
// +++ 添加调用 store action 前的日志 +++
|
||||
// +++ 调用 store action 前的日志 +++
|
||||
// console.log(`[LayoutRenderer DEBUG] Calling layoutStore.updateNodeSizes for node ID: ${props.layoutNode.id} with sizes:`, JSON.parse(JSON.stringify(childrenSizes)));
|
||||
// 调用 store action 来更新节点大小
|
||||
layoutStore.updateNodeSizes(props.layoutNode.id, childrenSizes);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { ref, onMounted, onBeforeUnmount, watch, defineExpose } from 'vue';
|
||||
import * as monaco from 'monaco-editor';
|
||||
|
||||
const localFontSize = ref(14); // 添加本地字体大小状态,默认 14
|
||||
const localFontSize = ref(14); // 本地字体大小状态,默认 14
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { useNotificationsStore } from '../stores/notifications.store';
|
||||
import { NotificationSetting, NotificationChannelType, NotificationEvent } from '../types/server.types';
|
||||
import NotificationSettingForm from './NotificationSettingForm.vue'; // Import the form component
|
||||
import NotificationSettingForm from './NotificationSettingForm.vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const store = useNotificationsStore();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { ref, onMounted, onUnmounted, watch, nextTick, computed, watchEffect } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useSettingsStore } from '../stores/settings.store';
|
||||
import { useConnectionsStore } from '../stores/connections.store'; // +++ Import connections store +++
|
||||
import { useConnectionsStore } from '../stores/connections.store';
|
||||
// @ts-ignore - guacamole-common-js 缺少官方类型定义
|
||||
import Guacamole from 'guacamole-common-js';
|
||||
import apiClient from '../utils/apiClient';
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useSshKeysStore } from '../stores/sshKeys.store';
|
||||
import SshKeyManagementModal from './SshKeyManagementModal.vue'; // Import the modal
|
||||
import SshKeyManagementModal from './SshKeyManagementModal.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: number | null; // The selected ssh_key_id (v-model)
|
||||
modelValue: number | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['update:modelValue']); // Removed 'use-direct-input' event
|
||||
|
||||
@@ -40,11 +40,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted, computed, type PropType } from 'vue'; // 添加 PropType
|
||||
import { ref, watch, onMounted, computed, type PropType } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { Line } from 'vue-chartjs';
|
||||
import { useSessionStore } from '../stores/session.store'; // 注入 sessionStore
|
||||
import { storeToRefs } from 'pinia'; // 导入 storeToRefs
|
||||
import { useSessionStore } from '../stores/session.store';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
Title,
|
||||
|
||||
@@ -25,6 +25,19 @@
|
||||
|
||||
<!-- 状态网格 -->
|
||||
<div v-else class="status-grid grid gap-3">
|
||||
<!-- IP 地址 (如果启用) -->
|
||||
<div v-if="statusMonitorShowIpBoolean && activeSessionId && sessionIpAddress" class="status-item grid grid-cols-[auto_1fr] items-center gap-3">
|
||||
<label class="font-semibold text-text-secondary text-left whitespace-nowrap">IP:</label>
|
||||
<div class="flex items-center">
|
||||
<span
|
||||
class="ip-address-value truncate text-left cursor-pointer hover:text-primary transition-colors"
|
||||
:title="sessionIpAddress"
|
||||
@click="copyIpToClipboard(sessionIpAddress)">
|
||||
{{ sessionIpAddress }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CPU 型号 -->
|
||||
<div class="status-item grid grid-cols-[auto_1fr] items-center gap-3">
|
||||
<label class="font-semibold text-text-secondary text-left whitespace-nowrap">{{ t('statusMonitor.cpuModelLabel') }}</label>
|
||||
@@ -122,9 +135,17 @@ import { useI18n } from 'vue-i18n';
|
||||
import StatusCharts from './StatusCharts.vue';
|
||||
import { useSessionStore } from '../stores/session.store'; // 注入 sessionStore
|
||||
import { storeToRefs } from 'pinia'; // 导入 storeToRefs
|
||||
import { useSettingsStore } from '../stores/settings.store'; // 导入设置 store
|
||||
import { useConnectionsStore } from '../stores/connections.store'; // 导入连接 store
|
||||
import { useUiNotificationsStore } from '../stores/uiNotifications.store'; // + 导入通知 store
|
||||
|
||||
const { t } = useI18n();
|
||||
const sessionStore = useSessionStore();
|
||||
const settingsStore = useSettingsStore(); // 实例化设置 store
|
||||
const connectionsStore = useConnectionsStore(); // 实例化连接 store
|
||||
const uiNotificationsStore = useUiNotificationsStore(); // + 实例化通知 store
|
||||
const { sessions } = storeToRefs(sessionStore); // 获取响应式的 sessions
|
||||
const { statusMonitorShowIpBoolean } = storeToRefs(settingsStore); // 获取 IP 显示设置
|
||||
const isSwitchingSession = ref(false);
|
||||
|
||||
interface ServerStatus {
|
||||
@@ -278,4 +299,30 @@ const swapDisplay = computed(() => {
|
||||
return `${formatMemorySize(used)} / ${formatMemorySize(total)} ${percent}`;
|
||||
});
|
||||
|
||||
const sessionIpAddress = computed(() => {
|
||||
const sessionState = currentSessionState.value;
|
||||
if (sessionState && sessionState.connectionId) {
|
||||
// 直接从 connectionsStore 的 connections 数组中查找
|
||||
const connectionIdAsNumber = parseInt(sessionState.connectionId, 10);
|
||||
if (isNaN(connectionIdAsNumber)) {
|
||||
return null; // 如果 connectionId 不是有效的数字,则返回 null
|
||||
}
|
||||
const connectionInfo = connectionsStore.connections.find(conn => conn.id === connectionIdAsNumber);
|
||||
return connectionInfo?.host || null;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
const copyIpToClipboard = async (ipAddress: string | null) => {
|
||||
if (!ipAddress) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(ipAddress);
|
||||
uiNotificationsStore.showSuccess(t('common.copied', '已复制!')); // + 使用通知 store 显示消息
|
||||
} catch (err) {
|
||||
console.error('Failed to copy IP address: ', err);
|
||||
uiNotificationsStore.showError(t('statusMonitor.copyIpError', '复制 IP 失败')); // + 可选:显示错误通知
|
||||
// 可以在这里添加一个错误提示,如果需要的话
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
@@ -128,7 +128,7 @@ const handleKeyDown = async (event: KeyboardEvent) => {
|
||||
selectTag(existingTag);
|
||||
} else if (!existingTag && allowCreate.value) { // Only create if allowed and not existing
|
||||
// 如果是新标签,则 emit 事件让父组件处理创建
|
||||
console.log(`[TagInput] Emitting create-tag for: ${trimmedInput}`); // +++ 添加日志 +++
|
||||
console.log(`[TagInput] Emitting create-tag for: ${trimmedInput}`);
|
||||
emit('create-tag', trimmedInput);
|
||||
// 父组件负责创建、更新 availableTags prop,然后 TagInput 会响应式更新
|
||||
// 父组件也负责将新创建的 tag ID 添加到 modelValue
|
||||
@@ -160,9 +160,9 @@ const removeTagLocally = (tagToRemove: GenericTag) => {
|
||||
|
||||
// 处理全局删除标签 (点击标签上的 'x' 图标) - Emit event
|
||||
const handleDeleteTagGlobally = (tagToDelete: GenericTag) => {
|
||||
console.log(`[TagInput] handleDeleteTagGlobally called for tag ID: ${tagToDelete.id}, Name: ${tagToDelete.name}`); // +++ 添加日志 +++
|
||||
console.log(`[TagInput] handleDeleteTagGlobally called for tag ID: ${tagToDelete.id}, Name: ${tagToDelete.name}`);
|
||||
// Emit event for parent to handle deletion confirmation and API call
|
||||
console.log(`[TagInput] Emitting delete-tag with ID: ${tagToDelete.id}`); // +++ 添加日志 +++
|
||||
console.log(`[TagInput] Emitting delete-tag with ID: ${tagToDelete.id}`);
|
||||
emit('delete-tag', tagToDelete.id);
|
||||
// Parent should handle confirmation, call store action, and update modelValue/availableTags
|
||||
// We might still want to remove it locally immediately for better UX,
|
||||
|
||||
@@ -562,7 +562,7 @@ const clearSearch = () => {
|
||||
searchAddon?.clearDecorations();
|
||||
};
|
||||
|
||||
// +++ 添加 clear 方法 +++
|
||||
// +++ clear 方法 +++
|
||||
const clear = () => {
|
||||
terminal?.clear();
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, defineEmits } from 'vue'; // +++ Import ref +++
|
||||
import { ref, defineEmits } from 'vue';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'send-key', keySequence: string): void;
|
||||
|
||||
@@ -17,14 +17,14 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSettingsStore } from '../../stores/settings.store';
|
||||
import { useAppearanceStore } from '../../stores/appearance.store'; // 导入外观 store
|
||||
import { useAppearanceStore } from '../../stores/appearance.store';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useAppearanceSettings } from '../../composables/settings/useAppearanceSettings';
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const { settings } = storeToRefs(settingsStore); // To ensure v-if="settings" works
|
||||
const appearanceStore = useAppearanceStore(); // 实例化外观 store
|
||||
const { settings } = storeToRefs(settingsStore);
|
||||
const appearanceStore = useAppearanceStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const {
|
||||
|
||||
@@ -222,7 +222,27 @@
|
||||
<p v-if="terminalEnableRightClickPasteMessage" :class="['text-sm', terminalEnableRightClickPasteSuccess ? 'text-success' : 'text-error']">{{ terminalEnableRightClickPasteMessage }}</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="border-border/50">
|
||||
<!-- Status Monitor Show IP -->
|
||||
<div class="settings-section-content">
|
||||
<h3 class="text-base font-semibold text-foreground mb-3">{{ $t('settings.statusMonitorShowIp.title', '状态监视器 IP 显示') }}</h3>
|
||||
<form @submit.prevent="handleUpdateStatusMonitorShowIpSetting" class="space-y-4">
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox" id="statusMonitorShowIp" v-model="statusMonitorShowIpEnabled"
|
||||
class="h-4 w-4 rounded border-border text-primary focus:ring-primary mr-2 cursor-pointer">
|
||||
<label for="statusMonitorShowIp" class="text-sm text-foreground cursor-pointer select-none">{{ $t('settings.statusMonitorShowIp.enableLabel', '在状态监视器中显示IP地址') }}</label>
|
||||
</div>
|
||||
<div class="flex items-center justify-between pt-2">
|
||||
<button type="submit"
|
||||
:disabled="statusMonitorShowIpLoading"
|
||||
class="px-4 py-2 bg-button text-button-text rounded-md shadow-sm hover:bg-button-hover focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary transition duration-150 ease-in-out text-sm font-medium">
|
||||
{{ $t('common.save') }}
|
||||
</button>
|
||||
<p v-if="statusMonitorShowIpMessage" :class="['text-sm', statusMonitorShowIpSuccess ? 'text-success' : 'text-error']">{{ statusMonitorShowIpMessage }}</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -283,6 +303,11 @@ const {
|
||||
showPopupFileManagerMessage,
|
||||
showPopupFileManagerSuccess,
|
||||
handleUpdateShowPopupFileManager,
|
||||
statusMonitorShowIpEnabled,
|
||||
statusMonitorShowIpLoading,
|
||||
statusMonitorShowIpMessage,
|
||||
statusMonitorShowIpSuccess,
|
||||
handleUpdateStatusMonitorShowIpSetting,
|
||||
} = useWorkspaceSettings();
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user