更新模板

This commit is contained in:
2026-01-21 01:37:34 +08:00
parent b7be8c51bf
commit c5c73828bd
83 changed files with 8687 additions and 1235 deletions

310
static/html/planet_3d.html Normal file
View File

@@ -0,0 +1,310 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>3D Planet Interaction</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background-color: #f8f9fa;
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
touch-action: none;
}
canvas { width: 100%; height: 100%; display: block; }
#loader {
position: absolute; top: 50%; left: 50%;
transform: translate(-50%, -50%);
color: #52c41a; font-size: 14px;
font-weight: 500;
display: flex; flex-direction: column; align-items: center;
z-index: 100;
}
.spinner {
width: 36px; height: 36px; border: 3px solid #e8f5e9;
border-top: 3px solid #52c41a; border-radius: 50%;
animation: spin 0.8s linear infinite; margin-bottom: 12px;
}
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
#error {
position: absolute; top: 50%; left: 50%;
transform: translate(-50%, -50%);
color: #ff4d4f; font-size: 14px;
text-align: center; display: none;
padding: 20px;
}
.controls-hint {
position: absolute; bottom: 20px; left: 50%;
transform: translateX(-50%);
color: #999; font-size: 12px;
background: rgba(255,255,255,0.9);
padding: 6px 12px; border-radius: 16px;
opacity: 0; transition: opacity 0.3s;
}
.controls-hint.show { opacity: 1; }
</style>
</head>
<body>
<div id="loader">
<div class="spinner"></div>
<span>加载 3D 模型...</span>
</div>
<div id="error">
<div>模型加载失败</div>
<div style="font-size: 12px; margin-top: 8px; color: #999;">请检查网络连接</div>
</div>
<div class="controls-hint" id="hint">双指缩放 · 单指旋转</div>
<!-- Three.js 核心库 -->
<script src="https://cdn.jsdelivr.net/npm/three@0.146.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.146.0/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.146.0/examples/js/loaders/GLTFLoader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.146.0/examples/js/loaders/FBXLoader.js"></script>
<script>
let scene, camera, renderer, controls, model;
let modelUrl = null;
let modelFormat = 'glb';
// 从 URL 参数获取模型信息
const urlParams = new URLSearchParams(window.location.search);
modelUrl = urlParams.get('modelUrl');
modelFormat = urlParams.get('format') || 'glb';
init();
animate();
function init() {
// 场景
scene = new THREE.Scene();
scene.background = new THREE.Color(0xf8f9fa);
// 相机
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.set(0, 100, 300);
// 光照系统
const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.0);
directionalLight.position.set(200, 200, 100);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
scene.add(directionalLight);
const fillLight = new THREE.DirectionalLight(0xffffff, 0.3);
fillLight.position.set(-100, 50, -100);
scene.add(fillLight);
const pointLight = new THREE.PointLight(0x52c41a, 0.4);
pointLight.position.set(-100, 50, -50);
scene.add(pointLight);
// 渲染器
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
// 控制器
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.08;
controls.autoRotate = true;
controls.autoRotateSpeed = 0.8;
controls.enablePan = true;
controls.panSpeed = 0.5;
controls.minDistance = 100;
controls.maxDistance = 600;
controls.maxPolarAngle = Math.PI * 0.85;
controls.minPolarAngle = Math.PI * 0.15;
// 交互时停止自动旋转
controls.addEventListener('start', () => {
controls.autoRotate = false;
showHint();
});
// 加载模型
if (modelUrl) {
loadModel(modelUrl, modelFormat);
} else {
createDefaultPlanet();
}
window.addEventListener('resize', onWindowResize);
// 接收来自小程序的消息
window.addEventListener('message', (event) => {
const data = event.data;
if (data && data.type === 'loadModel') {
loadModel(data.url, data.format || 'glb');
}
});
}
function loadModel(url, format) {
showLoader();
let loader;
if (format === 'fbx') {
loader = new THREE.FBXLoader();
} else {
loader = new THREE.GLTFLoader();
}
loader.load(
url,
(result) => {
hideLoader();
// 移除旧模型
if (model) {
scene.remove(model);
}
// 获取模型
model = format === 'fbx' ? result : result.scene;
// 居中和缩放
const box = new THREE.Box3().setFromObject(model);
const center = box.getCenter(new THREE.Vector3());
const size = box.getSize(new THREE.Vector3());
const maxDim = Math.max(size.x, size.y, size.z);
const scale = 150 / maxDim;
model.position.sub(center);
model.scale.setScalar(scale);
// 启用阴影
model.traverse((child) => {
if (child.isMesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});
scene.add(model);
},
(progress) => {
if (progress.total > 0) {
const percent = Math.round((progress.loaded / progress.total) * 100);
updateLoaderText(`加载中 ${percent}%`);
}
},
(error) => {
console.error('模型加载失败:', error);
hideLoader();
showError();
// 加载失败时显示默认星球
createDefaultPlanet();
}
);
}
function createDefaultPlanet() {
hideLoader();
const group = new THREE.Group();
// 星球本体 - 更精细的几何体
const planetGeom = new THREE.SphereGeometry(80, 64, 64);
const planetMat = new THREE.MeshPhongMaterial({
color: 0x95de64,
shininess: 40,
flatShading: false
});
const planet = new THREE.Mesh(planetGeom, planetMat);
planet.receiveShadow = true;
planet.castShadow = true;
group.add(planet);
// 装饰层 - 网格效果
const decoGeom = new THREE.SphereGeometry(82, 32, 32);
const decoMat = new THREE.MeshPhongMaterial({
color: 0x52c41a,
wireframe: true,
transparent: true,
opacity: 0.08
});
const deco = new THREE.Mesh(decoGeom, decoMat);
group.add(deco);
// 添加一些装饰点
const dotGeom = new THREE.SphereGeometry(3, 16, 16);
const dotMat = new THREE.MeshPhongMaterial({ color: 0x73d13d });
for (let i = 0; i < 20; i++) {
const dot = new THREE.Mesh(dotGeom, dotMat);
const phi = Math.random() * Math.PI * 2;
const theta = Math.random() * Math.PI;
const r = 82;
dot.position.set(
r * Math.sin(theta) * Math.cos(phi),
r * Math.sin(theta) * Math.sin(phi),
r * Math.cos(theta)
);
group.add(dot);
}
model = group;
scene.add(model);
}
function showLoader() {
document.getElementById('loader').style.display = 'flex';
document.getElementById('error').style.display = 'none';
}
function hideLoader() {
document.getElementById('loader').style.display = 'none';
}
function updateLoaderText(text) {
const loader = document.getElementById('loader');
const span = loader.querySelector('span');
if (span) span.textContent = text;
}
function showError() {
document.getElementById('error').style.display = 'block';
setTimeout(() => {
document.getElementById('error').style.display = 'none';
}, 3000);
}
function showHint() {
const hint = document.getElementById('hint');
hint.classList.add('show');
setTimeout(() => {
hint.classList.remove('show');
}, 2000);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
// 暴露给小程序调用的方法
window.loadModel = loadModel;
window.resetView = function() {
camera.position.set(0, 100, 300);
controls.reset();
controls.autoRotate = true;
};
</script>
</body>
</html>

BIN
static/icon/tabbar/cart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
static/icon/tabbar/shop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
static/images/farm_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

BIN
static/images/planet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 619 KiB

BIN
static/images/shop_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 KiB

View File

@@ -1,3 +1,346 @@
/* @tailwind base; */
@tailwind components;
@tailwind utilities;
/* 全局自适应样式 */
/* 安全区域适配 */
.safe-area-inset-top {
padding-top: constant(safe-area-inset-top);
padding-top: env(safe-area-inset-top);
}
.safe-area-inset-bottom {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
/* 响应式容器 */
.container {
width: 100%;
max-width: 750rpx;
margin: 0 auto;
padding: 0 30rpx;
box-sizing: border-box;
}
/* 弹性布局工具类 */
.flex {
display: flex;
}
.flex-row {
display: flex;
flex-direction: row;
}
.flex-col {
display: flex;
flex-direction: column;
}
.flex-wrap {
flex-wrap: wrap;
}
.flex-1 {
flex: 1;
}
.items-center {
align-items: center;
}
.items-start {
align-items: flex-start;
}
.items-end {
align-items: flex-end;
}
.justify-center {
justify-content: center;
}
.justify-between {
justify-content: space-between;
}
.justify-around {
justify-content: space-around;
}
.justify-start {
justify-content: flex-start;
}
.justify-end {
justify-content: flex-end;
}
/* 文字大小自适应 */
.text-xs {
font-size: 20rpx;
}
.text-sm {
font-size: 24rpx;
}
.text-base {
font-size: 28rpx;
}
.text-lg {
font-size: 32rpx;
}
.text-xl {
font-size: 36rpx;
}
.text-2xl {
font-size: 40rpx;
}
.text-3xl {
font-size: 48rpx;
}
/* 文字颜色 */
.text-primary {
color: #52c41a;
}
.text-secondary {
color: #666666;
}
.text-muted {
color: #999999;
}
.text-white {
color: #ffffff;
}
.text-dark {
color: #333333;
}
/* 背景颜色 */
.bg-primary {
background-color: #52c41a;
}
.bg-white {
background-color: #ffffff;
}
.bg-gray {
background-color: #f8f9fa;
}
/* 间距工具类 */
.p-0 { padding: 0; }
.p-1 { padding: 8rpx; }
.p-2 { padding: 16rpx; }
.p-3 { padding: 24rpx; }
.p-4 { padding: 32rpx; }
.p-5 { padding: 40rpx; }
.px-1 { padding-left: 8rpx; padding-right: 8rpx; }
.px-2 { padding-left: 16rpx; padding-right: 16rpx; }
.px-3 { padding-left: 24rpx; padding-right: 24rpx; }
.px-4 { padding-left: 32rpx; padding-right: 32rpx; }
.py-1 { padding-top: 8rpx; padding-bottom: 8rpx; }
.py-2 { padding-top: 16rpx; padding-bottom: 16rpx; }
.py-3 { padding-top: 24rpx; padding-bottom: 24rpx; }
.py-4 { padding-top: 32rpx; padding-bottom: 32rpx; }
.m-0 { margin: 0; }
.m-1 { margin: 8rpx; }
.m-2 { margin: 16rpx; }
.m-3 { margin: 24rpx; }
.m-4 { margin: 32rpx; }
.mx-auto { margin-left: auto; margin-right: auto; }
.mt-1 { margin-top: 8rpx; }
.mt-2 { margin-top: 16rpx; }
.mt-3 { margin-top: 24rpx; }
.mt-4 { margin-top: 32rpx; }
.mb-1 { margin-bottom: 8rpx; }
.mb-2 { margin-bottom: 16rpx; }
.mb-3 { margin-bottom: 24rpx; }
.mb-4 { margin-bottom: 32rpx; }
/* 圆角 */
.rounded-none { border-radius: 0; }
.rounded-sm { border-radius: 8rpx; }
.rounded { border-radius: 12rpx; }
.rounded-lg { border-radius: 16rpx; }
.rounded-xl { border-radius: 24rpx; }
.rounded-full { border-radius: 9999rpx; }
/* 阴影 */
.shadow-sm {
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
.shadow {
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
}
.shadow-lg {
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.12);
}
/* 宽度 */
.w-full { width: 100%; }
.w-half { width: 50%; }
.w-auto { width: auto; }
/* 高度 */
.h-full { height: 100%; }
.h-auto { height: auto; }
/* 溢出处理 */
.overflow-hidden { overflow: hidden; }
.overflow-auto { overflow: auto; }
.overflow-scroll { overflow: scroll; }
/* 文字截断 */
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.line-clamp-3 {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* 卡片样式 */
.card {
background-color: #ffffff;
border-radius: 16rpx;
padding: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
}
/* 按钮基础样式 */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 16rpx 32rpx;
border-radius: 12rpx;
font-size: 28rpx;
font-weight: 500;
transition: all 0.2s;
}
.btn-primary {
background-color: #52c41a;
color: #ffffff;
}
.btn-primary:active {
background-color: #389e0d;
}
.btn-outline {
background-color: transparent;
border: 2rpx solid #52c41a;
color: #52c41a;
}
.btn-outline:active {
background-color: rgba(82, 196, 26, 0.1);
}
/* 输入框样式 */
.input {
width: 100%;
height: 88rpx;
padding: 0 24rpx;
background-color: #f5f5f5;
border-radius: 12rpx;
font-size: 28rpx;
box-sizing: border-box;
}
.input:focus {
background-color: #ffffff;
border: 2rpx solid #52c41a;
}
/* 分割线 */
.divider {
height: 1rpx;
background-color: #f0f0f0;
margin: 24rpx 0;
}
/* 加载状态 */
.loading {
display: flex;
align-items: center;
justify-content: center;
padding: 40rpx;
color: #999999;
font-size: 24rpx;
}
/* 空状态 */
.empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx 40rpx;
color: #999999;
}
.empty-icon {
width: 160rpx;
height: 160rpx;
margin-bottom: 24rpx;
opacity: 0.5;
}
.empty-text {
font-size: 28rpx;
color: #999999;
}
/* 主题色变量 */
:root {
--primary-color: #52c41a;
--primary-light: #95de64;
--primary-dark: #389e0d;
--success-color: #52c41a;
--warning-color: #faad14;
--error-color: #ff4d4f;
--text-color: #333333;
--text-secondary: #666666;
--text-muted: #999999;
--border-color: #f0f0f0;
--bg-color: #f8f9fa;
--card-bg: #ffffff;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 187 KiB