update
This commit is contained in:
@@ -412,10 +412,15 @@ export const getVncSessionToken = async (req: Request, res: Response): Promise<v
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. 调用 GuacamoleService 获取 VNC 令牌
|
||||
const guacamoleToken = await GuacamoleService.getVncToken(connection, decryptedPassword);
|
||||
// 5. 从查询参数中获取可选的 width 和 height
|
||||
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 令牌返回给前端
|
||||
res.status(200).json({ token: guacamoleToken });
|
||||
|
||||
@@ -76,7 +76,7 @@ export const getRdpToken = async (connection: ConnectionWithTags, decryptedPassw
|
||||
* @param decryptedPassword 解密后的密码 (VNC 通常需要密码)
|
||||
* @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') {
|
||||
throw new Error('连接类型必须是 VNC。');
|
||||
}
|
||||
@@ -95,6 +95,13 @@ export const getVncToken = async (connection: ConnectionWithTags, decryptedPassw
|
||||
// username: connection.username, // 如果 VNC 服务支持用户名
|
||||
});
|
||||
|
||||
if (width !== undefined) {
|
||||
vncApiParams.append('width', String(width));
|
||||
}
|
||||
if (height !== undefined) {
|
||||
vncApiParams.append('height', String(height));
|
||||
}
|
||||
|
||||
// 如果 VNC 服务也支持用户名,可以取消注释上面的 username 参数
|
||||
// 注意:标准的 VNC 协议主要通过密码进行认证,用户名不是标准部分,但某些实现可能支持。
|
||||
// 这里假设 @nexus-terminal/vnc 的 /api/get-vnc-token 接受这些参数。
|
||||
|
||||
@@ -47,6 +47,8 @@ export const settingsController = {
|
||||
'timezone', // NEW: 添加时区键
|
||||
'rdpModalWidth', // NEW: 添加 RDP 模态框宽度键
|
||||
'rdpModalHeight', // NEW: 添加 RDP 模态框高度键
|
||||
'vncModalWidth', // NEW: 添加 VNC 模态框宽度键
|
||||
'vncModalHeight', // NEW: 添加 VNC 模态框高度键
|
||||
'ipBlacklistEnabled', // <-- 添加 IP 黑名单启用键
|
||||
'layoutLocked', // +++ 添加布局锁定键 +++
|
||||
'terminalScrollbackLimit', // NEW: 添加终端回滚行数键
|
||||
|
||||
@@ -27,8 +27,16 @@ const connectionStatus = ref<'disconnected' | 'connecting' | 'connected' | 'erro
|
||||
const statusMessage = ref('');
|
||||
const keyboard = ref<any | null>(null);
|
||||
const mouse = ref<any | null>(null);
|
||||
const desiredModalWidth = ref(1024);
|
||||
const desiredModalHeight = ref(768);
|
||||
// Initialize desiredModalWidth and desiredModalHeight from store or defaults
|
||||
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 MIN_MODAL_WIDTH = 800;
|
||||
@@ -63,7 +71,7 @@ const handleConnection = async () => {
|
||||
|
||||
try {
|
||||
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) {
|
||||
throw new Error('VNC Token not found from store action');
|
||||
}
|
||||
@@ -105,6 +113,15 @@ const handleConnection = async () => {
|
||||
if (displayEl && typeof displayEl.focus === 'function') {
|
||||
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(() => {
|
||||
nextTick(() => {
|
||||
@@ -302,41 +319,60 @@ const closeModal = () => {
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
if (validatedWidth !== Number(newWidth)) {
|
||||
nextTick(() => {
|
||||
desiredModalWidth.value = validatedWidth;
|
||||
});
|
||||
}
|
||||
|
||||
if (saveWidthTimeout) clearTimeout(saveWidthTimeout);
|
||||
saveWidthTimeout = setTimeout(() => {
|
||||
// console.log(`[VncModal] 防抖保存 - 保存宽度: ${validatedWidth}`);
|
||||
if (String(validatedWidth) !== settingsStore.settings.vncModalWidth) {
|
||||
settingsStore.updateSetting('vncModalWidth', String(validatedWidth));
|
||||
} else {
|
||||
// console.log(`[VncModal] 防抖保存 - 宽度 ${validatedWidth} 与存储值匹配。跳过冗余保存。`);
|
||||
}
|
||||
}, DEBOUNCE_DELAY);
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
if (validatedHeight !== Number(newHeight)) {
|
||||
nextTick(() => {
|
||||
desiredModalHeight.value = validatedHeight;
|
||||
});
|
||||
}
|
||||
|
||||
if (saveHeightTimeout) clearTimeout(saveHeightTimeout);
|
||||
saveHeightTimeout = setTimeout(() => {
|
||||
// console.log(`[VncModal] 防抖保存 - 保存高度: ${validatedHeight}`);
|
||||
if (String(validatedHeight) !== settingsStore.settings.vncModalHeight) {
|
||||
settingsStore.updateSetting('vncModalHeight', String(validatedHeight));
|
||||
} else {
|
||||
// console.log(`[VncModal] 防抖保存 - 高度 ${validatedHeight} 与存储值匹配。跳过冗余保存。`);
|
||||
}
|
||||
}, DEBOUNCE_DELAY);
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
const storeWidth = settingsStore.settings.vncModalWidth;
|
||||
const storeHeight = settingsStore.settings.vncModalHeight;
|
||||
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;
|
||||
});
|
||||
// The watchEffect that was here (lines 359-372) is removed as its functionality
|
||||
// is now covered by the direct initialization of desiredModalWidth/Height from the store
|
||||
// and the updated watch listeners.
|
||||
|
||||
onMounted(() => {
|
||||
if (props.connection) {
|
||||
@@ -373,6 +409,25 @@ const computedModalStyle = computed(() => {
|
||||
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>
|
||||
<template>
|
||||
|
||||
@@ -286,12 +286,24 @@ export const useConnectionsStore = defineStore('connections', {
|
||||
},
|
||||
|
||||
// +++ 新增:获取 VNC 会话令牌 +++
|
||||
async getVncSessionToken(connectionId: number): Promise<string | null> {
|
||||
async getVncSessionToken(connectionId: number, width?: number, height?: number): Promise<string | null> {
|
||||
// this.isLoading = true; // 考虑是否需要独立的加载状态,或者由调用方处理
|
||||
// this.error = null;
|
||||
try {
|
||||
// 调用后端 API GET /connections/:id/vnc-session
|
||||
const response = await apiClient.post<{ token: string }>(`/connections/${connectionId}/vnc-session`);
|
||||
let apiUrl = `/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;
|
||||
} catch (err: any) {
|
||||
console.error(`获取 VNC 会话令牌失败 (连接 ID: ${connectionId}):`, err);
|
||||
|
||||
@@ -50,6 +50,8 @@ interface SettingsState {
|
||||
timezone?: string; // NEW: 时区设置 (e.g., 'Asia/Shanghai', 'UTC')
|
||||
rdpModalWidth?: string; // NEW: RDP 模态框宽度
|
||||
rdpModalHeight?: string; // NEW: RDP 模态框高度
|
||||
vncModalWidth?: string; // NEW: VNC 模态框宽度
|
||||
vncModalHeight?: string; // NEW: VNC 模态框高度
|
||||
ipBlacklistEnabled?: string;
|
||||
dashboardSortBy?: SortField;
|
||||
dashboardSortOrder?: SortOrder;
|
||||
@@ -250,7 +252,14 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
if (settings.value.rdpModalHeight === undefined) {
|
||||
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) {
|
||||
settings.value.dashboardSortBy = 'last_connected_at';
|
||||
}
|
||||
@@ -364,6 +373,8 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
'timezone', // NEW: 添加时区键
|
||||
'rdpModalWidth', // NEW: 添加 RDP 模态框宽度键
|
||||
'rdpModalHeight', // NEW: 添加 RDP 模态框高度键
|
||||
'vncModalWidth', // NEW: 添加 VNC 模态框宽度键
|
||||
'vncModalHeight', // NEW: 添加 VNC 模态框高度键
|
||||
'ipBlacklistEnabled',
|
||||
'dashboardSortBy',
|
||||
'dashboardSortOrder',
|
||||
@@ -451,6 +462,8 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
'timezone', // NEW: 添加时区键
|
||||
'rdpModalWidth', // NEW: 添加 RDP 模态框宽度键
|
||||
'rdpModalHeight', // NEW: 添加 RDP 模态框高度键
|
||||
'vncModalWidth', // NEW: 添加 VNC 模态框宽度键
|
||||
'vncModalHeight', // NEW: 添加 VNC 模态框高度键
|
||||
'ipBlacklistEnabled',
|
||||
'dashboardSortBy',
|
||||
'dashboardSortOrder',
|
||||
|
||||
Reference in New Issue
Block a user