diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..b58b603f --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/aws.xml b/.idea/aws.xml new file mode 100644 index 00000000..b63b642c --- /dev/null +++ b/.idea/aws.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..03d9549e --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..639900d1 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..f12574a9 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/web_b2b.iml b/.idea/web_b2b.iml new file mode 100644 index 00000000..d6ebd480 --- /dev/null +++ b/.idea/web_b2b.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 0eadb3f1..5d2eaab8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # 基于Python开发的B2B企业英文网站 -> 基于React+django开发的b2b企业网站,适用场景包括外贸独立站、企业官网、产品展示网站等场景。自2025年9月9日起本项目不再维护。 +> 基于React+django开发的b2b企业网站(大部分代码使用AI编写),适用场景包括外贸独立站、企业官网、产品展示网站等场景。自2025年9月9日起本项目不再维护。 ## 在线演示 @@ -79,11 +79,15 @@ python manage.py runserver ``` npm install ``` -(2) 构建项目 +(2) 修改.env配置 + +修改.env文件中的域名,改成你自己的域名。 + +(3) 构建项目 ``` npm run build ``` -(3) 运行 +(4) 运行 ``` npm run start ``` diff --git a/server/info.log b/server/info.log index 77d33213..c4d240dc 100644 --- a/server/info.log +++ b/server/info.log @@ -51,3 +51,33 @@ Forbidden: /myapp/admin/overview/count "GET /myapp/index/home/section HTTP/1.1" 200 4959 "GET /myapp/index/thing/section?page=1&pageSize=9&searchQuery= HTTP/1.1" 200 3521 "GET /myapp/index/thing/section?page=1&pageSize=9 HTTP/1.1" 200 3521 +Watching for file changes with StatReloader +Watching for file changes with StatReloader +Watching for file changes with StatReloader +D:\git_workspace\web_b2b\server\server\settings.py changed, reloading. +Watching for file changes with StatReloader +"GET /myapp/index/common/section HTTP/1.1" 200 4265 +"GET /myapp/index/home/section HTTP/1.1" 200 4959 +"GET /myapp/index/home/section HTTP/1.1" 200 4959 +"GET /myapp/index/home/section HTTP/1.1" 200 4959 +"GET /myapp/index/common/section HTTP/1.1" 200 4265 +"GET /myapp/index/home/section HTTP/1.1" 200 4959 +"GET /myapp/index/common/section HTTP/1.1" 200 4265 +"GET /myapp/index/home/section HTTP/1.1" 200 4959 +"GET /myapp/index/home/section HTTP/1.1" 200 4959 +"GET /myapp/index/home/section HTTP/1.1" 200 4959 +"GET /myapp/index/common/section HTTP/1.1" 200 4265 +"GET /myapp/index/home/section HTTP/1.1" 200 4959 +"GET /myapp/index/home/section HTTP/1.1" 200 4959 +"GET /myapp/index/common/section HTTP/1.1" 200 4265 +"GET /myapp/index/home/section HTTP/1.1" 200 4959 +"GET /myapp/index/common/section HTTP/1.1" 200 4265 +"GET /myapp/index/home/section HTTP/1.1" 200 4959 +"GET /myapp/index/common/section HTTP/1.1" 200 4265 +"GET /myapp/index/common/section HTTP/1.1" 200 4265 +"GET /myapp/index/home/section HTTP/1.1" 200 4959 +"GET /myapp/index/home/section HTTP/1.1" 200 4959 +"GET /myapp/index/common/section HTTP/1.1" 200 4265 +"GET /myapp/index/thing/section?page=1&pageSize=9 HTTP/1.1" 200 3521 +"GET /myapp/index/thing/section?page=1&pageSize=12&searchQuery= HTTP/1.1" 200 3937 +"GET /myapp/index/about/section HTTP/1.1" 200 4216 diff --git a/server/myapp/views/admin/__pycache__/basicTdk.cpython-38.pyc b/server/myapp/views/admin/__pycache__/basicTdk.cpython-38.pyc new file mode 100644 index 00000000..483ad9a9 Binary files /dev/null and b/server/myapp/views/admin/__pycache__/basicTdk.cpython-38.pyc differ diff --git a/server/myapp/views/admin/__pycache__/case.cpython-38.pyc b/server/myapp/views/admin/__pycache__/case.cpython-38.pyc new file mode 100644 index 00000000..7cacdc1a Binary files /dev/null and b/server/myapp/views/admin/__pycache__/case.cpython-38.pyc differ diff --git a/server/myapp/views/admin/__pycache__/download.cpython-38.pyc b/server/myapp/views/admin/__pycache__/download.cpython-38.pyc new file mode 100644 index 00000000..e84e89c3 Binary files /dev/null and b/server/myapp/views/admin/__pycache__/download.cpython-38.pyc differ diff --git a/server/myapp/views/admin/__pycache__/faq.cpython-38.pyc b/server/myapp/views/admin/__pycache__/faq.cpython-38.pyc new file mode 100644 index 00000000..4a49ca22 Binary files /dev/null and b/server/myapp/views/admin/__pycache__/faq.cpython-38.pyc differ diff --git a/server/myapp/views/admin/basicTdk.py b/server/myapp/views/admin/basicTdk.py new file mode 100644 index 00000000..a5ded84f --- /dev/null +++ b/server/myapp/views/admin/basicTdk.py @@ -0,0 +1,44 @@ +# Create your views here. + +from rest_framework.decorators import api_view, authentication_classes + +from myapp.auth.authentication import AdminTokenAuthtication +from myapp.handler import APIResponse +from myapp.models import BasicTdk +from myapp.permission.permission import isDemoAdminUser, check_if_demo +from myapp.serializers import BasicTdkSerializer +from myapp.utils import after_call, clear_cache + + +@api_view(['GET']) +@authentication_classes([AdminTokenAuthtication]) +def list_api(request): + if request.method == 'GET': + basicTdk = BasicTdk.get_solo() + + serializer = BasicTdkSerializer(basicTdk, many=False) + return APIResponse(code=0, msg='查询成功', data=serializer.data) + + + + +@api_view(['POST']) +@authentication_classes([AdminTokenAuthtication]) +@check_if_demo +@after_call(clear_cache) +def update(request): + + try: + basicTdk = BasicTdk.get_solo() + serializer = BasicTdkSerializer(basicTdk, data=request.data) + if serializer.is_valid(): + serializer.save() + return APIResponse(code=0, msg='更新成功', data=serializer.data) + else: + print(serializer.errors) + except BasicTdk.DoesNotExist: + return APIResponse(code=1, msg='对象不存在') + + return APIResponse(code=1, msg='更新失败') + + diff --git a/server/myapp/views/admin/case.py b/server/myapp/views/admin/case.py new file mode 100644 index 00000000..058e7b5a --- /dev/null +++ b/server/myapp/views/admin/case.py @@ -0,0 +1,90 @@ +# Create your views here. + +from rest_framework.decorators import api_view, authentication_classes +from rest_framework.pagination import PageNumberPagination + +from myapp.auth.authentication import AdminTokenAuthtication +from myapp.handler import APIResponse +from myapp.models import Case +from myapp.permission.permission import isDemoAdminUser, check_if_demo +from myapp.serializers import CaseSerializer +from myapp.utils import after_call, clear_cache + + +class MyPageNumberPagination(PageNumberPagination): + page_size = 10 # 每页的默认项 + page_size_query_param = 'pageSize' # 允许通过 URL 参数设置每页的大小 + max_page_size = 100 # 最大页尺寸 + + +@api_view(['GET']) +@authentication_classes([AdminTokenAuthtication]) +def list_api(request): + if request.method == 'GET': + keyword = request.GET.get("keyword", '') + case = Case.objects.filter(title__contains=keyword).order_by('-create_time') + + # 分页 + paginator = MyPageNumberPagination() + paginated_case = paginator.paginate_queryset(case, request) + total = case.count() + + serializer = CaseSerializer(paginated_case, many=True) + return APIResponse(code=0, msg='查询成功', data=serializer.data, total=total) + + +@api_view(['POST']) +@authentication_classes([AdminTokenAuthtication]) +@check_if_demo +@after_call(clear_cache) +def create(request): + + if not request.data.get('title', None): + return APIResponse(code=1, msg='标题不能为空') + + data = request.data.copy() + serializer = CaseSerializer(data=data) + if serializer.is_valid(): + serializer.save() + return APIResponse(code=0, msg='创建成功', data=serializer.data) + else: + print(serializer.errors) + + return APIResponse(code=1, msg='创建失败') + + +@api_view(['POST']) +@authentication_classes([AdminTokenAuthtication]) +@check_if_demo +@after_call(clear_cache) +def update(request): + + try: + pk = request.data['id'] + case = Case.objects.get(pk=pk) + except Case.DoesNotExist: + return APIResponse(code=1, msg='对象不存在') + + data = request.data.copy() + serializer = CaseSerializer(case, data=data) + if serializer.is_valid(): + serializer.save() + return APIResponse(code=0, msg='更新成功', data=serializer.data) + else: + print(serializer.errors) + return APIResponse(code=1, msg='更新失败') + + +@api_view(['POST']) +@check_if_demo +@authentication_classes([AdminTokenAuthtication]) +def delete(request): + + try: + ids = request.data['ids'] + ids_arr = ids.split(',') + Case.objects.filter(id__in=ids_arr).delete() + except Case.DoesNotExist: + return APIResponse(code=1, msg='对象不存在') + + return APIResponse(code=0, msg='删除成功') diff --git a/server/myapp/views/admin/download.py b/server/myapp/views/admin/download.py new file mode 100644 index 00000000..558197b5 --- /dev/null +++ b/server/myapp/views/admin/download.py @@ -0,0 +1,85 @@ +# Create your views here. + +from rest_framework.decorators import api_view, authentication_classes +from rest_framework.pagination import PageNumberPagination + +from myapp.auth.authentication import AdminTokenAuthtication +from myapp.handler import APIResponse +from myapp.models import Download +from myapp.permission.permission import isDemoAdminUser, check_if_demo +from myapp.serializers import DownloadSerializer +from myapp.utils import after_call, clear_cache + + +class MyPageNumberPagination(PageNumberPagination): + page_size = 10 # 每页的默认项 + page_size_query_param = 'pageSize' # 允许通过 URL 参数设置每页的大小 + max_page_size = 100 # 最大页尺寸 + + +@api_view(['GET']) +@authentication_classes([AdminTokenAuthtication]) +def list_api(request): + if request.method == 'GET': + download = Download.objects.order_by('-create_time') + total = download.count() + + paginator = MyPageNumberPagination() + paginated_download = paginator.paginate_queryset(download, request) + serializer = DownloadSerializer(paginated_download, many=True) + return APIResponse(code=0, msg='查询成功', data=serializer.data, total=total) + + +@api_view(['POST']) +@authentication_classes([AdminTokenAuthtication]) +@check_if_demo +@after_call(clear_cache) +def create(request): + + data = request.data.copy() + serializer = DownloadSerializer(data=data) + if serializer.is_valid(): + serializer.save() + return APIResponse(code=0, msg='创建成功', data=serializer.data) + else: + print(serializer.errors) + + return APIResponse(code=1, msg='创建失败') + + +@api_view(['POST']) +@authentication_classes([AdminTokenAuthtication]) +@check_if_demo +@after_call(clear_cache) +def update(request): + + try: + pk = request.data['id'] + download = Download.objects.get(pk=pk) + except Download.DoesNotExist: + return APIResponse(code=1, msg='对象不存在') + + data = request.data.copy() + serializer = DownloadSerializer(download, data=data) + if serializer.is_valid(): + serializer.save() + return APIResponse(code=0, msg='更新成功', data=serializer.data) + else: + print(serializer.errors) + return APIResponse(code=1, msg='更新失败') + + +@api_view(['POST']) +@authentication_classes([AdminTokenAuthtication]) +@check_if_demo +@after_call(clear_cache) +def delete(request): + + try: + ids = request.data['ids'] + ids_arr = ids.split(',') + Download.objects.filter(id__in=ids_arr).delete() + except Download.DoesNotExist: + return APIResponse(code=1, msg='对象不存在') + + return APIResponse(code=0, msg='删除成功') diff --git a/server/myapp/views/admin/faq.py b/server/myapp/views/admin/faq.py new file mode 100644 index 00000000..e68384ad --- /dev/null +++ b/server/myapp/views/admin/faq.py @@ -0,0 +1,88 @@ +# Create your views here. + +from rest_framework.decorators import api_view, authentication_classes +from rest_framework.pagination import PageNumberPagination + +from myapp.auth.authentication import AdminTokenAuthtication +from myapp.handler import APIResponse +from myapp.models import Faq +from myapp.permission.permission import isDemoAdminUser, check_if_demo +from myapp.serializers import FaqSerializer +from myapp.utils import after_call, clear_cache + + +class MyPageNumberPagination(PageNumberPagination): + page_size = 10 # 每页的默认项 + page_size_query_param = 'pageSize' # 允许通过 URL 参数设置每页的大小 + max_page_size = 100 # 最大页尺寸 + + +@api_view(['GET']) +@authentication_classes([AdminTokenAuthtication]) +def list_api(request): + if request.method == 'GET': + faq = Faq.objects.order_by('-create_time') + total = faq.count() + + paginator = MyPageNumberPagination() + paginated_faq = paginator.paginate_queryset(faq, request) + serializer = FaqSerializer(paginated_faq, many=True) + return APIResponse(code=0, msg='查询成功', data=serializer.data, total=total) + + + + + +@api_view(['POST']) +@authentication_classes([AdminTokenAuthtication]) +@check_if_demo +@after_call(clear_cache) +def create(request): + + data = request.data.copy() + serializer = FaqSerializer(data=data) + if serializer.is_valid(): + serializer.save() + return APIResponse(code=0, msg='创建成功', data=serializer.data) + else: + print(serializer.errors) + + return APIResponse(code=1, msg='创建失败') + + +@api_view(['POST']) +@authentication_classes([AdminTokenAuthtication]) +@check_if_demo +@after_call(clear_cache) +def update(request): + + try: + pk = request.data['id'] + faq = Faq.objects.get(pk=pk) + except Faq.DoesNotExist: + return APIResponse(code=1, msg='对象不存在') + + data = request.data.copy() + serializer = FaqSerializer(faq, data=data) + if serializer.is_valid(): + serializer.save() + return APIResponse(code=0, msg='更新成功', data=serializer.data) + else: + print(serializer.errors) + return APIResponse(code=1, msg='更新失败') + + +@api_view(['POST']) +@authentication_classes([AdminTokenAuthtication]) +@check_if_demo +@after_call(clear_cache) +def delete(request): + + try: + ids = request.data['ids'] + ids_arr = ids.split(',') + Faq.objects.filter(id__in=ids_arr).delete() + except Faq.DoesNotExist: + return APIResponse(code=1, msg='对象不存在') + + return APIResponse(code=0, msg='删除成功') diff --git a/server/server/__pycache__/settings.cpython-38.pyc b/server/server/__pycache__/settings.cpython-38.pyc index aa6eb295..1b126979 100644 Binary files a/server/server/__pycache__/settings.cpython-38.pyc and b/server/server/__pycache__/settings.cpython-38.pyc differ diff --git a/server/server/settings.py b/server/server/settings.py index 80386c83..00daa8a4 100644 --- a/server/server/settings.py +++ b/server/server/settings.py @@ -102,9 +102,9 @@ DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', - 'NAME': 'web_b2b', + 'NAME': 'your-db-name', 'USER': 'root', - 'PASSWORD': '4643830', + 'PASSWORD': 'xxxxxxxx', 'HOST': '127.0.0.1', 'PORT': '3306', 'CONN_MAX_AGE': 60, # 连接复用时间 diff --git a/web/.env b/web/.env new file mode 100644 index 00000000..f21c27fd --- /dev/null +++ b/web/.env @@ -0,0 +1,5 @@ +NEXT_PUBLIC_HOST=mytest.com +NEXT_PUBLIC_BASE_URL=http://mytest.com +NEXT_PUBLIC_DJANGO_BASE_URL=http://127.0.0.1:8000 +NEXT_PUBLIC_BASE_PATH= +NEXT_PUBLIC_TEMPLATE_ID=010 \ No newline at end of file diff --git a/web/.gitignore b/web/.gitignore index 930d7301..d31dbd64 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -31,8 +31,7 @@ yarn-debug.log* yarn-error.log* .pnpm-debug.log* -# env files (can opt-in for committing if needed) -.env* + # vercel .vercel diff --git a/web/my.conf b/web/my.conf new file mode 100644 index 00000000..cf88274d --- /dev/null +++ b/web/my.conf @@ -0,0 +1,68 @@ + + + +server { + listen 80; + server_name xxxxxx.com www.xxxxxx.com; + + + location /upload/ { + access_log off; + log_not_found off; + alias /var/python_my/server/upload/; + add_header Cache-Control "public, max-age=90"; + } + + # ico文件 + location /favicon.ico { + access_log off; + log_not_found off; + alias /var/python_my/server/upload/img/favicon.ico; + add_header Cache-Control "public, max-age=90"; + } + + # django代理 + location /myapp/ { + proxy_pass http://127.0.0.1:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; # 获取客户端真实 IP + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 获取代理链中的真实 IP + proxy_set_header X-Forwarded-Proto $scheme; # 获取协议(http 或 https) + client_max_body_size 100M; # 上传限制 + + } + + location /_next/image { + proxy_pass http://127.0.0.1:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + add_header Cache-Control "public, max-age=31536000"; + } + + location /_next/static { + proxy_pass http://127.0.0.1:3000; + access_log off; + expires 1y; + add_header Cache-Control "public, max-age=31536000, immutable"; + } + + # 开发环境hmr + location /_next/webpack-hmr { + proxy_pass http://127.0.0.1:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + + location / { + proxy_pass http://127.0.0.1:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} diff --git a/web/src/app/(admin)/admin/basicInfo/page.jsx b/web/src/app/(admin)/admin/basicInfo/page.jsx index 7c13ce53..509fc810 100644 --- a/web/src/app/(admin)/admin/basicInfo/page.jsx +++ b/web/src/app/(admin)/admin/basicInfo/page.jsx @@ -2,7 +2,12 @@ import React, {useEffect, useState} from 'react'; import {Tabs} from "antd"; import SiteSettings from "@/components/admin/basicInfo/siteSettings"; +import TdkSettings from "@/components/admin/basicInfo/tdkSettings"; +import GlobalSettings from "@/components/admin/basicInfo/globalSettings"; +import AdditionalSettings from "@/components/admin/basicInfo/additionalSettings"; import BannerSettings from "@/components/admin/basicInfo/bannerSettings"; +import CommentSettings from "@/components/admin/basicInfo/commentSettings"; +import AdvantageSettings from "@/components/admin/basicInfo/advantageSettings"; export default function Page() { const onChange = (key) => { @@ -14,11 +19,36 @@ export default function Page() { label: '网站信息', children: , }, + { + key: '2', + label: 'TDK信息', + children: , + }, { key: '3', label: 'Banner信息', children: , }, + { + key: '4', + label: '全局变量', + children: , + }, + { + key: '5', + label: '附加变量', + children: , + }, + { + key: '6', + label: '优势变量', + children: , + }, + { + key: '7', + label: '客评变量', + children: , + }, ]; return ( diff --git a/web/src/app/(admin)/admin/case/page.jsx b/web/src/app/(admin)/admin/case/page.jsx new file mode 100644 index 00000000..ef479428 --- /dev/null +++ b/web/src/app/(admin)/admin/case/page.jsx @@ -0,0 +1,30 @@ +'use client'; +import React, {useEffect, useState} from 'react'; +import {Tabs} from "antd"; +import CaseList from "@/components/admin/case/caseList"; + +export default function Page() { + const onChange = (key) => { + console.log(key); + }; + const items = [ + { + key: '1', + label: '案例列表', + children: , + }, + ]; + + return ( + <> +
+
+ 24, align: 'center' }} + tabBarStyle={{paddingLeft: '20px', outline: 'none',fontWeight: '400'}} + className="bg-white custom-tab" defaultActiveKey="1" items={items} onChange={onChange}/> +
+
+ + ); +}; \ No newline at end of file diff --git a/web/src/app/(admin)/admin/download/page.jsx b/web/src/app/(admin)/admin/download/page.jsx new file mode 100644 index 00000000..bf9e1641 --- /dev/null +++ b/web/src/app/(admin)/admin/download/page.jsx @@ -0,0 +1,30 @@ +'use client'; +import React, {useEffect, useState} from 'react'; +import {Tabs} from "antd"; +import DownloadList from "@/components/admin/download/downloadList"; + +export default function Page() { + const onChange = (key) => { + console.log(key); + }; + const items = [ + { + key: '1', + label: '下载页管理', + children: , + }, + ]; + + return ( + <> +
+
+ 24, align: 'center' }} + tabBarStyle={{paddingLeft: '20px', outline: 'none',fontWeight: '400'}} + className="bg-white custom-tab" defaultActiveKey="1" items={items} onChange={onChange}/> +
+
+ + ); +}; \ No newline at end of file diff --git a/web/src/app/(admin)/admin/faq/page.jsx b/web/src/app/(admin)/admin/faq/page.jsx new file mode 100644 index 00000000..73aa4b9c --- /dev/null +++ b/web/src/app/(admin)/admin/faq/page.jsx @@ -0,0 +1,30 @@ +'use client'; +import React, {useEffect, useState} from 'react'; +import {Tabs} from "antd"; +import FaqList from "@/components/admin/faq/faqList"; + +export default function Page() { + const onChange = (key) => { + console.log(key); + }; + const items = [ + { + key: '1', + label: 'Faq列表', + children: , + }, + ]; + + return ( + <> +
+
+ 24, align: 'center' }} + tabBarStyle={{paddingLeft: '20px', outline: 'none',fontWeight: '400'}} + className="bg-white custom-tab" defaultActiveKey="1" items={items} onChange={onChange}/> +
+
+ + ); +}; \ No newline at end of file diff --git a/web/src/components/admin/basicInfo/additionalSettings.jsx b/web/src/components/admin/basicInfo/additionalSettings.jsx new file mode 100644 index 00000000..1faffa79 --- /dev/null +++ b/web/src/components/admin/basicInfo/additionalSettings.jsx @@ -0,0 +1,270 @@ +'use client'; +import HeadLabel from "@/components/admin/headLabel"; +import FormLabel from "@/components/admin/formLabel"; +import React, {useEffect, useState} from "react"; +import {Button, Input, message, Radio, Spin} from 'antd'; +import ImageUpload from "@/components/admin/imageUpload"; +import TextArea from "antd/es/input/TextArea"; +import {Divider} from "antd/lib"; +import axiosInstance from "@/utils/axios"; + +const AdditionalSettings = () => { + + const [currentItem, setCurrentItem] = useState({}); + const [loading, setLoading] = useState(false); + + // 为了制造Upload而用(关于配图) + const [aboutImageList, setAboutImageList] = useState([]); + + // 为了制造Upload而用(使命配图) + const [missionImageList, setMissionImageList] = useState([]); + + // 为了制造Upload而用(Contact底图) + const [contactImageList, setContactImageList] = useState([]); + + // 为了制造Upload而用(工厂配图) + const [companyImageList, setCompanyImageList] = useState([]); + + // 为了制造Upload而用(资质图片) + const [certificationImageList, setCertificationImageList] = useState([]); + + const fetchData = async () => { + try { + setLoading(true) + const {code, msg, data} = await axiosInstance.get('/myapp/admin/basicAdditional/list'); + if (code === 0) { + setCurrentItem(data); + fixToImageData(data); + } else { + message.error(msg || '网络异常') + } + setLoading(false) + } catch (err) { + console.log(err) + message.error('网络异常') + setLoading(false) + } + }; + + const fixToImageData = (initialData) => { + // 制造适合Upload的数据格式(关于配图) + setAboutImageList(createImageList(initialData?.global_addition_about_image)); + + // 制造适合Upload的数据格式(使命配图) + setMissionImageList(createImageList(initialData?.global_addition_mission_image)); + + // 制造适合Upload的数据格式(Contact底图) + setContactImageList(createImageList(initialData?.global_addition_contact_image)); + + // 制造适合Upload的数据格式(工厂配图) + setCompanyImageList(createImageList(initialData?.global_addition_company_image)); + + // 制造适合Upload的数据格式(资质图片) + setCertificationImageList(createImageList(initialData?.ext02)); + } + + useEffect(() => { + fetchData(); + }, []); + + + const createImageList = (imageString) => { + return imageString?.length > 0 + ? imageString.split("#").map((item) => ({ + success: true, + name: item, + status: 'done', + url: `${process.env.NEXT_PUBLIC_BASE_URL}/upload/img/${item}`, + })) + : []; + }; + + const handleInputChange = (name, value) => { + setCurrentItem((prev) => ({...prev, [name]: value})); + } + + const handleSave = async () => { + console.log(currentItem); + try { + const post_url = '/myapp/admin/basicAdditional/update'; + const formData = new FormData(); + formData.append('additional_mission', currentItem.additional_mission || ''); + formData.append('additional_about', currentItem.additional_about || ''); + formData.append('global_addition_about_image', currentItem.global_addition_about_image || ''); + formData.append('global_addition_mission_image', currentItem.global_addition_mission_image || ''); + formData.append('global_addition_contact_image', currentItem.global_addition_contact_image || ''); + formData.append('global_addition_company_image', currentItem.global_addition_company_image || ''); + formData.append('param_one_name', currentItem.param_one_name || ''); + formData.append('param_one_value', currentItem.param_one_value || ''); + formData.append('param_two_name', currentItem.param_two_name || ''); + formData.append('param_two_value', currentItem.param_two_value || ''); + formData.append('param_three_name', currentItem.param_three_name || ''); + formData.append('param_three_value', currentItem.param_three_value || ''); + formData.append('param_four_name', currentItem.param_four_name || ''); + formData.append('param_four_value', currentItem.param_four_value || ''); + formData.append('ext01', currentItem.ext01 || ''); + formData.append('ext02', currentItem.ext02 || ''); + + const {code, msg, data} = await axiosInstance.post(post_url, formData); + if (code === 0) { + message.success("操作成功"); + fetchData() + } else { + message.error(msg || '网络异常') + } + } catch (err) { + console.log(err) + } + }; + + const handleImageUploadChange = (imageUrlList, name) => { + let value = (imageUrlList && imageUrlList.length > 0) ? imageUrlList.join("#") : null; + setCurrentItem((prev) => ({...prev, [name]: value})); + }; + + return ( + <> + +
+
+
+ + -
- -
- -
- -
-
- - - - ); -} \ No newline at end of file diff --git a/web/src/components/index/sections/template02/navBar.jsx b/web/src/components/index/sections/template02/navBar.jsx deleted file mode 100644 index 410c2576..00000000 --- a/web/src/components/index/sections/template02/navBar.jsx +++ /dev/null @@ -1,35 +0,0 @@ - -import Link from "next/link"; -import NavBarClient from "@/components/index/sections/template02/navBarClient"; -import TopBar from "@/components/index/sections/template02/topbar"; - -export default function NavBar({sectionData}) { - return ( -
- {/* 引入封装的顶部信息栏组件 */} - - - {/* 主导航栏 */} -
- -
-
- ) -} \ No newline at end of file diff --git a/web/src/components/index/sections/template02/navBarClient.jsx b/web/src/components/index/sections/template02/navBarClient.jsx deleted file mode 100644 index e9cf65e4..00000000 --- a/web/src/components/index/sections/template02/navBarClient.jsx +++ /dev/null @@ -1,395 +0,0 @@ -'use client' -import {useState, useEffect, useRef} from 'react' -import { - Bars3Icon, - XMarkIcon, -} from '@heroicons/react/24/outline' -import {ChevronDownIcon} from '@heroicons/react/20/solid' -import {usePathname} from "next/navigation"; -import Link from "next/link"; -import lang from "@/locales"; -import Wechat from "@/components/index/sections/wechat"; - -// 移动端菜单项组件 -function MobileMenuItem({item, level = 0, setMobileMenuOpen}) { - const [isOpen, setIsOpen] = useState(false); - - // 根据层级动态调整缩进和字体大小 - const paddingLeft = `${1 + level * 1.5}rem`; // 动态缩进 - const fontSize = `${Math.max(1 - level * 0.1, 0.8)}rem`; // 动态字体,最低限制为 0.8rem - const fontWeight = level === 0 ? 'font-semibold' : 'font-medium'; // 当level > 0时字体不加粗 - const baseClasses = `block rounded-none py-2 text-gray-900 hover:bg-gray-50 ${fontWeight}`; - - // 滚动到顶部 - const scrollToTop = () => { - window.scrollTo({ - top: 0, - behavior: 'smooth' - }); - }; - - // 处理链接点击事件 - const handleLinkClick = () => { - // 点击链接后关闭移动端菜单 - setMobileMenuOpen(false); - // 滚动到顶部 - scrollToTop(); - }; - - if (item.type === 'link') { - return ( - - {lang[item.name] || item.name} - - ); - } - - if (item.type === 'dropdown') { - return ( -
- - {isOpen && ( -
- {item.subItems.map((subItem) => ( - - ))} -
- )} -
- ); - } - - return null; -} - -// 桌面端导航栏组件 -export function DesktopNav({navigationItems, current}) { - const [activeDropdown, setActiveDropdown] = useState(null); - const [activeSubMenu, setActiveSubMenu] = useState(null); - - // 滚动到顶部 - const scrollToTop = () => { - window.scrollTo({ - top: 0, - behavior: 'smooth' - }); - }; - - // 处理菜单项点击 - const handleMenuItemClick = () => { - // 点击菜单项后关闭所有下拉菜单 - setActiveDropdown(null); - setActiveSubMenu(null); - // 滚动到顶部 - scrollToTop(); - }; - - return ( -
- {navigationItems.map((item, index) => ( -
0 ? 'ml-10' : ''}`}> - {item.type === 'link' ? ( - - {lang[item.name]} - - ) : ( -
setActiveDropdown(item.name)} - onMouseLeave={() => { - setActiveDropdown(null) - setActiveSubMenu(null) - }} - > - = 0 ? 'text-mainColorNormal' : 'text-gray-900'} hover:text-mainColorNormal transition-colors duration-200 cursor-pointer h-full flex items-center gap-x-0.5 text-base/6 font-semibold`} - onClick={handleMenuItemClick} - > - {lang[item.name]} -
- )} -
- ))} -
- ); -} - -// 移动端导航菜单组件 -export function MobileNav({sectionData, mobileMenuOpen, setMobileMenuOpen}) { - const menuRef = useRef(null); - - // 处理点击外部关闭菜单 - useEffect(() => { - const handleClickOutside = (event) => { - if (menuRef.current && !menuRef.current.contains(event.target) && mobileMenuOpen) { - setMobileMenuOpen(false); - } - }; - - const handleEscapeKey = (event) => { - if (event.key === 'Escape' && mobileMenuOpen) { - setMobileMenuOpen(false); - } - }; - - if (mobileMenuOpen) { - document.addEventListener('mousedown', handleClickOutside); - document.addEventListener('keydown', handleEscapeKey); - } - - return () => { - document.removeEventListener('mousedown', handleClickOutside); - document.removeEventListener('keydown', handleEscapeKey); - }; - }, [mobileMenuOpen, setMobileMenuOpen]); - - if (!mobileMenuOpen) return null; - - return ( -
-
-
-
- - Logo - Logo - - -
-
-
-
- {sectionData && sectionData.navigationItems.map((item) => ( - - ))} -
-
-
- {/* Facebook */} - {sectionData?.basicGlobal?.global_facebook?.trim().length > 0 && ( - - - - - - )} - {/* Twitter */} - {sectionData?.basicGlobal?.global_twitter?.trim().length > 0 && ( - - - - - - )} - {/* Instagram */} - {sectionData?.basicGlobal?.global_instagram?.trim().length > 0 && ( - - - - - - )} - {/* LinkedIn */} - {sectionData?.basicGlobal?.global_linkedin?.trim().length > 0 && ( - - - - - - )} - {/* YouTube */} - {sectionData?.basicGlobal?.global_youtube?.trim().length > 0 && ( - - - - - - )} - {/* WhatsApp */} - {sectionData?.basicGlobal?.global_whatsapp?.trim().length > 0 && ( - - - - - - )} - -
-
-
-
-
-
- ); -} - -// 导航栏客户端包装器组件 -export default function NavBarClient({sectionData}) { - const [mobileMenuOpen, setMobileMenuOpen] = useState(false); - const pathname = usePathname(); - const [current, setCurrent] = useState(pathname); - - useEffect(() => { - setCurrent(pathname); - }, [pathname]); - - return ( - <> - {/* 桌面端导航 */} - - - {/* 移动端菜单按钮 */} -
- -
- - {/* 移动端导航菜单 */} - - - ); -} diff --git a/web/src/components/index/sections/template02/ourCategories.jsx b/web/src/components/index/sections/template02/ourCategories.jsx deleted file mode 100644 index 7f7f764c..00000000 --- a/web/src/components/index/sections/template02/ourCategories.jsx +++ /dev/null @@ -1,72 +0,0 @@ -'use client' -import Link from 'next/link'; -import Image from 'next/image'; -import lang from "@/locales"; - -// 模拟分类数据 - 减少到4个 -const categories = [ - { - id: 1, - name: 'Men', - image: 'https://images.unsplash.com/photo-1490367532201-b9bc1dc483f6?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=800&h=800&q=80', - }, - { - id: 2, - name: 'Women', - image: 'https://images.unsplash.com/photo-1483985988355-763728e1935b?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=800&h=800&q=80', - }, - { - id: 3, - name: 'Accessories', - image: 'https://images.unsplash.com/photo-1584917865442-de89df76afd3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=800&h=800&q=80', - }, - { - id: 4, - name: 'Footwear', - image: 'https://images.unsplash.com/photo-1549298916-b41d501d3772?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=800&h=800&q=80', - } -]; - -// 分类卡片组件 -function CategoryCard({ category }) { - return ( - -
-
- {category.title} -
-
-
-

