javascript
复制代码
// multi-screen-manager.js
import {
ElMessage,
ElMessageBox,
ElNotification,
ElLoading
} from 'element-plus';
/**
* 尝试在第二个显示器打开窗口
* @param {string} url 要打开的URL
* @param {object} options 窗口选项
* @param {number} options.width 窗口宽度
* @param {number} options.height 窗口高度
* @param {string} options.windowName 窗口名称
* @returns {Promise<object>} 窗口控制对象
*/
export const openInSecondScreen = async (url, options = {}) => {
const loading = ElLoading.service({
lock: true,
text: '正在打开第二屏幕窗口...',
background: 'rgba(0, 0, 0, 0.7)',
});
try {
// 首先尝试使用Window Management API
if ('getScreenDetails' in window) {
try {
const windowControl = await tryWithWindowManagementAPI(url, options);
if (windowControl) {
loading.close();
return windowControl;
}
} catch (error) {
console.warn('使用Window Management API失败:', error);
}
}
// 回退方案
console.log('使用回退方案打开窗口');
const windowControl = fallbackMaximizedOpen(url, options);
if (windowControl) {
loading.close();
return windowControl;
} else {
showPopupBlockedMessage();
throw new Error('弹出窗口被浏览器阻止,请允许此站点的弹出窗口');
}
} catch (error) {
loading.close();
console.error('打开第二屏幕失败:', error);
ElMessageBox.alert(`无法在第二屏幕打开窗口: ${error.message}`, '错误', {
confirmButtonText: '确定',
type: 'error',
});
throw error;
}
};
/**
* 使用Window Management API尝试打开第二屏幕
*/
const tryWithWindowManagementAPI = async (url, options) => {
try {
// 检查是否支持Permissions API
if (!navigator.permissions || !navigator.permissions.query) {
console.log('浏览器不支持Permissions API,使用回退方案');
return null;
}
// 查询窗口管理权限状态
const permissionStatus = await navigator.permissions.query({
name: 'window-management'
});
console.log('窗口管理权限状态:', permissionStatus.state);
// 根据权限状态处理
switch (permissionStatus.state) {
case 'granted':
// 已授权,直接获取屏幕信息
return await handleGrantedState(url, options);
case 'prompt':
// 需要用户授权,尝试请求权限
return await handlePromptState(url, options);
case 'denied':
// 权限被拒绝,提供用户指引
handleDeniedState();
return null;
default:
console.warn('未知的权限状态:', permissionStatus.state);
return null;
}
} catch (error) {
console.error('处理窗口管理权限时出错:', error);
return null;
}
};
/**
* 处理已授权状态
*/
const handleGrantedState = async (url, options) => {
try {
const screenDetails = await window.getScreenDetails();
// 检查是否有多个屏幕
if (screenDetails.screens.length <= 1) {
console.log('只检测到一个显示器,无法打开第二屏幕');
showSingleScreenMessage();
return null;
}
// 查找第二个屏幕(排除当前屏幕)
const secondScreen = screenDetails.screens.find(
screen => screen !== screenDetails.currentScreen
);
if (!secondScreen) {
console.log('未找到第二个显示器');
return null;
}
// 在第二个屏幕上打开窗口
return openWindowOnScreen(url, secondScreen, options);
} catch (error) {
console.error('获取屏幕信息失败:', error);
return null;
}
};
/**
* 处理待提示状态(需要用户授权)
*/
const handlePromptState = async (url, options) => {
try {
// 显示友好提示
const userConfirmed = await showPermissionRequestDialog();
if (!userConfirmed) {
console.log('用户取消了权限请求');
return null;
}
// 尝试获取权限(这会触发浏览器权限请求对话框)
const screenDetails = await window.getScreenDetails();
// 如果成功获取,说明用户点击了"允许"
console.log('用户已授权窗口管理权限');
return await handleGrantedState(url, options);
} catch (error) {
if (error.name === 'SecurityError' || error.name === 'NotAllowedError') {
// 用户拒绝了权限请求
console.warn('用户拒绝了窗口管理权限');
showPermissionDeniedMessage();
} else {
console.error('请求权限时出错:', error);
}
return null;
}
};
/**
* 处理被拒绝状态
*/
const handleDeniedState = () => {
console.warn('窗口管理权限已被拒绝');
const guideMessage = `
窗口管理权限已被拒绝,无法自动检测第二个显示器。
解决方法:
1. 点击浏览器地址栏左侧的锁图标或网站图标
2. 选择"网站设置"或"权限"
3. 找到"窗口管理"选项并设为"允许"
4. 刷新页面后重试
或者您可以选择:
• 手动在第二个显示器上打开窗口
• 使用系统快捷键(Win+P / Cmd+F2)管理显示器
`;
ElMessageBox.alert(guideMessage, '权限被拒绝', {
confirmButtonText: '确定',
dangerouslyUseHTMLString: false,
type: 'warning',
});
};
/**
* 在指定屏幕上打开窗口
*/
const openWindowOnScreen = (url, screen, options = {}) => {
const width = options.width || screen.availWidth;
const height = options.height || screen.availHeight;
const left = options.left || screen.availLeft;
const top = options.top || screen.availTop;
const features = `
left=${left},
top=${top},
width=${width},
height=${height},
scrollbars=${options.scrollbars !== false ? 'yes' : 'no'},
resizable=${options.resizable !== false ? 'yes' : 'no'},
menubar=${options.menubar ? 'yes' : 'no'},
toolbar=${options.toolbar ? 'yes' : 'no'},
status=${options.status ? 'yes' : 'no'},
location=${options.location ? 'yes' : 'no'}
`.replace(/\s+/g, '');
const windowName = options.windowName || 'secondScreenMaximized_' + Date.now();
const newWindow = window.open(url, windowName, features);
if (newWindow) {
console.log('成功在第二个显示器上打开窗口');
// 创建窗口控制对象
const windowControl = createWindowControl(newWindow, url, windowName);
// 显示成功消息
ElMessage({
message: '窗口已在第二屏幕打开',
type: 'success',
duration: 3000,
});
return windowControl;
}
return null;
};
/**
* 回退方案:基于主显示器尺寸计算第二个显示器的位置
*/
const fallbackMaximizedOpen = (url, options = {}) => {
console.log('使用回退方案打开窗口');
const dualScreenLeft = window.screen.availLeft || window.screenLeft || 0;
const dualScreenTop = window.screen.availTop || window.screenTop || 0;
const width = window.screen.availWidth || window.innerWidth;
// 假设第二个显示器在主显示器右侧(常见配置)
const left = options.left || (dualScreenLeft + width);
const top = options.top || dualScreenTop;
const screenWidth = options.width || window.screen.availWidth;
const screenHeight = options.height || window.screen.availHeight;
const features = `
left=${left},
top=${top},
width=${screenWidth},
height=${screenHeight},
scrollbars=yes,
resizable=yes,
location=yes
`.replace(/\s+/g, '');
const windowName = options.windowName || 'fallbackWindow_' + Date.now();
const newWindow = window.open(url, windowName, features);
if (newWindow) {
console.log('全屏窗口已打开(使用回退方案,可能不在第二个显示器上)');
// 检查窗口是否真的在屏幕上
setTimeout(() => {
if (newWindow.closed) return;
try {
const windowLeft = newWindow.screenX || newWindow.screenLeft;
if (windowLeft > window.screen.availWidth * 1.5) {
console.log('检测到可能的显示器配置不匹配,提供手动调整建议');
}
} catch (e) {
// 跨域限制,无法访问新窗口的属性
}
}, 500);
// 创建窗口控制对象
const windowControl = createWindowControl(newWindow, url, windowName);
// 显示提示消息
ElMessage({
message: '窗口已打开(使用回退方案)',
type: 'info',
duration: 3000,
});
return windowControl;
}
return null;
};
/**
* 检查显示器数量
* @returns {Promise<object>} 屏幕信息
*/
export const checkScreenCount = async () => {
const loading = ElLoading.service({
text: '正在检测显示器...',
background: 'rgba(0, 0, 0, 0.5)',
});
try {
if ('getScreenDetails' in window) {
try {
const permissionStatus = await navigator.permissions.query({
name: 'window-management'
});
if (permissionStatus.state === 'granted') {
const screenDetails = await window.getScreenDetails();
loading.close();
return {
count: screenDetails.screens.length,
details: screenDetails.screens,
currentScreen: screenDetails.currentScreen
};
}
} catch (error) {
console.error('检查屏幕数量失败:', error);
}
}
// 回退:基于浏览器推断
loading.close();
return {
count: 1, // 默认假设只有1个
details: [window.screen],
currentScreen: window.screen
};
} catch (error) {
loading.close();
console.error('检查屏幕数量失败:', error);
return {
count: 1,
details: [window.screen],
currentScreen: window.screen
};
}
};
/**
* 手动调整窗口位置
* @param {Window} windowRef 窗口引用
* @param {number} offsetX X轴偏移
* @param {number} offsetY Y轴偏移
* @returns {boolean} 是否成功
*/
export const adjustWindowPosition = (windowRef, offsetX = 0, offsetY = 0) => {
if (windowRef && !windowRef.closed) {
try {
windowRef.moveTo(
(windowRef.screenX || windowRef.screenLeft) + offsetX,
(windowRef.screenY || windowRef.screenTop) + offsetY
);
return true;
} catch (e) {
console.warn('无法移动窗口(可能受安全策略限制):', e);
return false;
}
}
return false;
};
/**
* 显示多屏操作帮助信息
*/
export const showMultiScreenHelp = () => {
const helpText = `
多显示器操作指南:
1. 自动检测(推荐):
• 授予窗口管理权限
• 系统自动识别第二显示器
• 一键打开全屏窗口
2. 手动操作:
• 使用 Win+P(Windows)或 Cmd+F2(Mac)切换显示器模式
• 将窗口拖动到屏幕边缘
• 使用系统快捷键管理窗口
3. 常见问题:
• 看不到第二个显示器?检查连接线和显示器电源
• 权限被拒绝?在浏览器设置中重新授权
• 窗口位置不对?尝试手动拖动调整
4. 系统支持:
• Windows 10/11:支持良好
• macOS:需要系统权限
• Linux:因发行版而异
`;
ElMessageBox.alert(helpText, '多显示器操作指南', {
confirmButtonText: '确定',
type: 'info',
});
};
/**
* 显示权限请求对话框
*/
const showPermissionRequestDialog = () => {
return new Promise((resolve) => {
const message = `
为了在第二个显示器上打开窗口,需要访问您的显示器信息。
点击"确定"后,浏览器会弹出权限请求对话框,请选择"允许"。
这可以让我们:
• 检测您的多显示器配置
• 在正确的显示器上打开窗口
• 提供更好的多屏体验
`;
ElMessageBox.confirm(message, '请求权限', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info',
}).then(() => {
resolve(true);
}).catch(() => {
resolve(false);
});
});
};
/**
* 显示单屏幕提示信息
*/
const showSingleScreenMessage = () => {
const message = '当前只检测到一个显示器。请连接第二个显示器后重试。';
ElMessageBox.alert(message, '单显示器', {
confirmButtonText: '确定',
type: 'warning',
});
};
/**
* 显示权限被拒绝信息
*/
const showPermissionDeniedMessage = () => {
const message = '您拒绝了窗口管理权限,将使用常规方式打开窗口。';
ElMessage({
message: message,
type: 'info',
duration: 3000,
});
};
/**
* 显示弹出窗口被阻止信息
*/
const showPopupBlockedMessage = () => {
const message = '弹出窗口被浏览器阻止。请允许此站点的弹出窗口,然后重试。';
ElMessageBox.alert(message, '弹出窗口被阻止', {
confirmButtonText: '确定',
type: 'error',
});
};
/**
* 创建窗口控制对象
*/
const createWindowControl = (newWindow, url, windowName) => {
// 添加窗口关闭监听
const checkWindowClosed = setInterval(() => {
if (newWindow.closed) {
clearInterval(checkWindowClosed);
console.log('第二屏幕窗口已关闭');
}
}, 1000);
return {
window: newWindow,
id: windowName,
url: url,
move: (x, y) => adjustWindowPosition(newWindow, x, y),
close: () => {
if (newWindow && !newWindow.closed) {
newWindow.close();
}
},
refresh: () => {
if (newWindow && !newWindow.closed) {
newWindow.location.reload();
}
},
focus: () => {
if (newWindow && !newWindow.closed) {
newWindow.focus();
return true;
}
return false;
},
resize: (width, height) => {
if (newWindow && !newWindow.closed) {
try {
newWindow.resizeTo(width, height);
return true;
} catch (e) {
console.warn('调整窗口大小失败:', e);
return false;
}
}
return false;
},
getState: () => {
if (newWindow && !newWindow.closed) {
try {
return {
closed: newWindow.closed,
screenX: newWindow.screenX,
screenY: newWindow.screenY,
innerWidth: newWindow.innerWidth,
innerHeight: newWindow.innerHeight,
outerWidth: newWindow.outerWidth,
outerHeight: newWindow.outerHeight
};
} catch (e) {
return null;
}
}
return null;
},
onClose: (callback) => {
const checkClose = setInterval(() => {
if (newWindow.closed) {
clearInterval(checkClose);
callback();
}
}, 500);
}
};
};