Browse Source

feat: 添加生产环境配置和API请求优化

- 新增`.env.production`文件,配置生产环境API基础URL
- 优化`vite.config.js`,支持环境变量和API代理配置
- 重构`request.js`,简化请求拦截器和响应处理逻辑
- 新增`cases.js`和`config.js`存储模块,管理案例和配置数据
- 更新`index.html`,修改页面标题为公司名称
- 优化`CaseDetail.vue`和`Team.vue`,使用新的存储模块和组件
- 新增`CaseItem.vue`和`TeamMemberCard.vue`组件,提升代码复用性
- 删除旧的`casesStore.js`和`caseList.vue`,统一使用新的存储和组件
- 更新`App.vue`,使用配置存储动态渲染导航栏和页脚内容
hfll
前端-胡立永 10 months ago
parent
commit
9a15e9f213
29 changed files with 3595 additions and 2537 deletions
  1. +4
    -4
      .env.development
  2. +3
    -0
      .env.production
  3. +158
    -0
      create_database.sql
  4. +9
    -6
      index.html
  5. +18
    -13
      src/App.vue
  6. +16
    -0
      src/api/index.js
  7. +1
    -0
      src/assets/scss/main.scss
  8. +251
    -0
      src/components/CallToAction.vue
  9. +207
    -0
      src/components/TeamMemberCard.vue
  10. +0
    -167
      src/components/case/caseList.vue
  11. +219
    -0
      src/components/cases/CaseItem.vue
  12. +74
    -0
      src/components/home/CaseSection.vue
  13. +115
    -120
      src/components/home/ServiceSection.vue
  14. +1
    -0
      src/main.js
  15. +19
    -0
      src/stores/cases.js
  16. +0
    -150
      src/stores/casesStore.js
  17. +26
    -0
      src/stores/config.js
  18. +0
    -84
      src/utils/README.md
  19. +0
    -46
      src/utils/api.js
  20. +44
    -100
      src/utils/request.js
  21. +597
    -110
      src/views/company/About.vue
  22. +3
    -0
      src/views/company/JobDetail.vue
  23. +34
    -129
      src/views/company/Team.vue
  24. +123
    -6
      src/views/pages/CaseDetail.vue
  25. +270
    -540
      src/views/pages/Cases.vue
  26. +893
    -249
      src/views/pages/Contact.vue
  27. +274
    -361
      src/views/pages/Home.vue
  28. +211
    -449
      src/views/pages/Services.vue
  29. +25
    -3
      vite.config.js

+ 4
- 4
.env.development View File

@ -1,5 +1,5 @@
# 开发环境API基础URL
VITE_API_BASE_URL=http://localhost:3000
# 其他开发环境变量
# VITE_APP_MODE=development
# 开发环境
VITE_APP_BASE_API = '/dev-api'

+ 3
- 0
.env.production View File

@ -0,0 +1,3 @@
# 生产环境
VITE_APP_BASE_API = '/prod-api'

+ 158
- 0
create_database.sql View File