{category.title}

-
- - ); -} - -export default function OurCategories({categoryData}) { - return ( -
-
-
-

{lang.OurCategories}

-

- {lang.OurCategoriesTip} -

-
- -
- {categoryData.map((category) => ( - - ))} -
- -
-
- ) -} \ No newline at end of file diff --git a/web/src/components/index/sections/template02/productList.jsx b/web/src/components/index/sections/template02/productList.jsx deleted file mode 100644 index 651bbaf6..00000000 --- a/web/src/components/index/sections/template02/productList.jsx +++ /dev/null @@ -1,204 +0,0 @@ -import Link from 'next/link'; -import Image from 'next/image'; -import Pagination from '@/components/index/sections/pagination'; -import SearchBar from "@/components/index/sections/searchBar"; -import SearchResultBar from "@/components/index/sections/SearchResultBar"; -import CategoryItem from "@/components/index/sections/categoryItem"; -import lang from "@/locales"; - - -// 模拟产品数据 -const products = [ - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, -]; - -// 分类组件 -export default function ProductList({ categoryId, pageNumber, total, categoryData, productData, featuredData, searchQuery }) { - - // 构建基础链接前缀,不包含页码部分 - let linkPrefix = '/product'; - - // 添加分类部分,如果存在 - if (categoryId) { - linkPrefix += `/category/${categoryId}`; - } - - // 添加页码路径前缀 - linkPrefix += '/page'; - - // 如果有搜索查询,我们需要在每个分页链接中保持它 - const searchSuffix = searchQuery ? `?s=${encodeURIComponent(searchQuery)}` : ''; - - let mCategoryData = categoryData; - mCategoryData.unshift({ - id: -1, - title: lang.AllProducts - }) - - return ( -
-
-
- {/* 左侧边栏 */} -
- - {/* 搜索框 */} - - - {/* 分类 - 使用折叠面板 */} -
-

