update
This commit is contained in:
@@ -412,10 +412,15 @@ export const getVncSessionToken = async (req: Request, res: Response): Promise<v
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 调用 GuacamoleService 获取 VNC 令牌
|
// 5. 从查询参数中获取可选的 width 和 height
|
||||||
const guacamoleToken = await GuacamoleService.getVncToken(connection, decryptedPassword);
|
const { width, height } = req.query;
|
||||||
|
const initialWidth = width ? parseInt(width as string, 10) : undefined;
|
||||||
|
const initialHeight = height ? parseInt(height as string, 10) : undefined;
|
||||||
|
|
||||||
console.log(`[Controller:getVncSessionToken] Received Guacamole token via GuacamoleService for VNC connection ${connectionId}`);
|
// 6. 调用 GuacamoleService 获取 VNC 令牌,传递尺寸信息
|
||||||
|
const guacamoleToken = await GuacamoleService.getVncToken(connection, decryptedPassword, initialWidth, initialHeight);
|
||||||
|
|
||||||
|
console.log(`[Controller:getVncSessionToken] Received Guacamole token via GuacamoleService for VNC connection ${connectionId} with size ${initialWidth}x${initialHeight}`);
|
||||||
|
|
||||||
// 6. 将 Guacamole 令牌返回给前端
|
// 6. 将 Guacamole 令牌返回给前端
|
||||||
res.status(200).json({ token: guacamoleToken });
|
res.status(200).json({ token: guacamoleToken });
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export const getRdpToken = async (connection: ConnectionWithTags, decryptedPassw
|
|||||||
* @param decryptedPassword 解密后的密码 (VNC 通常需要密码)
|
* @param decryptedPassword 解密后的密码 (VNC 通常需要密码)
|
||||||
* @returns Guacamole 令牌
|
* @returns Guacamole 令牌
|
||||||
*/
|
*/
|
||||||
export const getVncToken = async (connection: ConnectionWithTags, decryptedPassword?: string): Promise<string> => {
|
export const getVncToken = async (connection: ConnectionWithTags, decryptedPassword?: string, width?: number, height?: number): Promise<string> => {
|
||||||
if (connection.type !== 'VNC') {
|
if (connection.type !== 'VNC') {
|
||||||
throw new Error('连接类型必须是 VNC。');
|
throw new Error('连接类型必须是 VNC。');
|
||||||
}
|
}
|
||||||
@@ -95,6 +95,13 @@ export const getVncToken = async (connection: ConnectionWithTags, decryptedPassw
|
|||||||
// username: connection.username, // 如果 VNC 服务支持用户名
|
// username: connection.username, // 如果 VNC 服务支持用户名
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (width !== undefined) {
|
||||||
|
vncApiParams.append('width', String(width));
|
||||||
|
}
|
||||||
|
if (height !== undefined) {
|
||||||
|
vncApiParams.append('height', String(height));
|
||||||
|
}
|
||||||
|
|
||||||
// 如果 VNC 服务也支持用户名,可以取消注释上面的 username 参数
|
// 如果 VNC 服务也支持用户名,可以取消注释上面的 username 参数
|
||||||
// 注意:标准的 VNC 协议主要通过密码进行认证,用户名不是标准部分,但某些实现可能支持。
|
// 注意:标准的 VNC 协议主要通过密码进行认证,用户名不是标准部分,但某些实现可能支持。
|
||||||
// 这里假设 @nexus-terminal/vnc 的 /api/get-vnc-token 接受这些参数。
|
// 这里假设 @nexus-terminal/vnc 的 /api/get-vnc-token 接受这些参数。
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ export const settingsController = {
|
|||||||
'timezone', // NEW: 添加时区键
|
'timezone', // NEW: 添加时区键
|
||||||
'rdpModalWidth', // NEW: 添加 RDP 模态框宽度键
|
'rdpModalWidth', // NEW: 添加 RDP 模态框宽度键
|
||||||
'rdpModalHeight', // NEW: 添加 RDP 模态框高度键
|
'rdpModalHeight', // NEW: 添加 RDP 模态框高度键
|
||||||
|
'vncModalWidth', // NEW: 添加 VNC 模态框宽度键
|
||||||
|
'vncModalHeight', // NEW: 添加 VNC 模态框高度键
|
||||||
'ipBlacklistEnabled', // <-- 添加 IP 黑名单启用键
|
'ipBlacklistEnabled', // <-- 添加 IP 黑名单启用键
|
||||||
'layoutLocked', // +++ 添加布局锁定键 +++
|
'layoutLocked', // +++ 添加布局锁定键 +++
|
||||||
'terminalScrollbackLimit', // NEW: 添加终端回滚行数键
|
'terminalScrollbackLimit', // NEW: 添加终端回滚行数键
|
||||||
|
|||||||
@@ -27,8 +27,16 @@ const connectionStatus = ref<'disconnected' | 'connecting' | 'connected' | 'erro
|
|||||||
const statusMessage = ref('');
|
const statusMessage = ref('');
|
||||||
const keyboard = ref<any | null>(null);
|
const keyboard = ref<any | null>(null);
|
||||||
const mouse = ref<any | null>(null);
|
const mouse = ref<any | null>(null);
|
||||||
const desiredModalWidth = ref(1024);
|
// Initialize desiredModalWidth and desiredModalHeight from store or defaults
|
||||||
const desiredModalHeight = ref(768);
|
const initialStoreWidth = settingsStore.settings.vncModalWidth
|
||||||
|
? parseInt(settingsStore.settings.vncModalWidth, 10)
|
||||||
|
: 1024;
|
||||||
|
const initialStoreHeight = settingsStore.settings.vncModalHeight
|
||||||
|
? parseInt(settingsStore.settings.vncModalHeight, 10)
|
||||||
|
: 768;
|
||||||
|
|
||||||
|
const desiredModalWidth = ref(Math.max(MIN_MODAL_WIDTH, isNaN(initialStoreWidth) ? MIN_MODAL_WIDTH : initialStoreWidth));
|
||||||
|
const desiredModalHeight = ref(Math.max(MIN_MODAL_HEIGHT, isNaN(initialStoreHeight) ? MIN_MODAL_HEIGHT : initialStoreHeight));
|
||||||
const isKeyboardDisabledForInput = ref(false);
|
const isKeyboardDisabledForInput = ref(false);
|
||||||
|
|
||||||
const MIN_MODAL_WIDTH = 800;
|
const MIN_MODAL_WIDTH = 800;
|
||||||
@@ -63,7 +71,7 @@ const handleConnection = async () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const connectionsStore = useConnectionsStore();
|
const connectionsStore = useConnectionsStore();
|
||||||
const token = await connectionsStore.getVncSessionToken(props.connection.id);
|
const token = await connectionsStore.getVncSessionToken(props.connection.id, desiredModalWidth.value, desiredModalHeight.value);
|
||||||
if (!token) {
|
if (!token) {
|
||||||
throw new Error('VNC Token not found from store action');
|
throw new Error('VNC Token not found from store action');
|
||||||
}
|
}
|
||||||
@@ -105,6 +113,15 @@ const handleConnection = async () => {
|
|||||||
if (displayEl && typeof displayEl.focus === 'function') {
|
if (displayEl && typeof displayEl.focus === 'function') {
|
||||||
displayEl.focus();
|
displayEl.focus();
|
||||||
}
|
}
|
||||||
|
// Sync size on connect
|
||||||
|
if (vncDisplayRef.value && guacClient.value) {
|
||||||
|
const displayWidth = vncDisplayRef.value.offsetWidth;
|
||||||
|
const displayHeight = vncDisplayRef.value.offsetHeight;
|
||||||
|
if (displayWidth > 0 && displayHeight > 0) {
|
||||||
|
console.log(`[VncModal] Initial resize on connect: ${displayWidth}x${displayHeight}`);
|
||||||
|
guacClient.value.sendSize(displayWidth, displayHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
@@ -302,41 +319,60 @@ const closeModal = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
watch(desiredModalWidth, (newWidth, oldWidth) => {
|
watch(desiredModalWidth, (newWidth, oldWidth) => {
|
||||||
if (newWidth === oldWidth) return;
|
if (newWidth === oldWidth && typeof newWidth === 'number' && typeof oldWidth === 'number') {
|
||||||
|
// console.log(`[VncModal] 宽度监听触发,但值 (${newWidth}) 未改变。跳过。`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// console.log(`[VncModal] 监听 desiredModalWidth 触发: ${oldWidth} -> ${newWidth}`);
|
||||||
|
|
||||||
const validatedWidth = Math.max(MIN_MODAL_WIDTH, Number(newWidth) || MIN_MODAL_WIDTH);
|
const validatedWidth = Math.max(MIN_MODAL_WIDTH, Number(newWidth) || MIN_MODAL_WIDTH);
|
||||||
|
|
||||||
|
if (validatedWidth !== Number(newWidth)) {
|
||||||
|
nextTick(() => {
|
||||||
|
desiredModalWidth.value = validatedWidth;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (saveWidthTimeout) clearTimeout(saveWidthTimeout);
|
if (saveWidthTimeout) clearTimeout(saveWidthTimeout);
|
||||||
saveWidthTimeout = setTimeout(() => {
|
saveWidthTimeout = setTimeout(() => {
|
||||||
|
// console.log(`[VncModal] 防抖保存 - 保存宽度: ${validatedWidth}`);
|
||||||
if (String(validatedWidth) !== settingsStore.settings.vncModalWidth) {
|
if (String(validatedWidth) !== settingsStore.settings.vncModalWidth) {
|
||||||
settingsStore.updateSetting('vncModalWidth', String(validatedWidth));
|
settingsStore.updateSetting('vncModalWidth', String(validatedWidth));
|
||||||
|
} else {
|
||||||
|
// console.log(`[VncModal] 防抖保存 - 宽度 ${validatedWidth} 与存储值匹配。跳过冗余保存。`);
|
||||||
}
|
}
|
||||||
}, DEBOUNCE_DELAY);
|
}, DEBOUNCE_DELAY);
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(desiredModalHeight, (newHeight, oldHeight) => {
|
watch(desiredModalHeight, (newHeight, oldHeight) => {
|
||||||
if (newHeight === oldHeight) return;
|
if (newHeight === oldHeight && typeof newHeight === 'number' && typeof oldHeight === 'number') {
|
||||||
|
// console.log(`[VncModal] 高度监听触发,但值 (${newHeight}) 未改变。跳过。`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// console.log(`[VncModal] 监听 desiredModalHeight 触发: ${oldHeight} -> ${newHeight}`);
|
||||||
|
|
||||||
const validatedHeight = Math.max(MIN_MODAL_HEIGHT, Number(newHeight) || MIN_MODAL_HEIGHT);
|
const validatedHeight = Math.max(MIN_MODAL_HEIGHT, Number(newHeight) || MIN_MODAL_HEIGHT);
|
||||||
|
|
||||||
|
if (validatedHeight !== Number(newHeight)) {
|
||||||
|
nextTick(() => {
|
||||||
|
desiredModalHeight.value = validatedHeight;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (saveHeightTimeout) clearTimeout(saveHeightTimeout);
|
if (saveHeightTimeout) clearTimeout(saveHeightTimeout);
|
||||||
saveHeightTimeout = setTimeout(() => {
|
saveHeightTimeout = setTimeout(() => {
|
||||||
|
// console.log(`[VncModal] 防抖保存 - 保存高度: ${validatedHeight}`);
|
||||||
if (String(validatedHeight) !== settingsStore.settings.vncModalHeight) {
|
if (String(validatedHeight) !== settingsStore.settings.vncModalHeight) {
|
||||||
settingsStore.updateSetting('vncModalHeight', String(validatedHeight));
|
settingsStore.updateSetting('vncModalHeight', String(validatedHeight));
|
||||||
|
} else {
|
||||||
|
// console.log(`[VncModal] 防抖保存 - 高度 ${validatedHeight} 与存储值匹配。跳过冗余保存。`);
|
||||||
}
|
}
|
||||||
}, DEBOUNCE_DELAY);
|
}, DEBOUNCE_DELAY);
|
||||||
});
|
});
|
||||||
|
|
||||||
watchEffect(() => {
|
// The watchEffect that was here (lines 359-372) is removed as its functionality
|
||||||
const storeWidth = settingsStore.settings.vncModalWidth;
|
// is now covered by the direct initialization of desiredModalWidth/Height from the store
|
||||||
const storeHeight = settingsStore.settings.vncModalHeight;
|
// and the updated watch listeners.
|
||||||
console.log(`[VncModal] From store - Width: ${storeWidth}, Height: ${storeHeight}`);
|
|
||||||
|
|
||||||
const initialWidth = storeWidth ? parseInt(storeWidth, 10) : desiredModalWidth.value;
|
|
||||||
const initialHeight = storeHeight ? parseInt(storeHeight, 10) : desiredModalHeight.value;
|
|
||||||
|
|
||||||
const finalWidth = Math.max(MIN_MODAL_WIDTH, isNaN(initialWidth) ? MIN_MODAL_WIDTH : initialWidth);
|
|
||||||
const finalHeight = Math.max(MIN_MODAL_HEIGHT, isNaN(initialHeight) ? MIN_MODAL_HEIGHT : initialHeight);
|
|
||||||
console.log(`[VncModal] Applied - Width: ${finalWidth}, Height: ${finalHeight}`);
|
|
||||||
desiredModalWidth.value = finalWidth;
|
|
||||||
desiredModalHeight.value = finalHeight;
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.connection) {
|
if (props.connection) {
|
||||||
@@ -373,6 +409,25 @@ const computedModalStyle = computed(() => {
|
|||||||
height: `${actualHeight}px`,
|
height: `${actualHeight}px`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
watchEffect(() => {
|
||||||
|
// 依赖 computedModalStyle,当其变化时此 effect 会重新运行
|
||||||
|
const currentStyle = computedModalStyle.value;
|
||||||
|
|
||||||
|
if (guacClient.value && connectionStatus.value === 'connected' && vncDisplayRef.value) {
|
||||||
|
// 使用 nextTick 确保 DOM 更新完毕,vncDisplayRef 的尺寸已根据 currentStyle 刷新
|
||||||
|
nextTick(() => {
|
||||||
|
if (vncDisplayRef.value && guacClient.value) { // 再次检查,因为 nextTick 是异步的
|
||||||
|
const displayWidth = vncDisplayRef.value.offsetWidth;
|
||||||
|
const displayHeight = vncDisplayRef.value.offsetHeight;
|
||||||
|
|
||||||
|
if (displayWidth > 0 && displayHeight > 0) {
|
||||||
|
console.log(`[VncModal] Resizing VNC display to: ${displayWidth}x${displayHeight} due to style change.`);
|
||||||
|
guacClient.value.sendSize(displayWidth, displayHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -286,12 +286,24 @@ export const useConnectionsStore = defineStore('connections', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// +++ 新增:获取 VNC 会话令牌 +++
|
// +++ 新增:获取 VNC 会话令牌 +++
|
||||||
async getVncSessionToken(connectionId: number): Promise<string | null> {
|
async getVncSessionToken(connectionId: number, width?: number, height?: number): Promise<string | null> {
|
||||||
// this.isLoading = true; // 考虑是否需要独立的加载状态,或者由调用方处理
|
// this.isLoading = true; // 考虑是否需要独立的加载状态,或者由调用方处理
|
||||||
// this.error = null;
|
// this.error = null;
|
||||||
try {
|
try {
|
||||||
// 调用后端 API GET /connections/:id/vnc-session
|
let apiUrl = `/connections/${connectionId}/vnc-session`;
|
||||||
const response = await apiClient.post<{ token: string }>(`/connections/${connectionId}/vnc-session`);
|
const params = new URLSearchParams();
|
||||||
|
if (width !== undefined) {
|
||||||
|
params.append('width', String(width));
|
||||||
|
}
|
||||||
|
if (height !== undefined) {
|
||||||
|
params.append('height', String(height));
|
||||||
|
}
|
||||||
|
const queryString = params.toString();
|
||||||
|
if (queryString) {
|
||||||
|
apiUrl += `?${queryString}`;
|
||||||
|
}
|
||||||
|
// 调用后端 API POST /connections/:id/vnc-session (现在带有可选的 width/height 查询参数)
|
||||||
|
const response = await apiClient.post<{ token: string }>(apiUrl);
|
||||||
return response.data.token;
|
return response.data.token;
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error(`获取 VNC 会话令牌失败 (连接 ID: ${connectionId}):`, err);
|
console.error(`获取 VNC 会话令牌失败 (连接 ID: ${connectionId}):`, err);
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ interface SettingsState {
|
|||||||
timezone?: string; // NEW: 时区设置 (e.g., 'Asia/Shanghai', 'UTC')
|
timezone?: string; // NEW: 时区设置 (e.g., 'Asia/Shanghai', 'UTC')
|
||||||
rdpModalWidth?: string; // NEW: RDP 模态框宽度
|
rdpModalWidth?: string; // NEW: RDP 模态框宽度
|
||||||
rdpModalHeight?: string; // NEW: RDP 模态框高度
|
rdpModalHeight?: string; // NEW: RDP 模态框高度
|
||||||
|
vncModalWidth?: string; // NEW: VNC 模态框宽度
|
||||||
|
vncModalHeight?: string; // NEW: VNC 模态框高度
|
||||||
ipBlacklistEnabled?: string;
|
ipBlacklistEnabled?: string;
|
||||||
dashboardSortBy?: SortField;
|
dashboardSortBy?: SortField;
|
||||||
dashboardSortOrder?: SortOrder;
|
dashboardSortOrder?: SortOrder;
|
||||||
@@ -250,6 +252,13 @@ export const useSettingsStore = defineStore('settings', () => {
|
|||||||
if (settings.value.rdpModalHeight === undefined) {
|
if (settings.value.rdpModalHeight === undefined) {
|
||||||
settings.value.rdpModalHeight = '858';
|
settings.value.rdpModalHeight = '858';
|
||||||
}
|
}
|
||||||
|
// NEW: VNC Modal Size defaults
|
||||||
|
if (settings.value.vncModalWidth === undefined) {
|
||||||
|
settings.value.vncModalWidth = '1024'; // 默认宽度
|
||||||
|
}
|
||||||
|
if (settings.value.vncModalHeight === undefined) {
|
||||||
|
settings.value.vncModalHeight = '768'; // 默认高度
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.value.dashboardSortBy === undefined) {
|
if (settings.value.dashboardSortBy === undefined) {
|
||||||
settings.value.dashboardSortBy = 'last_connected_at';
|
settings.value.dashboardSortBy = 'last_connected_at';
|
||||||
@@ -364,6 +373,8 @@ export const useSettingsStore = defineStore('settings', () => {
|
|||||||
'timezone', // NEW: 添加时区键
|
'timezone', // NEW: 添加时区键
|
||||||
'rdpModalWidth', // NEW: 添加 RDP 模态框宽度键
|
'rdpModalWidth', // NEW: 添加 RDP 模态框宽度键
|
||||||
'rdpModalHeight', // NEW: 添加 RDP 模态框高度键
|
'rdpModalHeight', // NEW: 添加 RDP 模态框高度键
|
||||||
|
'vncModalWidth', // NEW: 添加 VNC 模态框宽度键
|
||||||
|
'vncModalHeight', // NEW: 添加 VNC 模态框高度键
|
||||||
'ipBlacklistEnabled',
|
'ipBlacklistEnabled',
|
||||||
'dashboardSortBy',
|
'dashboardSortBy',
|
||||||
'dashboardSortOrder',
|
'dashboardSortOrder',
|
||||||
@@ -451,6 +462,8 @@ export const useSettingsStore = defineStore('settings', () => {
|
|||||||
'timezone', // NEW: 添加时区键
|
'timezone', // NEW: 添加时区键
|
||||||
'rdpModalWidth', // NEW: 添加 RDP 模态框宽度键
|
'rdpModalWidth', // NEW: 添加 RDP 模态框宽度键
|
||||||
'rdpModalHeight', // NEW: 添加 RDP 模态框高度键
|
'rdpModalHeight', // NEW: 添加 RDP 模态框高度键
|
||||||
|
'vncModalWidth', // NEW: 添加 VNC 模态框宽度键
|
||||||
|
'vncModalHeight', // NEW: 添加 VNC 模态框高度键
|
||||||
'ipBlacklistEnabled',
|
'ipBlacklistEnabled',
|
||||||
'dashboardSortBy',
|
'dashboardSortBy',
|
||||||
'dashboardSortOrder',
|
'dashboardSortOrder',
|
||||||
|
|||||||
Reference in New Issue
Block a user