Files
Mycontent/reactH5/scripts/sync_styles_from_miniprogram.py
2026-04-28 18:39:08 +08:00

300 lines
9.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""从 miniprogram 同步 wxss 到 reactH5 的 cssP1 页面 + 全局 + TabBar + 登录)。"""
import os
import shutil
BASE = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", ".."))
# 写入 index wxss 后追加(对齐 scroll-view 横滑 / 触摸)
INDEX_CSS_H5_TAIL = """
/* ----- H5 增补(对齐小程序 scroll-view 与可点反馈;源 wxss 无此块) ----- */
.super-scroll {
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
}
.super-scroll-inner {
min-height: min-content;
}
.banner-card,
.contact-btn,
.featured-item,
.super-empty-btn,
.super-item-h,
.timeline-item,
.latest-expand-hint {
cursor: pointer;
-webkit-tap-highlight-color: transparent;
user-select: none;
}
.contact-btn:active,
.featured-item:active,
.banner-card:active,
.super-empty-btn:active,
.super-item-h:active {
opacity: 0.97;
}
/* ----- H5左右安全区刘海/曲面);并提高首页作用域优先级,避免被其它页面同名类覆盖 ----- */
.index-page.page {
padding-left: env(safe-area-inset-left, 0px);
padding-right: env(safe-area-inset-right, 0px);
box-sizing: border-box;
}
.index-page .main-content {
padding-left: 32rpx;
padding-right: 32rpx;
box-sizing: border-box;
}
.index-page .main-content .super-scroll,
.index-page .section .super-scroll,
.index-page .main-content .super-loading,
.index-page .section .super-loading {
margin-left: 0;
margin-right: 0;
}
"""
# TabBarH5 用内联 SVGTabBarIcons需固定尺寸类名
TABBAR_CSS_H5_TAIL = """
/* ----- H5 增补:普通 Tab 为矢量描边(见 TabBarIcons与 inactive #8e8e93 / active #00CED1 一致 ----- */
.tab-icon-svg {
width: 48rpx;
height: 48rpx;
display: block;
flex-shrink: 0;
}
/* ----- H5TabBar 贴合左右安全区,避免首尾图标贴直角边 ----- */
.tab-bar {
padding-left: env(safe-area-inset-left, 0px);
padding-right: env(safe-area-inset-right, 0px);
box-sizing: border-box;
}
"""
CHAPTERS_CSS_H5_TAIL = """
/* ----- H5目录页 scoped 兜底,避免与首页/匹配页同名类互相覆盖导致样式错乱 ----- */
.chapters-page.page {
padding-left: env(safe-area-inset-left, 0px);
padding-right: env(safe-area-inset-right, 0px);
box-sizing: border-box;
}
.chapters-page .chapters-content {
padding: 16rpx 24rpx;
width: 100%;
box-sizing: border-box;
}
.chapters-page .book-card-title {
font-size: 30rpx;
font-weight: 700;
color: #ffffff;
}
.chapters-page .part-title {
font-size: 27rpx;
font-weight: 600;
color: #ffffff;
}
.chapters-page .section-title {
font-size: 25rpx;
color: #ffffff;
white-space: normal;
word-break: break-word;
overflow-wrap: anywhere;
line-height: 1.35;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
flex: 1;
}
.chapters-page .tag {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 20rpx;
padding: 4rpx 14rpx;
min-width: 64rpx;
border-radius: 8rpx;
box-sizing: border-box;
text-align: center;
}
.chapters-page .tag-free {
background: rgba(0, 206, 209, 0.1);
color: #00CED1;
}
.chapters-page .section-item {
display: flex;
width: 100%;
box-sizing: border-box;
align-items: flex-start;
}
.chapters-page .chapters-list {
margin-top: 12rpx;
margin-left: 12rpx;
}
.chapters-page .part-item {
display: block;
width: 100%;
margin-bottom: 12rpx;
}
.chapters-page .part-item > .chapters-list {
display: block;
width: 100%;
}
.chapters-page .part-header {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
column-gap: 12rpx;
width: 100%;
box-sizing: border-box;
}
.chapters-page .part-left {
min-width: 0;
}
.chapters-page .part-right {
justify-self: end;
white-space: nowrap;
display: inline-flex;
align-items: center;
gap: 12rpx;
}
.chapters-page .section-list {
display: block;
width: 100%;
}
.chapters-page .chapter-header {
display: block;
width: 100%;
padding: 14rpx 24rpx;
font-size: 23rpx;
font-weight: 500;
color: rgba(255, 255, 255, 0.5);
border-bottom: 1rpx solid rgba(255, 255, 255, 0.04);
}
.chapters-page .section-left {
display: flex;
flex-direction: row;
align-items: flex-start;
gap: 14rpx;
flex: 1;
min-width: 0;
}
.chapters-page .section-right {
display: flex;
align-items: center;
gap: 12rpx;
flex-shrink: 0;
margin-left: 12rpx;
padding-top: 2rpx;
}
.chapters-page .card {
background: rgba(28, 28, 30, 0.7);
border-radius: 20rpx;
border: 1rpx solid rgba(255, 255, 255, 0.04);
margin: 0 12rpx 16rpx 12rpx;
box-sizing: border-box;
}
"""
PAIRS = [
("miniprogram/app.wxss", "reactH5/src/styles/app-global.css"),
("miniprogram/pages/index/index.wxss", "reactH5/src/pages/Index/IndexPage.css"),
("miniprogram/pages/chapters/chapters.wxss", "reactH5/src/pages/Chapters/ChaptersPage.css"),
("miniprogram/pages/match/match.wxss", "reactH5/src/pages/Match/MatchPage.css"),
("miniprogram/pages/my/my.wxss", "reactH5/src/pages/My/MyPage.css"),
("miniprogram/custom-tab-bar/index.wxss", "reactH5/src/components/TabBar/TabBar.css"),
("miniprogram/components/login-modal/login-modal.wxss", "reactH5/src/components/LoginModal/LoginModal.css"),
("miniprogram/pages/dev-login/dev-login.wxss", "reactH5/src/pages/DevLogin/DevLoginPage.css"),
]
def copy_tree_merge(src_dir: str, dst_dir: str) -> None:
if not os.path.isdir(src_dir):
print("SKIP dir missing:", src_dir)
return
os.makedirs(dst_dir, exist_ok=True)
for root, _, files in os.walk(src_dir):
rel = os.path.relpath(root, src_dir)
out_root = os.path.join(dst_dir, rel) if rel != "." else dst_dir
os.makedirs(out_root, exist_ok=True)
for name in files:
shutil.copy2(os.path.join(root, name), os.path.join(out_root, name))
print("ASSETS merged", src_dir, "->", dst_dir)
def main():
# 静态资源SVG/图片等与小程序共用一份
copy_tree_merge(
os.path.join(BASE, "miniprogram", "assets"),
os.path.join(BASE, "reactH5", "public", "assets"),
)
st_src = os.path.join(BASE, "miniprogram", "static")
st_dst = os.path.join(BASE, "reactH5", "public", "static")
if os.path.isdir(st_src):
os.makedirs(st_dst, exist_ok=True)
for fn in os.listdir(st_src):
shutil.copy2(os.path.join(st_src, fn), os.path.join(st_dst, fn))
print("STATIC", st_src, "->", st_dst)
# iconfont同步 wxss + 注入与小程序一致的 CDNapp.js loadIconFont
ic_wxss = os.path.join(BASE, "miniprogram", "static", "iconfont.wxss")
ic_css = os.path.join(BASE, "reactH5", "src", "styles", "iconfont.css")
if os.path.isfile(ic_wxss):
with open(ic_wxss, "r", encoding="utf-8", errors="replace") as f:
ic_text = f.read()
CDN_MARK = "at.alicdn.com"
if CDN_MARK not in ic_text:
old = " src: url('/static/iconfont.woff2') format('woff2'),"
new = (
" src: url('https://at.alicdn.com/t/c/font_5142223_1sq6pv9vvbt.woff2') format('woff2'),\n"
" url('/static/iconfont.woff2') format('woff2'),"
)
if old in ic_text:
ic_text = ic_text.replace(old, new, 1)
icon_cmp = os.path.join(BASE, "miniprogram", "components", "icon", "icon.wxss")
if os.path.isfile(icon_cmp):
with open(icon_cmp, "r", encoding="utf-8", errors="replace") as f:
cmp_txt = f.read().strip()
ic_text += "\n\n/* H5 appended: miniprogram/components/icon/icon.wxss */\n"
ic_text += cmp_txt + "\n"
ic_header = "/* Synced from miniprogram/static/iconfont.wxss + H5 CDN + icon/component */\n"
os.makedirs(os.path.dirname(ic_css), exist_ok=True)
with open(ic_css, "w", encoding="utf-8", newline="\n") as f:
f.write(ic_header + ic_text)
print("OK iconfont.css")
for src_rel, dst_rel in PAIRS:
src = os.path.join(BASE, src_rel.replace("/", os.sep))
dst = os.path.join(BASE, dst_rel.replace("/", os.sep))
if not os.path.isfile(src):
print("SKIP missing:", src_rel)
continue
with open(src, "r", encoding="utf-8", errors="replace") as f:
text = f.read()
# app.wxss 中两处 page { 均映射为 H5 根选择器
if "app.wxss" in src_rel:
text = text.replace("page {", ":root, html, body, #root {")
header = "/* Auto-synced from miniprogram → reactH5; 源: %s */\n" % src_rel
if "pages/index/index.wxss" in src_rel:
text += INDEX_CSS_H5_TAIL
if "pages/chapters/chapters.wxss" in src_rel:
text += CHAPTERS_CSS_H5_TAIL
if "custom-tab-bar/index.wxss" in src_rel:
text += TABBAR_CSS_H5_TAIL
os.makedirs(os.path.dirname(dst), exist_ok=True)
with open(dst, "w", encoding="utf-8", newline="\n") as f:
f.write(header + text)
print("OK", dst_rel, len(text), "chars")
if __name__ == "__main__":
main()