{lang.Categories}

-
- {mCategoryData.map((category) => ( - - ))} -
-
- - {/* 热门产品 - 在md尺寸以下隐藏 */} -
-

{lang.BestProducts}

-
    - {featuredData.map((product) => { - // 提取第一张图片 - const cover = product.cover ? product.cover.split('#')[0] : ''; - return ( -
  • - -
    -
    - {product.title} -
    -
    -
    -

    {product.title}

    -
    - -
  • - )})} -
-
-
- - {/* 右侧产品列表 */} -
- - {/* 搜索结果提示 */} - - - {/* 产品网格 */} -
- {productData.map((product) => { - - // 提取第一张图片 - const cover = product.cover ? product.cover.split('#')[0] : ''; - return ( -
-
- -
- {product.title} -
- -
- -
-

{product.title}

-
-
- )})} -
- - {/* 分页 */} -
- -
-
-
-
-
- ); -} \ No newline at end of file diff --git a/web/src/components/index/sections/template02/topbar.jsx b/web/src/components/index/sections/template02/topbar.jsx deleted file mode 100644 index 383ac4ce..00000000 --- a/web/src/components/index/sections/template02/topbar.jsx +++ /dev/null @@ -1,137 +0,0 @@ -'use client' -import Link from "next/link"; -import Wechat from "@/components/index/sections/wechat"; -import { useEffect, useState } from "react"; - -export default function TopBar({ sectionData }) { - const [showTopBar, setShowTopBar] = useState(true); - - useEffect(() => { - - window.scrollTo(0, 0); - - let lastScrollY = 0; - - const handleScroll = () => { - const currentScrollY = window.scrollY; - - // 当页面滚动到顶部时显示顶部信息栏 - if (currentScrollY === 0) { - setShowTopBar(true); - } - // 当向下滚动时隐藏顶部信息栏 - else if (currentScrollY > lastScrollY) { - setShowTopBar(false); - } - - lastScrollY = currentScrollY; - }; - - window.addEventListener('scroll', handleScroll); - - - - return () => { - window.removeEventListener('scroll', handleScroll); - }; - }, []); - - return ( -
-
-
-
- - - - {sectionData['basicGlobal']['global_email']} -
-
- - - - {sectionData['basicGlobal']['global_phone']} -
-
-
-
- {sectionData?.basicGlobal?.global_facebook?.trim().length > 0 && ( - - - - - - )} - {sectionData?.basicGlobal?.global_twitter?.trim().length > 0 && ( - - - - - - )} - {sectionData?.basicGlobal?.global_instagram?.trim().length > 0 && ( - - - - - - )} - {/* LinkedIn 图标 */} - {sectionData?.basicGlobal?.global_linkedin?.trim().length > 0 && ( - - - - - - )} - {/* YouTube 图标 */} - {sectionData?.basicGlobal?.global_youtube?.trim().length > 0 && ( - - - - - - )} - {/* WhatsApp 图标 */} - {sectionData?.basicGlobal?.global_whatsapp?.trim().length > 0 && ( - - - - - - )} - {/* WeChat 组件 */} - {sectionData?.basicGlobal?.global_wechat?.trim().length > 0 && ( - - )} -
-
-
-
- ); -} diff --git a/web/src/components/index/sections/template03/carousel.jsx b/web/src/components/index/sections/template03/carousel.jsx deleted file mode 100644 index 033fbdc9..00000000 --- a/web/src/components/index/sections/template03/carousel.jsx +++ /dev/null @@ -1,107 +0,0 @@ -'use client' -import React from "react"; -import Image from "next/image"; -import Link from "next/link"; -import {Swiper, SwiperSlide} from "swiper/react"; -import "swiper/css"; -import "swiper/css/navigation"; -import "swiper/css/pagination"; -import "swiper/css/effect-fade"; -import {Navigation, Autoplay, Pagination, EffectFade} from "swiper/modules"; - - -const Carousel = ({bannerData}) => { - - const coverArr = bannerData ? bannerData.split('#') : []; - - const colorValue = "bg-[#333] hover:bg-[#555] text-white hover:text-white backdrop-blur-sm"; - return ( -
- - {coverArr.map((image, index) => ( - - - Hero Section - - - ))} - - - {/* 自定义导航按钮 */} - { - coverArr.length > 1 ? ( - <> - - - - - ) : null - } - -
- ); -}; - -export default Carousel; \ No newline at end of file diff --git a/web/src/components/index/sections/template03/ftArea.jsx b/web/src/components/index/sections/template03/ftArea.jsx deleted file mode 100644 index 7f60df6b..00000000 --- a/web/src/components/index/sections/template03/ftArea.jsx +++ /dev/null @@ -1,46 +0,0 @@ -import {TruckIcon, HeadphonesIcon, ShieldCheckIcon, RefreshCwIcon} from 'lucide-react'; -import lang from "@/locales"; - -export default function FtArea() { - // 数据变量 - const facilityItems = [ - { - icon: , - title: lang.FtOneTitle, - description: lang.FtOneTip - }, - { - icon: , - title: lang.FtTwoTitle, - description: lang.FtTwoTip - }, - { - icon: , - title: lang.FtThreeTitle, - description: lang.FtThreeTip - }, - { - icon: , - title: lang.FtFourTitle, - description: lang.FtFourTip - } - ]; - - return ( -
-
-
- {facilityItems.map((item, index) => ( -
-
- {item.icon} -
-

{item.title}

-

{item.description}

-
- ))} -
-
-
- ); -} \ No newline at end of file diff --git a/web/src/components/index/sections/template03/navBar.jsx b/web/src/components/index/sections/template03/navBar.jsx deleted file mode 100644 index 570b1791..00000000 --- a/web/src/components/index/sections/template03/navBar.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import Link from "next/link"; -import TopBar from "@/components/index/sections/template03/topbar"; -import NavBarClient from "@/components/index/sections/template03/navBarClient"; - - -export default function NavBar({sectionData}) { - return ( -
- {/* 引入封装的顶部信息栏组件 */} - - - {/* 主导航栏 */} -
- -
-
- ) -} \ No newline at end of file diff --git a/web/src/components/index/sections/template03/productList.jsx b/web/src/components/index/sections/template03/productList.jsx deleted file mode 100644 index 9da2f4cd..00000000 --- a/web/src/components/index/sections/template03/productList.jsx +++ /dev/null @@ -1,200 +0,0 @@ -import Link from 'next/link'; -import Image from 'next/image'; -import Pagination from '@/components/index/sections/pagination'; -import SearchBar from "@/components/index/sections/searchBar"; -import SearchResultBar from "@/components/index/sections/SearchResultBar"; -import CategoryItem from "@/components/index/sections/categoryItem"; -import lang from "@/locales"; - - -// 模拟产品数据 -const products = [ - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, - { - id: 1, - name: 'Anchor Bracelet', - category: 'Accessories', - price: 150.00, - image: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - }, -]; - -// 分类组件 -export default function ProductList({ categoryId, pageNumber, total, categoryData, productData, featuredData, searchQuery }) { - - // 构建基础链接前缀,不包含页码部分 - let linkPrefix = '/product'; - - // 添加分类部分,如果存在 - if (categoryId) { - linkPrefix += `/category/${categoryId}`; - } - - // 添加页码路径前缀 - linkPrefix += '/page'; - - // 如果有搜索查询,我们需要在每个分页链接中保持它 - const searchSuffix = searchQuery ? `?s=${encodeURIComponent(searchQuery)}` : ''; - - let mCategoryData = categoryData; - mCategoryData.unshift({ - id: -1, - title: lang.AllProducts - }) - - return ( -
-
-
- {/* 左侧边栏 */} -
- {/* 搜索框 */} - - - {/* 分类 - 使用折叠面板 */} -
-

{lang.Categories}

-
- {mCategoryData.map((category) => ( - - ))} -
-
- - {/* 热门产品 - 在md尺寸以下隐藏 */} -
-

{lang.BestProducts}

-
    - {featuredData.map((product) => { - // 提取第一张图片 - const cover = product.cover ? product.cover.split('#')[0] : ''; - return ( -
  • - -
    -
    - {product.title} -
    -
    -
    -

    {product.title}

    -
    - -
  • - )})} -
