众所周知,思源不支持 prompt,确切的说 Electron 不支持。
但有时写脚本时确实需要一些交互式操作,如果使用思源 api 调用 Dialog 确实可以实现,但终究是麻烦了些。
就自己动手写了一个 prompt 模拟函数,放到 js 代码片段中即可,然后需要的时候就可以直接调用了。
效果
接口定义如下
// 弹出prompt对话框 // 参数说明 // message 提示消息,类似标题 // defaultValue 输入框默认值 // okName 确认按钮名字 // cancelName 取消按钮名字 // width对话框宽度,默认400,高度根据内容自适应 // modalMode,是否模态窗口,模态窗口,除了当前对话框,其他地方无法点击,false非模态窗,true模态窗 // clickOverlayClose 是否点击遮罩层关闭对话框,仅在模态窗口下有效 function showPrompt(message = '', defaultValue = '', okName="确定", cancelName="取消", width=400, modalMode=true, clickOverlayClose=false); // 弹出自定义prompt对话框(可自定义定义复杂表单) // 参数说明 // message 提示消息,类似标题 // html 对话框自定义表单内容的HTML代码 // onSubmit 提交时的回调函数,对话框的返回值依赖于这个回调函数的返回值 // onOpen 打开窗口时的回调函数,可以在打开窗口话进行一些初始化操作,比如某个输入框设为焦点 // okName 确认按钮名字 // cancelName 取消按钮名字 // width 对话框宽度,默认400px,高度根据内容自适应 // modalMode,是否模态窗口,模态窗口,除了当前对话框,其他地方无法点击,false非模态窗,true模态窗 // clickOverlayClose 是否点击遮罩层关闭对话框,仅在模态窗口下有效 function showPromptForm(message = '', html = '', onSubmit = null, onOpen = null, okName="确定", cancelName="取消", width=400, modalMode=true, clickOverlayClose=false);
调用示例
// showPrompt 示例用法 const result = await showPrompt('这里是提示文本', ''); console.log(result); // showPromptForm 示例用法 const result = await showPromptForm('这里是提示文本', ` <input type="text" id="promptName" placeholder="标题" /> <input type="text" id="promptHref" placeholder="链接" /> `, (promptContainer) => { const inputs = promptContainer.querySelectorAll('input[type="text"]'); const values = Array.from(inputs).map(input => input.value); return values; }, ()=> { document.getElementById("promptName").focus(); }); console.log(result);
完整代码如下
// 功能:模拟window.prompt函数 // 使用示例 /* // showPrompt 示例用法 const result = await showPrompt('这里是提示文本', ''); console.log(result); // showPromptForm 示例用法 const result = await showPromptForm('这里是提示文本', ` <input type="text" id="promptName" placeholder="标题" /> <input type="text" id="promptHref" placeholder="链接" /> `, (promptContainer) => { const inputs = promptContainer.querySelectorAll('input[type="text"]'); const values = Array.from(inputs).map(input => input.value); return values; }, ()=> { document.getElementById("promptName").focus(); }); console.log(result); */ (()=>{ // 样式变量 const dialogStyleText = ` .prompt-dialog { display: none; position: fixed; z-index: 9999; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 20px; border: 1px solid var(--b3-theme-surface-lighter); border-radius: var(--b3-border-radius-b); box-shadow: var(--b3-dialog-shadow); width: 400px; /* 设置初始宽度 */ text-align: left; /* 左对齐 */ font-size: 100%; font-family: var(--b3-font-family); color: var(--b3-theme-on-background); background-color: var(--b3-theme-surface); } .prompt-overlay { display: none; position: fixed; z-index: 9998; top: 0; left: 0; width: 100%; height: 100%; /*background-color: rgba(0, 0, 0, 0);*/ background-color: var(--b3-mask-background); /*backdrop-filter: blur(3px);*/ } .prompt-dialog .prompt-text { margin-bottom: 10px; } .prompt-dialog .prompt-input-container { margin-bottom: 20px; } .prompt-dialog .prompt-input-container input[type="text"] { width: calc(100% - 16px); /* 输入框占满容器宽度 */ padding: 4px 8px; line-height: 20px; margin-bottom: 10px; color: var(--b3-theme-on-background); transition: box-shadow 120ms 0ms cubic-bezier(0, 0, 0.2, 1); background-color: var(--b3-theme-background); border: 0; border-radius: var(--b3-border-radius); box-shadow: inset 0 0 0 .6px var(--b3-theme-on-surface); } .prompt-dialog .prompt-input-container input[type="text"]:hover{ box-shadow: inset 0 0 0 .6px var(--b3-theme-on-background); } .prompt-dialog .prompt-input-container input[type="text"]:focus{ box-shadow: inset 0 0 0 1px var(--b3-theme-primary), 0 0 0 3px var(--b3-theme-primary-lightest); } .prompt-dialog .prompt-input-container input[type="text"]:last-child { margin-bottom: 0; } .prompt-dialog .prompt-button-container { display: flex; justify-content: flex-end; /* 按钮靠右 */ } .prompt-dialog button { margin-left: 10px; line-height: 20px; padding: 4px 12px; color: var(--b3-theme-primary); box-shadow: inset 0 0 0 .6px var(--b3-theme-primary); background-color: rgba(0, 0, 0, 0); transition: box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1); border: 0; border-radius: var(--b3-border-radius); } .prompt-dialog button:focus, .prompt-dialog button:hover { background-color: var(--b3-theme-primary-lightest); box-shadow: inset 0 0 0 1px var(--b3-theme-primary); text-decoration: none; } @keyframes prompt-shake { 0% { transform: translate(-50%, -50%) rotate(0deg); } 25% { transform: translate(-50%, -50%) rotate(2deg); } 50% { transform: translate(-50%, -50%) rotate(0deg); } 75% { transform: translate(-50%, -50%) rotate(-2deg); } 100% { transform: translate(-50%, -50%) rotate(0deg); } } .prompt-dialog.prompt-shake { animation: prompt-shake 0.5s ease-in-out; } `; // HTML模板 const dialogHtml = ` <div class="prompt-text" id="promptText">请输入</div> <div class="prompt-input-container"> <input type="text" id="promptInput" /> </div> <div class="prompt-button-container"> <button id="promptCancel">取消</button> <button id="promptSubmit">确认</button> </div> `; // 创建样式 const dialogStyle = document.createElement('style'); dialogStyle.textContent = dialogStyleText; document.head.appendChild(dialogStyle); // 创建对话框 const dialog = document.createElement('div'); dialog.className = 'prompt-dialog'; dialog.id = 'promptDialog'; dialog.innerHTML = dialogHtml; document.body.appendChild(dialog); // 创建遮罩层 const overlay = document.createElement('div'); overlay.className = 'prompt-overlay'; document.body.appendChild(overlay); // 获取元素 const promptContainer = document.querySelector(".prompt-input-container"); const submitButton = document.getElementById('promptSubmit'); const cancelButton = document.getElementById('promptCancel'); const promptText = document.getElementById('promptText'); let focusEl; // 弹出对话框 function showPrompt(message = '', defaultValue = '', okName="确定", cancelName="取消", width=400, modalMode=true, clickOverlayClose=false) { return new Promise((resolve, reject) => { // 初始化窗口 dialog.style.width = (width || 400) + 'px'; promptText.textContent = message; let input = document.getElementById('promptInput'); if(!input) { promptContainer.innerHTML = `<input type="text" id="promptInput" />`; input = document.getElementById('promptInput'); } input.value = defaultValue || ''; submitButton.textContent = okName || '确定'; cancelButton.textContent = cancelName || '取消'; if(!cancelName) cancelButton.style.display = 'none'; dialog.style.display = 'block'; if(modalMode) overlay.style.display = 'block'; input.focus(); focusEl = promptContainer.querySelector(":focus"); // 监听按钮点击事件 submitButton.addEventListener('click', () => { dialog.style.display = 'none'; if(modalMode) overlay.style.display = 'none'; document.removeEventListener('keydown', handleKeydown); resolve(input.value); }); cancelButton.addEventListener('click', () => { dialog.style.display = 'none'; if(modalMode) overlay.style.display = 'none'; document.removeEventListener('keydown', handleKeydown); resolve(null); }); // 添加键盘事件监听器 const handleKeydown = () => { if (event.key === 'Escape') { cancelButton.click(); } else if (event.key === 'Enter') { submitButton.click(); } }; document.addEventListener('keydown', handleKeydown); // 监听焦点事件,focus冒泡必须第三参数是true promptContainer.addEventListener('focus', (event) => { focusEl = event.target; }, true); // 添加点击遮罩层关闭对话框的功能 if(modalMode){ overlay.addEventListener('click', () => { // 点击遮罩层关闭弹窗 if(clickOverlayClose) cancelButton.click(); // 重新获取焦点 if(focusEl) focusEl.focus(); // 添加抖动类 dialog.classList.add('prompt-shake'); // 移除抖动类 setTimeout(() => { dialog.classList.remove('prompt-shake'); }, 500); }); } }); } // 弹出对话框表单 function showPromptForm(message = '', html = '', onSubmit = null, onOpen = null, okName="确定", cancelName="取消", width=400, modalMode=true, clickOverlayClose=false) { return new Promise((resolve, reject) => { // 初始化窗口 dialog.style.width = (width || 400) + 'px'; if(html) promptContainer.innerHTML = html; promptText.textContent = message; submitButton.textContent = okName || '确定'; cancelButton.textContent = cancelName || '取消'; if(!cancelName) cancelButton.style.display = 'none'; dialog.style.display = 'block'; if(modalMode) overlay.style.display = 'block'; if(typeof onOpen === 'function') onOpen(promptContainer); focusEl = promptContainer.querySelector(":focus"); // 监听按钮点击事件 submitButton.addEventListener('click', () => { dialog.style.display = 'none'; if(modalMode) overlay.style.display = 'none'; document.removeEventListener('keydown', handleKeydown); if(typeof onSubmit === 'function') { resolve(onSubmit(promptContainer)); } else { resolve(undefined); } }); cancelButton.addEventListener('click', () => { dialog.style.display = 'none'; if(modalMode) overlay.style.display = 'none'; document.removeEventListener('keydown', handleKeydown); resolve(null); }); // 添加键盘事件监听器 const handleKeydown = () => { if (event.key === 'Escape') { cancelButton.click(); } else if (event.key === 'Enter') { submitButton.click(); } }; document.addEventListener('keydown', handleKeydown); // 监听焦点事件,focus冒泡必须第三参数是true promptContainer.addEventListener('focus', (event) => { focusEl = event.target; }, true); // 添加点击遮罩层关闭对话框的功能 if(modalMode){ overlay.addEventListener('click', () => { // 点击遮罩层关闭弹窗 if(clickOverlayClose) cancelButton.click(); // 重新获取焦点 if(focusEl) focusEl.focus(); // 添加抖动类 dialog.classList.add('prompt-shake'); // 移除抖动类 setTimeout(() => { dialog.classList.remove('prompt-shake'); }, 500); }); } }); } // 输出到全局变量 window.showPrompt = showPrompt; window.showPromptForm = showPromptForm; })();
注意事项
showPrompt
和showPromptForm
的区别,见上面的效果图,上面一个是 showPrompt,这个是对 window.prompt 的模拟,下面一个是 showPromptForm,是对 showPrompt 的扩展,可以用于实现复杂的表单功能。- 默认使用模态模式,如果不使用模态(即不使用遮罩层),调用时把 modalMode 参数设置为 false 即可。
- 模态窗口的背景色默认和思源保持一致,如果你想修改背景色,可以修改样式.prompt-overlay 的
background-color: var(--b3-mask-background);
这行代码。 - 模态窗口的宽度默认是 400px,如果想自定义宽度可通过 width 参数设置。
- 如果想支持
点击遮罩层关闭对话框的功能
,把 clickOverlayClose 参数设置为 true 即可。 - 点确认按钮返回值是字符串,点取消返回值是 null,但 showPromptForm,点确定的返回值是自己定义的。
- 按钮文字可通过
cancelName
和okName
参数设置。 - 更多功能请参考源码。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于