diff --git a/backend/config/__pycache__/settings.cpython-312.pyc b/backend/config/__pycache__/settings.cpython-312.pyc
index 0210904..86e1e32 100644
Binary files a/backend/config/__pycache__/settings.cpython-312.pyc and b/backend/config/__pycache__/settings.cpython-312.pyc differ
diff --git a/backend/config/__pycache__/settings.cpython-313.pyc b/backend/config/__pycache__/settings.cpython-313.pyc
index d702cc4..707d1a5 100644
Binary files a/backend/config/__pycache__/settings.cpython-313.pyc and b/backend/config/__pycache__/settings.cpython-313.pyc differ
diff --git a/backend/config/__pycache__/urls.cpython-312.pyc b/backend/config/__pycache__/urls.cpython-312.pyc
index 7669ebe..0a53fcf 100644
Binary files a/backend/config/__pycache__/urls.cpython-312.pyc and b/backend/config/__pycache__/urls.cpython-312.pyc differ
diff --git a/backend/config/__pycache__/urls.cpython-313.pyc b/backend/config/__pycache__/urls.cpython-313.pyc
index 0e17fa9..8d80a5b 100644
Binary files a/backend/config/__pycache__/urls.cpython-313.pyc and b/backend/config/__pycache__/urls.cpython-313.pyc differ
diff --git a/backend/config/settings.py b/backend/config/settings.py
index 35158e6..571e92e 100644
--- a/backend/config/settings.py
+++ b/backend/config/settings.py
@@ -32,6 +32,7 @@ ALLOWED_HOSTS = ["*"]
# Application definition
INSTALLED_APPS = [
+ 'unfold', # django-unfold必须在admin之前
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
@@ -40,6 +41,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
+ 'drf_spectacular', # Swagger文档生成
'shop',
]
@@ -126,3 +128,54 @@ USE_TZ = True
# https://docs.djangoproject.com/en/6.0/howto/static-files/
STATIC_URL = 'static/'
+STATIC_ROOT = BASE_DIR / 'staticfiles'
+
+# 静态文件配置
+STATICFILES_DIRS = [
+ BASE_DIR / 'static',
+]
+
+# Django REST Framework配置
+REST_FRAMEWORK = {
+ 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
+ 'DEFAULT_AUTHENTICATION_CLASSES': [],
+ 'DEFAULT_PERMISSION_CLASSES': [],
+}
+
+# drf-spectacular配置
+SPECTACULAR_SETTINGS = {
+ 'TITLE': '科技公司产品购买API',
+ 'DESCRIPTION': '科技公司产品购买官网的API文档',
+ 'VERSION': '1.0.0',
+ 'SERVE_INCLUDE_SCHEMA': True,
+ 'SERVE_PERMISSIONS': ['rest_framework.permissions.AllowAny'],
+ 'COMPONENT_SPLIT_REQUEST': True,
+ 'SCHEMA_PATH_PREFIX': r'/api/v[0-9]',
+ 'SWAGGER_UI_SETTINGS': {
+ 'deepLinking': True,
+ 'persistAuthorization': True,
+ 'displayOperationId': True,
+ },
+}
+
+# django-unfold配置
+UNFOLD = {
+ "SITE_TITLE": "科技公司产品管理",
+ "SITE_HEADER": "科技公司产品购买系统",
+ "SITE_URL": "/",
+ "COLORS": {
+ "primary": {
+ "50": "rgb(240 249 255)",
+ "100": "rgb(224 242 254)",
+ "200": "rgb(186 230 253)",
+ "300": "rgb(125 211 252)",
+ "400": "rgb(56 189 248)",
+ "500": "rgb(14 165 233)",
+ "600": "rgb(2 132 199)",
+ "700": "rgb(3 105 161)",
+ "800": "rgb(7 89 133)",
+ "900": "rgb(12 74 110)",
+ "950": "rgb(8 47 73)",
+ },
+ },
+}
diff --git a/backend/config/urls.py b/backend/config/urls.py
index f3bc0eb..4f02810 100644
--- a/backend/config/urls.py
+++ b/backend/config/urls.py
@@ -1,7 +1,19 @@
from django.contrib import admin
from django.urls import path, include
+from django.conf import settings
+from django.conf.urls.static import static
+from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView, SpectacularRedocView
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('shop.urls')),
+
+ # Swagger文档路由
+ path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
+ path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
+ path('api/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
]
+
+# 静态文件配置(开发环境)
+if settings.DEBUG:
+ urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
diff --git a/backend/requirements.txt b/backend/requirements.txt
index e3126a7..f6cc055 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -1,8 +1,18 @@
asgiref==3.11.0
+attrs==25.4.0
Django==6.0.1
django-cors-headers==4.9.0
+django-unfold==0.77.1
djangorestframework==3.16.1
+drf-spectacular==0.29.0
+inflection==0.5.1
+jsonschema==4.26.0
+jsonschema-specifications==2025.9.1
pillow==12.1.0
psycopg2-binary==2.9.11
+PyYAML==6.0.3
qrcode==8.2
+referencing==0.37.0
+rpds-py==0.30.0
sqlparse==0.5.5
+uritemplate==4.2.0
diff --git a/backend/shop/__pycache__/admin.cpython-312.pyc b/backend/shop/__pycache__/admin.cpython-312.pyc
index 47f0e05..b359979 100644
Binary files a/backend/shop/__pycache__/admin.cpython-312.pyc and b/backend/shop/__pycache__/admin.cpython-312.pyc differ
diff --git a/backend/shop/__pycache__/admin.cpython-313.pyc b/backend/shop/__pycache__/admin.cpython-313.pyc
index 3935e94..e8e6f5e 100644
Binary files a/backend/shop/__pycache__/admin.cpython-313.pyc and b/backend/shop/__pycache__/admin.cpython-313.pyc differ
diff --git a/backend/shop/__pycache__/models.cpython-312.pyc b/backend/shop/__pycache__/models.cpython-312.pyc
index 6b7e3df..9f9dae2 100644
Binary files a/backend/shop/__pycache__/models.cpython-312.pyc and b/backend/shop/__pycache__/models.cpython-312.pyc differ
diff --git a/backend/shop/__pycache__/models.cpython-313.pyc b/backend/shop/__pycache__/models.cpython-313.pyc
index 6d22e88..f0d45e9 100644
Binary files a/backend/shop/__pycache__/models.cpython-313.pyc and b/backend/shop/__pycache__/models.cpython-313.pyc differ
diff --git a/backend/shop/__pycache__/serializers.cpython-312.pyc b/backend/shop/__pycache__/serializers.cpython-312.pyc
index 8de83ef..d22b019 100644
Binary files a/backend/shop/__pycache__/serializers.cpython-312.pyc and b/backend/shop/__pycache__/serializers.cpython-312.pyc differ
diff --git a/backend/shop/__pycache__/serializers.cpython-313.pyc b/backend/shop/__pycache__/serializers.cpython-313.pyc
index 124ad0c..27c695d 100644
Binary files a/backend/shop/__pycache__/serializers.cpython-313.pyc and b/backend/shop/__pycache__/serializers.cpython-313.pyc differ
diff --git a/backend/shop/__pycache__/urls.cpython-312.pyc b/backend/shop/__pycache__/urls.cpython-312.pyc
index ad22381..72b89fc 100644
Binary files a/backend/shop/__pycache__/urls.cpython-312.pyc and b/backend/shop/__pycache__/urls.cpython-312.pyc differ
diff --git a/backend/shop/__pycache__/urls.cpython-313.pyc b/backend/shop/__pycache__/urls.cpython-313.pyc
index 6fa21a8..d815fff 100644
Binary files a/backend/shop/__pycache__/urls.cpython-313.pyc and b/backend/shop/__pycache__/urls.cpython-313.pyc differ
diff --git a/backend/shop/__pycache__/views.cpython-312.pyc b/backend/shop/__pycache__/views.cpython-312.pyc
index 4b4d42f..0ba8b2b 100644
Binary files a/backend/shop/__pycache__/views.cpython-312.pyc and b/backend/shop/__pycache__/views.cpython-312.pyc differ
diff --git a/backend/shop/__pycache__/views.cpython-313.pyc b/backend/shop/__pycache__/views.cpython-313.pyc
index 17b1a4f..54f0fe2 100644
Binary files a/backend/shop/__pycache__/views.cpython-313.pyc and b/backend/shop/__pycache__/views.cpython-313.pyc differ
diff --git a/backend/shop/admin.py b/backend/shop/admin.py
index 50e70b7..99780ed 100644
--- a/backend/shop/admin.py
+++ b/backend/shop/admin.py
@@ -1,6 +1,8 @@
from django.contrib import admin
from django.utils.html import format_html
from django.db.models import Sum
+from unfold.admin import ModelAdmin, TabularInline
+from unfold.decorators import display
from .models import ESP32Config, Order, Salesperson, WeChatPayConfig, Service, ARService, ProductFeature
import qrcode
from io import BytesIO
@@ -11,13 +13,13 @@ admin.site.site_header = "量迹AI硬件销售管理后台"
admin.site.site_title = "量迹AI后台"
admin.site.index_title = "欢迎使用量迹AI管理系统"
-class ProductFeatureInline(admin.TabularInline):
+class ProductFeatureInline(TabularInline):
model = ProductFeature
extra = 1
fields = ('title', 'description', 'icon_name', 'icon_image', 'icon_url', 'order')
@admin.register(WeChatPayConfig)
-class WeChatPayConfigAdmin(admin.ModelAdmin):
+class WeChatPayConfigAdmin(ModelAdmin):
list_display = ('app_id', 'mch_id', 'is_active', 'notify_url')
list_filter = ('is_active',)
search_fields = ('app_id', 'mch_id')
@@ -34,7 +36,7 @@ class WeChatPayConfigAdmin(admin.ModelAdmin):
)
@admin.register(ESP32Config)
-class ESP32ConfigAdmin(admin.ModelAdmin):
+class ESP32ConfigAdmin(ModelAdmin):
list_display = ('name', 'chip_type', 'price', 'has_camera', 'has_microphone')
list_filter = ('chip_type', 'has_camera')
search_fields = ('name', 'description')
@@ -53,13 +55,16 @@ class ESP32ConfigAdmin(admin.ModelAdmin):
)
@admin.register(Service)
-class ServiceAdmin(admin.ModelAdmin):
+class ServiceAdmin(ModelAdmin):
list_display = ('title', 'created_at')
search_fields = ('title', 'description')
fieldsets = (
('基本信息', {
'fields': ('title', 'description', 'color')
}),
+ ('价格与交付', {
+ 'fields': ('price', 'unit', 'delivery_time', 'delivery_content')
+ }),
('图标', {
'fields': ('icon', 'icon_url'),
'description': '图标上传和URL二选一,优先使用URL'
@@ -74,7 +79,7 @@ class ServiceAdmin(admin.ModelAdmin):
)
@admin.register(ARService)
-class ARServiceAdmin(admin.ModelAdmin):
+class ARServiceAdmin(ModelAdmin):
list_display = ('title', 'created_at')
search_fields = ('title', 'description')
fieldsets = (
@@ -88,7 +93,7 @@ class ARServiceAdmin(admin.ModelAdmin):
)
@admin.register(Salesperson)
-class SalespersonAdmin(admin.ModelAdmin):
+class SalespersonAdmin(ModelAdmin):
list_display = ('name', 'code', 'total_sales', 'view_promotion_url')
search_fields = ('name', 'code')
readonly_fields = ('promotion_qr_code', 'promotion_url_display', 'total_sales_display')
@@ -100,12 +105,11 @@ class SalespersonAdmin(admin.ModelAdmin):
)
return queryset
+ @display(description="累计销售额 (已支付)", ordering='_total_sales')
def total_sales(self, obj):
# 仅计算已支付的订单
paid_sales = obj.orders.filter(status='paid').aggregate(total=Sum('total_price'))['total']
return f"¥{paid_sales or 0:.2f}"
- total_sales.short_description = "累计销售额 (已支付)"
- total_sales.admin_order_field = '_total_sales'
def total_sales_display(self, obj):
return self.total_sales(obj)
@@ -116,15 +120,16 @@ class SalespersonAdmin(admin.ModelAdmin):
base_url = "http://localhost:5173"
return f"{base_url}/?ref={obj.code}"
+ @display(description="推广链接")
def view_promotion_url(self, obj):
url = self.promotion_url(obj)
- return format_html('打开推广链接', url)
- view_promotion_url.short_description = "推广链接"
+ return format_html('打开推广链接', url)
def promotion_url_display(self, obj):
return self.promotion_url(obj)
promotion_url_display.short_description = "完整推广链接"
+ @display(description="推广二维码")
def promotion_qr_code(self, obj):
if not obj.code:
return "请先保存以生成二维码"
@@ -144,9 +149,7 @@ class SalespersonAdmin(admin.ModelAdmin):
img.save(buffer, format="PNG")
img_str = base64.b64encode(buffer.getvalue()).decode()
- return format_html('', img_str)
-
- promotion_qr_code.short_description = "推广二维码"
+ return format_html('
', img_str)
fieldsets = (
('基本信息', {
@@ -161,7 +164,7 @@ class SalespersonAdmin(admin.ModelAdmin):
)
@admin.register(Order)
-class OrderAdmin(admin.ModelAdmin):
+class OrderAdmin(ModelAdmin):
list_display = ('id', 'customer_name', 'config', 'total_price', 'status', 'salesperson', 'created_at')
list_filter = ('status', 'salesperson', 'created_at')
search_fields = ('id', 'customer_name', 'phone_number', 'wechat_trade_no')
diff --git a/backend/shop/migrations/0008_service_delivery_content_service_delivery_time_and_more.py b/backend/shop/migrations/0008_service_delivery_content_service_delivery_time_and_more.py
new file mode 100644
index 0000000..bf9889a
--- /dev/null
+++ b/backend/shop/migrations/0008_service_delivery_content_service_delivery_time_and_more.py
@@ -0,0 +1,55 @@
+# Generated by Django 6.0.1 on 2026-02-02 06:16
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('shop', '0007_productfeature'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='service',
+ name='delivery_content',
+ field=models.TextField(blank=True, help_text='描述将交付给客户的具体成果', verbose_name='交付内容'),
+ ),
+ migrations.AddField(
+ model_name='service',
+ name='delivery_time',
+ field=models.CharField(blank=True, help_text='例如:3-5个工作日', max_length=50, verbose_name='预计交付周期'),
+ ),
+ migrations.AddField(
+ model_name='service',
+ name='price',
+ field=models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='起步价格'),
+ ),
+ migrations.AddField(
+ model_name='service',
+ name='unit',
+ field=models.CharField(default='次', help_text='例如:次、小时、月、个', max_length=20, verbose_name='计费单位'),
+ ),
+ migrations.CreateModel(
+ name='ServiceOrder',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('customer_name', models.CharField(max_length=100, verbose_name='客户姓名')),
+ ('company_name', models.CharField(blank=True, max_length=100, verbose_name='公司名称')),
+ ('phone_number', models.CharField(max_length=20, verbose_name='联系电话')),
+ ('email', models.EmailField(blank=True, max_length=254, verbose_name='电子邮箱')),
+ ('requirements', models.TextField(blank=True, verbose_name='具体需求描述')),
+ ('total_price', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='预估总价')),
+ ('status', models.CharField(choices=[('pending', '待沟通/待支付'), ('processing', '服务进行中'), ('completed', '已完成'), ('cancelled', '已取消')], default='pending', max_length=20, verbose_name='订单状态')),
+ ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
+ ('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
+ ('salesperson', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.salesperson', verbose_name='所属销售员')),
+ ('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.service', verbose_name='所选服务')),
+ ],
+ options={
+ 'verbose_name': '服务订单',
+ 'verbose_name_plural': '服务订单列表',
+ },
+ ),
+ ]
diff --git a/backend/shop/models.py b/backend/shop/models.py
index 017467f..b7395d0 100644
--- a/backend/shop/models.py
+++ b/backend/shop/models.py
@@ -137,6 +137,10 @@ class Service(models.Model):
icon_url = models.URLField(blank=True, null=True, verbose_name="图标 (URL)")
description = models.TextField(verbose_name="简介")
features = models.TextField(verbose_name="特性列表", help_text="每行一个特性")
+ price = models.DecimalField(max_digits=10, decimal_places=2, default=0, verbose_name="起步价格")
+ unit = models.CharField(max_length=20, default="次", verbose_name="计费单位", help_text="例如:次、小时、月、个")
+ delivery_time = models.CharField(max_length=50, blank=True, verbose_name="预计交付周期", help_text="例如:3-5个工作日")
+ delivery_content = models.TextField(blank=True, verbose_name="交付内容", help_text="描述将交付给客户的具体成果")
color = models.CharField(max_length=20, default="#00f0ff", verbose_name="主题色")
detail_image = models.ImageField(upload_to='services/details/', blank=True, null=True, verbose_name="详情页长图 (上传)")
detail_image_url = models.URLField(blank=True, null=True, verbose_name="详情页长图 (URL)")
@@ -150,6 +154,39 @@ class Service(models.Model):
verbose_name_plural = "AI服务管理"
+class ServiceOrder(models.Model):
+ """
+ AI服务订单模型
+ """
+ STATUS_CHOICES = (
+ ('pending', '待沟通/待支付'),
+ ('processing', '服务进行中'),
+ ('completed', '已完成'),
+ ('cancelled', '已取消'),
+ )
+
+ service = models.ForeignKey(Service, on_delete=models.CASCADE, verbose_name="所选服务")
+ customer_name = models.CharField(max_length=100, verbose_name="客户姓名")
+ company_name = models.CharField(max_length=100, blank=True, verbose_name="公司名称")
+ phone_number = models.CharField(max_length=20, verbose_name="联系电话")
+ email = models.EmailField(blank=True, verbose_name="电子邮箱")
+ requirements = models.TextField(verbose_name="具体需求描述", blank=True)
+
+ total_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="预估总价", default=0)
+ status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending', verbose_name="订单状态")
+
+ salesperson = models.ForeignKey(Salesperson, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="所属销售员")
+ created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
+ updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
+
+ def __str__(self):
+ return f"{self.customer_name} - {self.service.title}"
+
+ class Meta:
+ verbose_name = "服务订单"
+ verbose_name_plural = "服务订单列表"
+
+
class ARService(models.Model):
"""
AR体验服务模型
diff --git a/backend/shop/serializers.py b/backend/shop/serializers.py
index ce5ae6d..e7b5635 100644
--- a/backend/shop/serializers.py
+++ b/backend/shop/serializers.py
@@ -1,5 +1,5 @@
from rest_framework import serializers
-from .models import ESP32Config, Order, Salesperson, Service, ARService, ProductFeature
+from .models import ESP32Config, Order, Salesperson, Service, ARService, ProductFeature, ServiceOrder
class ProductFeatureSerializer(serializers.ModelSerializer):
"""
@@ -49,6 +49,38 @@ class ServiceSerializer(serializers.ModelSerializer):
return obj.detail_image.url
return None
+class ServiceOrderSerializer(serializers.ModelSerializer):
+ """
+ AI服务订单序列化器
+ """
+ service_name = serializers.CharField(source='service.title', read_only=True)
+ # 接收前端传来的 ref_code
+ ref_code = serializers.CharField(write_only=True, required=False, allow_blank=True)
+
+ class Meta:
+ model = ServiceOrder
+ fields = ['id', 'service', 'service_name', 'customer_name', 'company_name',
+ 'phone_number', 'email', 'requirements', 'total_price', 'status', 'created_at', 'ref_code']
+ read_only_fields = ['total_price', 'status', 'created_at']
+
+ def create(self, validated_data):
+ ref_code = validated_data.pop('ref_code', None)
+ service = validated_data.get('service')
+
+ # 默认设置预估总价为服务起步价
+ if service:
+ validated_data['total_price'] = service.price
+
+ # 尝试关联销售员
+ if ref_code:
+ try:
+ salesperson = Salesperson.objects.get(code=ref_code)
+ validated_data['salesperson'] = salesperson
+ except Salesperson.DoesNotExist:
+ pass
+
+ return super().create(validated_data)
+
class ARServiceSerializer(serializers.ModelSerializer):
"""
AR服务序列化器
diff --git a/backend/shop/urls.py b/backend/shop/urls.py
index 40e6243..ebf41eb 100644
--- a/backend/shop/urls.py
+++ b/backend/shop/urls.py
@@ -1,12 +1,13 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
-from .views import ESP32ConfigViewSet, OrderViewSet, order_check_view, ServiceViewSet, ARServiceViewSet
+from .views import ESP32ConfigViewSet, OrderViewSet, order_check_view, ServiceViewSet, ARServiceViewSet, ServiceOrderViewSet
router = DefaultRouter()
router.register(r'configs', ESP32ConfigViewSet)
router.register(r'orders', OrderViewSet)
router.register(r'services', ServiceViewSet)
router.register(r'ar', ARServiceViewSet)
+router.register(r'service-orders', ServiceOrderViewSet)
urlpatterns = [
path('', include(router.urls)),
diff --git a/backend/shop/views.py b/backend/shop/views.py
index 17e18f6..38b8606 100644
--- a/backend/shop/views.py
+++ b/backend/shop/views.py
@@ -2,8 +2,8 @@ from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django.shortcuts import render
-from .models import ESP32Config, Order, WeChatPayConfig, Service, ARService
-from .serializers import ESP32ConfigSerializer, OrderSerializer, ServiceSerializer, ARServiceSerializer
+from .models import ESP32Config, Order, WeChatPayConfig, Service, ARService, ServiceOrder
+from .serializers import ESP32ConfigSerializer, OrderSerializer, ServiceSerializer, ARServiceSerializer, ServiceOrderSerializer
class ARServiceViewSet(viewsets.ReadOnlyModelViewSet):
"""
@@ -28,6 +28,13 @@ class ServiceViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Service.objects.all().order_by('-created_at')
serializer_class = ServiceSerializer
+class ServiceOrderViewSet(viewsets.ModelViewSet):
+ """
+ AI服务订单管理
+ """
+ queryset = ServiceOrder.objects.all()
+ serializer_class = ServiceOrderSerializer
+
class ESP32ConfigViewSet(viewsets.ReadOnlyModelViewSet):
"""
提供ESP32配置选项的列表和详情
diff --git a/frontend/src/api.js b/frontend/src/api.js
index 8eb932e..29ea1d0 100644
--- a/frontend/src/api.js
+++ b/frontend/src/api.js
@@ -16,6 +16,7 @@ export const confirmPayment = (orderId) => api.post(`/orders/${orderId}/confirm_
export const getServices = () => api.get('/services/');
export const getServiceDetail = (id) => api.get(`/services/${id}/`);
+export const createServiceOrder = (data) => api.post('/service-orders/', data);
export const getARServices = () => api.get('/ar/');
export default api;
diff --git a/frontend/src/pages/Home.jsx b/frontend/src/pages/Home.jsx
index 3ae1e9c..71774e5 100644
--- a/frontend/src/pages/Home.jsx
+++ b/frontend/src/pages/Home.jsx
@@ -19,10 +19,9 @@ const Home = () => {
fetchProducts();
let i = 0;
const typingInterval = setInterval(() => {
- if (i < fullText.length) {
- setTypedText(prev => prev + fullText.charAt(i));
- i++;
- } else {
+ i++;
+ setTypedText(fullText.slice(0, i));
+ if (i >= fullText.length) {
clearInterval(typingInterval);
}
}, 150);
@@ -73,6 +72,26 @@ const Home = () => {
return (
+ * 具体价格可能因需求复杂度而异,提交需求后我们将提供详细报价单 +
+请填写您的联系方式和需求,我们的技术顾问将在 24 小时内与您联系。
+