更新模板
310
static/html/planet_3d.html
Normal 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
|
After Width: | Height: | Size: 2.1 KiB |
BIN
static/icon/tabbar/cart2.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
static/icon/tabbar/device.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
static/icon/tabbar/device2.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
static/icon/tabbar/shop.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
static/icon/tabbar/shop2.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
static/images/farm_icon.png
Normal file
|
After Width: | Height: | Size: 654 KiB |
BIN
static/images/logo_center_btn.png
Normal file
|
After Width: | Height: | Size: 186 KiB |
BIN
static/images/planet.png
Normal file
|
After Width: | Height: | Size: 619 KiB |
BIN
static/images/shop_icon.png
Normal file
|
After Width: | Height: | Size: 506 KiB |
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
BIN
static/logo.png
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 187 KiB |