Files
gym-manage/gym-manage-uniapp/scripts/convert-wxss-bg-to-image.js
T
2026-06-04 14:18:53 +08:00

294 lines
5.0 KiB
JavaScript
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.
/**
* 微信小程序组件/页面 WXSS 不支持本地 background-image
* 将 /static/images/ 背景图改为 <image> 标签,并清理 CSS。
*/
const fs = require('fs');
const path = require('path');
const root = path.join(__dirname, '..');
const COMPONENT_MAP = {
MemberInfoStatusBar: 'member-info-status-bar.css',
MemberInfoHeader: 'member-info-header.css',
MemberInfoMemberCard: 'member-info-member-card.css',
MemberInfoQuickActions: 'member-info-quick-actions.css',
MemberInfoBookingList: 'member-info-booking-list.css',
MemberInfoCheckInList: 'member-info-check-in-list.css',
MemberInfoBodyReport: 'member-info-body-report.css',
MemberInfoCouponPoints: 'member-info-coupon-points.css',
MemberInfoReferral: 'member-info-referral.css',
MemberInfoSettings: 'member-info-settings.css',
MemberInfoLogout: 'member-info-logout.css'
};
const PAGE_MAP = {
booking: ['booking-pixso.css'],
memberCard: ['member-card-pixso.css'],
userInfo: ['user-info-pixso.css']
};
function extractBgImages(css) {
const map = new Map();
const ruleRe = /^\.([a-zA-Z0-9_-]+)\s*\{([^}]*)\}/gm;
let match;
while ((match = ruleRe.exec(css)) !== null) {
const className = match[1];
const body = match[2];
const urlMatch = body.match(/background-image:\s*url\(\/static\/images\/([^)]+)\)/);
if (urlMatch) {
map.set(className, `/static/images/${urlMatch[1]}`);
}
}
return map;
}
function ensureImageDisplay(css, className) {
const ruleRe = new RegExp(`(\\.${className.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*\\{)([^}]*)(\\})`);
return css.replace(ruleRe, (full, head, body, tail) => {
if (/display:\s*block/.test(body)) return full;
return `${head}${body.trim()}\n display: block;\n${tail}`;
});
}
function getMode(className) {
if (/avatar|banner|photo|card-preview|AC\d/i.test(className)) return 'aspectFill';
return 'aspectFit';
}
function replaceViewWithImage(template, className, src) {
const mode = getMode(className);
const imageTag = `<image class="${className}" src="${src}" mode="${mode}" />`;
const escaped = className.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const patterns = [
new RegExp(`<view\\s+class="${escaped}"\\s*></view>`, 'g'),
new RegExp(`<view\\s+class="${escaped}"\\s*/>`, 'g'),
new RegExp(`<view\\s+class="${escaped}"\\s*>\\s*</view>`, 'g'),
new RegExp(`<view\\s+\\n\\s*class="${escaped}"\\s*\\n\\s*></view>`, 'g'),
new RegExp(`<view\\s+\\n\\s*class="${escaped}"\\s*\\n\\s*/>`, 'g'),
new RegExp(`<view\\s+[^>]*class="${escaped}"[^>]*>\\s*</view>`, 'g')
];
let result = template;
for (const re of patterns) {
result = result.replace(re, imageTag);
}
return result;
}
function stripAllLocalBgFromCss(css) {
let next = css;
next = next.replace(/background-image:\s*url\(\/static\/images\/[^)]+\);/g, '');
next = next.replace(/\n\s*background-size:\s*100%\s*100%;/g, '');
next = next.replace(/\n\s*background-repeat:\s*no-repeat;/g, '');
return next;
}
function convertPair(vuePath, cssPath) {
if (!fs.existsSync(vuePath) || !fs.existsSync(cssPath)) return;
let css = fs.readFileSync(cssPath, 'utf8');
const hadLocalBg = /background-image:\s*url\(\/static\/images\//.test(css);
if (!hadLocalBg) return;
const bgMap = extractBgImages(css);
let template = fs.readFileSync(vuePath, 'utf8');
const templateMatch = template.match(/<template>([\s\S]*?)<\/template>/);
if (!templateMatch) {
css = stripAllLocalBgFromCss(css);
fs.writeFileSync(cssPath, css, 'utf8');
console.log('css-only (no template)', path.relative(root, cssPath));
return;
}
let inner = templateMatch[1];
let changed = false;
for (const [className, src] of bgMap.entries()) {
const before = inner;
inner = replaceViewWithImage(inner, className, src);
if (inner !== before) {
changed = true;
css = ensureImageDisplay(css, className);
}
}
css = stripAllLocalBgFromCss(css);
if (changed) {
template = template.replace(templateMatch[1], inner);
fs.writeFileSync(vuePath, template, 'utf8');
}
fs.writeFileSync(cssPath, css, 'utf8');
console.log(
changed ? 'converted' : 'css-only',
path.relative(root, vuePath),
`(${bgMap.size} rules stripped)`
);
}
for (const [name, cssFile] of Object.entries(COMPONENT_MAP)) {
convertPair(
path.join(root, 'components/memberInfo', `${name}.vue`),
path.join(root, 'common/style/memberInfo', cssFile)
);
}
for (const [page, cssFiles] of Object.entries(PAGE_MAP)) {
for (const cssFile of cssFiles) {
convertPair(
path.join(root, 'pages/memberInfo', `${page}.vue`),
path.join(root, 'common/style/memberInfo/pages', cssFile)
);
}
}
console.log('done');