@ -0,0 +1,158 @@
-- 创建数据库
CREATE DATABASE IF NOT EXISTS hanhai_website COMMENT='瀚海黎明官方网站数据库';
USE hanhai_website;
-- 创建服务表:存储网站提供的服务信息
CREATE TABLE services (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '服务ID,自增主键',
icon VARCHAR(255) NOT NULL COMMENT '服务图标URL',
title VARCHAR(100) NOT NULL COMMENT '服务标题',
description TEXT NOT NULL COMMENT '服务详细描述',
category VARCHAR(50) NULL COMMENT '服务分类',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) COMMENT='服务信息表';
-- 创建案例表:存储公司完成的项目案例
CREATE TABLE cases (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '案例ID,自增主键',
title VARCHAR(100) NOT NULL COMMENT '案例标题',
description TEXT NOT NULL COMMENT '案例简介',
image VARCHAR(255) NOT NULL COMMENT '案例主图URL',
category VARCHAR(50) NOT NULL COMMENT '案例分类',
client VARCHAR(100) NOT NULL COMMENT '客户名称',
completion_date VARCHAR(50) NOT NULL COMMENT '完成日期',
challenge TEXT NOT NULL COMMENT '客户面临的挑战',
solution TEXT NOT NULL COMMENT '提供的解决方案',
results TEXT NOT NULL COMMENT '项目成果',
testimonial TEXT NULL COMMENT '客户评价',
testimonial_author VARCHAR(100) NULL COMMENT '评价人及职位',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) COMMENT='项目案例表';
-- 创建案例服务关联表:存储案例使用的服务列表
CREATE TABLE case_services (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '关联ID,自增主键',
case_id INT NOT NULL COMMENT '关联的案例ID',
service_name VARCHAR(100) NOT NULL COMMENT '服务名称',
FOREIGN KEY (case_id) REFERENCES cases(id) ON DELETE CASCADE COMMENT '外键关联案例表,案例删除时级联删除'
) COMMENT='案例服务关联表';
-- 创建案例图库表:存储案例的多张展示图片
CREATE TABLE case_gallery (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '图片ID,自增主键',
case_id INT NOT NULL COMMENT '关联的案例ID',
image_url VARCHAR(255) NOT NULL COMMENT '图片URL',
display_order INT NOT NULL DEFAULT 0 COMMENT '显示顺序',
FOREIGN KEY (case_id) REFERENCES cases(id) ON DELETE CASCADE COMMENT '外键关联案例表,案例删除时级联删除'
) COMMENT='案例图库表';
-- 创建团队成员表:存储公司团队成员信息
CREATE TABLE team_members (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '成员ID,自增主键',
name VARCHAR(50) NOT NULL COMMENT '成员姓名',
position VARCHAR(100) NOT NULL COMMENT '职位',
bio TEXT NOT NULL COMMENT '个人简介',
photo VARCHAR(255) NOT NULL COMMENT '照片URL',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) COMMENT='团队成员表';
-- 创建社交媒体表:存储团队成员的社交媒体链接
CREATE TABLE social_media (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '社交媒体ID,自增主键',
member_id INT NOT NULL COMMENT '关联的成员ID',
type VARCHAR(50) NOT NULL COMMENT '社交媒体类型(如wechat, linkedin)',
url VARCHAR(255) NOT NULL COMMENT '社交媒体链接',
qrcode VARCHAR(255) NULL COMMENT '二维码图片URL(微信等)',
FOREIGN KEY (member_id) REFERENCES team_members(id) ON DELETE CASCADE COMMENT '外键关联成员表,成员删除时级联删除'
) COMMENT='社交媒体表';
-- 创建职位表:存储招聘职位信息
CREATE TABLE job_openings (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '职位ID,自增主键',
title VARCHAR(100) NOT NULL COMMENT '职位名称',
description TEXT NOT NULL COMMENT '职位描述',
requirements TEXT NULL COMMENT '职位要求',
link VARCHAR(255) NOT NULL COMMENT '职位详情链接',
is_active BOOLEAN DEFAULT TRUE COMMENT '是否激活',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) COMMENT='招聘职位表';
-- 创建组件表:存储网站可复用组件的配置
CREATE TABLE components (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '组件ID,自增主键',
name VARCHAR(100) NOT NULL COMMENT '组件名称',
type VARCHAR(50) NOT NULL COMMENT '组件类型',
content JSON NOT NULL COMMENT '组件内容(JSON格式)',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) COMMENT='网站组件表';
-- 创建用户表:存储后台管理用户
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID,自增主键',
username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名',
password VARCHAR(255) NOT NULL COMMENT '密码(加密存储)',
email VARCHAR(100) NOT NULL UNIQUE COMMENT '电子邮箱',
role VARCHAR(20) NOT NULL DEFAULT 'editor' COMMENT '用户角色(admin/editor)',
last_login TIMESTAMP NULL COMMENT '最后登录时间',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) COMMENT='后台用户表';
-- 创建联系表单表:存储网站访客提交的联系信息
CREATE TABLE contact_messages (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '消息ID,自增主键',
name VARCHAR(100) NOT NULL COMMENT '联系人姓名',
email VARCHAR(100) NOT NULL COMMENT '联系人邮箱',
phone VARCHAR(20) NULL COMMENT '联系电话',
subject VARCHAR(200) NOT NULL COMMENT '主题',
message TEXT NOT NULL COMMENT '消息内容',
status VARCHAR(20) DEFAULT 'unread' COMMENT '消息状态(unread/read/replied)',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
) COMMENT='联系表单消息表';
-- 向服务表插入初始数据
INSERT INTO services (icon, title, description) VALUES
('https://cdn-icons-png.flaticon.com/512/2920/2920277.png', '定制软件开发', '根据您的业务需求,量身定制专属软件解决方案'),
('https://cdn-icons-png.flaticon.com/512/2586/2586488.png', '移动应用开发', '打造高性能、用户友好的iOS和Android应用'),
('https://cdn-icons-png.flaticon.com/512/1055/1055687.png', 'Web应用开发', '开发响应式、现代化的Web应用和网站'),
('https://cdn-icons-png.flaticon.com/512/1935/1935765.png', '企业软件解决方案', '提供ERP、CRM等企业级软件解决方案'),
('https://cdn-icons-png.flaticon.com/512/4727/4727266.png', '云服务与DevOps', '云架构设计、部署和DevOps自动化服务'),
('https://cdn-icons-png.flaticon.com/512/1055/1055666.png', 'UI/UX设计', '创造直观、美观且用户友好的界面设计');
-- 向案例表插入初始数据
INSERT INTO cases (title, description, image, category, client, completion_date, challenge, solution, results, testimonial, testimonial_author) VALUES
('智慧校园系统', '为教育机构打造的一体化校园管理系统,涵盖教学、行政、学生服务等多个模块', 'https://images.unsplash.com/photo-1523240795612-9a054b0db644?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80', '企业系统', '某知名高校', '2023年8月', '客户面临的挑战是多个独立系统并行运行,数据不一致,管理效率低下。学生和教职工需要在多个系统间切换,用户体验不佳。', '我们为客户设计并实现了一套集成化的智慧校园系统,整合了教务管理、学生服务、行政办公、资源管理等多个模块。系统采用了统一的用户界面和数据标准,实现了单点登录和数据共享。我们还开发了移动端应用,方便师生随时随地访问系统功能。', '系统上线后,管理效率提升了50%,数据处理错误减少了80%,用户满意度提高了60%。系统的自助服务功能减轻了行政人员的工作负担,让他们能够专注于更有价值的任务。', '微隐软件工作室的团队展现了卓越的项目管理和技术能力。他们深入理解了我们复杂的业务需求,并提供了一套既全面又易用的解决方案。新系统极大地改善了我们的管理效率和服务质量。', '陈校长 - 客户负责人');
-- 向案例服务关联表插入数据
INSERT INTO case_services (case_id, service_name) VALUES
(1, '系统规划'),
(1, '软件开发'),
(1, '数据迁移'),
(1, '用户培训'),
(1, '持续支持');
-- 向案例图库表插入数据
INSERT INTO case_gallery (case_id, image_url, display_order) VALUES
(1, 'https://images.unsplash.com/photo-1523050854058-8df90110c9f1?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80', 1),
(1, 'https://images.unsplash.com/photo-1562774053-701939374585?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80', 2),
(1, 'https://images.unsplash.com/photo-1577896851231-70ef18881754?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80', 3);
-- 向团队成员表插入初始数据
INSERT INTO team_members (name, position, bio, photo) VALUES
('张明', '创始人 & CEO', '拥有15年软件开发和团队管理经验,曾在多家知名科技公司担任技术负责人', 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80'),
('李婷', '技术总监', '计算机科学博士,专注于人工智能和大数据领域,拥有多项技术专利', 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80');
-- 向社交媒体表插入数据
INSERT INTO social_media (member_id, type, url, qrcode) VALUES
(1, 'linkedin', 'https://linkedin.com/', NULL),
(1, 'github', 'https://github.com/', NULL),
(1, 'wechat', 'javascript:void(0);', '/images/qrcode-wechat.jpg'),
(1, 'wecom', 'javascript:void(0);', '/images/qrcode-wecom.jpg'),
(2, 'linkedin', 'https://linkedin.com/', NULL),
(2, 'github', 'https://github.com/', NULL),
(2, 'xiaohongshu', 'https://xiaohongshu.com/', NULL);

+ 9
- 6
index.html View File

@ -1,13 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title>
</head>
<body>
<title>湖南瀚海黎明信息科技有限公司</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
</body>
</html>

+ 18
- 13
src/App.vue View File

@ -1,6 +1,11 @@
<script setup>
import { ref, onMounted } from 'vue';
import MobileNav from './components/MobileNav.vue';
import { useConfigStore } from '@/stores/config'
import { useCasesStore } from '@/stores/cases'
const { configParams, getConfigParams } = useConfigStore()
const { selectedCase, getSelectedCase } = useCasesStore()
//
const isMobileNavOpen = ref(false);
@ -30,6 +35,8 @@ function closeMobileNav() {
onMounted(() => {
//
getConfigParams()
getSelectedCase()
});
</script>
@ -39,17 +46,15 @@ onMounted(() => {
<header class="header">
<div class="container">
<div class="logo">
<router-link to="/">瀚海黎明</router-link>
<router-link to="/">{{ configParams.navbar_title }}</router-link>
</div>
<nav class="nav">
<ul class="nav-list">
<li><router-link to="/">首页</router-link></li>
<li><router-link to="/services">服务</router-link></li>
<li><router-link to="/cases">案例</router-link></li>
<li><router-link to="/about">关于我们</router-link></li>
<li><router-link to="/team">团队</router-link></li>
<!-- <li><router-link to="/blog">博客</router-link></li> -->
<li><router-link to="/contact">联系我们</router-link></li>
<li v-if="configParams.navbar_about_show == 1"><router-link to="/about">关于我们</router-link></li>
<li v-if="configParams.navbar_team_show == 1"><router-link to="/team">团队</router-link></li>
</ul>
</nav>
<div class="nav-actions">
@ -85,8 +90,8 @@ onMounted(() => {
<div class="container">
<div class="footer-content">
<div class="footer-logo">
<h3>瀚海黎明</h3>
<p>专业软件解决方案提供商</p>
<h3>{{ configParams.navbar_title }}</h3>
<p>{{ configParams.company_description }}</p>
</div>
<div class="footer-links">
<div class="link-group">
@ -98,7 +103,7 @@ onMounted(() => {
<li><router-link to="/about">关于我们</router-link></li>
</ul>
</div>
<div class="link-group">
<!-- <div class="link-group">
<h4>服务</h4>
<ul>
<li><a href="#">定制软件开发</a></li>
@ -106,19 +111,19 @@ onMounted(() => {
<li><a href="#">Web应用开发</a></li>
<li><a href="#">企业软件解决方案</a></li>
</ul>
</div>
</div> -->
<div class="link-group">
<h4>联系我们</h4>
<ul>
<li>深圳市南山区科技园</li>
<li>info@hanhaisoft.com</li>
<li>+86 755-8888-7777</li>
<li>{{ configParams.address }}</li>
<li>{{ configParams.email }}</li>
<li>{{ configParams.phone }}</li>
</ul>
</div>
</div>
</div>
<div class="footer-bottom">
<p>© 2023 瀚海黎明. 保留所有权利.</p>
<p>© 2025 {{ configParams.company_name }}. 保留所有权利.</p>
<div class="footer-nav">
<a href="#">隐私政策</a>
<a href="#">服务条款</a>


+ 16
- 0
src/api/index.js View File

@ -0,0 +1,16 @@
import { get, post, put, del } from '../utils/request'
// API接口管理
const api = {
// 获取配置数据
fetchConfigParams: () => get('/api/officialWebsite/configParams/list'),
getCategoryList: () => get('/api/officialWebsite/caseCategory/list'),
getCasesList: (params) => get('/api/officialWebsite/cases/list', params),
addLeaveMessage: (params) => post('/api/officialWebsite/leaveMessage', params),
getSelectedCase: () => get('/api/officialWebsite/cases/selected'),
getDevelopmentHistory: () => get('/api/officialWebsite/developmentHistory/list'),
getServiceProcess: () => get('/api/officialWebsite/serviceProcess/list')
}
export default api

+ 1
- 0
src/assets/scss/main.scss View File

@ -10,6 +10,7 @@ $text-light: #5a6a7e;
$light-bg: #f8f9fa;
$dark-bg: #1a1a1a;
$shadow-sm: 0 5px 15px rgba(0, 0, 0, 0.05);
$shadow-md: 0 10px 20px rgba(0, 0, 0, 0.08);
$shadow-lg: 0 15px 30px rgba(0, 0, 0, 0.1);
$shadow-xl: 0 10px 30px rgba(0, 0, 0, 0.3);


+ 251
- 0
src/components/CallToAction.vue View File

@ -0,0 +1,251 @@
<script setup>
// props
const props = defineProps({
//
title: {
type: String,
default: '准备好开始您的项目了吗?'
},
//
description: {
type: String,
default: '我们的团队随时准备为您提供专业的技术支持和服务'
},
//
primaryButtonText: {
type: String,
default: '立即咨询'
},
//
primaryButtonLink: {
type: String,
default: '/contact'
},
//
secondaryButtonText: {
type: String,
default: '电话联系'
},
//
secondaryButtonLink: {
type: String,
default: 'tel:+8612345678901'
},
//
showIcon: {
type: Boolean,
default: true
},
//
iconClass: {
type: String,
default: 'fas fa-rocket'
}
});
</script>
<template>
<section class="cta-section" data-aos="fade-up">
<div class="container">
<div class="cta-content">
<div v-if="showIcon" class="cta-icon" data-aos="zoom-in" data-aos-delay="200">
<i :class="iconClass"></i>
</div>
<h2 data-aos="fade-up" data-aos-delay="300">{{ title }}</h2>
<p data-aos="fade-up" data-aos-delay="400">{{ description }}</p>
<div class="cta-buttons" data-aos="fade-up" data-aos-delay="500">
<a :href="primaryButtonLink" class="btn-primary">
{{ primaryButtonText }}
<i class="fas fa-arrow-right"></i>
</a>
<a :href="secondaryButtonLink" class="btn-outline">{{ secondaryButtonText }}</a>
</div>
</div>
</div>
</section>
</template>
<style lang="scss" scoped>
.cta-section {
background: linear-gradient(135deg, #0056b3, #00337f);
background-size: 200% 200%;
color: white;
padding: 80px 0;
text-align: center;
margin-top: 40px;
position: relative;
overflow: hidden;
box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.1);
animation: gradientBG 15s ease infinite;
}
@keyframes gradientBG {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
.cta-section::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml;charset=utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320"%3E%3Cpath fill="%23ffffff" fill-opacity="0.05" d="M0,96L48,112C96,128,192,160,288,186.7C384,213,480,235,576,213.3C672,192,768,128,864,128C960,128,1056,192,1152,213.3C1248,235,1344,213,1392,202.7L1440,192L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"%3E%3C/path%3E%3C/svg%3E');
background-size: cover;
background-position: center;
opacity: 0.1;
z-index: 0;
}
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
}
.cta-content {
position: relative;
z-index: 1;
}
.cta-icon {
margin-bottom: 25px;
i {
font-size: 3.5rem;
color: #fff;
background: rgba(255, 255, 255, 0.1);
width: 100px;
height: 100px;
line-height: 100px;
border-radius: 50%;
display: inline-block;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
&:hover {
transform: translateY(-5px) scale(1.05);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2);
background: rgba(255, 255, 255, 0.2);
}
}
}
.cta-content {
h2 {
font-size: 2.5rem;
margin-bottom: 20px;
font-weight: 700;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
color: #fff;
}
p {
font-size: 1.2rem;
opacity: 0.9;
max-width: 600px;
margin: 0 auto 30px;
line-height: 1.6;
}
}
.cta-buttons {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 30px;
.btn-primary {
padding: 14px 32px;
font-size: 1.1rem;
font-weight: 600;
background-color: #3498db;
color: white;
border: 2px solid #3498db;
border-radius: 30px;
text-decoration: none;
transition: all 0.3s ease;
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2);
i {
margin-left: 8px;
transition: transform 0.3s ease;
}
&:hover {
background-color: #2980b9;
border-color: #2980b9;
transform: translateY(-3px);
i {
transform: translateX(5px);
}
}
}
.btn-outline {
background: transparent;
color: white;
border: 2px solid rgba(255, 255, 255, 0.6);
padding: 14px 32px;
font-size: 1.1rem;
font-weight: 600;
border-radius: 30px;
text-decoration: none;
transition: all 0.3s ease;
&:hover {
background: rgba(255, 255, 255, 0.1);
border-color: white;
transform: translateY(-3px);
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1);
}
}
}
/* 响应式样式 */
@media (max-width: 768px) {
.cta-content {
h2 {
font-size: 2rem;
}
p {
font-size: 1.1rem;
padding: 0 15px;
}
}
.cta-buttons {
flex-direction: column;
align-items: center;
width: 100%;
gap: 15px;
.btn-primary,
.btn-outline {
width: 80%;
max-width: 300px;
text-align: center;
}
}
.cta-icon {
i {
width: 80px;
height: 80px;
line-height: 80px;
font-size: 2.8rem;
}
}
}
</style>

+ 207
- 0
src/components/TeamMemberCard.vue View File

@ -0,0 +1,207 @@
<script setup>
// props
const props = defineProps({
//
member: {
type: Object,
required: true
},
//
enableHover: {
type: Boolean,
default: true
},
//
imageHeight: {
type: String,
default: '300px'
},
//
socialMapping: {
type: Object,
default: () => ({
//
linkedin: { icon: 'fab fa-linkedin', label: 'LinkedIn' },
github: { icon: 'fab fa-github', label: 'GitHub' },
twitter: { icon: 'fab fa-twitter', label: '推特' },
dribbble: { icon: 'fab fa-dribbble', label: 'Dribbble' },
behance: { icon: 'fab fa-behance', label: 'Behance' },
//
wechat: { icon: 'fab fa-weixin', label: '微信' },
wecom: { icon: 'fab fa-weixin', label: '企业微信' },
xiaohongshu: { icon: 'fas fa-book', label: '小红书' },
zhihu: { icon: 'fas fa-z', label: '知乎' },
weibo: { icon: 'fab fa-weibo', label: '微博' },
douyin: { icon: 'fab fa-tiktok', label: '抖音' }
})
}
});
//
const emit = defineEmits(['contact']);
//
const contactMember = (type) => {
emit('contact', {
memberId: props.member.id,
name: props.member.name,
contactType: type
});
};
</script>
<template>
<div class="member-card" :class="{ 'hover-enabled': enableHover }" data-aos="fade-up">
<div class="member-photo" :style="{ height: imageHeight }">
<img :src="member.photo" :alt="member.name">
<div class="floating-socials">
<a v-for="(social, index) in member.social" :key="index" :href="social.url"
:title="socialMapping[social.type]?.label || social.type" target="_blank" rel="noopener noreferrer"
@click.prevent="contactMember(social.type)">
<i :class="socialMapping[social.type]?.icon || `fab fa-${social.type}`"></i>
</a>
</div>
</div>
<div class="member-info">
<h3>{{ member.name }}</h3>
<p class="position">{{ member.position }}</p>
<p class="bio">{{ member.bio }}</p>
</div>
</div>
</template>
<style lang="scss" scoped>
/* 导入全局SCSS变量 */
@use '../assets/scss/main.scss' as *;
.member-card {
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
&.hover-enabled {
transition: transform 0.3s ease, box-shadow 0.3s ease;
&:hover {
transform: translateY(-10px);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
.member-photo img {
transform: scale(1.05);
}
}
}
.member-photo {
position: relative;
overflow: hidden;
&:before {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 50%;
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
z-index: 1;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.floating-socials {
position: absolute;
bottom: 15px;
right: 15px;
display: flex;
gap: 10px;
z-index: 2;
a {
width: 36px;
height: 36px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.9);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
i {
color: $primary-color;
font-size: 18px;
}
&:hover {
background-color: $primary-color;
transform: translateY(-3px);
i {
color: white;
}
}
}
}
}
.member-info {
padding: 20px;
h3 {
font-size: 1.4rem;
margin-bottom: 5px;
color: $text-color;
}
.position {
color: $primary-color;
font-weight: 500;
margin-bottom: 10px;
font-size: 1.1rem;
}
.bio {
color: $text-light;
font-size: 0.95rem;
line-height: 1.5;
margin-bottom: 15px;
}
}
}
/* 响应式处理 */
@media (max-width: 768px) {
.member-card {
.member-info {
h3 {
font-size: 1.2rem;
}
.position {
font-size: 0.9rem;
}
.bio {
font-size: 0.9rem;
}
}
.member-photo .floating-socials a {
width: 32px;
height: 32px;
i {
font-size: 16px;
}
}
}
}
</style>

+ 0
- 167
src/components/case/caseList.vue View File

@ -1,167 +0,0 @@
<script setup>
import { ref, onMounted, computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
//
const route = useRoute();
const router = useRouter();
//
const viewRelatedCase = (id) => {
router.push(`/cases/${id}`);
};
//
const props = defineProps({
cases: {
type: Array,
required: true
},
});
</script>
<template>
<!-- 相关案例 -->
<div class="related-grid">
<div v-for="caseItem in cases" :key="caseItem.id" class="related-case-card" data-aos="fade-up"
@click="viewRelatedCase(caseItem.id)">
<div class="case-image">
<img :src="caseItem.image" :alt="caseItem.title">
<div class="case-overlay">
<span class="view-more">查看详情 <i class="fas fa-arrow-right"></i></span>
</div>
</div>
<div class="case-content">
<div class="case-category">{{ caseItem.category }}</div>
<h3>{{ caseItem.title }}</h3>
<p>{{ caseItem.description }}</p>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
/* 导入全局SCSS变量 */
@use '../../assets/scss/main.scss' as *;
.related-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 30px;
@media (max-width: 768px) {
grid-template-columns: 1fr;
}
}
.related-case-card {
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: $shadow-sm;
transition: all 0.3s ease;
cursor: pointer;
height: 100%;
display: flex;
flex-direction: column;
&:hover {
transform: translateY(-10px);
box-shadow: $shadow-lg;
.case-image img {
transform: scale(1.05);
}
.case-overlay {
opacity: 1;
}
.view-more {
transform: translateY(0);
}
}
}
.case-image {
position: relative;
height: 220px;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.case-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba($primary-color, 0.7);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
}
.view-more {
color: white;
font-weight: 500;
display: flex;
align-items: center;
gap: 8px;
transform: translateY(20px);
transition: transform 0.3s ease 0.1s;
i {
transition: transform 0.2s ease;
}
&:hover i {
transform: translateX(5px);
}
}
}
.case-content {
padding: 25px;
flex-grow: 1;
display: flex;
flex-direction: column;
}
.case-category {
display: inline-block;
padding: 5px 12px;
background-color: rgba($primary-color, 0.1);
color: $primary-color;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 500;
margin-bottom: 15px;
}
.case-content h3 {
font-size: 1.4rem;
color: $text-color;
margin-bottom: 10px;
transition: color 0.3s ease;
}
.case-content p {
color: $text-light;
font-size: 0.95rem;
line-height: 1.6;
margin-bottom: 20px;
flex-grow: 1;
}
</style>

+ 219
- 0
src/components/cases/CaseItem.vue View File

@ -0,0 +1,219 @@
<script setup>
// props
const props = defineProps({
item: {
type: Object,
required: true
},
//
showCategory: {
type: Boolean,
default: true
},
// "link" "button"
buttonType: {
type: String,
default: 'link' // 'link' 'button'
}
});
//
const emit = defineEmits(['view-details']);
const viewDetails = () => {
emit('view-details', props.item.id);
};
</script>
<template>
<div class="case-card" data-aos="fade-up" :data-aos-delay="item.delay">
<div class="case-image">
<div v-if="showCategory" class="category-tag">{{ item.categoryName }}</div>
<img :src="item.imageUrl && item.imageUrl.split(',')[0]" :alt="item.title" />
</div>
<div class="case-content">
<h3>{{ item.title }}</h3>
<p>{{ item.description }}</p>
<!-- 根据buttonType渲染不同的按钮样式 -->
<a v-if="buttonType === 'link'" href="#" class="btn-link" @click.prevent="viewDetails">查看详情</a>
<button v-else class="btn-details" @click="viewDetails">查看详情</button>
</div>
</div>
</template>
<style lang="scss" scoped>
/* 导入全局SCSS变量 */
@use '../../assets/scss/main.scss' as *;
// .case-card {
// background: white;
// border-radius: 8px;
// overflow: hidden;
// box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
// transition: transform 0.3s ease, box-shadow 0.3s ease;
// &:hover {
// transform: translateY(-10px);
// box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
// .case-image img {
// transform: scale(1.05);
// }
// }
// .case-image {
// position: relative;
// img {
// width: 100%;
// height: 300px;
// object-fit: cover;
// transition: transform 0.5s ease;
// }
// .category-tag {
// position: absolute;
// top: 15px;
// right: 15px;
// background-color: rgba(52, 152, 219, 0.9);
// color: white;
// padding: 6px 12px;
// border-radius: 4px;
// font-size: 0.8rem;
// font-weight: 600;
// z-index: 2;
// }
// }
// .case-content {
// padding: 25px;
// h3 {
// font-size: 1.5rem;
// margin-bottom: 15px;
// color: #2c3e50;
// }
// p {
// color: #7f8c8d;
// margin-bottom: 20px;
// }
// }
// }
// .btn-link {
// color: #3498db;
// text-decoration: none;
// font-weight: 600;
// position: relative;
// cursor: pointer;
// &:after {
// content: '';
// position: absolute;
// width: 0;
// height: 2px;
// bottom: -5px;
// left: 0;
// background-color: #3498db;
// transition: width 0.3s ease;
// }
// &:hover {
// &:after {
// width: 100%;
// }
// }
// }
// .btn-details {
// background-color: #3498db;
// color: white;
// border: none;
// padding: 10px 20px;
// border-radius: 4px;
// font-weight: 600;
// cursor: pointer;
// transition: background-color 0.3s ease;
// &:hover {
// background-color: #2980b9;
// }
// }
.case-card {
border-radius: 12px;
overflow: hidden;
box-shadow: $shadow-sm;
transition: transform 0.3s ease, box-shadow 0.3s ease;
background: white;
position: relative;
&:hover {
transform: translateY(-10px);
box-shadow: $shadow-lg;
.case-image img {
transform: scale(1.05);
}
}
.case-image {
position: relative;
overflow: hidden;
img {
width: 100%;
height: 250px;
object-fit: cover;
transition: transform 0.5s ease;
}
}
.category-tag {
position: absolute;
top: 15px;
right: 15px;
background: rgba($primary-color, 0.8);
color: white;
padding: 5px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 500;
z-index: 1;
}
.case-content {
padding: 25px;
background: white;
h3 {
font-size: 1.3rem;
margin-bottom: 10px;
color: $text-color;
}
p {
color: $text-light;
margin-bottom: 15px;
line-height: 1.6;
}
}
}
.btn-details {
display: inline-block;
padding: 8px 20px;
background: transparent;
border: 1px solid $primary-color;
color: $primary-color;
border-radius: 30px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
background: $primary-color;
color: white;
}
}
</style>

+ 74
- 0
src/components/home/CaseSection.vue View File

@ -0,0 +1,74 @@
<script setup>
//
import CaseItem from '../cases/CaseItem.vue';
import { useRouter } from 'vue-router';
import { useCasesStore } from '@/stores/cases'
const { selectedCase } = useCasesStore()
//
const router = useRouter();
//
const viewCaseDetails = (id) => {
router.push(`/cases/${id}`);
};
</script>
<template>
<section class="cases-section" data-aos="fade-up">
<div class="section-header">
<h2>精选案例</h2>
<p>我们的成功项目展示</p>
</div>
<div class="cases-grid">
<CaseItem
v-for="item in selectedCase"
:key="item.id"
:item="item"
buttonType="link"
@view-details="viewCaseDetails"
/>
</div>
</section>
</template>
<style lang="scss" scoped>
/* 全局样式 */
.section-header {
text-align: center;
margin-bottom: 50px;
h2 {
font-size: 2.5rem;
color: #2c3e50;
margin-bottom: 15px;
}
p {
font-size: 1.2rem;
color: #7f8c8d;
}
}
.cases-section {
padding: 100px 20px;
background-color: #f8f9fa;
.cases-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 3fr));
gap: 30px;
margin-top: 40px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
}
}
@media (max-width: 768px) {
.cases-grid {
grid-template-columns: 2fr;
}
}
</style>

+ 115
- 120
src/components/home/ServiceSection.vue View File

@ -1,137 +1,132 @@
<script setup>
//
const services = [
{
id: 1,
icon: 'https://cdn-icons-png.flaticon.com/512/2920/2920277.png',
title: '定制软件开发',
description: '根据您的业务需求,量身定制专属软件解决方案',
delay: 100
},
{
id: 2,
icon: 'https://cdn-icons-png.flaticon.com/512/2586/2586488.png',
title: '移动应用开发',
description: '打造高性能、用户友好的iOS和Android应用',
delay: 200
},
{
id: 3,
icon: 'https://cdn-icons-png.flaticon.com/512/1055/1055687.png',
title: 'Web应用开发',
description: '开发响应式、现代化的Web应用和网站',
delay: 300
},
{
id: 4,
icon: 'https://cdn-icons-png.flaticon.com/512/1935/1935765.png',
title: '企业软件解决方案',
description: '提供ERP、CRM等企业级软件解决方案',
delay: 400
},
{
id: 5,
icon: 'https://cdn-icons-png.flaticon.com/512/4727/4727266.png',
title: '云服务与DevOps',
description: '云架构设计、部署和DevOps自动化服务',
delay: 500
},
{
id: 6,
icon: 'https://cdn-icons-png.flaticon.com/512/1055/1055666.png',
title: 'UI/UX设计',
description: '创造直观、美观且用户友好的界面设计',
delay: 600
}
{
id: 1,
icon: 'https://cdn-icons-png.flaticon.com/512/2920/2920277.png',
title: '定制软件开发',
description: '根据您的业务需求,量身定制专属软件解决方案',
delay: 100
},
{
id: 2,
icon: 'https://cdn-icons-png.flaticon.com/512/2586/2586488.png',
title: '移动应用开发',
description: '打造高性能、用户友好的iOS和Android应用',
delay: 200
},
{
id: 3,
icon: 'https://cdn-icons-png.flaticon.com/512/1055/1055687.png',
title: 'Web应用开发',
description: '开发响应式、现代化的Web应用和网站',
delay: 300
},
{
id: 4,
icon: 'https://cdn-icons-png.flaticon.com/512/1935/1935765.png',
title: '企业软件解决方案',
description: '提供ERP、CRM等企业级软件解决方案',
delay: 400
},
{
id: 5,
icon: 'https://cdn-icons-png.flaticon.com/512/4727/4727266.png',
title: '云服务与DevOps',
description: '云架构设计、部署和DevOps自动化服务',
delay: 500
},
{
id: 6,
icon: 'https://cdn-icons-png.flaticon.com/512/1055/1055666.png',
title: 'UI/UX设计',
description: '创造直观、美观且用户友好的界面设计',
delay: 600
}
];
</script>
<template>
<section class="services-section" data-aos="fade-up">
<div class="section-header">
<h2>我们的服务</h2>
<p>为您的业务提供全方位的软件解决方案</p>
</div>
<div class="services-grid">
<div
v-for="service in services"
:key="service.id"
class="service-card"
data-aos="fade-up"
:data-aos-delay="service.delay"
>
<div class="service-icon">
<img :src="service.icon" :alt="service.title" />
<section class="services-section" data-aos="fade-up">
<div class="section-header">
<h2>我们的服务</h2>
<p>为您的业务提供全方位的软件解决方案</p>
</div>
<div class="services-grid">
<div v-for="service in services" :key="service.id" class="service-card" data-aos="fade-up"
:data-aos-delay="service.delay">
<div class="service-icon">
<img :src="service.icon" :alt="service.title" />
</div>
<h3>{{ service.title }}</h3>
<p>{{ service.description }}</p>
</div>
</div>
<h3>{{ service.title }}</h3>
<p>{{ service.description }}</p>
</div>
</div>
</section>
</section>
</template>
<style lang="scss" scoped>
.services-section {
padding: 100px 20px;
background-color: #f8f9fa;
.section-header {
text-align: center;
margin-bottom: 50px;
h2 {
font-size: 2.5rem;
color: #2c3e50;
margin-bottom: 15px;
}
p {
font-size: 1.2rem;
color: #7f8c8d;
padding: 100px 20px;
background-color: #f8f9fa;
.section-header {
text-align: center;
margin-bottom: 50px;
h2 {
font-size: 2.5rem;
color: #2c3e50;
margin-bottom: 15px;
}
p {
font-size: 1.2rem;
color: #7f8c8d;
}
}
}
.services-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin-top: 40px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
.service-card {
background: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
transition: transform 0.3s ease, box-shadow 0.3s ease;
text-align: center;
&:hover {
transform: translateY(-10px);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
}
.service-icon {
margin-bottom: 20px;
img {
width: 80px;
height: 80px;
.services-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin-top: 40px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
.service-card {
background: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
transition: transform 0.3s ease, box-shadow 0.3s ease;
text-align: center;
&:hover {
transform: translateY(-10px);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
}
.service-icon {
margin-bottom: 20px;
img {
width: 80px;
height: 80px;
}
}
h3 {
font-size: 1.5rem;
margin-bottom: 15px;
color: #2c3e50;
}
p {
color: #7f8c8d;
}
}
}
h3 {
font-size: 1.5rem;
margin-bottom: 15px;
color: #2c3e50;
}
p {
color: #7f8c8d;
}
}
}
}
</style>

+ 1
- 0
src/main.js View File

@ -15,6 +15,7 @@ import './assets/scss/main.scss'
// 导入FontAwesome图标库
import '@fortawesome/fontawesome-free/css/all.min.css'
// 创建Pinia实例
const pinia = createPinia()


+ 19
- 0
src/stores/cases.js View File

@ -0,0 +1,19 @@
import { defineStore } from 'pinia'
import { ref, computed, reactive } from 'vue'
import api from '@/api'
// 定义案例数据存储
export const useCasesStore = defineStore('cases', () => {
const selectedCase = ref([])
const getSelectedCase = async () => {
const res = await api.getSelectedCase()
selectedCase.value = res.data
}
return {
selectedCase,
getSelectedCase,
}
})

+ 0
- 150
src/stores/casesStore.js View File

@ -1,150 +0,0 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
// 定义案例数据存储
export const useCasesStore = defineStore('cases', () => {
// 案例数据
const casesList = ref([
{
id: 1,
title: '企业资源管理系统',
description: '为某制造企业开发的一套完整ERP系统,实现了生产、销售、库存等全流程管理',
image: 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
category: '企业系统',
client: '某制造业集团',
completionDate: '2023年6月',
services: ['系统架构设计', '数据库优化', '前端开发', '后端开发', '系统集成'],
challenge: '客户面临的主要挑战是多个业务系统之间数据孤岛问题,导致信息流通不畅,管理效率低下。同时,随着业务规模扩大,原有系统已无法满足日益增长的业务需求。',
solution: '我们为客户设计并实现了一套全面的企业资源管理系统,整合了生产、销售、采购、库存、财务等多个模块。系统采用微服务架构,确保了各模块之间的松耦合与高内聚,同时保证了系统的可扩展性和稳定性。我们还为客户定制了数据分析和决策支持功能,帮助管理层快速获取业务洞察。',
results: '系统上线后,客户的运营效率提升了35%,信息处理时间缩短了60%,库存周转率提高了25%,大幅降低了运营成本。同时,实时的数据分析功能帮助客户更快速地响应市场变化,提升了企业的竞争力。',
testimonial: '微隐软件工作室的团队展现了卓越的专业能力和服务态度。他们不仅理解我们的业务需求,还能提供创新的技术解决方案。新系统极大地提升了我们的运营效率,是我们数字化转型的重要一步。',
testimonialAuthor: '张总 - 客户CIO',
gallery: [
'https://images.unsplash.com/photo-1454165804606-c3d57bc86b40?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
'https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
'https://images.unsplash.com/photo-1551434678-e076c223a692?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80'
]
},
{
id: 2,
title: '电商平台重构',
description: '帮助某电商企业重构其线上平台,提升用户体验和系统性能,实现销售额提升30%',
image: 'https://images.unsplash.com/photo-1556742049-0cfed4f6a45d?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
category: 'Web应用',
client: '某知名电商企业',
completionDate: '2023年9月',
services: ['用户体验设计', '前端重构', '后端优化', '性能调优', '安全加固'],
challenge: '客户的电商平台面临用户体验不佳、系统响应缓慢、移动端适配不足等问题,导致用户流失和转化率下降。同时,随着业务量增长,系统稳定性也面临挑战。',
solution: '我们对客户的电商平台进行了全面重构,采用了现代化的前端框架和响应式设计,优化了用户界面和交互流程。在后端,我们重构了核心服务,引入了微服务架构和缓存机制,提升了系统性能和可扩展性。同时,我们还加强了系统的安全性,实现了全面的HTTPS和数据加密。',
results: '重构后的平台页面加载速度提升了65%,用户停留时间增加了40%,转化率提高了25%,最终带来了30%的销售额增长。系统的稳定性也得到了显著提升,即使在促销高峰期也能保持良好的性能。',
testimonial: '微隐软件工作室的团队展现了卓越的技术实力和创新思维。他们不仅解决了我们平台的技术问题,还从用户体验和业务角度提供了宝贵建议。重构后的平台获得了用户的一致好评,为我们带来了实质性的业务增长。',
testimonialAuthor: '李总 - 客户产品总监',
gallery: [
'https://images.unsplash.com/photo-1556740758-90de374c12ad?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
'https://images.unsplash.com/photo-1556740772-1a741d976155?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
'https://images.unsplash.com/photo-1556761175-129418cb2dfe?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80'
]
},
{
id: 3,
title: '医疗服务APP',
description: '为连锁医疗机构开发的患者服务APP,实现在线挂号、问诊和健康管理等功能',
image: 'https://images.unsplash.com/photo-1576091160550-2173dba999ef?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
category: '移动应用',
client: '某连锁医疗机构',
completionDate: '2023年12月',
services: ['移动应用开发', 'UI/UX设计', '后端API开发', '数据安全', '系统集成'],
challenge: '客户希望通过数字化手段提升患者服务体验,减少排队等待时间,同时提高医疗资源利用效率。传统的线下预约和就诊流程繁琐,患者满意度不高。',
solution: '我们为客户开发了一款功能全面的医疗服务APP,支持在线挂号、远程问诊、检查报告查询、健康档案管理等功能。APP采用了直观的用户界面和流畅的交互设计,确保各年龄段用户都能轻松使用。在技术层面,我们实现了与医院HIS系统的无缝集成,并采用了严格的数据加密和隐私保护措施。',
results: 'APP上线后,患者预约效率提升了80%,平均等待时间减少了65%,患者满意度提高了45%。同时,医生的工作效率也得到了提升,资源利用率增加了30%。APP已成为客户数字化医疗服务的核心平台。',
testimonial: '微隐软件工作室深入理解了医疗行业的特殊需求,为我们打造了一款既专业又易用的医疗服务APP。他们在确保数据安全和系统稳定性方面表现出色,为我们的患者提供了便捷、高效的服务体验。',
testimonialAuthor: '王院长 - 客户医疗总监',
gallery: [
'https://images.unsplash.com/photo-1576091160399-112ba8d25d1d?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
'https://images.unsplash.com/photo-1579684385127-1ef15d508118?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
'https://images.unsplash.com/photo-1583324113626-70df0f4deaab?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80'
]
},
{
id: 4,
title: '金融数据可视化平台',
description: '为金融机构开发的数据分析和可视化平台,帮助决策者快速洞察市场趋势',
image: 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
category: 'Web应用',
client: '某投资管理公司',
completionDate: '2024年2月',
services: ['数据分析', '可视化设计', '前端开发', 'API集成', '实时数据处理'],
challenge: '客户需要一个能够整合多源金融数据,并提供直观、实时的可视化分析工具,帮助投资分析师和决策者快速识别市场趋势和投资机会。',
solution: '我们为客户开发了一个功能强大的金融数据可视化平台,整合了市场数据、公司财报、宏观经济指标等多种数据源。平台提供了丰富的图表类型和分析工具,支持自定义仪表盘和报告。我们采用了高性能的前端渲染技术和实时数据处理架构,确保了大数据量下的流畅体验。',
results: '平台上线后,客户的数据分析效率提升了70%,决策周期缩短了50%,投资组合表现超过了基准指数15%。平台的预测模型准确率达到了85%,为客户提供了显著的竞争优势。',
testimonial: '微隐软件工作室交付的数据可视化平台超出了我们的预期。他们不仅具备出色的技术能力,还展现了对金融行业的深刻理解。平台直观的界面和强大的分析功能极大地提升了我们的决策效率和准确性。',
testimonialAuthor: '赵总 - 客户投资总监',
gallery: [
'https://images.unsplash.com/photo-1460925895917-afdab827c52f?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
'https://images.unsplash.com/photo-1590283603385-17ffb3a7f29f?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
'https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80'
]
},
{
id: 5,
title: '智慧校园系统',
description: '为教育机构打造的一体化校园管理系统,涵盖教学、行政、学生服务等多个模块',
image: 'https://images.unsplash.com/photo-1523240795612-9a054b0db644?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
category: '企业系统',
client: '某知名高校',
completionDate: '2023年8月',
services: ['系统规划', '软件开发', '数据迁移', '用户培训', '持续支持'],
challenge: '客户面临的挑战是多个独立系统并行运行,数据不一致,管理效率低下。学生和教职工需要在多个系统间切换,用户体验不佳。',
solution: '我们为客户设计并实现了一套集成化的智慧校园系统,整合了教务管理、学生服务、行政办公、资源管理等多个模块。系统采用了统一的用户界面和数据标准,实现了单点登录和数据共享。我们还开发了移动端应用,方便师生随时随地访问系统功能。',
results: '系统上线后,管理效率提升了50%,数据处理错误减少了80%,用户满意度提高了60%。系统的自助服务功能减轻了行政人员的工作负担,让他们能够专注于更有价值的任务。',
testimonial: '微隐软件工作室的团队展现了卓越的项目管理和技术能力。他们深入理解了我们复杂的业务需求,并提供了一套既全面又易用的解决方案。新系统极大地改善了我们的管理效率和服务质量。',
testimonialAuthor: '陈校长 - 客户负责人',
gallery: [
'https://images.unsplash.com/photo-1523050854058-8df90110c9f1?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
'https://images.unsplash.com/photo-1562774053-701939374585?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
'https://images.unsplash.com/photo-1577896851231-70ef18881754?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80'
]
},
{
id: 6,
title: '品牌官网设计',
description: '为高端品牌设计的响应式官方网站,展现品牌形象并提升用户转化率',
image: 'https://images.unsplash.com/photo-1561070791-2526d30994b5?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
category: 'UI/UX设计',
client: '某高端消费品牌',
completionDate: '2024年1月',
services: ['品牌策略', 'UI/UX设计', '前端开发', 'CMS实现', 'SEO优化'],
challenge: '客户的原有网站设计过时,无法有效展现品牌高端形象,且缺乏移动端适配,导致用户体验不佳和转化率低下。',
solution: '我们为客户重新设计了品牌官网,采用了现代简约的设计风格,突出品牌的高端定位。网站采用了响应式设计,确保在各种设备上都能提供出色的用户体验。我们还优化了产品展示和购买流程,降低了用户转化的摩擦。在技术层面,我们实现了高性能的前端架构和易用的内容管理系统。',
results: '新网站上线后,用户停留时间增加了45%,页面跳出率降低了30%,转化率提升了35%。网站的加载速度提高了60%,搜索引擎排名也有显著提升。客户的品牌形象得到了有效提升,线上销售额增长了40%。',
testimonial: '微隐软件工作室的设计团队展现了卓越的创意和专业能力。他们不仅理解我们的品牌价值,还能将其完美地转化为视觉设计。新网站获得了客户和合作伙伴的一致好评,成为了我们品牌传播的重要窗口。',
testimonialAuthor: '林总 - 客户市场总监',
gallery: [
'https://images.unsplash.com/photo-1542744094-3a31f272c490?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
'https://images.unsplash.com/photo-1542744173-8659239e9452?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
'https://images.unsplash.com/photo-1542744173-05336fcc7ad4?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80'
]
}
])
// 获取单个案例的方法
const getCaseById = (id) => {
return casesList.value.find(item => item.id === id) || null
}
// 获取相关案例的方法
const getRelatedCases = (currentCaseId, category) => {
return casesList.value
.filter(item =>
item.id !== currentCaseId &&
item.category === category
)
.slice(0, 3)
}
return {
casesList,
getCaseById,
getRelatedCases
}
})

+ 26
- 0
src/stores/config.js View File

@ -0,0 +1,26 @@
import { defineStore } from 'pinia'
import { ref, computed, reactive } from 'vue'
import api from '@/api'
// 定义案例数据存储
export const useConfigStore = defineStore('config', () => {
const configParams = reactive({})
const map = {
0 : 'text',
1 : 'content',
2 : 'imageUrl',
}
const getConfigParams = async () => {
const res = await api.fetchConfigParams()
res.forEach(item => {
configParams[item.code] = item[map[item.type]]
})
}
return {
configParams,
getConfigParams
}
})

+ 0
- 84
src/utils/README.md View File

@ -1,84 +0,0 @@
# API请求工具使用说明
## 简介
本项目使用Axios封装了HTTP请求,并集中管理API接口,便于维护和使用。
## 文件结构
- `request.js`: Axios实例配置和请求/响应拦截器
- `api.js`: 集中管理所有API接口
## 使用方法
### 在组件中使用
```javascript
<script setup>
import { ref, onMounted } from 'vue'
import api from '@/utils/api'
// 定义响应式数据
const homeData = ref(null)
const loading = ref(false)
const error = ref(null)
// 获取首页数据
const fetchHomeData = async () => {
loading.value = true
error.value = null
try {
// 调用API
const res = await api.getHomeData()
homeData.value = res.data
} catch (err) {
error.value = err.message || '获取数据失败'
console.error('获取首页数据失败:', err)
} finally {
loading.value = false
}
}
// 页面加载时获取数据
onMounted(() => {
fetchHomeData()
})
</script>
```
### 添加新的API接口
在`api.js`文件中添加新的接口定义:
```javascript
// 在api对象中添加新接口
const api = {
// 现有接口...
// 新增接口
getNewData: (params) => get('/new-endpoint', params),
submitNewForm: (data) => post('/new-form', data),
}
```
### 自定义请求配置
所有请求方法都支持传入自定义配置:
```javascript
// 带自定义配置的请求
api.getCustomData = (params) => get('/custom', params, {
timeout: 5000,
headers: {
'Custom-Header': 'value'
}
})
```
## 注意事项
1. API基础URL通过环境变量`VITE_API_BASE_URL`配置
2. 默认已配置请求超时时间为15秒
3. 默认已配置token认证,会自动从localStorage获取token并添加到请求头
4. 响应拦截器会自动处理常见的HTTP错误状态码

+ 0
- 46
src/utils/api.js View File

@ -1,46 +0,0 @@
import { get, post, put, del } from './request'
// API接口管理
const api = {
// 示例接口
// 获取首页数据
getHomeData: () => get('/home'),
// 获取关于我们数据
getAboutData: () => get('/about'),
// 获取服务列表
getServices: () => get('/services'),
// 获取案例列表
getCases: (params) => get('/cases', params),
// 获取案例详情
getCaseDetail: (id) => get(`/cases/${id}`),
// 获取团队成员
getTeamMembers: () => get('/team'),
// 获取博客列表
getBlogs: (params) => get('/blogs', params),
// 获取博客详情
getBlogDetail: (id) => get(`/blogs/${id}`),
// 提交联系表单
submitContact: (data) => post('/contact', data),
// 用户相关接口
user: {
// 登录
login: (data) => post('/user/login', data),
// 注册
register: (data) => post('/user/register', data),
// 获取用户信息
getInfo: () => get('/user/info'),
// 更新用户信息
updateInfo: (data) => put('/user/info', data)
}
}
export default api

+ 44
- 100
src/utils/request.js View File

@ -1,112 +1,56 @@
import axios from 'axios'
import axios from 'axios';
// 创建axios实例
const service = axios.create({
// 设置基础URL,如果有环境变量可以使用环境变量
baseURL: import.meta.env.VITE_API_BASE_URL || '',
// 请求超时时间
timeout: 15000,
// 请求头设置
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
})
const api = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API,
timeout: 50000,
headers: {
'Content-Type': 'application/json'
}
});
// 请求拦截器
service.interceptors.request.use(
config => {
// 在发送请求之前做些什么
// 例如:可以在这里统一添加token
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
api.interceptors.request.use(
config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => {
return Promise.reject(error);
}
return config
},
error => {
// 对请求错误做些什么
console.error('请求错误:', error)
return Promise.reject(error)
}
)
);
// 响应拦截器
service.interceptors.response.use(
response => {
// 对响应数据做点什么
const res = response.data
// 根据自己的业务逻辑判断响应是否成功
// 这里假设后端返回的数据格式为 { code: 200, data: {}, message: '' }
if (res.code && res.code !== 200) {
// 处理业务错误
console.error('业务错误:', res.message || '未知错误')
return Promise.reject(new Error(res.message || '未知错误'))
}
return res
},
error => {
// 对响应错误做点什么
let message = '网络错误'
if (error.response) {
// 请求已发出,但服务器响应的状态码不在 2xx 范围内
switch (error.response.status) {
case 401:
message = '未授权,请重新登录'
// 可以在这里处理登出逻辑
break
case 403:
message = '拒绝访问'
break
case 404:
message = '请求的资源不存在'
break
case 500:
message = '服务器内部错误'
break
default:
message = `请求错误: ${error.response.status}`
}
} else if (error.request) {
// 请求已发出,但没有收到响应
message = '服务器未响应'
} else {
// 请求配置有误
message = error.message
api.interceptors.response.use(
response => {
return response.data;
},
error => {
if (error.response && error.response.status === 401) {
// 处理未授权的情况
// 可以在这里触发退出登录或跳转到登录页
}
return Promise.reject(error);
}
console.error('响应错误:', message)
return Promise.reject(error)
}
)
);
// 封装GET请求
export function get(url, params, config = {}) {
return service.get(url, {
params,
...config
})
}
// 封装HTTP请求方法
export const get = (url, params) => {
return api.get(url, { params });
};
// 封装POST请求
export function post(url, data, config = {}) {
return service.post(url, data, config)
}
export const post = (url, data) => {
return api.post(url, data);
};
// 封装PUT请求
export function put(url, data, config = {}) {
return service.put(url, data, config)
}
export const put = (url, data) => {
return api.put(url, data);
};
// 封装DELETE请求
export function del(url, params, config = {}) {
return service.delete(url, {
params,
...config
})
}
export const del = (url) => {
return api.delete(url);
};
// 导出axios实例
export default service
export default api;

+ 597
- 110
src/views/company/About.vue View File

@ -1,141 +1,628 @@
<script setup>
import { onMounted } from 'vue';
import { onMounted, ref, watch } from 'vue';
import PageHeader from '../../components/PageHeader.vue';
onMounted(() => {
//
import { useConfigStore } from '@/stores/config'
const { configParams } = useConfigStore()
import api from '@/api'
const developmentHistory = ref([])
const getDevelopmentHistory = async () => {
const res = await api.getDevelopmentHistory()
developmentHistory.value = res.data
}
const activeTab = ref('profile');
const isLoading = ref(false);
const historyLoaded = ref(false);
//
watch(activeTab, async (newTab) => {
if (newTab === 'history' && !historyLoaded.value) {
await loadHistoryData();
}
});
//
const loadHistoryData = async () => {
try {
isLoading.value = true;
await getDevelopmentHistory();
historyLoaded.value = true;
} catch (error) {
console.error('加载发展历程失败:', error);
} finally {
isLoading.value = false;
}
};
onMounted(async () => {
//
await loadHistoryData();
});
</script>
<template>
<div class="about-page">
<PageHeader
title="关于我们"
subtitle="了解瀚海黎明的故事与使命"
backgroundImage="https://images.unsplash.com/photo-1522071820081-009f0129c71c?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80"
/>
<section class="company-intro">
<div class="container">
<div class="intro-content" data-aos="fade-up">
<h2>公司简介</h2>
<p>瀚海黎明成立于2020年是一家专注于为企业提供高质量软件开发外包服务的技术公司我们的团队由经验丰富的软件工程师设计师和项目经理组成致力于通过技术创新帮助企业实现数字化转型</p>
<p>多年来我们已成功为金融医疗教育零售等多个行业的客户提供了定制化的软件解决方案帮助他们提升运营效率降低成本增强市场竞争力</p>
</div>
</div>
</section>
<section class="company-history" data-aos="fade-up">
<div class="container">
<h2>发展历程</h2>
<div class="timeline" v-for="(item, index) in 5">
<div class="timeline-item" data-aos="fade-right">
<div class="year">2020</div>
<div class="event">
<h3>公司成立</h3>
<p>瀚海黎明在深圳正式成立开始提供软件开发服务</p>
<div class="about-page">
<PageHeader :title="configParams.about_banner_title" :subtitle="configParams.about_banner_subtitle"
:backgroundImage="configParams.about_banner_bg" />
<section class="company-tabs">
<div class="container">
<div class="tab-nav">
<div class="tab" :class="{ active: activeTab === 'profile' }" @click="activeTab = 'profile'">
<i class="fas fa-building"></i> 公司简介
</div>
<div class="tab"
v-if="developmentHistory.length > 0"
:class="{ active: activeTab === 'history' }" @click="activeTab = 'history'">
<i class="fas fa-history"></i> 发展历程
</div>
<!-- <div class="tab" :class="{ active: activeTab === 'culture' }" @click="activeTab = 'culture'">
<i class="fas fa-lightbulb"></i> 企业文化
</div> -->
</div>
</div>
</div>
</section>
<div v-if="activeTab === 'profile'">
<section class="company-intro" v-if="configParams.about_company_profile">
<div class="container">
<div class="section-header" data-aos="fade-up">
<h2><i class="fas fa-building"></i> 公司简介</h2>
<div class="separator"></div>
</div>
<div class="intro-wrapper">
<div class="intro-content" data-aos="fade-up">
<div class="content-card">
<div class="card-decoration">
<i class="fas fa-quote-left"></i>
</div>
<div v-html="configParams.about_company_profile"></div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</section>
<section class="company-culture" data-aos="fade-up">
<div class="container">
<h2>企业文化</h2>
<div class="culture-values">
<div class="value-item" data-aos="fade-up" data-aos-delay="100">
<div class="value-icon"><!-- 图标 --></div>
<h3>创新</h3>
<p>不断探索新技术为客户创造更大价值</p>
</div>
<!-- 其他价值观 -->
<div v-if="activeTab === 'history'">
<section class="company-history" data-aos="fade-up">
<div class="container">
<div class="section-header" data-aos="fade-up">
<h2><i class="fas fa-history"></i> 发展历程</h2>
<div class="separator"></div>
</div>
<!-- 加载状态 -->
<div v-if="isLoading" class="loading-container">
<div class="loading-spinner">
<i class="fas fa-circle-notch fa-spin"></i>
</div>
<p>正在加载发展历程...</p>
</div>
<!-- 无数据状态 -->
<div v-else-if="developmentHistory.length === 0" class="no-data-container">
<i class="fas fa-info-circle"></i>
<p>暂无发展历程数据</p>
<button class="reload-btn" @click="loadHistoryData">
<i class="fas fa-sync-alt"></i> 重新加载
</button>
</div>
<!-- 数据显示 -->
<div v-else class="timeline-container">
<div class="timeline" v-for="(item, index) in developmentHistory" :key="index">
<div class="timeline-item" :class="{ 'right': index % 2 === 1 }" data-aos="fade-up">
<div class="year">
<span>{{ item.date }}</span>
<div class="dot"></div>
</div>
<div class="event">
<h3>{{ item.title }}</h3>
<p>{{ item.content }}</p>
<i class="milestone-icon fas" :class="index % 4 === 0 ? 'fa-rocket' :
index % 4 === 1 ? 'fa-award' :
index % 4 === 2 ? 'fa-handshake' : 'fa-trophy'"></i>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</section>
</div>
</div>
</template>
<style lang="scss" scoped>
/* 导入全局SCSS变量 */
@use '../../assets/scss/main.scss' as *;
@use "sass:color";
.section-header {
text-align: center;
margin-bottom: 50px;
h2 {
font-size: 2.5rem;
color: $primary-color;
margin-bottom: 20px;
i {
margin-right: 12px;
}
}
.separator {
height: 3px;
width: 80px;
background: $primary-color;
margin: 0 auto;
}
}
.company-tabs {
background-color: white;
padding: 20px 0;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
position: sticky;
top: 0;
z-index: 100;
.tab-nav {
display: flex;
justify-content: center;
gap: 20px;
.tab {
padding: 12px 24px;
border-radius: 30px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 600;
display: flex;
align-items: center;
/* 页面头部样式已移至PageHeader组件 */
i {
margin-right: 8px;
}
&:hover {
background-color: rgba($primary-color, 0.1);
}
&.active {
background-color: $primary-color;
color: white;
}
}
}
}
.company-intro {
padding: 80px 0;
background-color: white;
padding: 100px 0;
background-color: white;
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
width: 300px;
height: 300px;
background: rgba($primary-color, 0.05);
border-radius: 50%;
top: -150px;
left: -150px;
z-index: 0;
}
&::after {
content: '';
position: absolute;
width: 200px;
height: 200px;
background: rgba($primary-color, 0.05);
border-radius: 50%;
bottom: -100px;
right: -100px;
z-index: 0;
}
.intro-wrapper {
position: relative;
z-index: 1;
max-width: 900px;
margin: 0 auto;
@media (max-width: 992px) {
padding: 0 20px;
}
}
.intro-content {
line-height: 1.8;
.content-card {
background: white;
border-radius: 15px;
padding: 40px 50px;
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.08);
position: relative;
border-top: 5px solid $primary-color;
transition: transform 0.3s ease, box-shadow 0.3s ease;
&:hover {
transform: translateY(-5px);
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.12);
}
.card-decoration {
position: absolute;
top: -25px;
left: 50px;
width: 50px;
height: 50px;
background: $primary-color;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
box-shadow: 0 5px 15px rgba($primary-color, 0.5);
}
div[v-html] {
font-size: 1.1rem;
color: $text-color;
line-height: 1.8;
margin-top: 10px;
::v-deep(h3) {
color: $primary-color;
margin-top: 0;
font-size: 1.8rem;
position: relative;
display: inline-block;
&::after {
content: '';
position: absolute;
bottom: -8px;
left: 0;
width: 50px;
height: 3px;
background: $primary-color;
}
}
::v-deep(p) {
margin-bottom: 1.2rem;
}
::v-deep(strong) {
color: color.adjust($text-color, $lightness: -25%);
font-weight: 600;
}
::v-deep(ul) {
padding-left: 20px;
li {
margin-bottom: 10px;
position: relative;
&::before {
content: '•';
color: $primary-color;
font-weight: bold;
display: inline-block;
width: 1em;
margin-left: -1em;
}
}
}
}
&::before {
content: '';
position: absolute;
top: 20px;
right: 20px;
width: 60px;
height: 60px;
background: rgba($primary-color, 0.05);
border-radius: 50%;
}
&::after {
content: '';
position: absolute;
bottom: 30px;
left: 30px;
width: 40px;
height: 40px;
border: 3px solid rgba($primary-color, 0.1);
border-radius: 50%;
}
}
}
}
.company-history {
padding: 80px 0;
background-color: $light-bg;
padding: 80px 0;
background-color: $light-bg;
.timeline-container {
position: relative;
max-width: 1000px;
margin: 60px auto 0;
&::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 50%;
width: 2px;
background: $primary-color;
transform: translateX(-50%);
}
}
.timeline-item {
position: relative;
margin-bottom: 60px;
width: 50%;
padding-right: 40px;
&.right {
margin-left: 50%;
padding-right: 0;
padding-left: 40px;
.year {
right: auto;
left: -100px;
text-align: right;
}
.dot {
right: auto;
left: -9px;
}
}
.year {
position: absolute;
right: -100px;
top: 15px;
font-size: 24px;
font-weight: bold;
color: $primary-color;
width: 80px;
text-align: left;
}
.dot {
position: absolute;
right: -9px;
top: 20px;
width: 16px;
height: 16px;
border-radius: 50%;
background: $primary-color;
border: 3px solid white;
box-shadow: 0 0 0 4px rgba($primary-color, 0.3);
}
.event {
background: white;
padding: 25px;
border-radius: 10px;
box-shadow: $shadow-sm;
position: relative;
h3 {
margin-top: 0;
color: $primary-color;
}
p {
margin-bottom: 0;
line-height: 1.6;
}
.milestone-icon {
position: absolute;
top: 20px;
right: 20px;
font-size: 24px;
color: rgba($primary-color, 0.2);
}
}
@media (max-width: 768px) {
width: 100%;
padding-right: 0;
padding-left: 40px;
margin-left: 0;
&.right {
margin-left: 0;
}
.year {
left: -20px !important;
right: auto !important;
top: -40px;
text-align: left !important;
}
.dot {
left: -9px !important;
right: auto !important;
}
}
}
@media (max-width: 768px) {
.timeline-container::before {
left: 0;
}
}
}
.timeline {
position: relative;
max-width: 800px;
margin: 40px auto 0;
padding: 20px 0;
&::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 50%;
width: 2px;
background: $primary-color;
transform: translateX(-50%);
}
.timeline-item {
position: relative;
margin-bottom: 50px;
display: flex;
align-items: center;
.company-culture {
padding: 80px 0;
background-color: white;
.culture-values {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 30px;
margin-top: 40px;
.value-item {
text-align: center;
padding: 40px 30px;
background: $light-bg;
border-radius: 15px;
transition: all 0.3s ease;
box-shadow: $shadow-sm;
border-bottom: 4px solid transparent;
&:hover {
transform: translateY(-10px);
box-shadow: $shadow-md;
border-bottom: 4px solid $primary-color;
}
.value-icon {
margin-bottom: 20px;
i {
font-size: 36px;
color: $primary-color;
background: rgba($primary-color, 0.1);
width: 80px;
height: 80px;
line-height: 80px;
border-radius: 50%;
}
}
h3 {
margin-bottom: 15px;
color: $primary-color;
}
p {
color: $text-color;
line-height: 1.6;
}
}
}
.culture-mission {
margin-top: 60px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
@media (max-width: 768px) {
grid-template-columns: 1fr;
}
.mission-card {
background: linear-gradient(135deg, $primary-color, color.adjust($primary-color, $lightness: -15%));
border-radius: 15px;
overflow: hidden;
box-shadow: $shadow-md;
color: white;
.card-header {
padding: 25px;
display: flex;
align-items: center;
i {
font-size: 32px;
margin-right: 15px;
}
h3 {
margin: 0;
font-size: 1.8rem;
}
}
.card-content {
background: rgba(255, 255, 255, 0.1);
padding: 25px;
p {
margin: 0;
line-height: 1.8;
font-size: 1.1rem;
}
}
}
}
}
.loading-container,
.no-data-container {
text-align: center;
padding: 60px 0;
.year {
flex: 0 0 100px;
text-align: right;
font-size: 24px;
font-weight: bold;
color: $primary-color;
padding-right: 30px;
i {
font-size: 40px;
color: $primary-color;
margin-bottom: 20px;
}
.event {
flex: 1;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: $shadow-sm;
margin-left: 30px;
}
}
p {
font-size: 1.2rem;
color: $text-color;
margin-bottom: 20px;
}
}
.company-culture {
padding: 80px 0;
background-color: white;
.culture-values {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 30px;
margin-top: 40px;
.loading-spinner {
margin-bottom: 20px;
.value-item {
text-align: center;
padding: 30px;
background: $light-bg;
border-radius: 8px;
transition: transform 0.3s ease;
&:hover {
transform: translateY(-10px);
}
}
}
i {
font-size: 40px;
color: $primary-color;
}
}
.reload-btn {
background-color: $primary-color;
color: white;
border: none;
padding: 10px 20px;
border-radius: 30px;
font-size: 1rem;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 5px 15px rgba($primary-color, 0.3);
&:hover {
background-color: color.adjust($primary-color, $lightness: -10%);
transform: translateY(-2px);
}
i {
font-size: 1rem;
color: white;
margin-right: 8px;
margin-bottom: 0;
}
}
</style>

+ 3
- 0
src/views/company/JobDetail.vue View File

@ -3,6 +3,9 @@ import { ref, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import PageHeader from '../../components/PageHeader.vue';
import { useConfigStore } from '@/stores/config'
const { configParams } = useConfigStore()
//
const route = useRoute();
const jobId = route.params.id;


+ 34
- 129
src/views/company/Team.vue View File

@ -1,6 +1,10 @@
<script setup>
import { onMounted, ref } from 'vue';
import PageHeader from '../../components/PageHeader.vue';
import TeamMemberCard from '../../components/TeamMemberCard.vue';
import { useConfigStore } from '@/stores/config'
const { configParams } = useConfigStore()
//
const teamMembers = ref([
@ -13,7 +17,8 @@ const teamMembers = ref([
social: [
{ type: 'linkedin', url: 'https://linkedin.com/' },
{ type: 'github', url: 'https://github.com/' },
{ type: 'twitter', url: 'https://twitter.com/' }
{ type: 'wechat', url: 'javascript:void(0);', qrcode: '/images/qrcode-wechat.jpg' },
{ type: 'wecom', url: 'javascript:void(0);', qrcode: '/images/qrcode-wecom.jpg' }
]
},
{
@ -24,7 +29,8 @@ const teamMembers = ref([
photo: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80',
social: [
{ type: 'linkedin', url: 'https://linkedin.com/' },
{ type: 'github', url: 'https://github.com/' }
{ type: 'github', url: 'https://github.com/' },
{ type: 'xiaohongshu', url: 'https://xiaohongshu.com/' }
]
},
{
@ -35,7 +41,8 @@ const teamMembers = ref([
photo: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80',
social: [
{ type: 'linkedin', url: 'https://linkedin.com/' },
{ type: 'twitter', url: 'https://twitter.com/' }
{ type: 'zhihu', url: 'https://zhihu.com/' },
{ type: 'douyin', url: 'https://douyin.com/' }
]
},
{
@ -47,18 +54,12 @@ const teamMembers = ref([
social: [
{ type: 'linkedin', url: 'https://linkedin.com/' },
{ type: 'dribbble', url: 'https://dribbble.com/' },
{ type: 'behance', url: 'https://www.behance.net/' }
{ type: 'behance', url: 'https://www.behance.net/' },
{ type: 'weibo', url: 'https://weibo.com/' }
]
}
]);
//
const culturePhotos = ref([
'https://images.unsplash.com/photo-1522202176988-66273c2fd55f?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
'https://images.unsplash.com/photo-1556761175-4b46a572b786?ixlib=rb-1.2.1&auto=format&fit=crop&w=1267&q=80',
'https://images.unsplash.com/photo-1519389950473-47ba0277781c?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80'
]);
//
const jobOpenings = ref([
{
@ -87,6 +88,12 @@ const jobOpenings = ref([
}
]);
//
const handleContact = (contactInfo) => {
console.log('联系团队成员:', contactInfo);
//
};
</script>
<template>
@ -100,54 +107,18 @@ const jobOpenings = ref([
<section class="team-members">
<div class="container">
<h2 data-aos="fade-up">核心团队</h2>
<div class="members-grid">
<div
<TeamMemberCard
v-for="member in teamMembers"
:key="member.id"
class="member-card"
:member="member"
:enable-hover="true"
:image-height="'300px'"
data-aos="fade-up"
:data-aos-delay="100 + (member.id - 1) * 100"
>
<div class="member-photo">
<img :src="member.photo" :alt="member.name">
</div>
<div class="member-info">
<h3>{{ member.name }}</h3>
<p class="position">{{ member.position }}</p>
<p class="bio">{{ member.bio }}</p>
<div class="social-links">
<a
v-for="(social, index) in member.social"
:key="index"
:href="social.url"
target="_blank"
rel="noopener noreferrer"
>
<i :class="`fab fa-${social.type}`"></i>
</a>
</div>
</div>
</div>
</div>
</div>
</section>
<section class="team-culture" data-aos="fade-up">
<div class="container">
<h2>团队文化</h2>
<div class="culture-content">
<p>我们相信优秀的团队文化是公司成功的基石在瀚海黎明我们倡导开放协作创新的工作环境鼓励每位团队成员充分发挥自己的才能和创造力</p>
<div class="culture-photos">
<div
v-for="(photo, index) in culturePhotos"
:key="index"
class="culture-photo"
data-aos="zoom-in"
:data-aos-delay="index * 100"
>
<img :src="photo" alt="团队活动照片">
</div>
</div>
@contact="handleContact"
/>
</div>
</div>
</section>
@ -189,81 +160,7 @@ const jobOpenings = ref([
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 40px;
margin-top: 50px;
}
.member-card {
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: $shadow-sm;
transition: transform 0.3s ease;
&:hover {
transform: translateY(-10px);
}
.member-photo img {
width: 100%;
height: 300px;
object-fit: cover;
}
.member-info {
padding: 20px;
}
}
.position {
color: $primary-color;
font-weight: 500;
margin-bottom: 10px;
}
.social-links {
display: flex;
gap: 15px;
margin-top: 15px;
a {
color: $text-light;
font-size: 18px;
transition: color 0.3s ease;
&:hover {
color: $primary-color;
}
}
}
.team-culture {
padding: 80px 0;
background-color: $light-bg;
.culture-photos {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-top: 30px;
.culture-photo {
border-radius: 8px;
overflow: hidden;
box-shadow: $shadow-sm;
height: 200px;
img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
&:hover {
transform: scale(1.05);
}
}
}
}
margin-top: 30px;
}
.join-us {
@ -297,4 +194,12 @@ const jobOpenings = ref([
text-decoration: none;
font-weight: 500;
}
/* 响应式处理 */
@media (max-width: 768px) {
.members-grid {
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 25px;
}
}
</style>

+ 123
- 6
src/views/pages/CaseDetail.vue View File

@ -2,7 +2,10 @@
import { ref, onMounted, computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import PageHeader from '../../components/PageHeader.vue';
import caseList from '../../components/case/caseList.vue';
import CaseItem from '../../components/cases/CaseItem.vue';
import { useConfigStore } from '@/stores/config'
const { configParams } = useConfigStore()
//
const route = useRoute();
@ -31,7 +34,90 @@ const casesList = ref([
'https://images.unsplash.com/photo-1454165804606-c3d57bc86b40?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
'https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
'https://images.unsplash.com/photo-1551434678-e076c223a692?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80'
]
],
galleryContent: `
<div class="project-showcase">
<h3>系统架构概览</h3>
<p>本项目采用了先进的微服务架构通过模块化设计实现了高可用性和可扩展性系统的核心组件包括</p>
<ul>
<li><strong>前端框架</strong>基于Vue.js构建的响应式界面支持多端适配</li>
<li><strong>后端服务</strong>使用Spring Cloud微服务架构确保高可用性</li>
<li><strong>数据存储</strong>结合MySQL和MongoDB的混合存储策略</li>
<li><strong>消息队列</strong>采用Kafka实现系统间的异步通信</li>
<li><strong>缓存层</strong>使用Redis优化查询性能和降低数据库压力</li>
</ul>
<div class="image-showcase">
<img src="https://images.unsplash.com/photo-1454165804606-c3d57bc86b40?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80" alt="系统架构图" />
<p class="image-caption">系统整体架构示意图</p>
</div>
<h3>核心功能模块</h3>
<p>系统包含多个紧密集成的功能模块覆盖企业运营的各个方面</p>
<div class="feature-grid">
<div class="feature-item">
<h4>生产管理</h4>
<p>实现从生产计划到执行的全流程管理支持生产排程物料需求计划生产进度跟踪等功能</p>
</div>
<div class="feature-item">
<h4>库存管理</h4>
<p>提供实时库存监控自动补货批次管理库位管理等功能优化库存水平并降低管理成本</p>
</div>
<div class="feature-item">
<h4>销售管理</h4>
<p>涵盖从询价报价到订单履行的完整销售流程支持多渠道销售和客户关系管理</p>
</div>
<div class="feature-item">
<h4>财务管理</h4>
<p>提供应收应付成本核算财务报表等功能实现财务数据与业务数据的无缝集成</p>
</div>
</div>
<div class="image-showcase">
<img src="https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80" alt="系统界面示例" />
<p class="image-caption">系统主控制台界面</p>
</div>
<h3>数据可视化与分析</h3>
<p>系统内置强大的数据分析和可视化功能帮助管理层快速洞察业务状况并做出决策</p>
<ul>
<li>多维度的销售分析报表支持按产品客户区域等维度分析</li>
<li>生产效率和成本分析识别生产瓶颈和优化机会</li>
<li>库存优化建议基于历史数据和预测算法</li>
<li>财务绩效仪表盘展示关键财务指标和趋势</li>
</ul>
<div class="image-showcase">
<img src="https://images.unsplash.com/photo-1551434678-e076c223a692?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80" alt="数据分析界面" />
<p class="image-caption">系统数据分析仪表盘</p>
</div>
<h3>实施成果</h3>
<p>该系统的成功实施为客户带来了显著的业务价值和竞争优势</p>
<div class="results-grid">
<div class="result-item">
<div class="result-value">35%</div>
<div class="result-label">运营效率提升</div>
</div>
<div class="result-item">
<div class="result-value">60%</div>
<div class="result-label">信息处理时间缩短</div>
</div>
<div class="result-item">
<div class="result-value">25%</div>
<div class="result-label">库存周转率提高</div>
</div>
<div class="result-item">
<div class="result-value">40%</div>
<div class="result-label">决策效率提升</div>
</div>
</div>
<p class="conclusion">通过该系统的实施客户实现了业务流程的标准化和数字化为未来的智能制造转型奠定了坚实基础</p>
</div>
`
},
{
id: 2,
@ -206,14 +292,18 @@ onMounted(() => {
<h4>完成时间</h4>
<p>{{ currentCase.completionDate }}</p>
</div>
<div class="overview-item services" data-aos="fade-up" data-aos-delay="300">
<!-- <div class="overview-item" data-aos="fade-up" data-aos-delay="300">
<h4>UI设计图</h4>
<p><a href="https://www.figma.com/design/1234567890/1234567890?node-id=1234567890-1234567890" target="_blank">点击查看</a></p>
</div> -->
<!-- <div class="overview-item services" data-aos="fade-up" data-aos-delay="300">
<h4>服务内容</h4>
<div class="services-list">
<span v-for="(service, index) in currentCase.services" :key="index" class="service-tag">
{{ service }}
</span>
</div>
</div>
</div> -->
</div>
</div>
</section>
@ -265,7 +355,10 @@ onMounted(() => {
<section class="case-gallery">
<div class="container">
<h2 class="section-title" data-aos="fade-up">项目展示</h2>
<div class="gallery-grid">
<!-- 如果有富文本内容则显示富文本 -->
<div class="rich-text-content" v-if="currentCase.galleryContent" data-aos="fade-up" v-html="currentCase.galleryContent"></div>
<!-- 如果没有富文本内容则显示原有图库 -->
<div class="gallery-grid" v-else>
<div v-for="(image, index) in currentCase.gallery" :key="index" class="gallery-item"
data-aos="fade-up" :data-aos-delay="index * 100">
<img :src="image" :alt="`${currentCase.title} - 图片 ${index + 1}`">
@ -278,7 +371,16 @@ onMounted(() => {
<section class="related-cases" v-if="relatedCases.length > 0">
<div class="container">
<h2 class="section-title" data-aos="fade-up">相关案例</h2>
<caseList :cases="relatedCases" />
<div class="related-grid">
<CaseItem
v-for="caseItem in relatedCases"
:key="caseItem.id"
:item="caseItem"
:showCategory="true"
buttonType="button"
@view-details="viewRelatedCase"
/>
</div>
</div>
</section>
@ -533,6 +635,21 @@ onMounted(() => {
background-color: $light-bg;
}
.related-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 30px;
margin-top: 40px;
@media (max-width: 992px) {
grid-template-columns: repeat(2, 1fr);
}
@media (max-width: 768px) {
grid-template-columns: 1fr;
}
}
.section-title {
text-align: center;
margin-bottom: 50px;


+ 270
- 540
src/views/pages/Cases.vue View File

@ -1,170 +1,123 @@
<script setup>
import { ref, onMounted } from 'vue';
import { ref, onMounted, reactive } from 'vue';
import { useRouter } from 'vue-router';
import PageHeader from '../../components/PageHeader.vue';
import PageHeader from '@/components/PageHeader.vue';
import CaseItem from '@/components/cases/CaseItem.vue';
import CallToAction from '@/components/CallToAction.vue';
import api from '@/api'
import { useConfigStore } from '@/stores/config'
const { configParams } = useConfigStore()
//
const casesList = ref([
{
id: 1,
title: '企业资源管理系统',
description: '为某制造企业开发的一套完整ERP系统,实现了生产、销售、库存等全流程管理',
image: 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
category: '企业系统'
},
{
id: 2,
title: '电商平台重构',
description: '帮助某电商企业重构其线上平台,提升用户体验和系统性能,实现销售额提升30%',
image: 'https://images.unsplash.com/photo-1556742049-0cfed4f6a45d?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
category: 'Web应用'
},
{
id: 3,
title: '医疗服务APP',
description: '为连锁医疗机构开发的患者服务APP,实现在线挂号、问诊和健康管理等功能',
image: 'https://images.unsplash.com/photo-1576091160550-2173dba999ef?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
category: '移动应用'
},
{
id: 4,
title: '金融数据可视化平台',
description: '为金融机构开发的数据分析和可视化平台,帮助决策者快速洞察市场趋势',
image: 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
category: 'Web应用'
},
{
id: 5,
title: '智慧校园系统',
description: '为教育机构打造的一体化校园管理系统,涵盖教学、行政、学生服务等多个模块',
image: 'https://images.unsplash.com/photo-1523240795612-9a054b0db644?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
category: '企业系统'
},
{
id: 6,
title: '品牌官网设计',
description: '为高端品牌设计的响应式官方网站,展现品牌形象并提升用户转化率',
image: 'https://images.unsplash.com/photo-1561070791-2526d30994b5?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
category: 'UI/UX设计'
}
]);
//
const currentFilter = ref('全部');
const casesList = ref([]);
//
const filteredCases = ref([]);
//
const categories = ['全部', 'Web应用', '移动应用', '企业系统', 'UI/UX设计'];
//
const filterCases = (category) => {
currentFilter.value = category;
if (category === '全部') {
filteredCases.value = casesList.value;
} else {
filteredCases.value = casesList.value.filter(item => item.category === category);
}
};
const categories = ref([]);
const total = ref(0)
const queryParams = reactive({
categoryId: 0,
page: 1,
pageSize: 10
})
//
const router = useRouter();
//
const viewCaseDetails = (id) => {
//
router.push(`/cases/${id}`);
//
router.push(`/cases/${id}`);
};
function getCategoryList() {
api.getCategoryList().then(res => {
categories.value = res || []
categories.value.unshift({
id: 0,
title: '全部'
})
})
}
function getCasesList() {
api.getCasesList(queryParams).then(res => {
casesList.value = res.rows
total.value = res.total
})
}
function filterCases(category) {
queryParams.categoryId = category.id
getCasesList()
}
onMounted(() => {
//
filterCases('全部');
getCategoryList()
getCasesList()
});
</script>
<template>
<div class="cases-page">
<PageHeader
title="成功案例"
subtitle="我们为各行业客户提供的优质解决方案"
backgroundImage="https://images.unsplash.com/photo-1504384308090-c894fdcc538d?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80"
height="70vh"
>
<template #header-cta>
<div class="header-cta" data-aos="fade-up" data-aos-delay="400">
<a href="#cases-grid" class="btn-primary">浏览案例</a>
<a href="/contact" class="btn-outline">联系我们</a>
</div>
</template>
</PageHeader>
<section class="cases-filter">
<div class="container">
<div class="section-header" data-aos="fade-up">
<h2>我们的项目</h2>
<p>浏览我们为不同行业客户提供的解决方案</p>
</div>
<div class="filter-buttons" data-aos="fade-up">
<button
v-for="category in categories"
:key="category"
:class="['filter-btn', currentFilter === category ? 'active' : '']"
@click="filterCases(category)"
>
{{ category }}
</button>
</div>
</div>
</section>
<section id="cases-grid" class="cases-grid">
<div class="container">
<div class="grid">
<div
v-for="caseItem in filteredCases"
:key="caseItem.id"
class="case-card"
data-aos="fade-up"
>
<div class="case-image">
<div class="category-tag">{{ caseItem.category }}</div>
<img :src="caseItem.image" :alt="caseItem.title">
<div class="cases-page">
<PageHeader :title="configParams.case_banner_title" :subtitle="configParams.case_banner_subtitle"
:backgroundImage="configParams.case_banner_bg && configParams.case_banner_bg.split(',')[0]"
height="70vh">
<template #header-cta>
<div class="header-cta" data-aos="fade-up" data-aos-delay="400">
<a href="#cases-grid" class="btn-primary">浏览案例</a>
<a href="/contact" class="btn-outline">联系我们</a>
</div>
</template>
</PageHeader>
<section class="cases-filter">
<div class="container">
<div class="section-header" data-aos="fade-up">
<h2>我们的项目</h2>
<p>浏览我们为不同行业客户提供的解决方案</p>
</div>
<div class="filter-buttons" data-aos="fade-up">
<button v-for="category in categories" :key="category.id"
:class="['filter-btn', queryParams.categoryId === category.id ? 'active' : '']"
@click="filterCases(category)">
{{ category.title }}
</button>
</div>
</div>
<div class="case-content">
<h3>{{ caseItem.title }}</h3>
<p>{{ caseItem.description }}</p>
<button @click="viewCaseDetails(caseItem.id)" class="btn-details">查看详情</button>
</section>
<section id="cases-grid" class="cases-grid">
<div class="container">
<div class="grid">
<CaseItem
v-for="caseItem in casesList"
:key="caseItem.id"
:item="caseItem"
:showCategory="true"
buttonType="button"
@view-details="viewCaseDetails"
/>
</div>
<div v-if="casesList.length === 0" class="no-results" data-aos="fade-up">
<div class="empty-state">
<i class="empty-icon"></i>
<h3>暂无相关案例</h3>
<p>请尝试选择其他类别查看</p>
</div>
</div>
</div>
</div>
</div>
<div v-if="filteredCases.length === 0" class="no-results" data-aos="fade-up">
<div class="empty-state">
<i class="empty-icon"></i>
<h3>暂无相关案例</h3>
<p>请尝试选择其他类别查看</p>
</div>
</div>
</div>
</section>
<section class="cta-section" data-aos="fade-up">
<div class="container">
<div class="cta-content">
<div class="cta-icon" data-aos="zoom-in" data-aos-delay="200">
<i class="fas fa-rocket"></i>
</div>
<h2 data-aos="fade-up" data-aos-delay="300">准备好开始您的项目了吗</h2>
<p data-aos="fade-up" data-aos-delay="400">我们的团队随时准备为您提供专业的技术支持和服务</p>
<div class="cta-buttons" data-aos="fade-up" data-aos-delay="500">
<a href="/contact" class="btn-primary">立即咨询 <i class="fas fa-arrow-right"></i></a>
<a href="tel:+8612345678901" class="btn-outline">电话联系</a>
</div>
</div>
</div>
</section>
</div>
</section>
<!-- 使用通用CallToAction组件 -->
<CallToAction />
</div>
</template>
<style lang="scss" scoped>
@ -173,463 +126,240 @@ onMounted(() => {
/* 页面头部样式 */
.page-header {
height: 70vh;
position: relative;
overflow: hidden;
display: flex;
align-items: center;
color: white;
text-align: center;
perspective: 1000px;
height: 70vh;
position: relative;
overflow: hidden;
display: flex;
align-items: center;
color: white;
text-align: center;
perspective: 1000px;
}
.parallax-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('https://images.unsplash.com/photo-1504384308090-c894fdcc538d?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80');
background-size: cover;
background-position: center;
z-index: -1;
transform: translateZ(-5px) scale(1.5);
filter: brightness(0.9) contrast(1.1);
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('https://images.unsplash.com/photo-1504384308090-c894fdcc538d?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80');
background-size: cover;
background-position: center;
z-index: -1;
transform: translateZ(-5px) scale(1.5);
filter: brightness(0.9) contrast(1.1);
}
.overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(to right, rgba(0,0,0,0.7), rgba(0,0,0,0.4));
z-index: -1;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(to right, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.4));
z-index: -1;
}
.breadcrumb {
position: absolute;
top: 30px;
left: 30px;
padding: 10px 0;
font-size: 14px;
a {
color: #fff;
text-decoration: none;
transition: color 0.3s ease;
&:hover {
color: $primary-color;
position: absolute;
top: 30px;
left: 30px;
padding: 10px 0;
font-size: 14px;
a {
color: #fff;
text-decoration: none;
transition: color 0.3s ease;
&:hover {
color: $primary-color;
}
}
}
}
.header-content {
max-width: 800px;
margin: 0 auto;
padding: 0 20px;
max-width: 800px;
margin: 0 auto;
padding: 0 20px;
}
.header-content {
h1 {
font-size: 3.5rem;
font-weight: 700;
margin-bottom: 20px;
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
}
p {
font-size: 1.2rem;
margin-bottom: 30px;
opacity: 0.9;
}
h1 {
font-size: 3.5rem;
font-weight: 700;
margin-bottom: 20px;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}
p {
font-size: 1.2rem;
margin-bottom: 30px;
opacity: 0.9;
}
}
.header-cta {
display: flex;
justify-content: center;
gap: 15px;
margin-top: 30px;
display: flex;
justify-content: center;
gap: 15px;
margin-top: 30px;
}
.btn-primary, .btn-outline {
display: inline-block;
padding: 12px 30px;
border-radius: 30px;
font-weight: 500;
text-decoration: none;
transition: all 0.3s ease;
.btn-primary,
.btn-outline {
display: inline-block;
padding: 12px 30px;
border-radius: 30px;
font-weight: 500;
text-decoration: none;
transition: all 0.3s ease;
}
.btn-primary {
background: $primary-color;
color: white;
border: 2px solid $primary-color;
&:hover {
background: $secondary-color;
border-color: $secondary-color;
transform: translateY(-3px);
}
background: $primary-color;
color: white;
border: 2px solid $primary-color;
&:hover {
background: $secondary-color;
border-color: $secondary-color;
transform: translateY(-3px);
}
}
.btn-outline {
background: transparent;
color: white;
border: 2px solid white;
&:hover {
background: rgba(255,255,255,0.1);
transform: translateY(-3px);
}
background: transparent;
color: white;
border: 2px solid white;
&:hover {
background: rgba(255, 255, 255, 0.1);
transform: translateY(-3px);
}
}
/* 筛选部分样式 */
.cases-filter {
padding: 60px 0 40px;
background-color: $light-bg;
padding: 60px 0 40px;
background-color: $light-bg;
}
.section-header {
text-align: center;
margin-bottom: 30px;
h2 {
font-size: 2.2rem;
color: $text-color;
margin-bottom: 15px;
}
p {
color: $text-light;
max-width: 600px;
margin: 0 auto;
}
text-align: center;
margin-bottom: 30px;
h2 {
font-size: 2.2rem;
color: $text-color;
margin-bottom: 15px;
}
p {
color: $text-light;
max-width: 600px;
margin: 0 auto;
}
}
.filter-buttons {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 15px;
margin-top: 20px;
.filter-btn {
padding: 10px 25px;
background: white;
border: 1px solid #ddd;
border-radius: 30px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 15px;
&.active,
&:hover {
background: $primary-color;
color: white;
border-color: $primary-color;
box-shadow: 0 5px 15px rgba($primary-color, 0.2);
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 15px;
margin-top: 20px;
.filter-btn {
padding: 10px 25px;
background: white;
border: 1px solid #ddd;
border-radius: 30px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 15px;
&.active,
&:hover {
background: $primary-color;
color: white;
border-color: $primary-color;
box-shadow: 0 5px 15px rgba($primary-color, 0.2);
}
}
}
}
/* 案例网格样式 */
.cases-grid {
padding: 60px 0;
background-color: #fff;
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 30px;
}
}
padding: 60px 0;
background-color: #fff;
.case-card {
border-radius: 12px;
overflow: hidden;
box-shadow: $shadow-sm;
transition: transform 0.3s ease, box-shadow 0.3s ease;
background: white;
position: relative;
&:hover {
transform: translateY(-10px);
box-shadow: $shadow-lg;
.case-image img {
transform: scale(1.05);
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 30px;
}
}
.case-image {
position: relative;
overflow: hidden;
img {
width: 100%;
height: 250px;
object-fit: cover;
transition: transform 0.5s ease;
}
}
.category-tag {
position: absolute;
top: 15px;
right: 15px;
background: rgba($primary-color, 0.8);
color: white;
padding: 5px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 500;
z-index: 1;
}
.case-content {
padding: 25px;
background: white;
h3 {
font-size: 1.3rem;
margin-bottom: 10px;
color: $text-color;
}
p {
color: $text-light;
margin-bottom: 15px;
line-height: 1.6;
}
}
}
.btn-details {
display: inline-block;
padding: 8px 20px;
background: transparent;
border: 1px solid $primary-color;
color: $primary-color;
border-radius: 30px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
background: $primary-color;
color: white;
}
}
/* 空状态样式 */
.no-results {
padding: 50px 0;
text-align: center;
.empty-state {
padding: 40px;
background: $light-bg;
border-radius: 10px;
h3 {
margin: 15px 0;
color: $text-color;
}
p {
color: $text-light;
}
}
}
/* CTA部分样式 */
.cta-section {
background: linear-gradient(135deg, #0056b3, #00337f);
background-size: 200% 200%;
color: white;
padding: 80px 0;
text-align: center;
margin-top: 40px;
position: relative;
overflow: hidden;
box-shadow: 0 -10px 30px rgba(0,0,0,0.1);
animation: gradientBG 15s ease infinite;
}
@keyframes gradientBG {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
.cta-section::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml;charset=utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320"%3E%3Cpath fill="%23ffffff" fill-opacity="0.05" d="M0,96L48,112C96,128,192,160,288,186.7C384,213,480,235,576,213.3C672,192,768,128,864,128C960,128,1056,192,1152,213.3C1248,235,1344,213,1392,202.7L1440,192L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"%3E%3C/path%3E%3C/svg%3E');
background-size: cover;
background-position: center;
opacity: 0.1;
z-index: 0;
}
.cta-content {
position: relative;
z-index: 1;
}
.cta-icon {
margin-bottom: 25px;
}
padding: 50px 0;
text-align: center;
.cta-icon i {
font-size: 3.5rem;
color: #fff;
background: rgba(255,255,255,0.1);
width: 100px;
height: 100px;
line-height: 100px;
border-radius: 50%;
display: inline-block;
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
transition: all 0.3s ease;
}
.empty-state {
padding: 40px;
background: $light-bg;
border-radius: 10px;
.cta-icon i:hover {
transform: translateY(-5px) scale(1.05);
box-shadow: 0 15px 30px rgba(0,0,0,0.2);
background: rgba(255,255,255,0.2);
}
h3 {
margin: 15px 0;
color: $text-color;
}
.cta-content h2 {
font-size: 2.5rem;
margin-bottom: 20px;
font-weight: 700;
text-shadow: 0 2px 10px rgba(0,0,0,0.2);
color: #fff;
}
.cta-content p {
font-size: 1.2rem;
opacity: 0.9;
max-width: 600px;
margin: 0 auto 30px;
line-height: 1.6;
p {
color: $text-light;
}
}
}
.cta-buttons {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 30px;
}
/* 响应式样式 */
@media (max-width: 768px) {
.page-header {
height: 60vh;
}
.cta-buttons .btn-primary {
padding: 14px 32px;
font-size: 1.1rem;
font-weight: 600;
box-shadow: 0 8px 15px rgba(0,0,0,0.2);
transition: all 0.3s ease;
}
.header-content h1 {
font-size: 2.5rem;
}
.cta-buttons .btn-primary i {
margin-left: 8px;
transition: transform 0.3s ease;
}
.grid {
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}
.cta-buttons .btn-primary:hover i {
transform: translateX(5px);
}
.header-cta {
flex-direction: column;
align-items: center;
gap: 10px;
}
.cta-buttons .btn-outline {
background: transparent;
color: white;
border: 2px solid rgba(255,255,255,0.6);
padding: 14px 32px;
font-size: 1.1rem;
font-weight: 600;
transition: all 0.3s ease;
.btn-primary,
.btn-outline {
width: 80%;
text-align: center;
}
}
.cta-buttons .btn-outline:hover {
background: rgba(255,255,255,0.1);
border-color: white;
transform: translateY(-3px);
box-shadow: 0 8px 15px rgba(0,0,0,0.1);
}
@media (max-width: 480px) {
.header-content h1 {
font-size: 2rem;
}
/* 响应式样式 */
@media (max-width: 768px) {
.page-header {
height: 60vh;
}
.header-content h1 {
font-size: 2.5rem;
}
.grid {
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}
.header-cta {
flex-direction: column;
align-items: center;
gap: 10px;
}
.btn-primary, .btn-outline {
width: 80%;
text-align: center;
}
.cta-content h2 {
font-size: 2rem;
}
.cta-content p {
font-size: 1.1rem;
padding: 0 15px;
}
.cta-buttons {
flex-direction: column;
align-items: center;
width: 100%;
gap: 15px;
}
.cta-buttons .btn-primary,
.cta-buttons .btn-outline {
width: 80%;
max-width: 300px;
}
.cta-icon i {
width: 80px;
height: 80px;
line-height: 80px;
font-size: 2.8rem;
}
}
.filter-buttons {
gap: 10px;
}
@media (max-width: 480px) {
.header-content h1 {
font-size: 2rem;
}
.filter-buttons {
gap: 10px;
}
.filter-btn {
padding: 8px 15px;
font-size: 14px;
}
.filter-btn {
padding: 8px 15px;
font-size: 14px;
}
}
</style>

+ 893
- 249
src/views/pages/Contact.vue
File diff suppressed because it is too large
View File


+ 274
- 361
src/views/pages/Home.vue View File

@ -3,14 +3,33 @@
import { onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
import { initParallax, initAOS } from '../../plugins/animation.js';
import { useConfigStore } from '@/stores/config'
const { configParams } = useConfigStore()
//
import ServiceSection from '../../components/home/ServiceSection.vue';
import CaseSection from '@/components/home/CaseSection.vue';
// CTA
import CallToAction from '../../components/CallToAction.vue';
//
const router = useRouter();
//
const serviceSection = ref(null);
import api from '@/api'
const serviceProcess = ref([])
const getServiceProcess = async () => {
const res = await api.getServiceProcess()
serviceProcess.value = res.data
}
//
const scrollToServices = () => {
// 使DOM
@ -29,6 +48,7 @@ onMounted(() => {
//
initAOS();
initParallax();
getServiceProcess()
});
</script>
@ -37,11 +57,13 @@ onMounted(() => {
<!-- 英雄区域带视差效果 -->
<section class="hero-section parallax-container">
<!-- 添加一个注释确保视差容器不会阻止按钮点击 -->
<div class="parallax-bg" data-depth="0.4"></div>
<div class="parallax-bg"
:style="{ backgroundImage: `url(${configParams.home_banner_bg && configParams.home_banner_bg.split(',')[0]})` }"
data-depth="0.4"></div>
<div class="hero-overlay"></div>
<div class="hero-content hero-content-fixed" data-aos="fade-up" data-aos-duration="1000">
<h1 class="hero-title">数字化转型的<span class="highlight">可靠伙伴</span></h1>
<p class="hero-subtitle" data-aos="fade-up" data-aos-delay="200">专注于为企业提供高质量软件开发服务</p>
<p class="hero-subtitle" data-aos="fade-up" data-aos-delay="200">{{ configParams.home_banner_subtitle }}</p>
<div class="cta-buttons" data-aos="fade-up" data-aos-delay="400">
<button class="btn primary" @click="scrollToServices">了解更多</button>
<button class="btn secondary" @click="navigateToContact">联系我们</button>
@ -50,11 +72,14 @@ onMounted(() => {
</section>
<!-- 我们的服务视差滚动 -->
<ServiceSection />
<!-- <ServiceSection /> -->
<!-- 精选案例展示 -->
<CaseSection />
<!-- 我们的优势视差背景 -->
<section class="advantages-section parallax-container">
<div class="parallax-bg" data-depth="0.1"></div>
<div class="parallax-bg" :style="{ backgroundImage: `url(${configParams.home_advantages_bg})` }" data-depth="0.1"></div>
<div class="overlay-dark"></div>
<div class="container hero-content-fixed">
<div class="section-content">
@ -98,63 +123,9 @@ onMounted(() => {
</div>
</section>
<!-- 精选案例展示视差滚动 -->
<section class="cases-section" data-aos="fade-up">
<div class="section-header">
<h2>精选案例</h2>
<p>我们的成功项目展示</p>
</div>
<div class="cases-grid">
<div class="case-card" data-aos="fade-up" data-aos-delay="100">
<div class="case-image">
<img src="https://images.unsplash.com/photo-1460925895917-afdab827c52f?ixlib=rb-1.2.1&auto=format&fit=crop&w=1500&q=80"
alt="企业管理系统" />
</div>
<div class="case-content">
<h3>企业资源管理系统</h3>
<p>为某大型制造企业开发的一体化ERP系统整合了生产销售库存和财务管理</p>
<a href="#" class="btn-link">查看详情</a>
</div>
</div>
<div class="case-card" data-aos="fade-up" data-aos-delay="200">
<div class="case-image">
<img src="https://images.unsplash.com/photo-1526498460520-4c246339dccb?ixlib=rb-1.2.1&auto=format&fit=crop&w=1500&q=80"
alt="电子商务平台" />
</div>
<div class="case-content">
<h3>电子商务平台</h3>
<p>为零售客户开发的全渠道电商平台支持PC端移动端和小程序多端访问</p>
<a href="#" class="btn-link">查看详情</a>
</div>
</div>
<div class="case-card" data-aos="fade-up" data-aos-delay="300">
<div class="case-image">
<img src="https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-1.2.1&auto=format&fit=crop&w=1500&q=80"
alt="医疗管理系统" />
</div>
<div class="case-content">
<h3>医疗管理系统</h3>
<p>为医疗机构开发的患者管理系统提高了医疗服务效率和患者满意度</p>
<a href="#" class="btn-link">查看详情</a>
</div>
</div>
<div class="case-card" data-aos="fade-up" data-aos-delay="400">
<div class="case-image">
<img src="https://images.unsplash.com/photo-1454165804606-c3d57bc86b40?ixlib=rb-1.2.1&auto=format&fit=crop&w=1500&q=80"
alt="金融分析平台" />
</div>
<div class="case-content">
<h3>金融分析平台</h3>
<p>为金融机构开发的数据分析平台提供实时市场分析和风险评估</p>
<a href="#" class="btn-link">查看详情</a>
</div>
</div>
</div>
</section>
<!-- 客户评价视差背景 -->
<section class="testimonials-section parallax-container">
<div class="parallax-bg" data-depth="0.1"></div>
<div class="parallax-bg" :style="{ backgroundImage: `url(${configParams.home_testimonials_bg})` }" data-depth="0.1"></div>
<div class="section-content">
<div class="section-header" data-aos="fade-up">
<h2>客户评价</h2>
@ -212,7 +183,20 @@ onMounted(() => {
<p>简单高效的项目合作流程</p>
</div>
<div class="process-flow">
<div class="process-step" data-aos="fade-right" data-aos-delay="100">
<div class="process-step"
data-aos="fade-right"
v-for="(item, index) in serviceProcess"
:key="item.id"
:data-aos-delay="100 * (index + 1)">
<div class="step-number">{{ item.num }}</div>
<div class="step-content">
<h3>{{ item.title }}</h3>
<p>{{ item.info }}</p>
</div>
</div>
<!-- <div class="process-step" data-aos="fade-right" data-aos-delay="100">
<div class="step-number">01</div>
<div class="step-content">
<h3>需求分析</h3>
@ -253,28 +237,35 @@ onMounted(() => {
<h3>持续支持</h3>
<p>提供持续的技术支持和系统维护确保系统稳定运行</p>
</div>
</div>
</div> -->
</div>
</section>
<!-- 使用通用的CallToAction组件 -->
<CallToAction
title="准备好开始您的数字化转型了吗?"
description="瀚海黎明为您提供专业的软件开发服务"
primaryButtonText="开始项目咨询"
/>
</div>
</template>
<style scoped>
<style lang="scss" scoped>
/* 全局样式 */
.section-header {
text-align: center;
margin-bottom: 50px;
}
.section-header h2 {
font-size: 2.5rem;
color: #2c3e50;
margin-bottom: 15px;
}
.section-header p {
font-size: 1.2rem;
color: #7f8c8d;
h2 {
font-size: 2.5rem;
color: #2c3e50;
margin-bottom: 15px;
}
p {
font-size: 1.2rem;
color: #7f8c8d;
}
}
.btn {
@ -292,63 +283,40 @@ onMounted(() => {
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
z-index: 30; /* 确保按钮在最上层 */
pointer-events: auto; /* 确保可以接收鼠标事件 */
}
.btn.primary {
background-color: #3498db;
color: white;
}
.btn.primary:hover {
background-color: #2980b9;
transform: translateY(-3px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.3);
}
.btn.primary:active {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.btn.secondary {
background-color: transparent;
color: white;
border: 2px solid white;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.btn.secondary:hover {
background-color: rgba(255, 255, 255, 0.15);
transform: translateY(-3px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.2);
}
.btn.secondary:active {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.btn-link {
color: #3498db;
text-decoration: none;
font-weight: 600;
position: relative;
cursor: pointer;
}
.btn-link:after {
content: '';
position: absolute;
width: 0;
height: 2px;
bottom: -5px;
left: 0;
background-color: #3498db;
transition: width 0.3s ease;
}
.btn-link:hover:after {
width: 100%;
&.primary {
background-color: #3498db;
color: white;
&:hover {
background-color: #2980b9;
transform: translateY(-3px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.3);
}
&:active {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
}
&.secondary {
background-color: transparent;
color: white;
border: 2px solid white;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
&:hover {
background-color: rgba(255, 255, 255, 0.15);
transform: translateY(-3px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.2);
}
&:active {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
}
}
/* 英雄区域样式 */
@ -366,12 +334,11 @@ onMounted(() => {
/* 确保视差容器不会阻止点击事件 */
.parallax-container {
pointer-events: none; /* 默认不接收鼠标事件,让事件穿透到下层元素 */
}
/* 确保视差容器中的内容可以接收点击事件 */
.parallax-container .hero-content,
.parallax-container .hero-content * {
pointer-events: auto; /* 恢复内容区域的鼠标事件 */
.hero-content,
.hero-content * {
pointer-events: auto; /* 恢复内容区域的鼠标事件 */
}
}
.parallax-bg {
@ -380,7 +347,7 @@ onMounted(() => {
left: 0;
width: 100%;
height: 100%;
background-image: url('https://images.unsplash.com/photo-1451187580459-43490279c0fa?ixlib=rb-1.2.1&auto=format&fit=crop&w=1352&q=80');
// background-image: url('https://images.unsplash.com/photo-1451187580459-43490279c0fa?ixlib=rb-1.2.1&auto=format&fit=crop&w=1352&q=80');
background-size: cover;
background-position: center;
z-index: -2;
@ -439,21 +406,21 @@ onMounted(() => {
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
letter-spacing: 1px;
color: #fff;
}
.hero-title .highlight {
color: #3498db;
position: relative;
}
.hero-title .highlight::after {
content: '';
position: absolute;
bottom: -5px;
left: 0;
width: 100%;
height: 3px;
background-color: #3498db;
.highlight {
color: #3498db;
position: relative;
&::after {
content: '';
position: absolute;
bottom: -5px;
left: 0;
width: 100%;
height: 3px;
background-color: #3498db;
}
}
}
.hero-subtitle {
@ -473,7 +440,6 @@ onMounted(() => {
pointer-events: auto; /* 确保可以接收鼠标事件 */
}
/* 服务部分样式已移至ServiceSection.vue组件 */
/* 优势部分样式 */
.advantages-section {
@ -491,20 +457,20 @@ onMounted(() => {
/* 确保宽度占满容器 */
box-sizing: border-box;
/* 确保padding不会增加元素总宽度 */
}
.advantages-section .container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
position: relative;
z-index: 2;
}
.advantages-section .parallax-bg {
background-image: url('https://images.unsplash.com/photo-1504639725590-34d0984388bd?ixlib=rb-1.2.1&auto=format&fit=crop&w=1500&q=80');
filter: brightness(0.3);
/* 降低亮度以增加对比度 */
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
position: relative;
z-index: 2;
}
.parallax-bg {
// background-image: url('https://images.unsplash.com/photo-1504639725590-34d0984388bd?ixlib=rb-1.2.1&auto=format&fit=crop&w=1500&q=80');
filter: brightness(0.3);
/* 降低亮度以增加对比度 */
}
}
.overlay-dark {
@ -558,17 +524,13 @@ onMounted(() => {
/* 固定为3列布局 */
box-sizing: border-box;
/* 确保padding不会增加元素总宽度 */
}
@media (max-width: 992px) {
.advantages-grid {
@media (max-width: 992px) {
grid-template-columns: repeat(2, 1fr);
/* 中等屏幕2列 */
}
}
@media (max-width: 768px) {
.advantages-grid {
@media (max-width: 768px) {
grid-template-columns: 1fr;
/* 小屏幕1列 */
}
@ -585,97 +547,42 @@ onMounted(() => {
/* 添加阴影增强可见性 */
border: 1px solid rgba(255, 255, 255, 0.1);
/* 添加细边框增强可见性 */
}
.advantage-card:hover {
transform: translateY(-10px);
background: rgba(0, 0, 0, 0.6);
/* 悬停时背景更深 */
}
.advantage-number {
font-size: 3rem;
font-weight: 700;
color: rgba(52, 152, 219, 0.7);
/* 使用品牌蓝色,增加可见性 */
margin-bottom: 15px;
text-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
/* 添加文字阴影 */
}
.advantage-card h3 {
font-size: 1.5rem;
margin-bottom: 15px;
color: #ffffff;
/* 确保标题为纯白色 */
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
/* 添加文字阴影增强可读性 */
}
.advantage-card p {
color: rgba(255, 255, 255, 0.9);
/* 段落文字颜色 */
line-height: 1.6;
/* 增加行高提高可读性 */
font-size: 1rem;
/* 设置合适的字体大小 */
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
/* 轻微文字阴影 */
}
/* 案例部分样式 */
.cases-section {
padding: 100px 20px;
background-color: #f8f9fa;
}
.cases-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
gap: 30px;
margin-top: 40px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
}
.case-card {
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.case-card:hover {
transform: translateY(-10px);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
}
.case-image img {
width: 100%;
height: 300px;
object-fit: cover;
transition: transform 0.5s ease;
}
.case-card:hover .case-image img {
transform: scale(1.05);
}
.case-content {
padding: 25px;
}
.case-content h3 {
font-size: 1.5rem;
margin-bottom: 15px;
color: #2c3e50;
}
.case-content p {
color: #7f8c8d;
margin-bottom: 20px;
&:hover {
transform: translateY(-10px);
background: rgba(0, 0, 0, 0.6);
/* 悬停时背景更深 */
}
.advantage-number {
font-size: 3rem;
font-weight: 700;
color: rgba(52, 152, 219, 0.7);
/* 使用品牌蓝色,增加可见性 */
margin-bottom: 15px;
text-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
/* 添加文字阴影 */
}
h3 {
font-size: 1.5rem;
margin-bottom: 15px;
color: #ffffff;
/* 确保标题为纯白色 */
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
/* 添加文字阴影增强可读性 */
}
p {
color: rgba(255, 255, 255, 0.9);
/* 段落文字颜色 */
line-height: 1.6;
/* 增加行高提高可读性 */
font-size: 1rem;
/* 设置合适的字体大小 */
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
/* 轻微文字阴影 */
}
}
/* 客户评价部分样式 */
@ -684,20 +591,20 @@ onMounted(() => {
padding: 100px 20px;
color: white;
overflow: hidden;
}
.testimonials-section .parallax-bg {
background-image: url('https://images.unsplash.com/photo-1557682250-33bd709cbe85?ixlib=rb-1.2.1&auto=format&fit=crop&w=1500&q=80');
filter: brightness(0.4);
}
.testimonials-slider {
display: flex;
flex-wrap: wrap;
gap: 30px;
justify-content: center;
max-width: 1200px;
margin: 0 auto;
.parallax-bg {
//background-image: url('https://images.unsplash.com/photo-1557682250-33bd709cbe85?ixlib=rb-1.2.1&auto=format&fit=crop&w=1500&q=80');
filter: brightness(0.4);
}
.testimonials-slider {
display: flex;
flex-wrap: wrap;
gap: 30px;
justify-content: center;
max-width: 1200px;
margin: 0 auto;
}
}
.testimonial-card {
@ -708,57 +615,59 @@ onMounted(() => {
flex: 1;
min-width: 300px;
max-width: 400px;
}
.testimonial-rating {
color: #f1c40f;
font-size: 1.5rem;
margin-bottom: 20px;
}
.testimonial-text {
font-style: italic;
margin-bottom: 20px;
line-height: 1.6;
}
.testimonial-author {
display: flex;
align-items: center;
}
.author-avatar {
width: 60px;
height: 60px;
border-radius: 50%;
margin-right: 15px;
object-fit: cover;
}
.author-info h4 {
font-size: 1.2rem;
margin-bottom: 5px;
}
.author-info p {
opacity: 0.8;
font-size: 0.9rem;
.testimonial-rating {
color: #f1c40f;
font-size: 1.5rem;
margin-bottom: 20px;
}
.testimonial-text {
font-style: italic;
margin-bottom: 20px;
line-height: 1.6;
}
.testimonial-author {
display: flex;
align-items: center;
.author-avatar {
width: 60px;
height: 60px;
border-radius: 50%;
margin-right: 15px;
object-fit: cover;
}
.author-info {
h4 {
font-size: 1.2rem;
margin-bottom: 5px;
}
p {
opacity: 0.8;
font-size: 0.9rem;
}
}
}
}
/* 合作流程部分样式 */
.process-section {
padding: 100px 20px;
background-color: #f8f9fa;
}
.process-flow {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin-top: 40px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
.process-flow {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin-top: 40px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
}
}
.process-step {
@ -768,52 +677,56 @@ onMounted(() => {
padding: 30px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.process-step:hover {
transform: translateY(-10px);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
}
.step-number {
font-size: 2.5rem;
font-weight: 700;
color: #3498db;
margin-right: 20px;
line-height: 1;
}
.step-content h3 {
font-size: 1.5rem;
margin-bottom: 10px;
color: #2c3e50;
}
.step-content p {
color: #7f8c8d;
&:hover {
transform: translateY(-10px);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
}
.step-number {
font-size: 2.5rem;
font-weight: 700;
color: #3498db;
margin-right: 20px;
line-height: 1;
}
.step-content {
h3 {
font-size: 1.5rem;
margin-bottom: 10px;
color: #2c3e50;
}
p {
color: #7f8c8d;
}
}
}
/* 响应式调整 */
@media (max-width: 768px) {
.hero-content h1 {
font-size: 2.5rem;
}
.hero-content p {
font-size: 1.2rem;
.hero-content {
h1 {
font-size: 2.5rem;
}
p {
font-size: 1.2rem;
}
}
.cases-grid {
grid-template-columns: 1fr;
}
.process-step {
flex-direction: column;
}
.step-number {
margin-right: 0;
margin-bottom: 15px;
.step-number {
margin-right: 0;
margin-bottom: 15px;
}
}
}
</style>

+ 211
- 449
src/views/pages/Services.vue View File

@ -1,27 +1,35 @@
<script setup>
import { onMounted } from 'vue';
import { onMounted, ref } from 'vue';
import { initAOS } from '../../plugins/animation';
import PageHeader from '../../components/PageHeader.vue';
import CallToAction from '../../components/CallToAction.vue';
import CaseItem from '../../components/cases/CaseItem.vue';
import { useConfigStore } from '@/stores/config'
const { configParams } = useConfigStore()
import CaseSection from '@/components/home/CaseSection.vue';
import api from '@/api'
const serviceProcess = ref([])
const getServiceProcess = async () => {
const res = await api.getServiceProcess()
serviceProcess.value = res.data
}
onMounted(() => {
//
initAOS();
// FontAwesome
if (!document.getElementById('font-awesome-css')) {
const link = document.createElement('link');
link.id = 'font-awesome-css';
link.rel = 'stylesheet';
link.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css';
document.head.appendChild(link);
}
getServiceProcess()
});
</script>
<template>
<div class="services-page">
<PageHeader title="我们的服务" subtitle="为您的业务提供全方位的软件解决方案"
backgroundImage="https://images.unsplash.com/photo-1454165804606-c3d57bc86b40?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80"
<PageHeader :title="configParams.service_banner_title" :subtitle="configParams.service_banner_subtitle"
:backgroundImage="configParams.service_banner_bg && configParams.service_banner_bg.split(',')[0]"
height="70vh">
<template #header-cta>
<div class="header-cta" data-aos="fade-up" data-aos-delay="300">
@ -32,150 +40,104 @@ onMounted(() => {
<section id="services-detail" class="services-detail">
<div class="container">
<div class="service-item" data-aos="fade-up">
<div class="service-icon">
<i class="fas fa-laptop-code"></i>
</div>
<div class="service-content">
<h2>定制软件开发</h2>
<p>我们提供端到端的定制软件开发服务从需求分析到设计开发测试和部署确保软件完全符合您的业务需求</p>
<ul>
<li>业务流程自动化</li>
<li>企业资源规划(ERP)系统</li>
<li>客户关系管理(CRM)系统</li>
<li>数据分析与报表系统</li>
</ul>
</div>
</div>
<h2 class="section-title">我们的服务流程</h2>
<div class="process-flow">
<div class="service-item" data-aos="fade-up" data-aos-delay="100">
<div class="service-icon">
<i class="fas fa-mobile-alt"></i>
</div>
<div class="service-content">
<h2>移动应用开发</h2>
<p>我们开发高性能用户友好的移动应用帮助您的业务在移动端获得更多机会</p>
<ul>
<li>iOS和Android原生应用</li>
<li>跨平台应用开发</li>
<li>移动电商解决方案</li>
<li>企业移动办公应用</li>
</ul>
</div>
</div>
<div class="process-item" data-aos="fade-up"
v-for="(item, index) in serviceProcess"
:key="item.id"
:data-aos-delay="100 * (index + 1)">
<div class="service-item" data-aos="fade-up" data-aos-delay="200">
<div class="service-icon">
<i class="fas fa-globe"></i>
</div>
<div class="service-content">
<h2>网站设计与开发</h2>
<p>我们创建美观响应式的网站确保在所有设备上都能提供出色的用户体验</p>
<ul>
<li>企业官网设计与开发</li>
<li>电子商务网站</li>
<li>内容管理系统</li>
<li>网站性能优化</li>
</ul>
</div>
</div>
<div class="process-icon"
v-if="item.image"
v-html="item.image">
</div>
<div class="service-item" data-aos="fade-up" data-aos-delay="300">
<div class="service-icon">
<i class="fas fa-cloud"></i>
</div>
<div class="service-content">
<h2>云服务与DevOps</h2>
<p>我们提供全面的云服务解决方案帮助您的业务实现数字化转型</p>
<ul>
<li>云迁移与部署</li>
<li>持续集成与部署(CI/CD)</li>
<li>容器化与微服务架构</li>
<li>云基础设施管理</li>
</ul>
<div class="process-content">
<h3>{{ item.num }}.{{ item.title }}</h3>
<p>{{ item.info }}</p>
</div>
</div>
</div>
</div>
</section>
<section class="service-process" data-aos="fade-up">
<div class="container">
<h2>我们的服务流程</h2>
<div class="process-steps">
<div class="step" data-aos="fade-right" data-aos-delay="100">
<div class="step-number">01</div>
<h3>需求分析</h3>
<p>深入了解您的业务需求和目标确定项目范围和技术要求</p>
</div>
<div class="step" data-aos="fade-right" data-aos-delay="200">
<div class="step-number">02</div>
<h3>方案设计</h3>
<p>制定详细的技术方案和项目计划确保解决方案符合您的业务目标</p>
</div>
<div class="step" data-aos="fade-right" data-aos-delay="300">
<div class="step-number">03</div>
<h3>开发实现</h3>
<p>采用敏捷开发方法快速迭代定期交付可用的软件版本</p>
<!-- <div class="process-item" data-aos="fade-up" data-aos-delay="100">
<div class="process-icon">
<i class="fas fa-search"></i>
</div>
<div class="process-content">
<h3>01. 需求分析</h3>
<p>深入了解您的业务需求和目标确定项目范围和技术要求</p>
</div>
</div>
<div class="step" data-aos="fade-right" data-aos-delay="400">
<div class="step-number">04</div>
<h3>测试验收</h3>
<p>全面的功能测试性能测试和安全测试确保软件质量</p>
<div class="process-item" data-aos="fade-up" data-aos-delay="200">
<div class="process-icon">
<i class="fas fa-drafting-compass"></i>
</div>
<div class="process-content">
<h3>02. 方案设计</h3>
<p>制定详细的技术方案和项目计划确保解决方案符合您的业务目标</p>
</div>
</div>
<div class="step" data-aos="fade-right" data-aos-delay="500">
<div class="step-number">05</div>
<h3>部署上线</h3>
<p>专业的部署服务确保系统平稳上线并提供详细的使用文档</p>
<div class="process-item" data-aos="fade-up" data-aos-delay="300">
<div class="process-icon">
<i class="fas fa-code"></i>
</div>
<div class="process-content">
<h3>03. 开发实现</h3>
<p>采用敏捷开发方法快速迭代定期交付可用的软件版本</p>
</div>
</div>
</div>
</div>
</section>
<section class="case-studies" data-aos="fade-up">
<div class="container">
<h2>成功案例</h2>
<p class="section-desc">我们为各行业客户提供了成功的软件解决方案</p>
<div class="cases-grid">
<div class="case-card" data-aos="fade-up" data-aos-delay="100">
<div class="case-image">
<img src="https://images.unsplash.com/photo-1460925895917-afdab827c52f?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=80"
alt="金融行业案例">
<div class="process-item" data-aos="fade-up" data-aos-delay="400">
<div class="process-icon">
<i class="fas fa-tasks"></i>
</div>
<div class="case-content">
<h3>某大型金融机构</h3>
<p>为客户开发了全面的风险管理系统帮助其提高了30%的风险识别率</p>
<a href="#" class="read-more">查看详情</a>
<div class="process-content">
<h3>04. 测试验收</h3>
<p>全面的功能测试性能测试和安全测试确保软件质量</p>
</div>
</div>
<div class="case-card" data-aos="fade-up" data-aos-delay="200">
<div class="case-image">
<img src="https://images.unsplash.com/photo-1576091160550-2173dba999ef?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=80"
alt="医疗行业案例">
<div class="process-item" data-aos="fade-up" data-aos-delay="500">
<div class="process-icon">
<i class="fas fa-rocket"></i>
</div>
<div class="case-content">
<h3>医疗健康集团</h3>
<p>开发了患者管理系统帮助医院提高了40%的工作效率</p>
<a href="#" class="read-more">查看详情</a>
<div class="process-content">
<h3>05. 部署上线</h3>
<p>专业的部署服务确保系统平稳上线并提供详细的使用文档</p>
</div>
</div>
<div class="case-card" data-aos="fade-up" data-aos-delay="300">
<div class="case-image">
<img src="https://images.unsplash.com/photo-1556740738-b6a63e27c4df?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=80"
alt="零售行业案例">
<div class="process-item" data-aos="fade-up" data-aos-delay="600">
<div class="process-icon">
<i class="fas fa-headset"></i>
</div>
<div class="case-content">
<h3>全国连锁零售企业</h3>
<p>开发了全渠道零售系统帮助客户销售额提升25%</p>
<a href="#" class="read-more">查看详情</a>
<div class="process-content">
<h3>06. 持续支持</h3>
<p>提供长期的技术支持和维护服务确保系统稳定运行和持续优化</p>
</div>
</div>
</div> -->
</div>
</div>
</section>
<CaseSection />
<!-- 使用CallToAction组件替换原有联系部分 -->
<CallToAction
title="准备好开始您的项目了吗?"
description="联系我们,获取专业的软件解决方案"
primaryButtonText="联系我们"
primaryButtonLink="/contact"
secondaryButtonText="服务热线"
secondaryButtonLink="tel:+8612345678901"
iconClass="fas fa-paper-plane"
/>
<!-- 原有联系部分代码注释 -->
<!--
<section class="contact-section parallax-container">
<div class="parallax-bg"></div>
<div class="container">
@ -192,6 +154,7 @@ onMounted(() => {
</div>
</div>
</section>
-->
</div>
</template>
@ -207,333 +170,124 @@ onMounted(() => {
padding: 0 20px;
}
/* 服务详情部分 */
/* 服务详情部分改为服务流程 */
.services-detail {
padding: 80px 0;
background-color: white;
.service-item {
.section-title {
text-align: center;
font-size: 2.5rem;
margin-bottom: 50px;
color: $text-color;
position: relative;
&::after {
content: '';
position: absolute;
bottom: -15px;
left: 50%;
transform: translateX(-50%);
width: 80px;
height: 3px;
background: linear-gradient(90deg, $primary-color, $secondary-color);
border-radius: 3px;
}
}
.process-flow {
position: relative;
&::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 40px;
width: 2px;
background: linear-gradient(to bottom, rgba($primary-color, 0.3), rgba($secondary-color, 0.3));
z-index: 0;
}
}
.process-item {
display: flex;
margin-bottom: 60px;
padding: 30px;
background: white;
border-radius: 8px;
box-shadow: $shadow-sm;
transition: transform 0.3s ease, box-shadow 0.3s ease;
margin-bottom: 50px;
position: relative;
z-index: 1;
&:hover {
transform: translateY(-5px);
box-shadow: $shadow-lg;
&:last-child {
margin-bottom: 0;
}
.service-icon {
flex: 0 0 100px;
.process-icon {
flex: 0 0 80px;
width: 80px;
height: 80px;
border-radius: 50%;
background: linear-gradient(135deg, $primary-color, $secondary-color);
display: flex;
align-items: center;
justify-content: center;
font-size: 3rem;
color: $primary-color;
box-shadow: 0 5px 15px rgba($primary-color, 0.3);
margin-right: 30px;
transition: transform 0.3s ease;
font-size: 2rem;
color: white;
}
.service-content {
flex: 1;
h2 {
font-size: 1.8rem;
margin-bottom: 15px;
color: $text-color;
}
p {
margin-bottom: 20px;
color: $text-light;
line-height: 1.6;
}
ul {
padding-left: 20px;
li {
margin-bottom: 10px;
color: $text-light;
}
}
}
}
}
/* 服务流程部分 */
.service-process {
padding: 80px 0;
background-color: $light-bg;
text-align: center;
h2 {
font-size: 2.5rem;
margin-bottom: 30px;
color: $text-color;
}
.process-steps {
display: flex;
justify-content: space-between;
margin-top: 50px;
flex-wrap: wrap;
.step {
.process-content {
flex: 1;
min-width: 200px;
padding: 0 20px;
text-align: center;
margin-bottom: 30px;
.step-number {
font-size: 48px;
font-weight: bold;
color: $primary-color;
margin-bottom: 20px;
}
background: white;
border-radius: 10px;
padding: 25px 30px;
box-shadow: $shadow-sm;
transition: transform 0.3s ease, box-shadow 0.3s ease;
h3 {
font-size: 1.5rem;
margin-bottom: 15px;
color: $text-color;
font-weight: 600;
}
p {
color: $text-light;
line-height: 1.6;
margin: 0;
}
}
}
}
/* 案例研究部分 */
.case-studies {
padding: 80px 0;
text-align: center;
h2 {
font-size: 2.5rem;
margin-bottom: 15px;
color: $text-color;
}
.section-desc {
max-width: 700px;
margin: 0 auto 50px;
color: $text-light;
}
.cases-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin-top: 40px;
.case-card {
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: $shadow-sm;
transition: transform 0.3s ease, box-shadow 0.3s ease;
&:hover {
transform: translateY(-10px);
box-shadow: $shadow-lg;
}
.case-image {
img {
width: 100%;
height: 200px;
object-fit: cover;
}
&:hover {
.process-icon {
transform: scale(1.1) rotate(5deg);
}
.case-content {
padding: 20px;
text-align: left;
h3 {
font-size: 1.5rem;
margin-bottom: 10px;
color: $text-color;
}
p {
color: $text-light;
margin-bottom: 15px;
line-height: 1.6;
}
.read-more {
display: inline-block;
color: $primary-color;
font-weight: 600;
text-decoration: none;
transition: color 0.3s ease;
&:hover {
color: $secondary-color;
}
}
}
}
}
}
/* 联系部分 */
.contact-section {
position: relative;
padding: 120px 0;
color: white;
text-align: center;
overflow: hidden;
display: flex;
align-items: center;
min-height: 60vh;
.parallax-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('https://images.unsplash.com/photo-1557804506-669a67965ba0?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80');
background-size: cover;
background-position: center;
background-attachment: fixed;
filter: brightness(0.3);
transform: scale(1.1);
transition: transform 0.5s ease;
}
&:hover {
.parallax-bg {
transform: scale(1);
}
}
.contact-content {
position: relative;
z-index: 1;
padding: 30px;
border-radius: 10px;
background-color: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(5px);
box-shadow: $shadow-xl;
max-width: 800px;
margin: 0 auto;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
h2 {
font-size: 2.8rem;
margin-bottom: 20px;
text-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
font-weight: 700;
}
p {
font-size: 1.3rem;
margin-bottom: 30px;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.contact-buttons {
display: flex;
justify-content: center;
gap: 20px;
margin-bottom: 25px;
}
.quick-contact {
margin-top: 20px;
font-size: 1.1rem;
span {
display: inline-block;
padding: 8px 15px;
background-color: rgba(255, 255, 255, 0.1);
border-radius: 20px;
margin: 0 5px;
transition: all 0.3s ease;
&:hover {
background-color: rgba(255, 255, 255, 0.2);
transform: translateY(-3px);
}
i {
margin-right: 8px;
color: $primary-color;
}
.process-content {
transform: translateY(-5px);
box-shadow: $shadow-lg;
}
}
}
}
/* 按钮样式 */
.btn-primary,
.btn-secondary {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 14px 32px;
border-radius: 30px;
text-decoration: none;
font-weight: 600;
transition: all 0.3s ease;
box-shadow: $shadow-sm;
&:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
}
i {
margin-right: 8px;
}
}
.btn-primary {
background-color: $primary-color;
color: white;
&:hover {
background-color: $secondary-color;
}
}
.btn-secondary {
background-color: rgba(255, 255, 255, 0.15);
color: white;
border: 1px solid rgba(255, 255, 255, 0.3);
&:hover {
background-color: rgba(255, 255, 255, 0.25);
}
}
/* 响应式设计 */
@media (max-width: 992px) {
.process-steps {
flex-direction: column;
}
.step {
margin-bottom: 40px;
}
.service-item {
flex-direction: column;
.services-detail {
.process-flow {
&::before {
left: 35px;
}
}
.service-icon {
margin-right: 0;
margin-bottom: 20px;
.process-item {
.process-icon {
flex: 0 0 70px;
width: 70px;
height: 70px;
}
}
}
}
@ -567,40 +321,48 @@ onMounted(() => {
.cases-grid {
grid-template-columns: 1fr;
}
.contact-section {
padding: 80px 0;
.services-detail {
padding: 60px 0;
.contact-content {
padding: 20px;
h2 {
font-size: 2.2rem;
}
p {
font-size: 1.1rem;
.section-title {
font-size: 2rem;
margin-bottom: 40px;
}
.process-flow {
&::before {
left: 25px;
}
}
.process-item {
margin-bottom: 40px;
.contact-buttons {
flex-direction: column;
gap: 15px;
.process-icon {
flex: 0 0 50px;
width: 50px;
height: 50px;
margin-right: 20px;
i {
font-size: 1.5rem;
}
}
.quick-contact {
span {
display: block;
margin: 10px auto;
max-width: 280px;
.process-content {
padding: 20px;
h3 {
font-size: 1.3rem;
margin-bottom: 10px;
}
p {
font-size: 0.95rem;
}
}
}
}
.btn-primary,
.btn-secondary {
width: 100%;
padding: 12px 20px;
}
}
</style>

+ 25
- 3
vite.config.js View File

@ -1,7 +1,29 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { defineConfig, loadEnv } from 'vite';
import path from 'path';
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
export default defineConfig(({ command, mode }) => {
const env = loadEnv(mode, process.cwd());
return {
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
},
server: {
host: '0.0.0.0',
port: env.VITE_APP_PORT ? Number(env.VITE_APP_PORT) : 3000,
open: true,
proxy: {
[env.VITE_APP_BASE_API]: {
target: 'http://localhost:8080',
changeOrigin: true,
ws: true,
rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')
}
}
},
}
})

Loading…
Cancel
Save