2 Star 0 Fork 0

陈博/wp-components-center

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
output.txt 50.67 KB
一键复制 编辑 原始数据 按行查看 历史
陈博 提交于 2025-03-09 20:13 +08:00 . commit
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498
项目目录结构:
```
development
├── .dev.config.js
├── build
│ ├── build.js
│ ├── locationer.js
│ ├── render.js
│ ├── rollup.config.js
│ ├── rollup.plugin.minify.js
│ ├── template-o1.html
│ ├── template.html
│ └── utils.js
├── gulpfile.js
├── mocks
│ ├── AnotherComponent.js
│ ├── defaultMocks.js
│ └── mocker.js
├── package.json
├── postcss.config.js
├── src
│ ├── components
│ │ ├── AnotherComponent.vue
│ │ ├── MyComponent.vue
│ │ ├── header.vue
│ │ └── linkGround.vue
│ ├── location
│ │ ├── AnotherComponent
│ │ │ └── en.js
│ │ ├── globals
│ │ │ ├── global.cn.js
│ │ │ ├── global.en.js
│ │ │ └── globals.js
│ │ └── location.js
│ └── style
│ └── main.css
├── tailwind.config.js
├── yarn-error.log
└── yarn.lock
```
tailwind.config.js文件路径: tailwind.config.js
tailwind.config.js文件内容:
```
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/components/**/*.vue', // 扫描所有组件
],
theme: {
extend: {},
},
plugins: [],
};```
postcss.config.js文件路径: postcss.config.js
postcss.config.js文件内容:
```
module.exports = {
plugins: [
require('@tailwindcss/postcss'),
require('autoprefixer'),
],
};```
.dev.config.js文件路径: .dev.config.js
.dev.config.js文件内容:
```
const fs = require('fs');
const path = require('path')
const comPathDir = path.resolve(__dirname, 'src', 'components')
const cpublishPathDir = path.resolve(__dirname, '..', 'components')
const components = []
fs.readdirSync(comPathDir).filter(file => {
const fullPath = path.join(comPathDir, file);
const extname = path.extname(fullPath);
// console.log(fullPath, extname)
if(fs.statSync(fullPath).isFile() && extname=='.vue'){
components.push(path.basename(fullPath).replace('.vue', ''))
};
});
module.exports = {
debugComponents: ['AnotherComponent'],
allComponents: components,
defaultLocation: 'en',
publishPath: cpublishPathDir
}```
gulpfile.js文件路径: gulpfile.js
gulpfile.js文件内容:
```
const gulp = require('gulp');
const connect = require('gulp-connect');
const devConfig = require('./.dev.config.js')
const browserSync = require('browser-sync').create();
const {moveTo} = require('./build/utils.js')
const { exec } = require('child_process');
const fs = require('fs');
const path = require('path');
const PUBLISH_PATH = devConfig.publishPath;
const devComponents = devConfig.debugComponents ? devConfig.debugComponents : []
// 组件列表(假设我们要渲染这些组件)
var components = devConfig.allComponents;
if(devComponents.length>0){
components = components.filter((d) => {
return devComponents.includes(d)
})
}
const server = {
proxy: 'localhost:8080', // 代理到 gulp-connect 的服务器
port: 3000 // BrowserSync 的访问端口
}
// 启动本地服务器并启用热加载
gulp.task('serve', function(done) {
connect.server({
root: './dist', // 指定服务器根目录
livereload: true, // 启用 livereload
mime: {
'.js': 'application/javascript'
}
});
// 启动 BrowserSync
browserSync.init(server);
// 监听文件变化
gulp.watch(
['src/components/**/*.vue', 'src/location/**/*.js', 'mocks/**/*.js'],
gulp.series('build', 'render', 'generate-html', "location",'publish', 'reload'));
// 监听 Ctrl+C 终止信号
process.on('SIGINT', function() {
connect.serverClose(); // 关闭 connect 服务器
browserSync.exit(); // 关闭 BrowserSync
done(); // 通知 Gulp 任务完成
});
});
// 构建任务
gulp.task('build', async function(done) {
return new Promise((res,rej)=>{
exec('yarn build', function(err, stdout, stderr) {
if (err) {
console.error(`构建失败: ${stderr}`);
rej(err);
} else {
console.log(`构建成功: ${stdout}`);
res();
}
});
})
});
// 构建任务
gulp.task('render', async function(done) {
return new Promise((res, rej)=>{
exec('yarn render', function(err, stdout, stderr) {
if (err) {
console.error(`预渲染失败: ${stderr}`);
rej(err);
} else {
console.log(`预渲染成功: ${stdout}`);
res();
}
});
})
});
gulp.task('location', async function(){
console.log('location start...')
return new Promise((res, rej)=>{
exec('yarn location', function(err, stdout, stderr) {
if (err) {
console.error(`国际化处理失败: ${stderr}`);
rej(err);
} else {
console.log(`国际化处理成功: ${stdout}`);
res();
}
});
})
})
gulp.task('publish', async function(){
return new Promise((res, rej)=>{
const distPath = path.join(__dirname, "dist");
components.forEach((moudleName) => {
const opath = path.join(distPath, moudleName, `${moudleName}.bundle*.js`)
const ocsspath = path.join(distPath, moudleName, `${moudleName}.css`)
const ohtmlpath = path.join(distPath, moudleName, `${moudleName}*.html`)
moveTo(opath, path.join(PUBLISH_PATH, moudleName), {overwrite: true})
moveTo(ocsspath, path.join(PUBLISH_PATH, moudleName), {overwrite: true})
moveTo(ohtmlpath, path.join(PUBLISH_PATH, moudleName), {overwrite: true})
})
res()
})
})
// 生成预览页
// 定义生成 HTML 的任务
gulp.task('generate-html', (done) => {
return new Promise((res,rej)=>{
// 指定 dist 目录路径
const distDir = path.resolve(__dirname, './dist');
const htmltemplate = path.resolve(__dirname, './build/template.html');
// 遍历 dist 目录,筛选出所有子文件夹(组件目录)
const components = fs.readdirSync(distDir).filter(file => {
const fullPath = path.join(distDir, file);
return fs.statSync(fullPath).isDirectory();
});
var scripts = "";
var divs = "";
var styles = "";
components.forEach((component,i)=>{
if( devComponents.length>0 && !devComponents.includes(component)){
return;
}
const comId = 'wp-cc-' + String(Math.round(Math.random()*1000000000));
const scriptSrc = `http://localhost:${server.port}/${component}/${component}.bundle.js?${comId}`;//path.join(component, `${component}.bundle.js`);
const scriptTag = `<script type="text/javascript" src="${scriptSrc}"></script>`;
scripts += scriptTag + '\n';
// $('body').append(scriptTag); // 添加到 <body> 末尾
styles += `<link rel="stylesheet" id="dashicons-css" href="http://localhost:${server.port}/${component}/${component}.css?${comId}" media="all">`;
const htmlpath = path.join(distDir, component, `${component}.ori.html`);
const html = fs.readFileSync(htmlpath, 'utf8');
divs += html.replace('<wpcc-component>', `<wpcc-component ${comId}>`);
divs += `<script>(function(Vue){document.addEventListener('DOMContentLoaded', function(){const mountPoint=document.querySelector('[${comId}]');if(!mountPoint) return;const app = Vue.createApp(window.wpcc_components['${component}']);const componentInstance = app.mount(mountPoint);const componentRoot = componentInstance.\$el;mountPoint.parentNode.replaceChild(componentRoot, mountPoint);})})($wpcc_vue);</script>`;
})
const htmlContent = fs.readFileSync(htmltemplate, 'utf8');
var htmlstr = htmlContent.replace('{{SCRIPTS_TAGS}}', scripts);
htmlstr = htmlstr.replace('{{COMPONENTS_DIVS}}', divs);
htmlstr = htmlstr.replace('{{STYLES}}', styles);
const filepath = path.join(distDir, 'index.html');
fs.writeFile(filepath, htmlstr, (err) => {
if (err) {
console.error('Error writing file:', err);
rej()
} else {
console.log('index.html has been created successfully.');
res()
}
});
})
});
// 刷新浏览器
gulp.task('reload', function(done) {
browserSync.reload();
done();
});
// 默认任务
gulp.task('default', gulp.series('build', 'render', 'generate-html', "location",'publish', 'serve'));```
location.js文件路径: src/location/location.js
location.js文件内容:
```
/**
* 预配置的国际化语言 JSON
*/
const languageData = {
"home": "主页",
"about": "关于我们",
"contact": "联系我们",
"welcome_message": "欢迎使用我们的服务!"
};
/**
* 替换字符串中的 <%location:xxx%> 为对应的国际化语言值
* @param {string} input - 输入字符串
* @param {Object} langData - 语言数据 JSON
* @returns {string} - 替换后的字符串
*/
function replaceLanguageTags(input, langData = languageData) {
// 正则表达式:匹配 <%location:xxx%> 形式
// 捕获 xxx 部分
const regex = /<%location:([^%]+)%>/g;
return input.replace(regex, (match, key) => {
// 去掉 key 前后空格
const trimmedKey = key.trim();
// 在语言数据中查找对应的值
const replacement = langData[trimmedKey];
// 如果找到对应的值,则替换;否则保留原始占位符
return replacement !== undefined ? replacement : match;
});
}
// // 测试代码
// const testString = `
// Welcome to <%location:home%> and <%location:about%>!
// Here is a <%location:welcome_message%>
// Unknown tag: <%location:unknown%>
// `;
// const result = replaceLanguageTags(testString);
// console.log(result);```
globals.js文件路径: src/location/globals/globals.js
globals.js文件内容:
```
module.exports = {
'product.name': 'glb.productname',
'welcome.hi': 'glb.welcome',
'product.title': 'Product Name'
}```
global.cn.js文件路径: src/location/globals/global.cn.js
global.cn.js文件内容:
```
module.exports = {
'product.name': '商品名称',
'welcome.hi': '欢迎!'
}```
global.en.js文件路径: src/location/globals/global.en.js
global.en.js文件内容:
```
module.exports = {
'product.name': 'product nameqqq',
'welcome.hi': 'welcome hi'
}```
en.js文件路径: src/location/AnotherComponent/en.js
en.js文件内容:
```
module.exports = {
'product.name': 'product Name'
}```
header.vue文件路径: src/components/header.vue
header.vue文件内容:
```
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
// 移动端菜单状态
const isDrawerOpen = ref(false);
// 当前选中的子菜单和对应图片
const activeSubmenu = ref(null);
const activeImage = ref(null);
// 导航数据
const children = [
{
name: "Tungsten Rings",
image: "https://d24r3siuq13q9r.cloudfront.net/202210/48cc8710-9893-4bc6-bbd2-2021ff1a3dda.png",
},
{
name: "Glowstone",
image: "https://gentlebands.com/wp-content/uploads/2022/07/Glowstone-Collection-36x36.png",
},
{
name: "Dinosaur Fossil",
image: "https://d24r3siuq13q9r.cloudfront.net/202210/8aeb9f35-f9ed-47de-9100-ec82ed003bff.png",
},
{
name: "Meteorite Rings",
image: "https://gentlebands.com/wp-content/uploads/2022/07/METEORITE-36x36.png",
},
{
name: "Wood",
image: "https://d24r3siuq13q9r.cloudfront.net/202405/6a842a4c-a7f9-4008-be2f-6fb908bc9604.png",
},
{
name: "Titanium Wedding Bands",
image: "https://gentlebands.com/wp-content/uploads/2022/07/TITANIUM-36x36.png",
},
{
name: "Whiskey Barrel",
image: "https://gentlebands.com/wp-content/uploads/2022/07/ANWhiskey-BarrelTLERS-36x36.png",
},
{
name: "Damascus Steel",
image: "https://gentlebands.com/wp-content/uploads/2022/07/DAMASCUS-STEEL-36x36.png",
},
{
name: "Antler Wedding Rings",
image: "https://gentlebands.com/wp-content/uploads/2022/07/ANTLERS-36x36.png",
},
];
const navItems = [
{
title: "Shop by Material",
children,
},
{ title: "Shop by Collection" },
{ title: "Shop by Color" },
{ title: "Best Sellers" },
{ title: "Gifts" },
{ title: "Sale" },
{ title: "Our Story" },
];
// 处理子菜单hover
const handleSubmenuHover = (item) => {
if (item && item.image) {
activeImage.value = item.image;
}
};
// 处理移动端抽屉开关
const toggleDrawer = () => {
isDrawerOpen.value = !isDrawerOpen.value;
if (isDrawerOpen.value) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "";
}
};
// 监听窗口大小变化,自动关闭抽屉
const handleResize = () => {
if (window.innerWidth >= 1024 && isDrawerOpen.value) {
isDrawerOpen.value = false;
document.body.style.overflow = "";
}
};
onMounted(() => {
window.addEventListener("resize", handleResize);
});
onUnmounted(() => {
window.removeEventListener("resize", handleResize);
});
</script>
<template>
<header class="fixed top-0 left-0 right-0 z-50 bg-white shadow-md">
<div class="container mx-auto px-4">
<div class="flex items-center justify-between h-20">
<!-- Logo -->
<div class="flex-shrink-0">
<a href="/" class="text-xl font-bold">
<img src="https://d24r3siuq13q9r.cloudfront.net/202209/e4fe151a-52db-44d9-8322-65403d4b8eee.png" class="h-12" />
</a>
</div>
<!-- 桌面端导航 -->
<nav class="hidden lg:flex gap-x-8">
<div v-for="item in navItems" :key="item.title" class="relative group">
<button class="px-3 py-2 text-gray-700 hover:text-gray-900 group-hover:text-gray-900 focus:outline-none" @mouseenter="item.children && handleSubmenuHover(item.children[0])">
{{ item.title }}
</button>
<!-- 下拉菜单 -->
<div
v-if="item.children"
class="absolute left-0 w-[800px] p-4 mt-2 bg-white shadow-lg rounded-md opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-300 flex gap-8"
>
<!-- 左侧子菜单 -->
<ul class="w-1/3">
<li v-for="child in item.children" :key="child.name" @mouseenter="handleSubmenuHover(child)" class="px-4 py-2 hover:bg-gray-100 rounded-md cursor-pointer">
{{ child.name }}
</li>
</ul>
<!-- 右侧图片预览 -->
<div class="w-2/3">
<img :src="activeImage" :alt="activeSubmenu?.name" class="w-full h-48 object-contain rounded-md bg-black" />
</div>
</div>
</div>
</nav>
<div>
<button class="group relative p-2 rounded-md hover:bg-gray-100 focus:outline-none align-middle">
<span>🔍</span>
<input class="absolute p-2 border-slate-500 border-2 rounded-lg invisible group-focus-within:visible focus:outline-none top-full left-0 border-lightgray" />
</button>
<button class="p-2 rounded-md hover:bg-gray-100 focus:outline-none align-middle">🛒</button>
<!-- 移动端菜单按钮 -->
<button class="lg:hidden p-2 rounded-md text-gray-600 hover:text-gray-900 hover:bg-gray-100 focus:outline-none align-middle" @click="toggleDrawer">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
</div>
</div>
<div
class="lg:hidden fixed top-0 right-0 w-full h-full bg-white transform transition-transform duration-300 ease-in-out overflow-y-auto"
:class="{ 'translate-x-full': !isDrawerOpen, 'translate-x-0': isDrawerOpen }"
>
<div class="px-4">
<div class="flex justify-between items-center mb-4 sticky top-0 bg-white h-20">
<h2 class="mx-auto">
<img src="https://d24r3siuq13q9r.cloudfront.net/202209/e4fe151a-52db-44d9-8322-65403d4b8eee.png" class="h-12" />
</h2>
<button class="p-2 rounded-md text-gray-600 hover:text-gray-900 hover:bg-gray-100 focus:outline-none" @click="toggleDrawer">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<!-- 移动端菜单内容 -->
<nav class="space-y-1">
<details v-for="(item, index) in navItems" :key="item.title" class="sweep max-h-16 overflow-hidden" open>
<summary class="text-gray-700 text-2xl font-medium list-none font-[math]">
<input class="hidden" type="checkbox" :name="'sm-screen-details-' + index" :id="'sm-screen-details-' + index" />
<label class="px-4 leading-[4rem] w-full block" :for="'sm-screen-details-' + index">{{ item.title }}</label>
</summary>
<ul class="ml-4 space-y-1 duration-500">
<li v-for="child in item.children" :key="child.name" class="leading-9 px-3 py-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-md cursor-pointer">
<span class="font-[math]">{{ child.name }}</span>
<img class="float-right" height="36" :src="child.image" />
</li>
</ul>
</details>
</nav>
</div>
</div>
</header>
</template>
<style scoped>
@tailwind base;
@tailwind components;
@tailwind utilities;
details.sweep:has(input:checked) {
max-height: 2000px;
}
details.sweep:has(input:checked) > ul {
animation: sweep 0.5s ease-in-out;
}
@keyframes sweep {
0% {
opacity: 0;
transform: translateX(-1rem);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
</style>
```
AnotherComponent.vue文件路径: src/components/AnotherComponent.vue
AnotherComponent.vue文件内容:
```
<template>
<div class="bg-green-200 p-4">
<Header />
<LinkPanel></LinkPanel>
<div><%location:product.title%></div>
<div class="mydivs" style="background-color: red;">
<ul>
<li v-for="(item, i) in items" :key="'item_'+i">
<span>{{ item.productName }}</span>
<span>{{ item.productSku }}</span>
<span>{{ item.productPrice }}</span>
</li>
</ul>
</div>
</div>
</template>
<script>
import LinkPanel from './linkGround.vue'
import Header from "./header.vue"
export default {
name: 'AnotherComponent',
components:{
LinkPanel,Header
},
data(){
return {
title: {
productListTitle: '<%location:product.name%>'
},
items: _WPCC_GET_DATAS('array:product.all.list()')
}
},
props: {
message: {
type: String,
default: 'Hello!, -- Worldddddfdsadfd!'
}
}
}
</script>
<style scoped>
@tailwind base;
@tailwind components;
@tailwind utilities;
.another-component {
color: white;
background:red;
}
</style>```
MyComponent.vue文件路径: src/components/MyComponent.vue
MyComponent.vue文件内容:
```
<template>
<div class="w-full max-w-sm px-4 py-3 bg-white rounded-md shadow-md dark:bg-gray-800">
<div class="flex items-center justify-between">
<span class="text-sm font-light text-gray-800 dark:text-gray-400">Courses and MOOCs</span>
<span class="px-3 py-1 text-xs text-blue-800 uppercase bg-blue-200 rounded-full dark:bg-blue-300 dark:text-blue-900">psychology</span>
</div>
<div>
<h1 class="mt-2 text-lg font-semibold text-gray-800 dark:text-white">AP® Psychology - Course 5: Health and Behavior</h1>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-300">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Odio eligendi similique exercitationem optio libero vitae accusamus cupiditate laborum eos.</p>
</div>
<div>
<div class="flex items-center mt-2 text-gray-700 dark:text-gray-200">
<span>Visit on:</span>
<a class="mx-2 text-blue-600 cursor-pointer dark:text-blue-400 hover:underline">edx.org</a>
<span>or</span>
<a class="mx-2 text-blue-600 cursor-pointer dark:text-blue-400 hover:underline">classcentral.com</a>
</div>
<div class="flex items-center justify-center mt-4">
<a class="mr-2 text-gray-800 cursor-pointer dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300">
<svg class="w-5 h-5 fill-current" xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'>
<path d='M496,109.5a201.8,201.8,0,0,1-56.55,15.3,97.51,97.51,0,0,0,43.33-53.6,197.74,197.74,0,0,1-62.56,23.5A99.14,99.14,0,0,0,348.31,64c-54.42,0-98.46,43.4-98.46,96.9a93.21,93.21,0,0,0,2.54,22.1,280.7,280.7,0,0,1-203-101.3A95.69,95.69,0,0,0,36,130.4C36,164,53.53,193.7,80,211.1A97.5,97.5,0,0,1,35.22,199v1.2c0,47,34,86.1,79,95a100.76,100.76,0,0,1-25.94,3.4,94.38,94.38,0,0,1-18.51-1.8c12.51,38.5,48.92,66.5,92.05,67.3A199.59,199.59,0,0,1,39.5,405.6,203,203,0,0,1,16,404.2,278.68,278.68,0,0,0,166.74,448c181.36,0,280.44-147.7,280.44-275.8,0-4.2-.11-8.4-.31-12.5A198.48,198.48,0,0,0,496,109.5Z' />
</svg>
</a>
<a class="mr-2 text-gray-800 cursor-pointer dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300">
<svg class="w-5 h-5 fill-current" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.8283 12L16.2426 13.4142L19.071 10.5858C20.6331 9.02365 20.6331 6.49099 19.071 4.9289C17.5089 3.3668 14.9762 3.3668 13.4141 4.9289L10.5857 7.75732L11.9999 9.17154L14.8283 6.34311C15.6094 5.56206 16.8757 5.56206 17.6568 6.34311C18.4378 7.12416 18.4378 8.39049 17.6568 9.17154L14.8283 12Z" />
<path d="M12 14.8285L13.4142 16.2427L10.5858 19.0711C9.02365 20.6332 6.49099 20.6332 4.9289 19.0711C3.3668 17.509 3.3668 14.9764 4.9289 13.4143L7.75732 10.5858L9.17154 12L6.34311 14.8285C5.56206 15.6095 5.56206 16.8758 6.34311 17.6569C7.12416 18.4379 8.39049 18.4379 9.17154 17.6569L12 14.8285Z" />
<path d="M14.8284 10.5857C15.2189 10.1952 15.2189 9.56199 14.8284 9.17147C14.4379 8.78094 13.8047 8.78094 13.4142 9.17147L9.17154 13.4141C8.78101 13.8046 8.78101 14.4378 9.17154 14.8283C9.56206 15.2188 10.1952 15.2188 10.5857 14.8283L14.8284 10.5857Z" />
</svg>
</a>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Card',
props: {
title: {
type: String,
required: true,
},
initcount: {
type: Number,
default: 0
},
description: {
type: String,
required: true,
default: "description"
}
},
data() {
return {
count: this.initcount
}
},
methods: {
onButtonClick() {
this.count++;
}
}
}
</script>
<style scoped>
.card {
border: 1px solid #ccc;
border-radius: 5px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.card-header {
background-color: #f8f9fa;
padding: 10px;
}
.card-body {
padding: 15px;
}
.card-footer {
background-color: #f8f9fa;
padding: 10px;
text-align: right;
}
</style>```
linkGround.vue文件路径: src/components/linkGround.vue
linkGround.vue文件内容:
```
<template>
<div class="flex flex-col items-center py-16">
<div class="h-32 w-32">
<TransitionRoot
appear
:show="isShowing"
as="template"
enter="transform transition duration-[400ms]"
enter-from="opacity-0 rotate-[-120deg] scale-50"
enter-to="opacity-100 rotate-0 scale-100"
leave="transform duration-200 transition ease-in-out"
leave-from="opacity-100 rotate-0 scale-100 "
leave-to="opacity-0 scale-95 "
>
<div class="h-full w-full rounded-md bg-white shadow-lg" />
</TransitionRoot>
</div>
<button
@click="resetIsShowing"
class="mt-8 flex transform items-center rounded-full bg-black/20 px-3 py-2 text-sm font-medium text-white transition hover:scale-105 hover:bg-black/30 focus:outline-none active:bg-black/40"
>
<svg viewBox="0 0 20 20" fill="none" class="h-5 w-5 opacity-70">
<path
d="M14.9497 14.9498C12.2161 17.6835 7.78392 17.6835 5.05025 14.9498C2.31658 12.2162 2.31658 7.784 5.05025 5.05033C7.78392 2.31666 12.2161 2.31666 14.9497 5.05033C15.5333 5.63385 15.9922 6.29475 16.3266 7M16.9497 2L17 7H16.3266M12 7L16.3266 7"
stroke="currentColor"
stroke-width="1.5"
/>
</svg>
<span class="ml-3">Click to transition</span>
</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { TransitionRoot } from '@headlessui/vue'
const isShowing = ref(true)
function resetIsShowing() {
isShowing.value = false
setTimeout(() => {
isShowing.value = true
}, 500)
}
</script>```
main.css文件路径: src/style/main.css
main.css文件内容:
```
@tailwind base;
@tailwind components;
@tailwind utilities;```
mocker.js文件路径: mocks/mocker.js
mocker.js文件内容:
```
const path = require('path')
const fs = require('fs')
/**
* 模拟 mock 数据
*/
const mockData = require('./defaultMocks');
/**
* 根据默认值类型返回默认值
* @param {string} type - 默认值类型
* @returns {*} - 默认值
*/
function getDefaultValue(type) {
switch (type.toLowerCase()) {
case 'array':
return [];
case 'object':
return {};
case 'string':
return '';
case 'number':
return 0;
default:
return null;
}
}
/**
* 处理 sub 操作:取子集
* @param {Array} data - 数据数组
* @param {string} start - 开始下标
* @param {string} end - 结束下标
* @returns {Array} - 子集
*/
function handleSubOperation(data, start, end) {
const startIndex = parseInt(start, 10);
const endIndex = parseInt(end, 10);
if (!Array.isArray(data) || isNaN(startIndex) || isNaN(endIndex)) {
return data;
}
return data.slice(startIndex, endIndex + 1);
}
/**
* 处理 random 操作:随机取指定数量的元素
* @param {Array} data - 数据数组
* @param {string} count - 随机取的数量
* @returns {Array} - 随机子集
*/
function handleRandomOperation(data, count) {
const num = parseInt(count, 10);
if (!Array.isArray(data) || isNaN(num) || num <= 0) {
return data;
}
const shuffled = [...data].sort(() => 0.5 - Math.random());
return shuffled.slice(0, Math.min(num, data.length));
}
/**
* 处理 mock 数据
* @param {string} type - 默认值类型
* @param {string} key - mock 数据中的 key
* @param {string[]} params - 操作参数
* @returns {*} - 处理后的结果
*/
function processMockData(type, key, params, moudleName) {
const mockpath = path.join(__dirname, `${moudleName}.js`);
const exift = fs.existsSync(mockpath);
const mockDatas = exift ? require(`./${moudleName}.js`) : mockData;
// 从 mock 数据中获取值,如果不存在则根据类型返回默认值
const data = mockDatas[key] !== undefined ? mockDatas[key] : getDefaultValue(type);
// 如果没有参数,直接返回数据
if (params.length === 0) {
return JSON.stringify(data);
}
// 根据第一个参数(操作类型)处理数据
const operation = params[0].toLowerCase();
if (operation === 'sub' && params.length >= 3) {
return JSON.stringify(handleSubOperation(data, params[1], params[2]));
} else if (operation === 'random' && params.length >= 2) {
return JSON.stringify(handleRandomOperation(data, params[1]));
}
// 如果操作不匹配,返回原始数据
return JSON.stringify(data);
}
/**
* 替换字符串中的 _WPCC_xxx("xxx:xxx(xxx, xxx, xxx...)") 为 mock 数据
* @param {string} input - 输入字符串
* @returns {string} - 替换后的字符串
*/
function replaceWpccWithMockData(input, moudleName) {
// 正则表达式:
// 捕获处理方法、默认值类型、key 和括号内的参数
const regex = /_WPCC_[a-zA-Z0-9_]+?\("([a-z]+):([a-zA-Z0-9._]+)(?:\(([^()]*)\))?"\)/g;
return input.replace(regex, (match, type, key, params) => {
// params 是括号内的参数字符串,例如 "sub,1,3" 或 "random,2"
// 如果没有参数,则 params 为 undefined
const paramList = params ? params.split(',').map(p => p.trim()) : [];
// console.log(moudleName)
// 处理 mock 数据
const result = processMockData(type, key, paramList, moudleName);
return result;
});
}
module.exports = replaceWpccWithMockData
// // 测试代码
// const testString = `
// Some text before _WPCC_GET_DATAS("array:products(sub,1,3)") some text in between
// Another call _WPCC_FETCH_DATA("array:products(random,2)") end text
// Direct output _WPCC_TEST("string:greeting()")
// Number output _WPCC_NUM("number:count()")
// Unknown key _WPCC_UNKNOWN("array:unknown()")`;
// const result = replaceWpccWithMockData(testString);
// console.log(result);```
defaultMocks.js文件路径: mocks/defaultMocks.js
defaultMocks.js文件内容:
```
module.exports = {
"product.all.list": [
{
'productName': 'productName-indefault',
'productSku': 'productSku2-indefault',
'productPrice': 'productPriced3-indefault'
}
]
}```
AnotherComponent.js文件路径: mocks/AnotherComponent.js
AnotherComponent.js文件内容:
```
module.exports = {
"product.all.list": [
{
'productName': 'productName1',
'productSku': 'productSku2',
'productPrice': 'productPriced3'
}
]
}```
locationer.js文件路径: build/locationer.js
locationer.js文件内容:
```
const fs = require('fs');
const path = require('path');
const { moveTo, copyFiles } = require('./utils.js');
const devConfig = require('../.dev.config.js')
const devComponents = devConfig.debugComponents ? devConfig.debugComponents : []
const DEFAULT_LOCATION = devConfig.defaultLocation||"en";
const PUBLISH_PATH = devConfig.publishPath;
// 组件列表(假设我们要渲染这些组件)
var components = devConfig.allComponents;
if(devComponents.length>0){
components = components.filter((d) => {
return devComponents.includes(d)
})
}
/**
* 处理国际化标签占位符,生成对应语言的 JS 文件
* @param {string} inputString - 原始 JS 文件字符串
* @param {string} moduleName - 模块名
* @param {string} globalsDir - 全局语言定义目录,默认为 src/location/globals/
* @param {string} modulesDir - 模块语言定义目录,默认为 src/location/
* @param {string} outputDir - 输出目录,默认为 dist/
*/
function processI18nTags( moduleName, globalsDir = 'src/location/globals/', modulesDir = 'src/location/', outputDir = 'dist/') {
const parentpath = path.join(__dirname, "..");
globalsDir = path.join(parentpath, globalsDir)
modulesDir = path.join(parentpath, modulesDir)
outputDir = path.join(parentpath, outputDir)
const componentCJSPath = path.join(outputDir, moduleName, `${moduleName}.cjs.ori.js`);
const componentBUNDLEPath = path.join(outputDir, moduleName, `${moduleName}.bundle.ori.js`);
const componentHTMLPath = path.join(outputDir, moduleName, `${moduleName}.ori.html`);
const files = [
componentCJSPath, componentBUNDLEPath,componentHTMLPath
]
files.forEach((filepath, i)=>{
let {name:oriFileName, ext:oriExtName }= path.parse(filepath);
//console.log(oriFileName);
oriFileName = oriFileName.replace('.ori', "");
if(!fs.existsSync(filepath)){
throw new Error(`模块原始js文件不存在:${filepath}`);
}
const inputString = fs.readFileSync(filepath, 'utf8');
// 1. 确定所有语言
const languages = getLanguages(globalsDir);
// 2. 读取默认全局标签定义 (globals.js)
let defaultGlobalLangData;
try {
//console.log(path.join(globalsDir, 'globals.js'));
defaultGlobalLangData = readLanguageFile(path.join(globalsDir, 'globals.js'));
} catch (error) {
//src/location/globals/globals.js
throw new Error(`无法读取默认全局语言文件 ${path.join(globalsDir, 'globals.js')}: ${error.message}`);
}
// 3. 为每种语言生成对应的文件
languages.forEach(lang => {
try {
// 读取特定语言的全局标签定义 (global.xxx.js)
const specificGlobalLangData = readLanguageFile(path.join(globalsDir, `global.${lang}.js`), true);
// 读取模块语言定义 (模块名.xxx.js),如果不存在则返回空对象
const moduleLangData = readLanguageFile(path.join(modulesDir, `${moduleName}.${lang}.js`), true);
// 合并语言定义:默认全局 -> 特定语言全局 -> 模块定义(后覆盖前)
const mergedLangData = Object.assign({},{...defaultGlobalLangData}, {...specificGlobalLangData}, {...moduleLangData})
// {
// ...defaultGlobalLangData, // 默认全局标签
// ...specificGlobalLangData, // 特定语言全局标签
// ...moduleLangData // 模块标签(优先级最高)
// };
// 替换占位符
const replacedContent = replaceLanguageTags(inputString, mergedLangData);
// 确定输出目录和文件
const outputModuleDir = path.join(outputDir, moduleName);
const outputFilePath = path.join(outputModuleDir, `${oriFileName}.${lang}${oriExtName}`);
// 确保输出目录存在
if (!fs.existsSync(outputModuleDir)) {
fs.mkdirSync(outputModuleDir, { recursive: true });
}
// 写入文件
fs.writeFileSync(outputFilePath, replacedContent, 'utf8');
if( lang == DEFAULT_LOCATION){
// 写入文件如默认语言文件
fs.writeFileSync(path.join(outputModuleDir, `${oriFileName}${oriExtName}`), replacedContent, 'utf8');
PUBLISH_PATH && copyFiles(outputFilePath, path.join(PUBLISH_PATH, moduleName,`${oriFileName}${oriExtName}`));
}
PUBLISH_PATH && moveTo(outputFilePath, path.join(PUBLISH_PATH,moduleName), {overwrite: true});
console.log(`${outputFilePath} ok`);
} catch (error) {
console.error(`处理语言 ${lang} 失败: ${error.message}`);
}
});
})
}
/**
* 获取所有语言(从 globals 目录中提取语言缩写)
* @param {string} globalsDir - 全局语言定义目录
* @returns {string[]} - 语言缩写数组
*/
function getLanguages(globalsDir) {
if (!fs.existsSync(globalsDir)) {
throw new Error(`全局语言目录 ${globalsDir} 不存在`);
}
const files = fs.readdirSync(globalsDir);
//console.log(files)
const languages = files
.filter(file => file.startsWith('global.') && file.endsWith('.js') && file !== 'globals.js')
.map(file => file.replace('global.', '').replace('.js', ''));
if (languages.length === 0) {
throw new Error(`在 ${globalsDir} 中未找到任何特定语言定义文件 (global.xxx.js)`);
}
return languages;
}
/**
* 读取语言定义文件
* @param {string} filePath - 文件路径
* @param {boolean} optional - 是否可选(如果为 true,文件不存在返回空对象)
* @returns {Object} - 语言定义对象
*/
function readLanguageFile(filePath, optional = false) {
if (!fs.existsSync(filePath)) {
if (optional) {
return {};
}
throw new Error(`语言文件 ${filePath} 不存在`);
}
try {
const content = require(filePath);
if (typeof content !== 'object' || content === null) {
throw new Error(`语言文件 ${filePath} 必须导出一个对象`);
}
return content;
} catch (error) {
throw new Error(`读取语言文件 ${filePath} 失败: ${error.message}`);
}
}
// function replaceLanguageTags(input, langData) {
// const str = input.replace(/\&lt\;\%/g, '<%').replace(/\%\&gt\;/g, '%>')
// const regex = /<%location:([^%]+)%>/g;
// return str.replace(regex, (match, key) => {
// const trimmedKey = key.trim();
// const replacement = langData[trimmedKey];
// console.log('replacement:', replacement, langData)
// return replacement !== undefined ? replacement : match;
// });
// }
/**
* 替换字符串中的 <%location:xxx%> 或 &lt;%location:xxx%&gt; 为对应的语言值
* @param {string} input - 输入字符串
* @param {Object} langData - 语言数据对象
* @returns {string} - 替换后的字符串
*/
function replaceLanguageTags(input, langData) {
// 正则表达式:匹配 <%location:xxx%> 或 &lt;%location:xxx%&gt;
// 使用分组匹配两种边界形式
// console.log(input);
input = input.replace(/\&lt\;\%/g, '<%').replace(/\%\&gt\;/g, '%>')
const regex = /(?:<%|&lt;%)location:([^%>&]+)(?:%>|%&gt;)/g;
return input.replace(regex, (match, key) => {
// 去掉 key 前后空格
const trimmedKey = key.trim();
// 在语言数据中查找对应的值
const replacement = langData[trimmedKey];
// 如果找到替换值则返回替换值,否则保留原始占位符
return replacement !== undefined ? replacement : match;
});
}
// 渲染所有组件
async function locationAll() {
for (const name of components) {
await processI18nTags(name);
}
}
// 执行渲染
locationAll().catch(err => console.error('国际化处理失败:', err));
module.exports = processI18nTags;```
build.js文件路径: build/build.js
build.js文件内容:
```
const { execSync } = require('child_process');
const fs = require('fs');
const devConfig = require('../.dev.config.js');
const devComponents = devConfig.debugComponents || [];
let components = devConfig.allComponents;
if (devComponents.length > 0) {
components = components.filter(d => devComponents.includes(d));
}
components.forEach(name => {
// 为每个组件生成临时 Tailwind 配置文件
const tempConfig = {
content: [`src/components/${name}.vue`],
theme: { extend: {} },
plugins: [],
};
fs.writeFileSync('./tailwind.temp.config.js', `module.exports = ${JSON.stringify(tempConfig, null, 2)};`);
// 运行 Rollup 构建
execSync(`rollup -c build/rollup.config.js --environment COMPONENT:${name}`, { stdio: 'inherit' });
// 删除临时配置文件
fs.unlinkSync('./tailwind.temp.config.js');
});
console.log('Build completed for all components.');```
utils.js文件路径: build/utils.js
utils.js文件内容:
```
const fs = require('fs');
const path = require('path');
const glob = require('glob');
/**
* 复制文件或目录
* @param {string} sourcePath - 源路径(文件或目录)
* @param {string} destPath - 目标路径
* @param {Object} [options] - 可选配置
* @param {boolean} [options.overwrite=false] - 是否覆盖已存在的文件
* @param {boolean} [options.preserveStructure=true] - 是否保持目录结构
*/
function copyFiles(sourcePath, destPath, options = {}) {
const { overwrite = false, preserveStructure = true } = options;
try {
// 检查源路径是否存在
if (!fs.existsSync(sourcePath)) {
throw new Error(`源路径 ${sourcePath} 不存在`);
}
const stats = fs.statSync(sourcePath);
if (stats.isFile()) {
// 单个文件复制
copySingleFile(sourcePath, destPath, overwrite);
} else if (stats.isDirectory()) {
// 目录复制
copyDirectory(sourcePath, destPath, overwrite, preserveStructure);
} else {
throw new Error(`源路径 ${sourcePath} 不是文件或目录`);
}
console.log(`复制从 ${sourcePath} 到 ${destPath} 完成`);
} catch (error) {
console.error(`复制失败: ${error.message}`);
throw error;
}
}
/**
* 复制单个文件
* @param {string} sourceFile - 源文件路径
* @param {string} destFile - 目标文件路径
* @param {boolean} overwrite - 是否覆盖
*/
function copySingleFile(sourceFile, destFile, overwrite) {
// 如果目标路径是目录,提取文件名
const destStats = fs.existsSync(destFile) ? fs.statSync(destFile) : null;
if (destStats && destStats.isDirectory()) {
destFile = path.join(destFile, path.basename(sourceFile));
}
// 检查目标文件是否已存在
if (fs.existsSync(destFile) && !overwrite) {
throw new Error(`目标文件 ${destFile} 已存在,且未启用覆盖`);
}
// 确保目标目录存在
const destDir = path.dirname(destFile);
if (!fs.existsSync(destDir)) {
fs.mkdirSync(destDir, { recursive: true });
}
// 复制文件
fs.copyFileSync(sourceFile, destFile);
// console.log(`复制文件 ${sourceFile} 到 ${destFile} 成功`);
}
/**
* 复制目录
* @param {string} sourceDir - 源目录路径
* @param {string} destDir - 目标目录路径
* @param {boolean} overwrite - 是否覆盖
* @param {boolean} preserveStructure - 是否保持目录结构
*/
function copyDirectory(sourceDir, destDir, overwrite, preserveStructure) {
// 如果不保持目录结构,目标路径直接为 destDir
const effectiveDestDir = preserveStructure ? path.join(destDir, path.basename(sourceDir)) : destDir;
// 检查目标目录是否存在
if (fs.existsSync(effectiveDestDir) && !overwrite) {
throw new Error(`目标目录 ${effectiveDestDir} 已存在,且未启用覆盖`);
}
// 如果目标目录不存在,创建
if (!fs.existsSync(effectiveDestDir)) {
fs.mkdirSync(effectiveDestDir, { recursive: true });
}
// 读取源目录内容
const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
for (const entry of entries) {
const srcPath = path.join(sourceDir, entry.name);
const dstPath = path.join(effectiveDestDir, entry.name);
if (entry.isDirectory()) {
// 递归复制子目录
copyDirectory(srcPath, effectiveDestDir, overwrite, true);
} else {
// 复制文件
copySingleFile(srcPath, dstPath, overwrite);
}
}
}
/**
* 移动文件或目录到目标目录,保持文件名不变,支持通配符
* @param {string} sourcePath - 源路径(文件、目录或通配符,例如 /path/to/abc*.js)
* @param {string} destDir - 目标目录路径
* @param {Object} [options] - 可选配置
* @param {boolean} [options.overwrite=false] - 是否覆盖已存在的文件
* @param {boolean} [options.delSource=false] - 是否删除源文件
*/
function moveTo(sourcePath, destDir, options = {}) {
const { overwrite = false, delSource = false } = options;
try {
// 检查并创建目标目录
if (fs.existsSync(destDir)) {
const destStats = fs.statSync(destDir);
if (!destStats.isDirectory()) {
throw new Error(`目标路径 ${destDir} 不是一个目录`);
}
} else {
fs.mkdirSync(destDir, { recursive: true });
}
// 规范化源路径
sourcePath = path.normalize(sourcePath);
// 尝试使用 glob 匹配
const sourceFiles = glob.sync(sourcePath, { cwd: process.cwd(), absolute: true });
if (sourceFiles.length > 0) {
// 通配符匹配成功,处理每个文件
sourceFiles.forEach(file => {
if (!fs.existsSync(file)) return; // 跳过不存在的文件
const destPath = path.join(destDir, path.basename(file));
copySingleFile(file, destPath, overwrite);
if (delSource) {
fs.unlinkSync(file);
console.log(`删除源文件 ${file} 成功`);
}
//console.log(`移动从 ${file} 到 ${destPath} 完成`);
});
} else {
// 非通配符路径或匹配失败,按原有逻辑处理
if (!fs.existsSync(sourcePath)) {
throw new Error(`源路径 ${sourcePath} 不存在`);
}
const destPath = path.join(destDir, path.basename(sourcePath));
copyFiles(sourcePath, destPath, { overwrite, preserveStructure: false });
if (delSource) {
const stats = fs.statSync(sourcePath);
if (stats.isDirectory()) {
fs.rmSync(sourcePath, { recursive: true, force: true });
console.log(`删除源目录 ${sourcePath} 成功`);
} else {
fs.unlinkSync(sourcePath);
console.log(`删除源文件 ${sourcePath} 成功`);
}
}
//console.log(`移动从 ${sourcePath} 到 ${destPath} 完成`);
}
} catch (error) {
console.error(`移动失败: ${error.message}`);
throw error;
}
}
// // 测试代码
// if (require.main === module) {
// try {
// // 示例 1:移动单个文件
// moveTo('src/test.js', 'dist', { overwrite: true });
// // 示例 2:移动整个目录
// moveTo('src/location', 'dist', { overwrite: true });
// } catch (error) {
// console.error(error);
// }
// }
module.exports = { copyFiles, moveTo };```
rollup.config.js文件路径: build/rollup.config.js
rollup.config.js文件内容:
```
const vue = require('rollup-plugin-vue');
const resolve = require('@rollup/plugin-node-resolve').default;
const postcss = require('rollup-plugin-postcss');
const minifyPlugin = require('./rollup.plugin.minify');
const devConfig = require('../.dev.config.js');
const devComponents = devConfig.debugComponents || [];
let components = devConfig.allComponents;
if (devComponents.length > 0) {
components = components.filter(d => devComponents.includes(d));
}
module.exports = components.map(name => ({
input: `src/components/${name}.vue`,
output: [
{
format: 'cjs',
file: `dist/${name}/${name}.cjs.ori.js`,
exports: 'default',
},
{
format: 'iife',
name: name,
file: `dist/${name}/${name}.bundle.ori.js`,
globals: { vue: 'Vue' },
},
],
plugins: [
vue({
css: false, // 禁用 rollup-plugin-vue 的默认 CSS 处理,交给 postcss 处理
}),
resolve(),
postcss({
extract: `dist/${name}/${name}.css`,
minimize: true,
config: {
path: './tailwind.config.js', // 使用项目根目录的配置文件
},
plugins: [
require('@tailwindcss/postcss'),
require('autoprefixer'),
],
}),
minifyPlugin({
compress: {
drop_console: true,
},
mangle: {
keep_fnames: false,
reserved: [],
properties: {
regex: /^_WPCC_/,
},
},
}),
],
external: ['vue'],
}));```
render.js文件路径: build/render.js
render.js文件内容:
```
const fs = require('fs');
const path = require('path');
const { createSSRApp } = require('vue');
const { renderToString } = require('@vue/server-renderer');
const cheerio = require('cheerio');
const devConfig = require('../.dev.config.js')
const devComponents = devConfig.debugComponents ? devConfig.debugComponents : []
// 组件列表(假设我们要渲染这些组件)
var components = devConfig.allComponents;
if(devComponents.length>0){
components = components.filter((d) => {
return devComponents.includes(d)
})
}
// 输出目录
const outputDir = path.resolve(__dirname, '../../components');
const localOutputDir = path.resolve(__dirname, '../dist');
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir);
if (!fs.existsSync(localOutputDir)) fs.mkdirSync(localOutputDir);
// 处理所有路由
// 输出目录
// const outputDir = path.resolve(__dirname, '../dist');
// if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir);
// 渲染单个组件的函数
async function renderComponent(componentName) {
const componentJsPath = path.join(localOutputDir, componentName, `${componentName}.cjs.ori.js`);
const App = require(componentJsPath)
const app = createSSRApp(App); // 将 App.vue 的组件对象传递给 createSSRApp
const renderedHtml = await renderToString(app); // 渲染为 HTML 字符串
const comScriptId = app._component.__scopeId
const $ = cheerio.load(renderedHtml);
const $dom = $('body').children(`[${comScriptId}]`).first();
// 保存到文件
// const outputPath = path.join(outputDir, componentName, `${componentName}.ori.html`);
const localOutputPath = path.join(localOutputDir, componentName, `${componentName}.ori.html`);
// fs.writeFileSync(outputPath, `<wpcc-component>${$('body').html()}</wpcc-component>`, 'utf8');
fs.writeFileSync(localOutputPath, `<wpcc-component>${$('body').html()}</wpcc-component>`, 'utf8');
console.log(`成功渲染 ${componentName}结果已保存到 ${localOutputPath}`);
}
// 渲染所有组件
async function renderAll() {
for (const name of components) {
await renderComponent(name);
}
}
// 执行渲染
renderAll().catch(err => console.error('渲染出错:', err));
module.exports = renderComponent```
rollup.plugin.minify.js文件路径: build/rollup.plugin.minify.js
rollup.plugin.minify.js文件内容:
```
const terser = require('terser');
const mocker = require('../mocks/mocker')
const path = require('path')
module.exports = function minifyPlugin(options = {}) {
return {
name: 'minify', // 插件名称
async renderChunk(code, chunk, outputOptions) {
// 使用 Terser 压缩代码
const minified = await terser.minify(code, options);
const name = path.parse(outputOptions.file).name.split('.')[0];
if (minified.error) {
throw minified.error; // 如果压缩出错,抛出异常
}
// console.log(outputOptions);
var codestr = minified.code;
//console.log(name, 'sdd');
if(outputOptions.format == 'iife' || outputOptions.format == 'cjs'){
codestr = mocker(codestr, name);
}
if(outputOptions.format == 'iife'){
codestr = codestr.replace(/^var\s+[a-zA-Z_$][a-zA-Z0-9_$]*=\s*/, `window.wpcc_components=window.wpcc_components||{};window.wpcc_components['${name}']=`);
codestr = `(function(Vue){var component=${codestr}})($wpcc_vue)`;
}
return {
code: codestr, // 返回压缩后的代码
map: minified.map // 返回 source map(如果有)
};
}
};
}```
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/facemap/wp-components-center.git
git@gitee.com:facemap/wp-components-center.git
facemap
wp-components-center
wp-components-center
master

搜索帮助