|
|
|
@@ -0,0 +1,223 @@
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|