export default function showDialog(options) { const view = new plus.nativeObj.View('customModal', { top: '0', left: '0', height: '100%', width: '100%', backgroundColor: 'rgba(0,0,0,0.5)', position: 'fixed' }); const screenWidth = plus.screen.resolutionWidth; const screenHeight = plus.screen.resolutionHeight; const borderRadius = 10; const contentPadding = 20; // 内容左右内边距 const lineHeight = 24; // 单行文本行高 const maxLines = 2; // 最大行数 const fontSize = 16; // 字体大小 // 判断内容和标题是否存在 const hasContent = options.content && options.content.trim() !== ''; const hasTitle = options.title && options.title.trim() !== ''; // 弹窗宽度计算 const modalWidth = Math.min(0.8 * screenWidth, 500); const modalLeft = (screenWidth - modalWidth) / 2; // 内容区域可用宽度 const contentAvailableWidth = modalWidth - 2 * contentPadding - 10; // 动态计算各部分高度 const titleHeight = hasTitle ? 30 : 0; const titleToContentSpacing = (hasTitle && hasContent) ? 10 : 0; const contentHeight = hasContent ? (lineHeight * maxLines) : 0; const contentToBtnSpacing = 10; const btnHeight = 50; // 弹窗总高度 let modalHeight = contentPadding + titleHeight + titleToContentSpacing + contentHeight + contentToBtnSpacing + btnHeight; modalHeight = options.showCancel ? Math.min(modalHeight, 300) : Math.min(modalHeight, 250); // 位置计算 const modalTop = (screenHeight - modalHeight) / 2; const contentAreaTop = modalTop + contentPadding; const titleTop = hasTitle ? contentAreaTop : 0; // Fix: Calculate contentTop properly when there's no content let contentTop; if (hasContent) { contentTop = hasTitle ? (titleTop + titleHeight + titleToContentSpacing) : contentAreaTop; } else { contentTop = hasTitle ? (titleTop + titleHeight) : contentAreaTop; } const btnTop = hasContent ? (contentTop + contentHeight + contentToBtnSpacing) : (hasTitle ? (titleTop + titleHeight + contentToBtnSpacing) : (contentAreaTop + (modalHeight - btnHeight - contentPadding))); // 既无标题也无内容时 // 绘制弹窗背景 view.draw([{ tag: 'rect', id: 'bg', position: { top: modalTop + 'px', left: modalLeft + 'px', width: modalWidth + 'px', height: modalHeight + 'px' }, rectStyles: { color: '#fff', radius: borderRadius + 'px' } }]); // 绘制标题和内容 const elements = []; // 标题(仅当存在时) if (hasTitle) { elements.push({ tag: 'font', id: 'title', text: options.title, position: { top: titleTop + 'px', left: modalLeft + 'px', width: modalWidth + 'px', height: titleHeight + 'px' }, textStyles: { color: '#000', size: '20px', align: 'center', fontWeight: 'bold', verticalAlign: 'middle' } }); } if (hasContent) { const text = options.content; const maxLines = 2; // 最大显示行数 const lineHeightPx = lineHeight; const leftPos = modalLeft + contentPadding; const textWidth = contentAvailableWidth; const fontSizePx = fontSize; // 按 \n 分割段落 const paragraphs = text.split('\n'); let lines = []; // 遍历每个段落,计算换行 for (let para of paragraphs) { if (lines.length >= maxLines) break; // 估算每行能容纳的字符数(中文按 1.2 倍宽度计算) const avgCharWidth = fontSizePx * (isChinese(para) ? 1.2 : 0.6); const maxCharsPerLine = Math.floor(textWidth / avgCharWidth); let currentLine = ''; for (let i = 0; i < para.length; i++) { const char = para[i]; currentLine += char; // 如果当前行字符数超过限制,换行 if (currentLine.length >= maxCharsPerLine) { lines.push(currentLine); currentLine = ''; if (lines.length >= maxLines) break; } } // 添加剩余部分 if (currentLine && lines.length < maxLines) { lines.push(currentLine); } } // 如果超出最大行数,最后一行加 "..." if (lines.length > maxLines) { lines = lines.slice(0, maxLines); const lastLine = lines[maxLines - 1]; lines[maxLines - 1] = lastLine.substring(0, lastLine.length - 3) + '...'; } // 绘制每一行文本(确保行高正确) lines.forEach((line, index) => { const topPos = contentTop + index * lineHeightPx; elements.push({ tag: 'font', id: 'content_line_' + index, text: line, position: { top: topPos + 'px', left: leftPos + 'px', width: textWidth + 'px', height: lineHeightPx + 'px' }, textStyles: { color: '#666', size: fontSizePx + 'px', align: 'center', verticalAlign: 'top', lineHeight: lineHeightPx + 'px' } }); }); } // 判断是否主要是中文 function isChinese(text) { return /[\u4e00-\u9fa5]/.test(text); } // 分割线 elements.push({ tag: 'rect', id: 'divider', position: { top: btnTop + 'px', left: modalLeft + 'px', width: modalWidth + 'px', height: '1px' }, rectStyles: { color: '#b3b3b3' } }); view.draw(elements); // 绘制按钮(不变) if (options.showCancel) { const btnWidth = Math.floor((modalWidth - 2 * borderRadius) / 2); view.draw([ { tag: 'rect', id: 'cancelBtn', position: { top: (btnTop + 2) + 'px', left: (modalLeft + borderRadius) + 'px', width: btnWidth + 'px', height: (btnHeight - 3) + 'px' }, rectStyles: { color: '#fff' } }, { tag: 'font', id: 'cancelText', text: options.cancelText || '取消', position: { top: btnTop + 'px', left: (modalLeft + borderRadius) + 'px', width: btnWidth + 'px', height: btnHeight + 'px' }, textStyles: { color: '#007AFF', size: '16px', align: 'center', verticalAlign: 'middle' } }, { tag: 'rect', id: 'btnDivider', position: { top: btnTop + 'px', left: (modalLeft + borderRadius + btnWidth) + 'px', width: '1px', height: btnHeight + 'px' }, rectStyles: { color: '#b3b3b3' } }, { tag: 'rect', id: 'confirmBtn', position: { top: (btnTop + 2) + 'px', left: (modalLeft + borderRadius + btnWidth + 1) + 'px', width: btnWidth + 'px', height: (btnHeight - 3) + 'px' }, rectStyles: { color: '#fff' } }, { tag: 'font', id: 'confirmText', text: options.confirmText || '确定', position: { top: btnTop + 'px', left: (modalLeft + borderRadius + btnWidth + 1) + 'px', width: btnWidth + 'px', height: btnHeight + 'px' }, textStyles: { color: '#007AFF', size: '16px', align: 'center', verticalAlign: 'middle' } } ]); } else { view.draw([ { tag: 'rect', id: 'confirmBtn', position: { top: (btnTop + 2) + 'px', left: (modalLeft + borderRadius) + 'px', width: (modalWidth - 2 * borderRadius) + 'px', height: (btnHeight - 3) + 'px' }, rectStyles: { color: '#fff' } }, { tag: 'font', id: 'confirmText', text: options.confirmText || '确定', position: { top: btnTop + 'px', left: (modalLeft + borderRadius) + 'px', width: (modalWidth - 2 * borderRadius) + 'px', height: btnHeight + 'px' }, textStyles: { color: '#007AFF', size: '16px', align: 'center', verticalAlign: 'middle' } } ]); } // 点击事件(不变) view.addEventListener("click", function(e) { const x = e.clientX; const y = e.clientY; if (options.showCancel) { const btnWidth = Math.floor((modalWidth - 2 * borderRadius) / 2); const cancelBtnLeft = modalLeft + borderRadius; if (y > btnTop && y < btnTop + btnHeight) { if (x >= cancelBtnLeft && x < cancelBtnLeft + btnWidth) { options.cancel?.(); view.close(); } else if (x >= cancelBtnLeft + btnWidth + 1 && x < cancelBtnLeft + 2 * btnWidth + 1) { options.success?.({ confirm: true }); view.close(); } } } else { const btnLeft = modalLeft + borderRadius; if (y > btnTop && y < btnTop + btnHeight && x >= btnLeft && x < btnLeft + (modalWidth - 2 * borderRadius)) { options.success?.({ confirm: true }); view.close(); } } }, false); view.show(); return { close: () => { view.close(); } }; }