Files
gym-manage/gym-manage-uniapp/scripts/convert-pixso-pages.js
T
2026-06-04 14:18:53 +08:00

240 lines
7.8 KiB
JavaScript

const fs = require('fs');
const path = require('path');
const CSS_DIR = path.join(__dirname, '../common/style/memberInfo/pages');
const STYLE_PREFIX = '@/common/style/memberInfo/pages';
const PAGES = {
booking: {
cssFile: 'booking-pixso.css',
pageCssFile: 'booking-page.css',
rootClass: 'Pixso-frame-2_964',
backFrameClass: 'Pixso-frame-2_969',
extraData: `,
activeTab: 'ongoing'`,
extraMethods: `,
switchTab(tab) {
this.activeTab = tab
},
cancelBooking() {
uni.showModal({
title: '取消预约',
content: '确定要取消该预约吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '已取消', icon: 'success' })
}
}
})
}`
},
memberCard: {
cssFile: 'member-card-pixso.css',
pageCssFile: 'member-card-page.css',
rootClass: 'Pixso-frame-2_877',
backFrameClass: 'Pixso-frame-2_882',
extraData: `,
activeFilter: 'all'`,
extraMethods: `,
switchFilter(filter) {
this.activeFilter = filter
},
renewCard() {
uni.showToast({ title: '续费功能开发中', icon: 'none' })
}`
},
userInfo: {
cssFile: 'user-info-pixso.css',
pageCssFile: 'user-info-page.css',
rootClass: 'Pixso-frame-2_791',
backFrameClass: 'Pixso-frame-2_796',
navWrapperClass: 'stroke-wrapper-2_795',
navContentClass: 'frame-content-2_795',
navTitleClass: 'Pixso-paragraph-2_799',
saveFrameClass: 'Pixso-frame-2_800',
extraData: `,
name: '张小芳',
phone: '138****6789',
gender: 'female',
birthday: '1995年06月15日',
height: '165',
weight: '63.5',
fitnessGoals: ['增肌']`,
extraMethods: `,
handleSave() {
uni.showToast({ title: '保存成功', icon: 'success' })
},
changeAvatar() {
uni.showToast({ title: '更换头像功能开发中', icon: 'none' })
},
rebindPhone() {
uni.showToast({ title: '换绑功能开发中', icon: 'none' })
},
selectGender(gender) {
this.gender = gender
},
toggleGoal(goal) {
const index = this.fitnessGoals.indexOf(goal)
if (index >= 0) {
this.fitnessGoals.splice(index, 1)
} else {
this.fitnessGoals.push(goal)
}
},
isGoalSelected(goal) {
return this.fitnessGoals.includes(goal)
}`
}
};
function alignCss(css) {
let result = css.replace(/@import\s+['"]\.\/base\.css['"];\s*/g, '');
const replacements = [
['url(@/assets/images/', 'url(@/static/images/'],
['box-shadow: 0px 8px 16px 0px rgba(255, 107, 53, 0.25098039215686274)', 'box-shadow: var(--shadow-orange-glow)'],
['background-color: rgba(255, 255, 255, 1)', 'background-color: var(--bg-white)'],
['color: rgba(255, 255, 255, 1)', 'color: var(--text-inverse)'],
['border-color: rgba(255, 255, 255, 1)', 'border-color: var(--bg-white)'],
['rgba(11, 43, 75, 1)', 'var(--primary-dark)'],
['rgba(26, 74, 111, 1)', 'var(--primary-deep)'],
['rgba(255, 107, 53, 1)', 'var(--accent-orange)'],
['rgba(255, 140, 90, 1)', 'var(--accent-orange-light)'],
['rgba(249, 250, 254, 1)', 'var(--bg-light)'],
['rgba(30, 42, 58, 1)', 'var(--text-dark)'],
['rgba(94, 111, 141, 1)', 'var(--text-muted)'],
['rgba(138, 153, 180, 1)', 'var(--text-light)'],
['rgba(233, 237, 242, 1)', 'var(--border-light)'],
['rgba(46, 204, 113, 1)', 'var(--success-green)'],
['rgba(231, 76, 60, 1)', 'var(--error-red)'],
['height: 900px', 'height: auto;\n min-height: 100%'],
['height: 2300px', 'height: auto;\n min-height: 100%'],
['height: 1901px', 'height: auto']
];
for (const [from, to] of replacements) {
result = result.split(from).join(to);
}
return result;
}
function convertTemplate(template, config) {
let result = template.replace(/\r\n/g, '\n');
result = result.replace(/<div\b/g, '<view');
result = result.replace(/<\/div>/g, '</view>');
result = result.replace(/<p\b/g, '<text');
result = result.replace(/<\/p>/g, '</text>');
result = result.replace(/\s+id="[^"]*"/g, '');
result = result.replace(/\{\{\s*"([^"]*?)"\s*\}\}/gs, '$1');
result = result.replace(/\{\{\s*'([^']*?)'\s*\}\}/gs, '$1');
result = result.replace(
'<view class="scroll-container">',
'<view class="scroll-container theme-light">'
);
result = result.replace(/>\s*9:41\s*</g, '>{{ statusBarTime }}<');
if (config.backFrameClass) {
result = result.replace(
new RegExp(`class="${config.backFrameClass}"`, 'g'),
`class="${config.backFrameClass} nav-back" @tap.stop="goBack"`
);
}
if (config.navTitleClass) {
result = result.replace(
new RegExp(`class="${config.navTitleClass}"`, 'g'),
`class="${config.navTitleClass} nav-title"`
);
}
if (config.saveFrameClass) {
result = result.replace(
new RegExp(`class="${config.saveFrameClass}"`, 'g'),
`class="${config.saveFrameClass}" @tap="handleSave"`
);
}
if (config.bindings) {
for (const { from, to } of config.bindings) {
result = result.replace(from, to);
}
}
return result.trim();
}
function buildPageCss(config) {
return `@import './sub-page-base.css';
.${config.rootClass} {
width: 100%;
box-sizing: border-box;
}
`;
}
function convertPage(pageName, config) {
const vuePath = path.join(__dirname, `../pages/memberInfo/${pageName}.vue`);
const cssPath = path.join(CSS_DIR, config.cssFile);
const pageCssPath = path.join(CSS_DIR, config.pageCssFile);
const content = fs.readFileSync(vuePath, 'utf8').replace(/\r\n/g, '\n');
const templateMatch = content.match(/<template>([\s\S]*?)<\/template>/);
const styleMatch = content.match(/<style>([\s\S]*?)<\/style>/);
if (!templateMatch || !styleMatch) {
throw new Error(`Could not parse ${pageName}.vue`);
}
const template = convertTemplate(templateMatch[1], config);
const css = alignCss(styleMatch[1].trim());
const pageCss = buildPageCss(config);
const extraMethods = (config.extraMethods || '').replace(/^\s*,\s*/, '');
const extraDataBlock = (config.extraData || '').replace(/^\s*,\s*/, '');
const dataBody = pageName === 'userInfo'
? '...userInfoMock'
: extraDataBlock;
const vueOutput = `<template>
${template}
</template>
<script>
import { statusBarTimeMixin, subPageMixin } from '@/common/memberInfo/mixins.js'${pageName === 'userInfo' ? "\nimport { userInfoMock } from '@/common/memberInfo/mockData.js'" : ''}
export default {
mixins: [statusBarTimeMixin, subPageMixin],
data() {
return {
${dataBody}
}
},
methods: {
${extraMethods}
}
}
</script>
<style>
@import '${STYLE_PREFIX}/${config.pageCssFile}';
@import '${STYLE_PREFIX}/${config.cssFile}';
</style>
`;
fs.writeFileSync(cssPath, css + '\n', 'utf8');
fs.writeFileSync(pageCssPath, pageCss, 'utf8');
fs.writeFileSync(vuePath, vueOutput, 'utf8');
console.log(`converted ${pageName}.vue -> ${config.cssFile}`);
}
const target = process.argv[2];
const entries = target
? [[target, PAGES[target]]].filter(([, config]) => config)
: Object.entries(PAGES);
for (const [pageName, config] of entries) {
convertPage(pageName, config);
}
console.log('done');