Compare commits

...

6 Commits

Author SHA1 Message Date
5ddb2265cf chore: 开启局域网监听 2025-09-05 13:00:25 +08:00
eb175889c6 feat: 网站首页添加新内容
- 首页推荐产品
2025-09-05 12:59:52 +08:00
6230c7ff17 feat: 新增页面内容
- 服务支持页面内容
2025-09-04 15:59:18 +08:00
eec2b0ae9f style: 调整代码格式 2025-09-04 15:06:09 +08:00
3be665449c feat: 增添新页面 & 修改样式
- 增添服务支持-常见问题 & 服务支持-文档资料
- 调整QuestionList样式
2025-09-04 14:56:46 +08:00
ea409d49b0 refactor(production): 重构产品页代码
- 将Production的documents类型改写为单独的interface:ProductionDocument
2025-09-04 14:55:32 +08:00
13 changed files with 477 additions and 121 deletions

View File

@ -6,7 +6,6 @@
</NuxtLink>
</div>
<!-- 导航菜单 -->
<el-menu
:default-active="activeName" class="header-menu" mode="horizontal" :ellipsis="false"

View File

@ -2,11 +2,8 @@
<div class="question-list">
<el-collapse class="question-collapse" accordion>
<el-collapse-item
v-for="(question, index) in questions"
:key="index"
:title="question.title"
:name="String(index)"
>
v-for="(question, index) in questions" :key="index" :title="question.title"
:name="String(index)">
<markdown-renderer :content="question.content || ''" />
</el-collapse-item>
</el-collapse>
@ -31,14 +28,33 @@ defineProps({
border: none;
}
.question-collapse :deep(.el-collapse-item) {
margin-bottom: 1rem;
border-radius: 8px;
}
.question-collapse :deep(.el-collapse-item__header) {
font-size: 1rem;
padding: 1rem;
border-radius: 8px;
background-color: #f5f7fa;
transition: all 0.3s ease;
&.is-active {
background-color: #e1e6eb;
color: var(--el-color-primary);
}
}
.question-collapse :deep(.el-collapse-item) {
margin-bottom: 1rem;
border: 1px solid var(--el-border-color-light);
box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.1);
.question-collapse :deep(.el-collapse-item__wrap) {
border: none;
}
.question-collapse :deep(.el-collapse-item__content) {
padding: 1rem;
font-size: 0.9rem;
}
</style>

View File

@ -1,32 +1,108 @@
<template>
<div class="homepage">
<div class="carousel">
<el-carousel height="auto" :interval="5000" arrow="never" autoplay>
<el-carousel-item v-for="(item, index) in 3" :key="index">
<div v-if="!pending" class="carousel">
<el-carousel class="homepage-carousel" height="auto" :interval="5000" arrow="never" autoplay>
<el-carousel-item v-for="(item, index) in carouselImages" :key="index">
<div class="carousel-item">
<!-- <el-image class="carousel-image" :src="useStrapiMedia('/uploads/201605211508029798_e37af77a48.png')" fit="fill" /> -->
<p class="image-label">{{ item }}</p>
<el-image
class="carousel-image" :src="useStrapiMedia(item.url || '')"
:alt="item.alternativeText || `Carousel Image ${index + 1}`" fit="contain" lazy />
<p v-if="item.caption" class="caption">{{ item.caption }}</p>
</div>
</el-carousel-item>
</el-carousel>
<div class="recommend-production" style="padding: 2rem;">
<div class="section">
<h2 style="font-size: 1.5rem; font-weight: bold; margin-bottom: 1rem;">推荐产品</h2>
<p>探索我们的精选产品满足您的各种需求无论是创新技术还是经典设计我们都为您提供优质选择</p>
<el-carousel
class="production-carousel" height="auto" arrow="never" indicator-position="outside"
:autoplay="false">
<el-carousel-item
v-for="n in Math.floor(recommend_productions.length / 3) + 1" :key="n"
class="production-list">
<div class="block-group">
<el-card
v-for="(item, index) in recommend_productions.slice((n - 1) * 3, n * 3)" :key="index"
class="block production-card" @click="handleProductionCardClick(item.documentId || '')">
<template #header>
<el-image
:src="useStrapiMedia(item.cover?.url || '')"
:alt="item.cover?.alternativeText || item.title" fit="cover"
style="width: 100%; height: 200px; border-radius: 8px;" lazy />
</template>
<div class="card-body">
<!-- Name -->
<div class="text-center">
<span class="production-name">{{ item.title }}</span>
</div>
<!-- Description -->
<div class="card-description text-left opacity-25">{{ item.summary }}</div>
</div>
</el-card>
</div>
</el-carousel-item>
</el-carousel>
</div>
</div>
</div>
<div v-else class="loading">
<el-skeleton :rows="3" animated />
</div>
</div>
</template>
<style scoped>
.carousel-item {
width: 100%;
height: 100%;
background-color: #f5f7fa;
}
<script setup lang="ts">
const { findOne } = useStrapi()
const { getStrapiLocale } = useLocalizations()
const strapiLocale = getStrapiLocale()
.carousel-image {
position: relative;
width: 100%;
height: 100%;
}
const carouselImages = ref<StrapiImage[]>([])
const recommend_productions = ref<Production[]>([])
.el-carousel__item {
const pending = ref(true)
onMounted(async () => {
try {
const response = await findOne<StrapiHomepage>('homepage', undefined, {
populate: {
carousel: {
populate: '*',
},
recommend_productions: {
populate: {
cover: {
populate: '*',
},
},
}
},
locale: strapiLocale,
})
if (response.data) {
carouselImages.value = response.data.carousel || []
recommend_productions.value = response.data.recommend_productions || []
console.log('推荐产品:', recommend_productions.value)
}
} catch (error) {
console.error('Error fetching homepage data:', error)
} finally {
pending.value = false
}
})
const handleProductionCardClick = (documentId: string) => {
// 使用路由导航到产品详情页
if (documentId) {
const localePath = useLocalePath()
const router = useRouter()
router.push(localePath(`/productions/${documentId}`))
}
}
</script>
<style scoped lang="scss">
.homepage-carousel .el-carousel__item {
width: 100%;
height: 33vw;
/* 16:9 Aspect Ratio */
@ -40,12 +116,90 @@
margin: 0;
}
.el-carousel__item:nth-child(2n) {
/* background-color: #99a9bf; */
.homepage-carousel .carousel-item {
width: 100%;
height: 100%;
background-color: #f5f7fa;
}
.el-carousel__item:nth-child(2n + 1) {
/* background-color: #d3dce6; */
.carousel-image {
position: relative;
width: 100%;
height: 100%;
}
.caption {
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(0, 0, 0, 0.5);
color: white;
padding: 5px 10px;
border-radius: 5px;
font-size: 14px;
}
.production-carousel :deep(.el-carousel__button) {
/* 指示器按钮样式 */
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #475669;
transition: all 0.3s ease;
}
.production-list {
display: flex;
padding: 1rem;
height: 400px;
}
.block-group {
display: flex;
gap: 1rem;
width: 100%;
max-width: 1200px;
margin: 0 auto;
height: 100%;
}
.block {
width: 30%;
height: 100%;
border: 2px dashed #a3aac6;
}
.production-card {
transition: all 0.3s ease;
text-align: center;
}
.production-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.production-name {
font-size: 1rem;
font-weight: 600;
}
.card-description {
font-size: 0.8rem;
margin-top: 5px;
}
.production-card .el-image {
height: 150px;
border-radius: 4px;
}
.card-body {
margin: 10px auto;
padding: 0px auto;
height: 100px;
}
.section p {

View File

@ -36,7 +36,7 @@
<question-list :questions="production.questions" />
</el-tab-pane>
<el-tab-pane label="相关文档" name="documents">
<document-list :documents="production.documents" />
<document-list :documents="production.production_documents.map(item => item.document) || []" />
</el-tab-pane>
</el-tabs>
</div>
@ -95,8 +95,8 @@ onMounted(async () => {
questions: {
populate: '*',
},
documents: {
populate: '*',
production_documents: {
populate: 'document',
},
},
locale: strapiLocale,
@ -106,6 +106,7 @@ onMounted(async () => {
production.value = {
...item,
}
console.log('Fetched production:', production.value)
}
} catch (error) {
console.error('Failed to fetch production:', error)

View File

@ -19,8 +19,7 @@ v-for="(group, type) in groupedProductions" :key="type" :title="type || '未分
<div class="group-list">
<production-card
v-for="production in group" :key="production.documentId || production.id"
:slug="production.documentId"
:image-url="useStrapiMedia(production?.cover?.url || '')"
:slug="production.documentId" :image-url="useStrapiMedia(production?.cover?.url || '')"
:name="production.title" :description="production.summary || ''" />
</div>
</el-collapse-item>
@ -65,12 +64,9 @@ onMounted(async () => {
cover: {
populate: '*',
},
production_images: {
populate: '*',
},
production_type: {
populate: '*',
}
populate: '*'
},
},
filters: {
show_in_production_list: {

View File

@ -1,6 +1,10 @@
<template>
<div class="page-container">
<support-tabs model-value="documents"/>
<div v-if="pending">
<el-skeleton :rows="5" animated />
</div>
<div v-else>
<support-tabs model-value="documents" />
<div class="page-header">
<h1 class="page-title">{{ $t('navigation.documents') }}</h1>
<el-breadcrumb class="breadcrumb" separator="/">
@ -15,11 +19,39 @@
</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="page-content">
<document-list :documents="documents" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
const { find } = useStrapi()
const { getStrapiLocale } = useLocalizations()
const strapiLocale = getStrapiLocale()
const pending = ref(true)
const documents = ref<StrapiMedia[]>([])
onMounted(async () => {
try {
const response = await find<ProductionDocument>('production-documents', {
locale: strapiLocale,
populate: 'document',
})
if(response.data) {
documents.value = response.data.map(item => ({
...item.document,
})) || []
}
} catch (error) {
console.error('Error fetching documents:', error)
} finally {
pending.value = false
}
})
</script>
<style scoped>
@ -38,4 +70,8 @@
.breadcrumb {
margin-left: auto;
}
.page-content {
padding: 1rem 2rem 2rem;
}
</style>

View File

@ -1,5 +1,9 @@
<template>
<div class="page-container">
<div v-if="pending" class="flex justify-center items-center h-64">
<el-spinner />
</div>
<div v-else>
<support-tabs model-value="faq" />
<div class="page-header">
<h1 class="page-title">{{ $t('navigation.faq') }}</h1>
@ -15,12 +19,37 @@
</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="page-content">
<question-list :questions="questions" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
const { find } = useStrapi();
const { getStrapiLocale } = useLocalizations();
const strapiLocale = getStrapiLocale();
const questions = ref<Question[]>([]);
const pending = ref(true);
onMounted(async () => {
try {
const faqData = await find<Question>('questions', {
locale: strapiLocale,
});
if (faqData) {
questions.value = faqData.data || [];
}
} catch (error) {
console.error('Failed to fetch FAQ data:', error);
} finally {
pending.value = false;
}
})
</script>
<style scoped>
@ -40,4 +69,7 @@
margin-left: auto;
}
.page-content {
padding: 1rem 2rem 2rem;
}
</style>

View File

@ -1,6 +1,8 @@
<template>
<div class="page-container">
<support-tabs />
<div class="page-content">
<div class="page-header">
<h1 class="page-title">{{ $t('navigation.support') }}</h1>
<el-breadcrumb class="breadcrumb" separator="/">
@ -12,28 +14,89 @@
</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="page-content">
<!-- <el-divider content-position="left">选择我们的服务</el-divider>
<div class="button-group">
<NuxtLink :to="$localePath('/about/contact-us')">
<el-card class="card-button">
<el-icon class="icon" size="80">
<section style="margin-bottom: 2rem;">
<p>金申机械制造有限公司致力于为客户提供优质的产品与服务针对纸管机分纸机纸吸管等产品我们提供全方位的售后服务确保客户能够安心地使用我们的产品</p>
</section>
<div class="card-group">
<el-card class="card">
<el-row>
<el-col :span="6">
<el-icon class="card-icon" size="80">
<ElIconQuestionFilled />
</el-icon>
</el-col>
<el-col :span="18">
<div class="card-title">
<span>{{ $t('navigation.faq') }}</span>
</div>
</el-col>
</el-row>
<el-row>
<div class="card-content">
<p>我们为用户整理了常见问题的答案帮助您快速解决疑惑</p>
</div>
</el-row>
<el-row>
<NuxtLink class="card-link" :to="$localePath('/support/faq')">
<el-button class="card-button" round>
<span>了解更多 > </span>
</el-button>
</NuxtLink>
</el-row>
</el-card>
<el-card class="card">
<el-row>
<el-col :span="6">
<el-icon class="card-icon" size="80">
<ElIconDocumentChecked />
</el-icon>
</el-col>
<el-col :span="18">
<div class="card-title">
<span>{{ $t('navigation.documents') }}</span>
</div>
</el-col>
</el-row>
<el-row>
<div class="card-content">
<p>我们为用户整理了常见问题的答案为您快速解决疑惑</p>
</div>
</el-row>
<el-row>
<NuxtLink class="card-link" :to="$localePath('/support/documents')">
<el-button class="card-button" round>
<span>了解更多 > </span>
</el-button>
</NuxtLink>
</el-row>
</el-card>
<el-card class="card">
<el-row>
<el-col :span="6">
<el-icon class="card-icon" size="80">
<ElIconService />
</el-icon>
<br>
联系我们
</el-card>
</el-col>
<el-col :span="18">
<div class="card-title">
<span>{{ $t('navigation.contact-info') }}</span>
</div>
</el-col>
</el-row>
<el-row>
<div class="card-content">
<p>通过电话邮箱联系我们我们将现场为您服务</p>
</div>
</el-row>
<el-row>
<NuxtLink class="card-link" :to="$localePath('/support/contact-us')">
<el-button class="card-button" round>
<span>了解更多 > </span>
</el-button>
</NuxtLink>
<NuxtLink :to="$localePath('/about/contact-us')">
<el-card class="card-button">
<el-icon class="icon" size="80">
<ElIconService />
</el-icon>
<br>
联系我们
</el-row>
</el-card>
</NuxtLink>
</div> -->
</div>
</div>
</div>
</template>
@ -42,9 +105,9 @@
</script>
<style scoped>
.page-header {
display: flex;
padding: 2rem 2rem 0rem;
}
.page-title {
@ -59,12 +122,55 @@
}
.page-content {
padding: 2rem;
padding: 1rem 2rem 2rem;
max-width: 1200px;
margin: 0 auto;
}
:deep(.el-divider__text) {
font-size: 1.2rem;
section {
line-height: 1.6;
width: 60%;
}
.card-group {
display: flex;
gap: 100px;
justify-content: space-around;
margin-bottom: 2rem;
}
.el-card {
width: 100%;
padding: 20px;
box-shadow: none;
border-radius: 8px;
}
.card-icon {
color: var(--el-color-primary);
}
.card-title {
display: flex;
height: 100%;
align-items: center;
margin-left: 2rem;
font-size: 1.5rem;
font-weight: bold;
color: var(--el-color-primary);
}
.card-link {
margin-left: auto;
}
.card-button {
cursor: pointer;
text-align: center;
font-size: 1rem;
color: var(--el-color-primary);
transition: all 0.3s ease;
}
.button-group {
@ -75,18 +181,20 @@
gap: 20px;
}
.card-button {
width: 20%;
min-width: 200px;
padding: 20px;
margin: 0 auto;
cursor: pointer;
text-align: center;
font-size: 1.5em;
.el-row {
margin-bottom: 20px;
}
.card-button:hover {
transform: translateY(-4px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
.el-row:last-child {
margin-bottom: 0;
}
.el-col {
border-radius: 4px;
}
.grid-content {
border-radius: 4px;
min-height: 36px;
}
</style>

View File

@ -45,3 +45,5 @@ export interface StrapiResponse<T> {
}
}
}
export type StrapiRelation<T, K extends keyof T = never> = Omit<T, K | keyof StrapiEntity> & StrapiEntity;

View File

@ -1,4 +1,4 @@
import type { StrapiEntity, StrapiImage, StrapiMedia } from './common';
import type { StrapiEntity, StrapiImage, StrapiMedia, StrapiRelation } from './common';
export interface ProductionType extends StrapiEntity {
type: string;
@ -22,7 +22,12 @@ export interface Production extends StrapiEntity {
production_images: StrapiImage[];
production_details: string;
production_specs: ProductionSpecGroup[];
questions: Question[];
documents: StrapiMedia[];
production_documents: StrapiRelation<ProductionDocument, 'related_productions'>[];
questions: StrapiRelation<Question, 'related_productions'>[];
show_in_production_list: boolean;
}
export interface ProductionDocument extends StrapiEntity {
document: StrapiMedia;
related_productions: StrapiRelation<Production, 'production_documents'>[];
}

View File

@ -1,4 +1,5 @@
export interface Question extends StrapiEntity {
title: string;
content: string;
related_productions: StrapiRelation<Production, 'questions'>[];
}

View File

@ -8,4 +8,5 @@ export interface StrapiContactInfo extends StrapiEntity {
export interface StrapiHomepage extends StrapiEntity {
carousel: StrapiImage[];
recommend_productions: StrapiRelation<Production>[];
}

View File

@ -49,6 +49,11 @@ export default defineNuxtConfig({
},
},
devServer: {
port: 3000,
host: '0.0.0.0',
},
elementPlus: {
icon: "ElIcon",
importStyle: "scss",