diff --git a/scripts/fix_modal_bug.py b/scripts/fix_modal_bug.py new file mode 100644 index 0000000..4f8579e --- /dev/null +++ b/scripts/fix_modal_bug.py @@ -0,0 +1,519 @@ +#!/usr/bin/env python3 +""" +批量修复 ~cu-modal / ~show 弹窗隐患。 +替换策略: + 1. 模板中所有 ~cu-modal → 对应的本地类名 + 2. 模板中所有 ~show → show (普通类) + 3. 模板中所有 ~bottom-modal → (去除,样式合并到本地类) + 4. 模板中所有 ~cu-dialog → 对应的本地类名 + 5. style 中追加完整的自包含弹窗样式 +""" + +import re +import os + +# 每个文件的修复配置 +CONFIGS = { + "src/components/uploadPics.wpy": { + "type": "bottom", + "modal_cls": "uploadpics-mask", + "dialog_cls": "uploadpics-sheet", + "trigger": "~show", + "style_add": """ +.uploadpics-mask { + position: fixed; + top: 0; right: 0; bottom: 0; left: 0; + z-index: 1110; + background: rgba(0,0,0,.6); + opacity: 0; + pointer-events: none; + transition: all .3s ease; +} +.uploadpics-mask.show { + opacity: 1; + pointer-events: auto; +} +.uploadpics-sheet { + position: absolute; + left: 0; right: 0; bottom: 0; + background: #fff; + border-radius: 32rpx 32rpx 0 0; +} +._item { + padding: 32rpx; + border-bottom: 1rpx solid #f5f5f5; + font-size: 32rpx; + color: #333; + text-align: center; +} +._cancel { + border-top: 16rpx solid #f8f8f8; + margin-bottom: 24rpx; + color: #999; +} +""", + }, + "src/components/visitorModel.wpy": { + "type": "center", + "modal_cls": "visitormodel-mask", + "dialog_cls": "visitormodel-dialog", + "trigger": "~show", + "style_add": """ +.visitormodel-mask { + position: fixed; + top: 0; right: 0; bottom: 0; left: 0; + z-index: 1110; + background: rgba(0,0,0,.6); + opacity: 0; + pointer-events: none; + transition: opacity .3s; +} +.visitormodel-mask.show { + opacity: 1; + pointer-events: auto; +} +.visitormodel-dialog { + position: absolute; + top: 50%; left: 50%; + transform: translate(-50%,-50%) scale(.85); + width: 680rpx; + max-width: 100%; + background: #fff; + border-radius: 20rpx; + overflow: hidden; + transition: transform .3s; +} +.visitormodel-mask.show .visitormodel-dialog { + transform: translate(-50%,-50%) scale(1); +} +""", + }, + "src/components/recommendDialog.wpy": { + "type": "center", + "modal_cls": "recommend-mask", + "dialog_cls": "recommend-dialog", + "trigger": "~show", + "style_add": """ +.recommend-mask { + position: fixed; + top: 0; right: 0; bottom: 0; left: 0; + z-index: 1110; + background: rgba(0,0,0,.6); + opacity: 0; + pointer-events: none; + transition: opacity .3s; +} +.recommend-mask.show { + opacity: 1; + pointer-events: auto; +} +.recommend-dialog { + position: absolute; + top: 50%; left: 50%; + transform: translate(-50%,-50%) scale(.85); + width: 680rpx; + max-width: 100%; + background: #fff; + border-radius: 20rpx; + overflow: hidden; + transition: transform .3s; +} +.recommend-mask.show .recommend-dialog { + transform: translate(-50%,-50%) scale(1); +} +""", + }, + "src/components/shareComponent.wpy": { + "type": "center", + "modal_cls": "sharec-mask", + "dialog_cls": "sharec-dialog", + "trigger": "~show", + "style_add": """ +.sharec-mask { + position: fixed; + top: 0; right: 0; bottom: 0; left: 0; + z-index: 1110; + background: rgba(0,0,0,.6); + opacity: 0; + pointer-events: none; + transition: opacity .3s; +} +.sharec-mask.show { + opacity: 1; + pointer-events: auto; +} +.sharec-dialog { + position: absolute; + top: 50%; left: 50%; + transform: translate(-50%,-50%) scale(.85); + width: 680rpx; + max-width: 100%; + background: #fff; + border-radius: 20rpx; + overflow: hidden; + transition: transform .3s; +} +.sharec-mask.show .sharec-dialog { + transform: translate(-50%,-50%) scale(1); +} +""", + }, + "src/components/wxShareCom.wpy": { + "type": "center", + "modal_cls": "wxsharec-mask", + "dialog_cls": "wxsharec-dialog", + "trigger": "~show", + "style_add": """ +.wxsharec-mask { + position: fixed; + top: 0; right: 0; bottom: 0; left: 0; + z-index: 1110; + background: rgba(0,0,0,.6); + opacity: 0; + pointer-events: none; + transition: opacity .3s; +} +.wxsharec-mask.show { + opacity: 1; + pointer-events: auto; +} +.wxsharec-dialog { + position: absolute; + top: 50%; left: 50%; + transform: translate(-50%,-50%) scale(.85); + width: 680rpx; + max-width: 100%; + background: #fff; + border-radius: 20rpx; + overflow: hidden; + transition: transform .3s; +} +.wxsharec-mask.show .wxsharec-dialog { + transform: translate(-50%,-50%) scale(1); +} +""", + }, + "src/components/dynamicOperation.wpy": { + "type": "bottom", + "modal_cls": "dynop-mask", + "dialog_cls": "dynop-sheet", + "trigger": "~show", + "style_add": """ +.dynop-mask { + position: fixed; + top: 0; right: 0; bottom: 0; left: 0; + z-index: 1110; + background: rgba(0,0,0,.6); + opacity: 0; + pointer-events: none; + transition: all .3s; +} +.dynop-mask.show { + opacity: 1; + pointer-events: auto; +} +.dynop-sheet { + position: absolute; + left: 0; right: 0; bottom: 0; + background: #fff; + border-radius: 30rpx 30rpx 0 0; + width: 100%; + z-index: 1111; + view { + height: 90rpx; + text-align: center; + line-height: 100rpx; + margin: 0 30rpx; + color: #333; + } + view:last-of-type { + border-top: 1rpx solid #f5f5f5; + color: #999; + } +} +""", + }, + "src/components/UnlockingDialog.wpy": { + "type": "center", + "modal_cls": "unlock-mask", + "dialog_cls": "unlock-dialog", + "trigger": "~show", + "style_add": """ +.unlock-mask { + position: fixed; + top: 0; right: 0; bottom: 0; left: 0; + z-index: 1110; + background: rgba(0,0,0,.6); + opacity: 0; + pointer-events: none; + transition: opacity .3s; +} +.unlock-mask.show { + opacity: 1; + pointer-events: auto; +} +.unlock-dialog { + position: absolute; + top: 50%; left: 50%; + transform: translate(-50%,-50%) scale(.85); + width: 680rpx; + max-width: 100%; + background: #fff; + border-radius: 20rpx; + overflow: hidden; + transition: transform .3s; +} +.unlock-mask.show .unlock-dialog { + transform: translate(-50%,-50%) scale(1); +} +""", + }, + "src/components/loginDialog.wpy": { + "type": "center", + "modal_cls": "logindlg-mask", + "dialog_cls": "logindlg-dialog", + "trigger": "~show", + "style_add": """ +.logindlg-mask { + position: fixed; + top: 0; right: 0; bottom: 0; left: 0; + z-index: 9999; + background: rgba(0,0,0,.6); + opacity: 0; + pointer-events: none; + transition: opacity .3s; +} +.logindlg-mask.show { + opacity: 1; + pointer-events: auto; +} +.logindlg-dialog { + position: absolute; + top: 50%; left: 50%; + transform: translate(-50%,-50%) scale(.85); + width: 680rpx; + max-width: 100%; + background: #fff; + border-radius: 20rpx; + overflow: hidden; + transition: transform .3s; +} +.logindlg-mask.show .logindlg-dialog { + transform: translate(-50%,-50%) scale(1); +} +""", + }, + "src/components/residenceDialog.wpy": { + "type": "bottom", + "modal_cls": "residence-mask", + "dialog_cls": "residence-sheet", + "trigger": "~show", + "style_add": """ +.residence-mask { + position: fixed; + top: 0; right: 0; bottom: 0; left: 0; + z-index: 1110; + background: rgba(0,0,0,.6); + opacity: 0; + pointer-events: none; + transition: all .3s; +} +.residence-mask.show { + opacity: 1; + pointer-events: auto; +} +.residence-sheet { + position: absolute; + left: 0; right: 0; bottom: 0; + background: #fff; + border-radius: 32rpx 32rpx 0 0; + overflow: hidden; +} +""", + }, + "src/components/dynamicDiscuss.wpy": { + "type": "bottom", + "modal_cls": "discs-mask", + "dialog_cls": "discs-sheet", + "trigger": "~show", + "style_add": """ +.discs-mask { + position: fixed; + top: 0; right: 0; bottom: 0; left: 0; + z-index: 1110; + background: rgba(0,0,0,.6); + opacity: 0; + pointer-events: none; + transition: all .3s; +} +.discs-mask.show { + opacity: 1; + pointer-events: auto; +} +.discs-sheet { + position: absolute; + left: 0; right: 0; bottom: 0; + background: #fff; + border-radius: 20rpx 20rpx 0 0; + overflow: hidden; +} +""", + }, + "src/components/cu-custom.wpy": { + "type": "center", + "modal_cls": "cucust-mask", + "dialog_cls": "cucust-dialog", + "trigger": "~show", + "style_add": """ +.cucust-mask { + position: fixed; + top: 0; right: 0; bottom: 0; left: 0; + z-index: 1110; + background: rgba(0,0,0,.6); + opacity: 0; + pointer-events: none; + transition: opacity .3s; +} +.cucust-mask.show { + opacity: 1; + pointer-events: auto; +} +.cucust-dialog { + position: absolute; + top: 50%; left: 50%; + transform: translate(-50%,-50%) scale(.85); + width: 680rpx; + max-width: 100%; + background: #fff; + border-radius: 20rpx; + overflow: hidden; + transition: transform .3s; +} +.cucust-mask.show .cucust-dialog { + transform: translate(-50%,-50%) scale(1); +} +""", + }, + "src/components/uploadVideo.wpy": { + "type": "bottom", + "modal_cls": "uploadvid-mask", + "dialog_cls": "uploadvid-sheet", + "trigger": "~show", + "style_add": """ +.uploadvid-mask { + position: fixed; + top: 0; right: 0; bottom: 0; left: 0; + z-index: 1110; + background: rgba(0,0,0,.6); + opacity: 0; + pointer-events: none; + transition: all .3s; +} +.uploadvid-mask.show { + opacity: 1; + pointer-events: auto; +} +.uploadvid-sheet { + position: absolute; + left: 0; right: 0; bottom: 0; + background: #fff; + border-radius: 32rpx 32rpx 0 0; +} +""", + }, +} + + +def transform_template(content, cfg): + """把模板中的 ~cu-modal / ~show / ~bottom-modal / ~cu-dialog 替换掉""" + modal = cfg["modal_cls"] + dialog = cfg["dialog_cls"] + + lines = content.split("\n") + result = [] + for line in lines: + orig = line + + # 1. ~cu-dialog → dialog类名 + line = re.sub(r"~cu-dialog\b", dialog, line) + + # 2. ~bottom-modal → (去除,样式已在本地) + line = re.sub(r"~\s*bottom-modal", "", line) + + # 3. ~cu-modal 处理 + # 情况A: class="~cu-modal ..." (注意前面可能有其他class如 shareComponent的 cu-modal) + # 情况B: :class="{'~show': ...}" class="~cu-modal" + # 统一:去掉所有 ~cu-modal,保留其他已有class,在class属性末尾追加modal类名 + if "~cu-modal" in line: + # 先把 :class={'~show':cond} 里的 ~show 改成 show + line = re.sub(r":class\s*=\s*\{'~show'\s*:", ":class=\"{'show':", line) + # {{cond?'~show':''}} → {{cond?'show':''}} + line = re.sub(r"\?'~show'", "? 'show'", line) + + # 去除 ~cu-modal,保留其他普通class + # 匹配 class="... ~cu-modal ..." 或 class="~cu-modal" + # WePY: class="xxx ~cu-modal yyy" → class="xxx yyy modal_cls" + # class="~cu-modal xxx" → class="xxx modal_cls" + # class="~cu-modal" → class="modal_cls" + + def fix_cu_modal(m): + raw = m.group(0) + # 提取已有class值 + cls_val = re.search(r'class="([^"]*)"', raw) + if not cls_val: + return raw + classes = cls_val.group(1) + # 去掉 ~cu-modal 和多余的空格 + classes = re.sub(r"~\s*cu-modal", "", classes) + classes = re.sub(r"\s+", " ", classes).strip() + # 去掉 shareComponent 的普通 cu-modal (不再需要) + classes = re.sub(r"\bcu-modal\b", "", classes) + classes = re.sub(r"\s+", " ", classes).strip() + # 追加本地modal类 + extra = " " + modal if classes else modal + new_cls = (classes + extra).strip() + return 'class="' + new_cls + '"' + + line = re.sub(r'class="[^"]*"', fix_cu_modal, line, count=1) + + # 4. 单独处理 :class="{'~show': ...}" 的情况(还没处理到的情况) + line = re.sub(r":class\s*=\s*\{'~show'\s*:", ":class=\"{'show':", line) + line = re.sub(r"\?'~show':", "? 'show':", line) + + result.append(line) + return "\n".join(result) + + +def append_style(content, style_add): + """在 前追加样式""" + marker = "" + if marker in content: + idx = content.index(marker) + return content[:idx] + "\n" + style_add + "\n" + marker + content[idx + len(marker):] + return content + + +def process_file(filepath, cfg): + with open(filepath, "r", encoding="utf-8") as f: + content = f.read() + + new_content = transform_template(content, cfg) + new_content = append_style(new_content, cfg["style_add"]) + + with open(filepath, "w", encoding="utf-8") as f: + f.write(new_content) + + print(f"✓ {filepath}") + + +def main(): + base = "/Users/d/fulllink-project/ufutx.love.mp.v2" + for rel_path, cfg in CONFIGS.items(): + filepath = os.path.join(base, rel_path) + if os.path.exists(filepath): + process_file(filepath, cfg) + else: + print(f"✗ 文件不存在: {filepath}") + +if __name__ == "__main__": + main() diff --git a/src/components/UnlockingDialog.wpy b/src/components/UnlockingDialog.wpy index 9eb9540..473b5a2 100644 --- a/src/components/UnlockingDialog.wpy +++ b/src/components/UnlockingDialog.wpy @@ -1,6 +1,6 @@