-
-
- - {/* 右侧产品列表 */} -
- - {/* 搜索结果提示 */} - - - {/* 产品网格 */} -
- {productData.map((product) => { - - // 提取第一张图片 - const cover = product.cover ? product.cover.split('#')[0] : ''; - return ( -
-
- -
- {product.title} -
-

{product.title}

- -
-
- )})} -
- - {/* 分页 */} -
- -
-
-
-
-
- ); -} \ No newline at end of file diff --git a/web/src/components/index/sections/template03/topbar.jsx b/web/src/components/index/sections/template03/topbar.jsx deleted file mode 100644 index 55f1fe3c..00000000 --- a/web/src/components/index/sections/template03/topbar.jsx +++ /dev/null @@ -1,142 +0,0 @@ -'use client' -import Link from "next/link"; -import Wechat from "@/components/index/sections/wechat"; -import { useEffect, useState } from "react"; - -export default function TopBar({ sectionData }) { - const [showTopBar, setShowTopBar] = useState(true); - - useEffect(() => { - window.scrollTo(0, 0); - - let lastScrollY = 0; - - const handleScroll = () => { - const currentScrollY = window.scrollY; - - // 当页面滚动到顶部时显示顶部信息栏 - if (currentScrollY === 0) { - setShowTopBar(true); - } - // 当向下滚动时隐藏顶部信息栏 - else if (currentScrollY > lastScrollY) { - setShowTopBar(false); - } - - lastScrollY = currentScrollY; - }; - - window.addEventListener('scroll', handleScroll); - - return () => { - window.removeEventListener('scroll', handleScroll); - }; - }, []); - - return ( -
-
- - -
-
- {sectionData?.basicGlobal?.global_facebook?.trim().length > 0 && ( - - - - - - )} - {sectionData?.basicGlobal?.global_twitter?.trim().length > 0 && ( - - - - - - )} - {sectionData?.basicGlobal?.global_instagram?.trim().length > 0 && ( - - - - - - )} - {/* LinkedIn 图标 */} - {sectionData?.basicGlobal?.global_linkedin?.trim().length > 0 && ( - - - - - - )} - {/* YouTube 图标 */} - {sectionData?.basicGlobal?.global_youtube?.trim().length > 0 && ( - - - - - - )} - {/* WhatsApp 图标 */} - {sectionData?.basicGlobal?.global_whatsapp?.trim().length > 0 && ( - - - - - - )} - {/* WeChat 组件 */} - {sectionData?.basicGlobal?.global_wechat?.trim().length > 0 && ( - - )} - -
-
- -
-
- ); -} diff --git a/web/src/components/index/sections/template04/aboutUs.jsx b/web/src/components/index/sections/template04/aboutUs.jsx deleted file mode 100644 index b3410920..00000000 --- a/web/src/components/index/sections/template04/aboutUs.jsx +++ /dev/null @@ -1,91 +0,0 @@ -'use client' -import Image from 'next/image'; -import Link from 'next/link'; -import lang from "@/locales"; -import React from "react"; - -export default function AboutUs({aboutData, companyName, statsData}) { - // 卡片基本样式 - const baseCardStyle = "p-6 rounded-0"; - - // 卡片样式 - const cardStyles = [ - `${baseCardStyle} bg-gradient-to-br from-blue-50 to-indigo-100`, - `${baseCardStyle} bg-gradient-to-br from-green-50 to-teal-100`, - `${baseCardStyle} bg-gradient-to-br from-amber-50 to-yellow-100`, - `${baseCardStyle} bg-gradient-to-br from-rose-50 to-pink-100` - ]; - - return ( -
-
- {/* 标题区域 */} -
-

- {lang.AboutUs} -

-
- -
-
-
-
- -
- {/* 左侧 */} -
-

- {companyName} -

-

-

- - - {lang.ViewMore} - - - - -
- - {/* 右侧 */} -
- {companyName} -
-
-
- - {/* 统计指标 */} -
-
-
{statsData.param_one_value}
-
{statsData.param_one_name}
-
-
-
{statsData.param_two_value}
-
{statsData.param_two_name}
-
-
-
{statsData.param_three_value}
-
{statsData.param_three_name}
-
-
-
{statsData.param_four_value}
-
{statsData.param_four_name}
-
-
-
-
- ) -} \ No newline at end of file diff --git a/web/src/components/index/sections/template04/advantages.jsx b/web/src/components/index/sections/template04/advantages.jsx deleted file mode 100644 index 86d4ba3c..00000000 --- a/web/src/components/index/sections/template04/advantages.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from "react"; -import Image from "next/image"; -import lang from "@/locales"; - - -export default function Advantages({advantageData}) { - return ( -
-
- - - -
- - -
-
- {/*标题区域*/} -
- // - {lang.OurAdvantages} - -
-

- {lang.OurAdvantagesTip} -

-
- - {/* 卡片列表 */} -
- {advantageData.map((advantage) => ( -
-
- {advantage.advantage_title} -
-

{advantage.advantage_title}

-

{advantage.advantage_description}

-
- ))} -
-
-
- ) -} \ No newline at end of file diff --git a/web/src/components/index/sections/template04/caseDetail.jsx b/web/src/components/index/sections/template04/caseDetail.jsx deleted file mode 100644 index 7840d6fc..00000000 --- a/web/src/components/index/sections/template04/caseDetail.jsx +++ /dev/null @@ -1,116 +0,0 @@ -import Link from 'next/link'; -import Image from 'next/image'; -import {formatDate} from "@/utils/tools"; -import lang from "@/locales"; -import HtmlPanel from "@/components/index/sections/htmlPanel"; - -export default function CaseDetail({ detailData, categoryData, recommendData }){ - return ( -
-
- {/* 面包屑导航 */} -
-
- {lang.Home} - / - {lang.Case} - / - {detailData.title} -
-
- -
- {/* 左侧案例详情 */} -
-
-

{detailData.title}

- -
- {lang.Client}: {detailData.client} - - {formatDate(detailData.create_time.slice(0, 10))} -
- -
- {detailData.title} -
- - -
-
- - {/* 右侧产品分类和推荐产品 */} -
-
- {/* 产品分类 */} -
-

{lang.ProductCategories}

-
    - {categoryData.map((category) => ( -
  • - - - - - {category.title} - -
  • - ))} -
-
- - {/* 推荐产品 */} - { - recommendData?.length > 0 && ( -
-

{lang.RecommendedProducts}

-
- {recommendData.map((product) => { - // 提取第一张图片 - const cover = product.cover ? product.cover.split('#')[0] : ''; - return ( -
- {product.title} -
-

- {product.title} -

-

- {product.category_title} -

- - ) - })} -
-
- ) - } -
-
-
-
-
- ); -} \ No newline at end of file diff --git a/web/src/components/index/sections/template04/caseList.jsx b/web/src/components/index/sections/template04/caseList.jsx deleted file mode 100644 index 6f1596d1..00000000 --- a/web/src/components/index/sections/template04/caseList.jsx +++ /dev/null @@ -1,125 +0,0 @@ -import Pagination from "@/components/index/sections/pagination"; -import Link from 'next/link'; -import Image from 'next/image'; -import lang from "@/locales"; - -// 示例案例数据 -const cases = [ - { - id: 1, - title: 'Global E-commerce Platform Redesign', - client: 'TechRetail Inc.', - category: 'E-commerce', - description: 'Complete redesign of a global e-commerce platform resulting in 45% increase in conversions and 30% reduction in cart abandonment.', - imageUrl: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - href: '/case-studies/global-ecommerce-redesign', - date: 'June 2023' - }, - { - id: 2, - title: 'Enterprise IoT Dashboard System', - client: 'IndusTech Solutions', - category: 'Industrial IoT', - description: 'Development of a comprehensive IoT dashboard monitoring over 5,000 connected devices across 12 manufacturing plants.', - imageUrl: 'https://images.unsplash.com/photo-1581092918056-0c4c3acd3789?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - href: '/case-studies/enterprise-iot-dashboard', - date: 'April 2023' - }, - { - id: 3, - title: 'Fintech Mobile Application', - client: 'GlobalPay Financial', - category: 'Finance', - description: 'Award-winning mobile banking application with state-of-the-art security features and intuitive UX, serving over 2 million users.', - imageUrl: 'https://images.unsplash.com/photo-1563986768494-4dee2763ff3f?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - href: '/case-studies/fintech-mobile-app', - date: 'February 2023' - }, - { - id: 4, - title: 'Healthcare Patient Management System', - client: 'MediCare Solutions', - category: 'Healthcare', - description: 'Integrated patient management system connecting 35 hospitals and 120 clinics, streamlining care for over 500,000 patients.', - imageUrl: 'https://images.unsplash.com/photo-1576091160550-2173dba999ef?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - href: '/case-studies/healthcare-patient-management', - date: 'December 2022' - }, - { - id: 5, - title: 'Smart City Infrastructure Project', - client: 'MetroTech Urban Development', - category: 'Smart City', - description: 'Comprehensive smart city solution including traffic management, public safety, and environmental monitoring systems.', - imageUrl: 'https://images.unsplash.com/photo-1519501025264-65ba15a82390?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1742&q=80', - href: '/case-studies/smart-city-infrastructure', - date: 'November 2022' - }, - { - id: 6, - title: 'Educational Platform for Remote Learning', - client: 'EduFuture Academy', - category: 'Education', - description: 'Interactive learning platform supporting over 50,000 students with real-time collaboration and AI-driven personalized learning paths.', - imageUrl: 'https://images.unsplash.com/photo-1610484826967-09c5720778c7?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', - href: '/case-studies/educational-platform', - date: 'October 2022' - } -]; - -export default function CaseList({pageNumber=1, total, caseData}) { - return ( -
-
- {/* 案例列表 */} -
- {caseData.map((caseItem) => ( -
- {/* 整个卡片可点击 */} - - View case study - - - {/* 图片部分 */} -
- {caseItem.title} -
- - {/* 内容部分 */} -
-
-

- {caseItem.title} -

-

- {lang.Client}: {caseItem.client} -

-
-
- {caseItem.date} - - {lang.ViewCaseStudy} - - - - -
-
-
- ))} -
- - {/* 分页 */} -
- -
-
-
- ); -} \ No newline at end of file diff --git a/web/src/components/index/sections/template04/certification.jsx b/web/src/components/index/sections/template04/certification.jsx deleted file mode 100644 index 5720578a..00000000 --- a/web/src/components/index/sections/template04/certification.jsx +++ /dev/null @@ -1,202 +0,0 @@ -'use client' -import React, { useState, useEffect } from "react"; -import Image from "next/image"; -import lang from "@/locales"; - -export default function Certification({ certificationImageData }) { - const [modalOpen, setModalOpen] = useState(false); - const [currentImage, setCurrentImage] = useState(""); - const [currentIndex, setCurrentIndex] = useState(0); - const [isVisible, setIsVisible] = useState(false); - - // 处理图片数据 - let dataList = certificationImageData?.split("#").filter(item => item.trim()) || []; - - // 打开图片模态框 - const openModal = (image, index) => { - setCurrentImage(image); - setCurrentIndex(index); - setModalOpen(true); - // 使用 requestAnimationFrame 确保状态更新和动画同步 - requestAnimationFrame(() => { - setIsVisible(true); - }); - document.body.style.overflow = "hidden"; - }; - - // 关闭图片模态框 - const closeModal = () => { - setIsVisible(false); - // 等待动画完成后再关闭模态框 - setTimeout(() => { - setModalOpen(false); - document.body.style.overflow = "auto"; - }, 300); - }; - - // 处理ESC键关闭模态框 - const handleKeyDown = (e) => { - if (e.key === "Escape") { - closeModal(); - } - }; - - // 添加键盘事件监听 - useEffect(() => { - if (modalOpen) { - window.addEventListener('keydown', handleKeyDown); - } - return () => { - window.removeEventListener('keydown', handleKeyDown); - }; - }, [modalOpen]); - - // 切换到下一张图片 - const nextImage = (e) => { - if (e) e.stopPropagation(); - const newIndex = (currentIndex + 1) % dataList.length; - setCurrentIndex(newIndex); - setCurrentImage(`${process.env.NEXT_PUBLIC_BASE_URL}/upload/img/${dataList[newIndex]}`); - }; - - // 切换到上一张图片 - const prevImage = (e) => { - if (e) e.stopPropagation(); - const newIndex = (currentIndex - 1 + dataList.length) % dataList.length; - setCurrentIndex(newIndex); - setCurrentImage(`${process.env.NEXT_PUBLIC_BASE_URL}/upload/img/${dataList[newIndex]}`); - }; - - return ( -
-
-
- {/*标题区域*/} -
-
-
- - {lang.Certification} -
-
-
-
- - {/* 图片网格 */} -
- {dataList.map((item, index) => ( -
openModal(`${process.env.NEXT_PUBLIC_BASE_URL}/upload/img/${item}`, index)} - style={{ - opacity: 0, - transform: 'translateY(20px)', - animation: `fadeInUp 0.3s ease forwards ${index * 0.1}s` - }} - > - {/* 外层灰色边框 */} -
-
-
- {`Certification -
-
-
- ))} -
-
- - {/* 图片查看模态框 */} - {modalOpen && ( -
- {/* 关闭按钮 */} - - - {/* 上一张按钮 */} - - - {/* 下一张按钮 */} - - - {/* 图片容器 */} -
e.stopPropagation()} - > - Enlarged factory image -
-
- )} - - {/* 添加自定义动画样式 */} - -
- ); -} \ No newline at end of file diff --git a/web/src/components/index/sections/template04/companyNews.jsx b/web/src/components/index/sections/template04/companyNews.jsx deleted file mode 100644 index 9bc58c66..00000000 --- a/web/src/components/index/sections/template04/companyNews.jsx +++ /dev/null @@ -1,68 +0,0 @@ -import Link from 'next/link'; -import Image from 'next/image'; -import {formatDate} from "@/utils/tools"; -import lang from "@/locales"; - - -export default function CompanyNews({newsData}) { - return ( -
- {/* 标题区域 */} -
-

- {lang.CompanyNews} -

- - {lang.ViewMore} - - - - -
- -
-
-
-
- -
- {newsData.map((post) => ( - - {/* 图片容器 */} -
- {post.title} -
- - {/* 内容区域 */} -
-

- {post.title} -

- - {/* 日期显示 */} -
- {formatDate(post.create_time, 'DD-MM-YY')} -
-
- - ))} -
-
-
- ) -} diff --git a/web/src/components/index/sections/template04/contactUs.jsx b/web/src/components/index/sections/template04/contactUs.jsx deleted file mode 100644 index b0c73882..00000000 --- a/web/src/components/index/sections/template04/contactUs.jsx +++ /dev/null @@ -1,63 +0,0 @@ -import lang from "@/locales"; -import Image from "next/image"; -import React from "react"; - -export default function ContactUs({contactData}) { - return ( -
- {/* 背景图片 */} -
- contact us - {/* 深色遮罩 */} -
- - {/*白色遮罩*/} -
- - - -
-
- - {/* 内容区域 */} -
-

{lang.ContactUs}

-

- {lang.ContactUsTip1} -

-

- {lang.ContactUsTip2} -

- - {lang.GetAQuote} - -
-
- ); -} \ No newline at end of file diff --git a/web/src/components/index/sections/template04/customersSay.jsx b/web/src/components/index/sections/template04/customersSay.jsx deleted file mode 100644 index 261cf6e7..00000000 --- a/web/src/components/index/sections/template04/customersSay.jsx +++ /dev/null @@ -1,63 +0,0 @@ -import lang from "@/locales"; -import Image from "next/image"; -import Link from "next/link"; - -export default function CustomersSay({commentData}){ - - return( -
-
- {/* 标题区域 */} -
-

- {lang.OurCustomersSay} -

- -
- -
-
-
-
- -
- {commentData.map((testimonial) => ( -
- {/* 引号 */} -
- -
- - {/* 评价内容 */} -

- {testimonial.comment_content} -

- - {/* 客户信息 */} -
-
- {testimonial.comment_name} -
-
-

- {testimonial.comment_name} -

-

- From {testimonial.comment_location || "Unknown"} -

-
-
-
- ))} -
-
-
-
- ) -} \ No newline at end of file diff --git a/web/src/components/index/sections/template04/downloadList.jsx b/web/src/components/index/sections/template04/downloadList.jsx deleted file mode 100644 index b96985ae..00000000 --- a/web/src/components/index/sections/template04/downloadList.jsx +++ /dev/null @@ -1,131 +0,0 @@ -import lang from "@/locales"; - - -export default function DownloadList({downloadData}) { - // 根据文件扩展名确定图标和颜色 - const getFileIconByType = (filename) => { - const extension = filename ? filename.split('.').pop().toLowerCase() : ''; - - // 图标和颜色配置 - const fileTypes = { - 'pdf': { - color: 'text-red-500', - bgColor: 'bg-red-50', - icon: ( - - - - - - - ) - }, - 'xlsx': { - color: 'text-green-600', - bgColor: 'bg-green-50', - icon: ( - - - - - - - - - - - - - ) - }, - 'doc': { - color: 'text-blue-600', - bgColor: 'bg-blue-50', - icon: ( - - - - - - - - - - ) - }, - 'default': { - color: 'text-gray-500', - bgColor: 'bg-gray-50', - icon: ( - - - - ) - } - }; - - // 如果找不到特定扩展名配置,则使用默认 - return fileTypes[extension] || fileTypes['default']; - }; - - return ( -
-
- - - {/* 卡片列表 */} -
-
    - {downloadData.map((file) => { - const fileIcon = getFileIconByType(file.title); - - return ( -
  • -
    - {/* File icon */} -
    - {fileIcon.icon} -
    - - {/* File information */} -
    -

    {file.title}

    -

    {file.summary}

    -
    - - {/* Download link */} - -
    -
  • - ) - })} -
-
-
-
- ); -} \ No newline at end of file diff --git a/web/src/components/index/sections/template04/featuredProducts.jsx b/web/src/components/index/sections/template04/featuredProducts.jsx deleted file mode 100644 index 61fb66da..00000000 --- a/web/src/components/index/sections/template04/featuredProducts.jsx +++ /dev/null @@ -1,79 +0,0 @@ -import Link from 'next/link'; -import Image from 'next/image'; -import lang from "@/locales"; -import React from "react"; - -// 产品卡片组件 -function ProductCard({ product }) { - // 提取第一张图片 - const cover = product.cover ? product.cover.split('#')[0] : ''; - return ( -
-
- -
- {product.title} -
- -
- -

- - {product.title} - -

-
- ); -} - -export default function FeaturedProducts({featuredData}) { - return ( -
-
- - {/* 标题区域 */} -
-

- {lang.FeaturedProducts} -

- - {lang.ViewMore} - - - - -
- -
-
-
-
- -
- {featuredData.map((product) => ( - - ))} -
-
-
-
- ); -} \ No newline at end of file diff --git a/web/src/components/index/sections/template04/footer.jsx b/web/src/components/index/sections/template04/footer.jsx deleted file mode 100644 index 72122c5b..00000000 --- a/web/src/components/index/sections/template04/footer.jsx +++ /dev/null @@ -1,85 +0,0 @@ -import Link from 'next/link'; -import lang from "@/locales" -import Image from 'next/image'; -import CopyrightBar from "@/components/index/sections/copyrightBar"; - -export default function Footer({sectionData}) { - return ( -
-
- -
-
- - {/*logo区域*/} -
- - Logo - logo - -
- {/* 菜单区域 */} -
-

Menu

-
    - {sectionData.navData.map((item) => ( -
  • - - {lang[item.name] || item.name} - -
  • - ))} -
-
- - {/* 社交媒体区域 */} -
-

Follow Me

- -
- - {/* 办公地址区域 */} -
-

Office Address

-
-

{sectionData.contactData.global_address}

-

- - {sectionData.contactData.global_phone} - -

-

- - {sectionData.contactData.global_email} - -

-
-
-
-
- {/* 版权信息 */} -
- -
-
- ) -} diff --git a/web/src/components/index/sections/template04/ftArea.jsx b/web/src/components/index/sections/template04/ftArea.jsx deleted file mode 100644 index 7f60df6b..00000000 --- a/web/src/components/index/sections/template04/ftArea.jsx +++ /dev/null @@ -1,46 +0,0 @@ -import {TruckIcon, HeadphonesIcon, ShieldCheckIcon, RefreshCwIcon} from 'lucide-react'; -import lang from "@/locales"; - -export default function FtArea() { - // 数据变量 - const facilityItems = [ - { - icon: , - title: lang.FtOneTitle, - description: lang.FtOneTip - }, - { - icon: , - title: lang.FtTwoTitle, - description: lang.FtTwoTip - }, - { - icon: , - title: lang.FtThreeTitle, - description: lang.FtThreeTip - }, - { - icon: , - title: lang.FtFourTitle, - description: lang.FtFourTip - } - ]; - - return ( -
-
-
- {facilityItems.map((item, index) => ( -
-
- {item.icon} -
-

{item.title}

-

{item.description}

-
- ))} -
-
-
- ); -} \ No newline at end of file diff --git a/web/src/components/index/sections/template04/getInTouch.jsx b/web/src/components/index/sections/template04/getInTouch.jsx deleted file mode 100644 index dcfbb0a2..00000000 --- a/web/src/components/index/sections/template04/getInTouch.jsx +++ /dev/null @@ -1,443 +0,0 @@ -'use client' -import React, {useState, useEffect, useRef} from 'react'; -import api from "@/utils/axios"; -import lang from '@/locales'; -import Wechat from "@/components/index/sections/wechat"; - -export default function GetInTouch({contactData}) { - const [formData, setFormData] = useState({ - name: '', - phone: '', - email: '', - message: '' - }); - const [loading, setLoading] = useState(false); - const [alertState, setAlertState] = useState({ - open: false, - title: '', - description: '', - variant: 'default' // 'default' or 'destructive' - }); - const alertRef = useRef(null); - - // 处理点击外部关闭 - useEffect(() => { - const handleClickOutside = (event) => { - if (alertRef.current && !alertRef.current.contains(event.target)) { - closeAlert(); - } - }; - - if (alertState.open) { - document.addEventListener('mousedown', handleClickOutside); - } - - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, [alertState.open]); - - // 处理 ESC 键关闭 - useEffect(() => { - const handleEsc = (event) => { - if (event.key === 'Escape') { - closeAlert(); - } - }; - - if (alertState.open) { - document.addEventListener('keydown', handleEsc); - } - - return () => { - document.removeEventListener('keydown', handleEsc); - }; - }, [alertState.open]); - - const handleChange = (e) => { - const {name, value} = e.target; - setFormData(prevState => ({ - ...prevState, - [name]: value - })); - }; - - const showAlert = (title, description, variant = 'default') => { - setAlertState({ - open: true, - title, - description, - variant - }); - }; - - const closeAlert = () => { - setAlertState(prev => ({ ...prev, open: false })); - }; - - const handleSubmit = async (e) => { - e.preventDefault(); - - // 基本验证 - if (!formData.name || !formData.email || !formData.message) { - showAlert('Validation Failed', 'Please fill in all required fields (Name, Email, and Message)', 'destructive'); - return; - } - - // 邮箱格式验证 - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - if (!emailRegex.test(formData.email)) { - showAlert('Validation Failed', 'Please enter a valid email address', 'destructive'); - return; - } - - try { - setLoading(true); - const submitData = new FormData(); - submitData.append('name', formData.name); - submitData.append('email', formData.email); - submitData.append('tel', formData.phone); - submitData.append('message', formData.message); - - const { code, msg } = await api.post('/myapp/index/inquiry/create', submitData); - - if (code === 0) { - showAlert('Success', 'We will contact you as soon as possible', 'default'); - // 重置表单 - setFormData({ - name: '', - phone: '', - email: '', - message: '' - }); - } else { - showAlert('Failed', msg || 'Please try again later', 'destructive'); - } - } catch (error) { - console.error('Error submitting form:', error); - showAlert('Submission Failed', 'Please check your network connection', 'destructive'); - } finally { - setLoading(false); - } - }; - - return ( -
- {/* 自定义 Alert Dialog */} - {alertState.open && ( -
-
-
-

- {alertState.title} -

-

- {alertState.description} -

-
- -
-
-
-
- )} - -
- {/*标题区域*/} -
-
- // - {lang.GetInTouch} -
-

- {lang.GetInTouchTip} -

-
- - {/* 卡片区域 */} -
-
- - {/* 左侧联系表单 */} -
-
- -

- - - - - - {lang.SendUsAMessage} -

- -
-
- -
-
-
- -
-
- -
-
-
- -
- -
- -
-
-
- - {/* 右侧联系信息 */} -
-
-
- -

- - - - - - {lang.ContactInformation} -

- -
- {/* 邮箱 */} -
-
- - - -
-
-

{lang.Email}

- {contactData.global_email} -
-
- - {/* 电话 */} -
-
- - - -
-
-

- {lang.PhoneNumber} -

- {contactData.global_phone} -
-
- - {/* 地址 */} -
-
- - - - -
-
-

- {lang.Address} -

-

{contactData.global_address}

-
-
-
- - {/* 社交媒体 */} -
-

{lang.FollowUs}

-
- {/* Facebook */} - {contactData.global_facebook?.length > 0 && ( - - - - - - )} - - {/* Twitter */} - {contactData.global_twitter?.length > 0 && ( - - - - - - )} - - {/* Instagram */} - {contactData.global_instagram?.length > 0 && ( - - - - - - )} - - {/* LinkedIn */} - {contactData.global_linkedin?.length > 0 && ( - - - - - - )} - - {/* YouTube */} - {contactData.global_youtube?.length > 0 && ( - - - - - - )} - - {/* WhatsApp */} - {contactData.global_whatsapp?.length > 0 && ( - - - - - - )} - - {/* WeChat */} - {contactData.global_wechat?.length > 0 && ( - - )} -
-
-
-
-
-
-
-
- ); -} \ No newline at end of file diff --git a/web/src/components/index/sections/template04/navBarClient.jsx b/web/src/components/index/sections/template04/navBarClient.jsx deleted file mode 100644 index 365dabfd..00000000 --- a/web/src/components/index/sections/template04/navBarClient.jsx +++ /dev/null @@ -1,392 +0,0 @@ -'use client' -import {useState, useEffect, useRef} from 'react' -import { - Bars3Icon, - XMarkIcon, -} from '@heroicons/react/24/outline' -import {ChevronDownIcon} from '@heroicons/react/20/solid' -import {usePathname} from "next/navigation"; -import Link from "next/link"; -import lang from "@/locales"; -import Wechat from "@/components/index/sections/wechat"; - -// 移动端菜单项组件 -function MobileMenuItem({item, level = 0, setMobileMenuOpen}) { - const [isOpen, setIsOpen] = useState(false); - const paddingLeft = `${1 + level * 1.5}rem`; - const fontSize = `${Math.max(1 - level * 0.1, 0.8)}rem`; - const fontWeight = level === 0 ? 'font-semibold' : 'font-medium'; - const baseClasses = `block rounded-none py-2 text-gray-900 hover:bg-gray-50 ${fontWeight}`; - - const scrollToTop = () => { - window.scrollTo({ - top: 0, - behavior: 'smooth' - }); - }; - - const handleLinkClick = () => { - setMobileMenuOpen(false); - scrollToTop(); - }; - - if (item.type === 'link') { - return ( - - {lang[item.name] || item.name} - - ); - } - - if (item.type === 'dropdown') { - return ( -
- -
- {item.subItems.map((subItem) => ( - - ))} -
-
- ); - } - - return null; -} - -// 桌面端导航栏组件 -export function DesktopNav({navigationItems, current}) { - const [activeDropdown, setActiveDropdown] = useState(null); - const [activeSubMenu, setActiveSubMenu] = useState(null); - - const scrollToTop = () => { - window.scrollTo({ - top: 0, - behavior: 'smooth' - }); - }; - - const handleMenuItemClick = () => { - setActiveDropdown(null); - setActiveSubMenu(null); - scrollToTop(); - }; - - return ( -
- {navigationItems.map((item, index) => ( - item.type === 'link' ? ( - 0 ? 'ml-10' : ''}`} - onClick={scrollToTop} - > - {lang[item.name]} - - ) : ( -
0 ? 'ml-10' : ''}`} - onMouseEnter={() => setActiveDropdown(item.name)} - onMouseLeave={() => { - setActiveDropdown(null) - setActiveSubMenu(null) - }} - > - = 0 ? 'text-mainColorNormal' : 'text-gray-900'} hover:text-mainColorNormal transition-colors duration-200 cursor-pointer h-full flex items-center gap-x-0.5 text-base/6 font-semibold`} - onClick={handleMenuItemClick} - > - {lang[item.name]} -
- ) - ))} -
- ); -} - -// 移动端导航菜单组件 -export function MobileNav({sectionData, mobileMenuOpen, setMobileMenuOpen}) { - const menuRef = useRef(null); - - // 处理点击外部关闭菜单 - useEffect(() => { - const handleClickOutside = (event) => { - if (menuRef.current && !menuRef.current.contains(event.target)) { - setMobileMenuOpen(false); - } - }; - - if (mobileMenuOpen) { - document.addEventListener('mousedown', handleClickOutside); - } - - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, [mobileMenuOpen, setMobileMenuOpen]); - - // 处理 ESC 键关闭菜单 - useEffect(() => { - const handleEsc = (event) => { - if (event.key === 'Escape') { - setMobileMenuOpen(false); - } - }; - - if (mobileMenuOpen) { - document.addEventListener('keydown', handleEsc); - } - - return () => { - document.removeEventListener('keydown', handleEsc); - }; - }, [mobileMenuOpen, setMobileMenuOpen]); - - if (!mobileMenuOpen) return null; - - return ( -
- {/* 背景遮罩 */} -
setMobileMenuOpen(false)} - /> - - {/* 菜单面板 */} -
-
- - Logo - Logo - - -
-
-
-
- {sectionData && sectionData.navigationItems.map((item) => ( - - ))} -
-
-
- {/* Facebook */} - {sectionData?.basicGlobal?.global_facebook?.trim().length > 0 && ( - - - - - - )} - {/* Twitter */} - {sectionData?.basicGlobal?.global_twitter?.trim().length > 0 && ( - - - - - - )} - {/* Instagram */} - {sectionData?.basicGlobal?.global_instagram?.trim().length > 0 && ( - - - - - - )} - {/* LinkedIn */} - {sectionData?.basicGlobal?.global_linkedin?.trim().length > 0 && ( - - - - - - )} - {/* YouTube */} - {sectionData?.basicGlobal?.global_youtube?.trim().length > 0 && ( - - - - - - )} - {/* WhatsApp */} - {sectionData?.basicGlobal?.global_whatsapp?.trim().length > 0 && ( - - - - - - )} - -
-
-
-
-
-
- ); -} - -// 导航栏客户端包装器组件 -export default function NavBarClient({sectionData}) { - const [mobileMenuOpen, setMobileMenuOpen] = useState(false); - const pathname = usePathname(); - const [current, setCurrent] = useState(pathname); - - useEffect(() => { - setCurrent(pathname); - }, [pathname]); - - return ( - <> - {/* 桌面端导航 */} - - - {/* 移动端菜单按钮 */} -
- -
- - {/* 移动端导航菜单 */} - - - ); -} diff --git a/web/src/components/index/sections/template04/newsDetail.jsx b/web/src/components/index/sections/template04/newsDetail.jsx deleted file mode 100644 index 49166725..00000000 --- a/web/src/components/index/sections/template04/newsDetail.jsx +++ /dev/null @@ -1,239 +0,0 @@ -import Link from 'next/link'; -import Image from 'next/image'; -import {Suspense} from 'react'; -import {formatDate} from "@/utils/tools"; -import lang from "@/locales"; -import HtmlPanel from "@/components/index/sections/htmlPanel"; - -// 骨架屏组件 -const ImageSkeleton = ({className}) => ( -
-); - -export default function NewsDetail({ - detailData, - categoryData, - recommendData, - shareLinks -}) { - if (!detailData) { - return ( -
-
-

News Article Not Found

-

The news article you are looking for does not exist or has been - removed.

- - Return to News List - -
-
- ); - } - - return ( -
-
- {/* 面包屑导航 */} -
-
- {lang.Home} - / - {lang.News} - / - {detailData.title} -
-
- -
- {/* 左侧新闻详情 */} -
-
- -
- {/* 标题信息和日期 */} -

{detailData.title}

- -
- - - - - {formatDate(detailData.create_time.slice(0, 10))} - - - - - - - {detailData.source} - -
- - {/* 主图 */} -
- }> - {detailData.title} - -
- - {/* 文章内容 */} - - - {/* 分享按钮 */} -
- Share: -
- - - - - - - - - - - - - - - -
-
-
-
-
- - {/* 右侧产品推荐和分类 */} -
- {/* 产品分类 */} -
-
-

- - - - {lang.ProductCategories} -

-
-
-
    - {categoryData.map(category => ( -
  • - -
    - {category.title} - -
  • - ))} -
-
-
- - {/* 推荐产品 */} - { - recommendData?.length > 0 && ( -
-
-

- - - - {lang.RecommendedProducts} -

-
-
-
- {recommendData.map(product => { - // 提取第一张图片 - const cover = product.cover ? product.cover.split('#')[0] : ''; - return ( - -
- }> - {product.title} - -
-
-

{product.title}

-
- - ) - })} -
- - {lang.ViewAllProducts} - - - - -
-
- ) - } -
-
-
-
- ); -} \ No newline at end of file diff --git a/web/src/components/index/sections/template04/newsList.jsx b/web/src/components/index/sections/template04/newsList.jsx deleted file mode 100644 index 58250a87..00000000 --- a/web/src/components/index/sections/template04/newsList.jsx +++ /dev/null @@ -1,214 +0,0 @@ -import Pagination from "@/components/index/sections/pagination"; -import Link from 'next/link'; -import Image from 'next/image'; -import {formatDate} from "@/utils/tools"; - -const posts = [ - { - id: 1, - title: 'Boost your conversion rate', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1496128858413-b36217c2ce36?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3603&q=80', - date: 'Mar 16, 2020', - datetime: '2020-03-16', - category: {title: 'Marketing', href: '#'}, - author: { - name: 'Michael Foster', - role: 'Co-Founder / CTO', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1519244703995-f4e0f30006d5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - }, - { - id: 2, - title: 'How to use search engine optimization to drive traffic to your website and increase visibility in search results', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1547586696-ea22b4d4235d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3603&q=80', - date: 'Apr 20, 2020', - datetime: '2020-04-20', - category: {title: 'SEO', href: '#'}, - author: { - name: 'Lindsay Walton', - role: 'Front-end Developer', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1517841905240-472988babdf9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - }, - { - id: 3, - title: 'Improve your customer experience', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1492724441997-5dc865305da7?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3603&q=80', - date: 'May 8, 2020', - datetime: '2020-05-08', - category: {title: 'Customer Success', href: '#'}, - author: { - name: 'Tom Cook', - role: 'Product Manager', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - }, - { - id: 4, - title: 'The future of e-commerce and retail technology', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1554200876-56c2f25224fa?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3600&q=80', - date: 'Jun 12, 2020', - datetime: '2020-06-12', - category: {title: 'E-commerce', href: '#'}, - author: { - name: 'Sarah Johnson', - role: 'E-commerce Strategist', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - }, - { - id: 5, - title: 'Mobile app development trends for 2023', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1551434678-e076c223a692?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3600&q=80', - date: 'Jul 5, 2020', - datetime: '2020-07-05', - category: {title: 'Mobile Development', href: '#'}, - author: { - name: 'David Wilson', - role: 'Mobile Developer', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - }, - { - id: 6, - title: 'AI and machine learning applications in business', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1558494949-ef010cbdcc31?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3600&q=80', - date: 'Aug 17, 2020', - datetime: '2020-08-17', - category: {title: 'Artificial Intelligence', href: '#'}, - author: { - name: 'Emily Chen', - role: 'AI Researcher', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - }, - { - id: 7, - title: 'Sustainable practices in modern businesses', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1542601906990-b4d3fb778b09?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3600&q=80', - date: 'Sep 3, 2020', - datetime: '2020-09-03', - category: {title: 'Sustainability', href: '#'}, - author: { - name: 'Robert Green', - role: 'Sustainability Consultant', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - }, - { - id: 8, - title: 'Cybersecurity essentials for small businesses', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1550751827-4bd374c3f58b?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3600&q=80', - date: 'Oct 22, 2020', - datetime: '2020-10-22', - category: {title: 'Cybersecurity', href: '#'}, - author: { - name: 'Jennifer Lee', - role: 'Security Specialist', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1487412720507-e7ab37603c6f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - }, - { - id: 9, - title: 'Remote work strategies for distributed teams', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1522202176988-66273c2fd55f?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3600&q=80', - date: 'Nov 10, 2020', - datetime: '2020-11-10', - category: {title: 'Remote Work', href: '#'}, - author: { - name: 'Alex Morgan', - role: 'Remote Work Consultant', - href: '#', - imageUrl: - 'https://images.unsplash.com/photo-1531427186611-ecfd6d936c79?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - } -] - -export default function NewsList({pageNumber=1, total, newsData}) { - return ( -
-
-
- {newsData.map((post, index) => ( -
- - detail - - -
- {post.title -
-
-
-
-

- {post.title} -

-
-
- -
-
-
- ))} -
- -
- -
-
-
- ) -} diff --git a/web/src/components/index/sections/template04/ourCategories.jsx b/web/src/components/index/sections/template04/ourCategories.jsx deleted file mode 100644 index f0b31448..00000000 --- a/web/src/components/index/sections/template04/ourCategories.jsx +++ /dev/null @@ -1,85 +0,0 @@ -'use client' -import Link from 'next/link'; -import Image from 'next/image'; -import lang from "@/locales"; -import React, {useState, useEffect} from "react"; - -// 分类卡片组件 -function CategoryCard({category}) { - return ( - -
- {category.title} -
-

{category.title}

- - ); -} - -export default function OurCategories({categoryData}) { - // 添加客户端初始化状态 - const [isClient, setIsClient] = useState(false); - - // 仅在客户端执行的useEffect - useEffect(() => { - setIsClient(true); - }, []); - - // 使用内联样式来防止初始状态的闪烁 - const initialHiddenStyle = { - opacity: isClient ? 1 : 0, - transition: 'opacity 0.2s ease-in-out', - minHeight: '100px' - }; - - return ( -
-
-
-

- {lang.ProductCategories || 'Product Categories'} -

- - {lang.ViewMore || 'View All'} - - - - -
- -
-
-
-
- -
- {categoryData?.slice(0,4).map((category) => ( -
- -
- ))} -
- -
-
-
- ) -} \ No newline at end of file diff --git a/web/src/components/index/sections/template04/ourFactory.jsx b/web/src/components/index/sections/template04/ourFactory.jsx deleted file mode 100644 index a94aa7e2..00000000 --- a/web/src/components/index/sections/template04/ourFactory.jsx +++ /dev/null @@ -1,200 +0,0 @@ -'use client' -import React, { useState, useEffect } from "react"; -import Image from "next/image"; -import lang from "@/locales"; - -export default function OurFactory({ companyImageData }) { - const [modalOpen, setModalOpen] = useState(false); - const [currentImage, setCurrentImage] = useState(""); - const [currentIndex, setCurrentIndex] = useState(0); - const [isVisible, setIsVisible] = useState(false); - - // 处理图片数据 - let dataList = companyImageData?.split("#").filter(item => item.trim()) || []; - - // 打开图片模态框 - const openModal = (image, index) => { - setCurrentImage(image); - setCurrentIndex(index); - setModalOpen(true); - // 使用 requestAnimationFrame 确保状态更新和动画同步 - requestAnimationFrame(() => { - setIsVisible(true); - }); - document.body.style.overflow = "hidden"; - }; - - // 关闭图片模态框 - const closeModal = () => { - setIsVisible(false); - // 等待动画完成后再关闭模态框 - setTimeout(() => { - setModalOpen(false); - document.body.style.overflow = "auto"; - }, 300); - }; - - // 处理ESC键关闭模态框 - const handleKeyDown = (e) => { - if (e.key === "Escape") { - closeModal(); - } - }; - - // 添加键盘事件监听 - useEffect(() => { - if (modalOpen) { - window.addEventListener('keydown', handleKeyDown); - } - return () => { - window.removeEventListener('keydown', handleKeyDown); - }; - }, [modalOpen]); - - // 切换到下一张图片 - const nextImage = (e) => { - if (e) e.stopPropagation(); - const newIndex = (currentIndex + 1) % dataList.length; - setCurrentIndex(newIndex); - setCurrentImage(`${process.env.NEXT_PUBLIC_BASE_URL}/upload/img/${dataList[newIndex]}`); - }; - - // 切换到上一张图片 - const prevImage = (e) => { - if (e) e.stopPropagation(); - const newIndex = (currentIndex - 1 + dataList.length) % dataList.length; - setCurrentIndex(newIndex); - setCurrentImage(`${process.env.NEXT_PUBLIC_BASE_URL}/upload/img/${dataList[newIndex]}`); - }; - - return ( -
-
-
- {/*标题区域*/} -
-
-
- - {lang.OurFactory} -
-
-
-
- - {/* 工厂图片网格 */} -
- {dataList.map((item, index) => ( -
openModal(`${process.env.NEXT_PUBLIC_BASE_URL}/upload/img/${item}`, index)} - style={{ - opacity: 0, - transform: 'translateY(20px)', - animation: `fadeInUp 0.3s ease forwards ${index * 0.1}s` - }} - > -
-
- {`Factory -
-
-
- ))} -
-
- - {/* 图片查看模态框 */} - {modalOpen && ( -
- {/* 关闭按钮 */} - - - {/* 上一张按钮 */} - - - {/* 下一张按钮 */} - - - {/* 图片容器 */} -
e.stopPropagation()} - > - Enlarged factory image -
-
- )} - - {/* 添加自定义动画样式 */} - -
- ); -} \ No newline at end of file diff --git a/web/src/components/index/sections/template04/ourMission.jsx b/web/src/components/index/sections/template04/ourMission.jsx deleted file mode 100644 index 4238fba7..00000000 --- a/web/src/components/index/sections/template04/ourMission.jsx +++ /dev/null @@ -1,43 +0,0 @@ -import lang from "@/locales"; -import Image from "next/image"; - -const stats = [ - { label: 'Global Customers', value: '10,000+' }, - { label: 'Countries Served', value: '80+' }, - { label: 'Years Experience', value: '18+' }, -] - -export default function OurMission({missionData, statsData}) { - return ( -
-
- {/* 左侧:图片 */} -
-
- Our Global Mission -
-
- - {/* 右侧:使命描述 */} -
- {/*标题区域*/} -
- // - {lang.OurMission} - -
-

-

-
-
-
- ) -} \ No newline at end of file diff --git a/web/src/components/index/sections/template04/recommendedForYou.jsx b/web/src/components/index/sections/template04/recommendedForYou.jsx deleted file mode 100644 index d36509fa..00000000 --- a/web/src/components/index/sections/template04/recommendedForYou.jsx +++ /dev/null @@ -1,68 +0,0 @@ -import React from "react"; -import Link from "next/link"; -import Image from "next/image"; -import lang from '@/locales'; - - -export default function RecommendedForYou({recommendData}) { - return ( -
-
- {/*标题区域*/} -
-
- // - {lang.RecommendedForYou} -
-

- {lang.RecommendedForYouTip} -

-
- -
- {recommendData.map((product) => { - // 提取第一张图片 - const cover = product.cover ? product.cover.split('#')[0] : ''; - return ( -
-
- {product.title} -
-
-
-

- -

-

-
-

{product.category_title}

-
-
- ) - })} -
- -
- - {lang.ViewAllProducts} - - - - -
-
-
- ) -} diff --git a/web/src/components/index/sections/template04/testimonials.jsx b/web/src/components/index/sections/template04/testimonials.jsx deleted file mode 100644 index dae5f0aa..00000000 --- a/web/src/components/index/sections/template04/testimonials.jsx +++ /dev/null @@ -1,109 +0,0 @@ -export default function Testimonials() { - // 客户评价数据 - const testimonials = [ - { - id: 1, - name: "Jane Anderson", - location: "Homeowner, San Diego", - content: "\"The team at Earthly Elegance totally transformed our backyard into a stunning oasis. Their attention to detail and creativity exceeded our expectations!\"", - avatar: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=150&h=150&q=80", - rating: 5 - }, - { - id: 2, - name: "James Head", - location: "Homeowner, New York City", - content: "\"The team at Earthly Elegance totally transformed our backyard into a stunning oasis. Their attention to detail and creativity exceeded our expectations!\"", - avatar: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=150&h=150&q=80", - rating: 5 - }, - { - id: 3, - name: "James Head", - location: "Homeowner, New York City", - content: "\"The team at Earthly Elegance totally transformed our backyard into a stunning oasis. Their attention to detail and creativity exceeded our expectations!\"", - avatar: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=150&h=150&q=80", - rating: 5 - }, - { - id: 4, - name: "James Head", - location: "Homeowner, New York City", - content: "\"The team at Earthly Elegance totally transformed our backyard into a stunning oasis. Their attention to detail and creativity exceeded our expectations!\"", - avatar: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=150&h=150&q=80", - rating: 5 - } - ]; - - // 渲染星星评分 - const renderStars = (rating) => { - const stars = []; - for (let i = 0; i < rating; i++) { - stars.push( - - - - ); - } - return stars; - }; - - return ( -
-
- {/* 标题区域 */} -
-
- - - - TESTIMONIALS -
-

- Honest Reviews from our
Customers -

-
- - {/* 评价卡片区域 */} -
- {testimonials.map((testimonial) => ( -
- {/* 头像和个人信息 */} -
- {testimonial.name} -
-

{testimonial.name}

-

{testimonial.location}

-
-
- - {/* 评价内容 */} -

- {testimonial.content} -

- - {/* 评分和Google图标 */} -
-
- {renderStars(testimonial.rating)} -
-
- - - - - - -
-
-
- ))} -
-
-
- ); -} \ No newline at end of file diff --git a/web/src/components/index/sections/template04/topbar.jsx b/web/src/components/index/sections/template04/topbar.jsx deleted file mode 100644 index 2a14531e..00000000 --- a/web/src/components/index/sections/template04/topbar.jsx +++ /dev/null @@ -1,142 +0,0 @@ -'use client' -import Link from "next/link"; -import Wechat from "@/components/index/sections/wechat"; -import { useEffect, useState } from "react"; - -export default function TopBar({ sectionData }) { - const [showTopBar, setShowTopBar] = useState(true); - - useEffect(() => { - window.scrollTo(0, 0); - - let lastScrollY = 0; - - const handleScroll = () => { - const currentScrollY = window.scrollY; - - // 当页面滚动到顶部时显示顶部信息栏 - if (currentScrollY === 0) { - setShowTopBar(true); - } - // 当向下滚动时隐藏顶部信息栏 - else if (currentScrollY > lastScrollY) { - setShowTopBar(false); - } - - lastScrollY = currentScrollY; - }; - - window.addEventListener('scroll', handleScroll); - - return () => { - window.removeEventListener('scroll', handleScroll); - }; - }, []); - - return ( -
-
- - -
-
- {sectionData?.basicGlobal?.global_facebook?.trim().length > 0 && ( - - - - - - )} - {sectionData?.basicGlobal?.global_twitter?.trim().length > 0 && ( - - - - - - )} - {sectionData?.basicGlobal?.global_instagram?.trim().length > 0 && ( - - - - - - )} - {/* LinkedIn 图标 */} - {sectionData?.basicGlobal?.global_linkedin?.trim().length > 0 && ( - - - - - - )} - {/* YouTube 图标 */} - {sectionData?.basicGlobal?.global_youtube?.trim().length > 0 && ( - - - - - - )} - {/* WhatsApp 图标 */} - {sectionData?.basicGlobal?.global_whatsapp?.trim().length > 0 && ( - - - - - - )} - {/* WeChat 组件 */} - {sectionData?.basicGlobal?.global_wechat?.trim().length > 0 && ( - - )} - -
-
- -
-
- ); -} diff --git a/web/src/components/index/sections/template03/aboutUs.jsx b/web/src/components/index/sections/template10/aboutUs.jsx similarity index 92% rename from web/src/components/index/sections/template03/aboutUs.jsx rename to web/src/components/index/sections/template10/aboutUs.jsx index 813584e9..c2aab030 100644 --- a/web/src/components/index/sections/template03/aboutUs.jsx +++ b/web/src/components/index/sections/template10/aboutUs.jsx @@ -6,13 +6,11 @@ import React from "react"; export default function AboutUs({aboutData, companyName, statsData}) { return ( -
+
-

{lang.AboutUs}

-

- {lang.AboutUsTip} -

+

{lang.AboutUs}

+
diff --git a/web/src/components/index/sections/template04/carousel.jsx b/web/src/components/index/sections/template10/carousel.jsx similarity index 69% rename from web/src/components/index/sections/template04/carousel.jsx rename to web/src/components/index/sections/template10/carousel.jsx index 8686f17a..ed089840 100644 --- a/web/src/components/index/sections/template04/carousel.jsx +++ b/web/src/components/index/sections/template10/carousel.jsx @@ -9,14 +9,19 @@ import "swiper/css/pagination"; import "swiper/css/effect-fade"; import {Navigation, Autoplay, Pagination, EffectFade} from "swiper/modules"; import lang from '@/locales'; - - +import { useRouter } from 'next/navigation'; const Carousel = ({bannerData, heroText}) => { const coverArr = bannerData ? bannerData.split('#') : []; + const router = useRouter(); const colorValue = "bg-mainColorNormalAlpha-50 hover:bg-mainColorNormal text-white hover:text-white backdrop-blur-sm"; const commonStyle = "z-10 absolute flex items-center justify-center transform -translate-y-1/2 w-[40px] h-[40px] focus:outline-none transition-all duration-300 ease-in-out hidden md:block"; + + const handleSlideClick = () => { + router.push('/product'); + }; + return (
{ prevEl: ".custom-prev", }} pagination={{ - clickable: true, // 确保分页器可以点击 + clickable: true, }} autoplay={{ - delay: 3500, + delay: 5000, disableOnInteraction: false, + waitForTransition: false, + }} + speed={800} + effect="fade" + fadeEffect={{ + crossFade: true }} - speed={1500} - effect="fade" // 设置为淡入淡出效果 - fadeEffect={{crossFade: false}} // 可选:添加交叉淡化效果 loop={true} allowTouchMove={true} - preventInteractionOnTransition={false} // 允许在过渡期间进行交互 + preventInteractionOnTransition={true} + watchSlidesProgress={true} + observer={true} + observeParents={true} > {coverArr.map((image, index) => ( - +
{ quality={90} priority={index === 0} /> - {/*蒙层*/} -
+ {/*蒙层 - 只在有 heroText 时显示 */} + {heroText &&
}
))} -
-
- {/*hero 文本区域*/} -
-

- {heroText || ''} -

-
- - {lang.GetAFreeQuoteBtn} - -
-
-
- {/* 自定义导航按钮 */} + {/* 导航按钮 - 提高层级 */} { coverArr.length > 1 ? ( <> - ) : null } - + {/* 文本区域 - 只在有 heroText 时显示 */} + {heroText && ( +
+
+

+
+ {heroText} +
+

+
+
+ )}
); }; diff --git a/web/src/components/index/sections/template04/categoryItem.jsx b/web/src/components/index/sections/template10/categoryItem.jsx similarity index 85% rename from web/src/components/index/sections/template04/categoryItem.jsx rename to web/src/components/index/sections/template10/categoryItem.jsx index f5cb50f8..3d8e2836 100644 --- a/web/src/components/index/sections/template04/categoryItem.jsx +++ b/web/src/components/index/sections/template10/categoryItem.jsx @@ -1,7 +1,6 @@ 'use client' import {useState} from "react"; import Link from "next/link"; -import {ChevronDownIcon} from "@heroicons/react/20/solid"; export default function CategoryItem({ category, currentCategoryId }) { @@ -9,7 +8,7 @@ export default function CategoryItem({ category, currentCategoryId }) { const hasSubCategories = category.children && category.children.length > 0; const isActive = currentCategoryId === category.id; - const mainStyle = "bg-white min-w-[80px] text-center hover:bg-[#ebf2f5] font-semibold py-2.5 px-5 text-gray-700 border-[1px] border-[#e3ebee] rounded-full"; + const mainStyle = "bg-white min-w-[80px] text-center hover:bg-[#ebf2f5] font-normal py-2.5 px-5 text-gray-700 border-[1px] border-[#e3ebee] rounded-full"; const selectedStyle = "bg-mainColorNormal min-w-[80px] text-center py-2.5 px-5 text-white rounded-full"; return ( diff --git a/web/src/components/index/sections/template03/certification.jsx b/web/src/components/index/sections/template10/certification.jsx similarity index 100% rename from web/src/components/index/sections/template03/certification.jsx rename to web/src/components/index/sections/template10/certification.jsx diff --git a/web/src/components/index/sections/template03/companyNews.jsx b/web/src/components/index/sections/template10/companyNews.jsx similarity index 94% rename from web/src/components/index/sections/template03/companyNews.jsx rename to web/src/components/index/sections/template10/companyNews.jsx index 606945d3..3afcc5cf 100644 --- a/web/src/components/index/sections/template03/companyNews.jsx +++ b/web/src/components/index/sections/template10/companyNews.jsx @@ -8,8 +8,8 @@ export default function CompanyNews({newsData}) { return (
-

- {lang.CompanyNews} +

+ {lang.NewsAndEvents}

{lang.CompanyNewsTip} diff --git a/web/src/components/index/sections/template10/customersSay.jsx b/web/src/components/index/sections/template10/customersSay.jsx new file mode 100644 index 00000000..ee1fcb76 --- /dev/null +++ b/web/src/components/index/sections/template10/customersSay.jsx @@ -0,0 +1,78 @@ +import lang from "@/locales"; +import Image from "next/image"; + +export default function CustomersSay({commentData}){ + // 客户评价数据 + const testimonials = [ + { + id: 1, + content: "Fast shipping and excellent customer service. The product was even better than expected. I will definitely be a returning customer.", + name: "JENNIFER LEWIS", + avatar: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=150&h=150&q=80", + }, + { + id: 2, + content: "Great user experience on your website. Great user experience on your website.I found exactly what I was looking for at a great price. I will definitely be telling my friends.", + name: "ALICIA HEART", + avatar: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=150&h=150&q=80", + }, + { + id: 3, + content: "Thank you for the excellent shopping experience. It arrived quickly and was exactly as described. I will definitely be shopping with you again in the future.", + name: "JUAN CARLOS", + avatar: "https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=150&h=150&q=80", + } + ]; + + return( +

+
+
+

+ {lang.OurCustomersSay} +

+

+ {lang.OurCustomersSayTip} +

+
+ +
+ {commentData.map((testimonial) => ( +
+ {/* 引号 */} +
+ +
+ + {/* 评价内容 */} +

+ {testimonial.comment_content} +

+ + {/* 客户信息 */} +
+
+ {testimonial.comment_name} +
+
+

+ {testimonial.comment_name} +

+

+ From {testimonial.comment_location || "Unknown"} +

+
+
+
+ ))} +
+
+
+ ) +} \ No newline at end of file diff --git a/web/src/components/index/sections/template03/featuredProducts.jsx b/web/src/components/index/sections/template10/featuredProducts.jsx similarity index 79% rename from web/src/components/index/sections/template03/featuredProducts.jsx rename to web/src/components/index/sections/template10/featuredProducts.jsx index f1ac5e71..43ef29f3 100644 --- a/web/src/components/index/sections/template03/featuredProducts.jsx +++ b/web/src/components/index/sections/template10/featuredProducts.jsx @@ -11,7 +11,7 @@ function ProductCard({ product }) {
-
+
{product.title} +
-

{lang.FeaturedProducts}

-

- {lang.FeaturedProductsTip} -

+

{lang.PopularProducts}

+
-
+
{featuredData.map((product) => ( ))} @@ -53,9 +51,9 @@ export default function FeaturedProducts({featuredData}) {
- {lang.ViewAllProducts} + {lang.AllProducts} diff --git a/web/src/components/index/sections/template03/footer.jsx b/web/src/components/index/sections/template10/footer.jsx similarity index 91% rename from web/src/components/index/sections/template03/footer.jsx rename to web/src/components/index/sections/template10/footer.jsx index e372f1d2..f027d99d 100644 --- a/web/src/components/index/sections/template03/footer.jsx +++ b/web/src/components/index/sections/template10/footer.jsx @@ -5,7 +5,6 @@ export default function Footer({sectionData}) { return (
-
{/* Footer */}
@@ -19,11 +18,8 @@ export default function Footer({sectionData}) {
  • - - + + {lang[item.name] || item.name} @@ -31,6 +27,7 @@ export default function Footer({sectionData}) { ))}
  • + {/* 产品分类 */}

    {lang.ProductCategories} @@ -40,11 +37,8 @@ export default function Footer({sectionData}) {
  • - - + + {item.title} @@ -95,10 +89,9 @@ export default function Footer({sectionData}) { {/* Social Icons */}
    + className="flex flex-col sm:flex-row justify-between items-center border-t border-gray-400 pt-8">
    -

    © {new Date().getFullYear()} {sectionData.contactData.global_company_name}. - {lang.AllRightsReserved} +

    Copyright © {new Date().getFullYear()} {sectionData.contactData.global_company_name}

    Powered by

    {lang.Email}

    {contactData.global_email} + className="text-gray-900 hover:text-mainColorNormal font-medium text-base transition-colors">{contactData.global_email}
    @@ -217,7 +217,7 @@ export default function GetInTouch({contactData}) { {lang.PhoneNumber}

    {contactData.global_phone} + className="text-gray-900 hover:text-mainColorNormal font-medium text-base transition-colors">{contactData.global_phone}
  • diff --git a/web/src/components/index/sections/template04/navBar.jsx b/web/src/components/index/sections/template10/navBar.jsx similarity index 82% rename from web/src/components/index/sections/template04/navBar.jsx rename to web/src/components/index/sections/template10/navBar.jsx index 2a6a6cd6..55ae56ff 100644 --- a/web/src/components/index/sections/template04/navBar.jsx +++ b/web/src/components/index/sections/template10/navBar.jsx @@ -1,5 +1,5 @@ import Link from "next/link"; -import NavBarClient from "@/components/index/sections/template04/navBarClient"; +import NavBarClient from "@/components/index/sections/template10/navBarClient"; export default function NavBar({sectionData}) { @@ -10,9 +10,9 @@ export default function NavBar({sectionData}) {