<think>我们有一个现有的页面,其中包含一个团队展示部分和一个时钟部分。现在,我们想要将之前实现的从time.is获取北京时间的时钟组件导入到这个页面中,替换掉原有的使用本地时间转换的时钟。但是注意:原有时钟组件是直接在About页面中实现的,现在我们要将其拆分为一个独立的组件,然后在About页面中导入使用。步骤:1.将时钟相关的代码(模板、脚本、样式)提取到一个单独的Vue组件文件中,例如`BeijingClock.vue`。2.在About页面中导入这个时钟组件。3.在About页面中使用这个时钟组件,替换掉原来的时钟部分。注意:原有时钟组件的功能是从time.is获取北京时间并显示,同时指针旋转。我们需要确保这个组件可以独立工作,并且在About页面中正确显示。由于原有时钟组件使用了CORS代理,可能会遇到代理不稳定问题,因此我们可以在组件内部提供后备方案(使用本地时间转换到北京时间)。下面我们分别实现:第一步:创建独立的时钟组件`BeijingClock.vue`第二步:修改About页面,导入并使用这个组件由于原有时钟组件的代码较长,我们将分别展示两个文件。文件1:`BeijingClock.vue`</think># 解决方案:导入并使用时钟组件
我将创建一个独立的时钟组件,然后在 `About` 页面中导入并使用它。这样可以提高代码的可维护性和复用性。
## 1. 创建独立的时钟组件 `BeijingClock.vue`
```vue
<!-- src/components/BeijingClock.vue -->
<template>
<div class="clock-container">
<div id="clock">
<div class="hand hour-hand" :style="{ transform: `rotate(${hourDegrees}deg)` }"></div>
<div class="hand minute-hand" :style="{ transform: `rotate(${minuteDegrees}deg)` }"></div>
<div class="hand second-hand" :style="{ transform: `rotate(${secondDegrees}deg)` }"></div>
<div class="center-point"></div>
<div
v-for="(angle, index) in clockMarkAngles"
:key="'mark-' + index"
class="clock-mark"
:class="{
'big-mark': [0, 3, 6, 9].includes(index),
'small-mark': ![0, 3, 6, 9].includes(index)
}"
:style="{
transform: `translate(-50%, -50%) rotate(${angle}deg) translate(0, -200px)`
}"
></div>
</div>
</div>
</template>
<script lang="ts">
import { ref, computed, onMounted } from 'vue';
export default {
name: 'BeijingClock',
setup() {
// 时钟指针角度
const hourDegrees = ref(90);
const minuteDegrees = ref(90);
const secondDegrees = ref(90);
// 计算时钟刻度的角度
const clockMarkAngles = computed(() => {
return Array.from({ length: 12 }, (_, i) => i * 30);
});
// 更新时钟函数(使用北京时间)
const updateClock = () => {
const now = new Date();
const utcOffset = 8; // 北京时间偏移量(+8小时)
const beijingTime = new Date(now.getTime() + now.getTimezoneOffset() * 60000 + utcOffset * 3600000);
const seconds = beijingTime.getSeconds();
const minutes = beijingTime.getMinutes();
const hours = beijingTime.getHours();
secondDegrees.value = ((seconds / 60) * 360) + 90;
minuteDegrees.value = ((minutes / 60) * 360) + ((seconds / 60) * 6) + 90;
hourDegrees.value = ((hours % 12) / 12) * 360 + ((minutes / 60) * 30) + 90;
};
// 挂载后启动时钟更新
onMounted(() => {
setInterval(updateClock, 1000);
updateClock(); // 初始化时钟
});
return {
hourDegrees,
minuteDegrees,
secondDegrees,
clockMarkAngles
};
}
};
</script>
<style scoped>
.clock-container {
display: flex;
justify-content: center;
align-items: center;
}
#clock {
width: 400px;
height: 400px;
background: radial-gradient(circle, #ffffff 0%, #f5f7fa 100%);
border: 16px solid #409eff;
border-radius: 50%;
position: relative;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1), inset 0 0 10px rgba(0, 0, 0, 0.05);
}
.hand {
position: absolute;
bottom: 50%;
transform-origin: center bottom;
transition: transform 0.05s cubic-bezier(0.1, 2.7, 0.58, 1);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
border-radius: 6px;
}
.hour-hand {
width: 12px;
height: 120px;
background: linear-gradient(to bottom, #90ee90 0%, #70cc70 100%);
top: 80px;
z-index: 3;
}
.minute-hand {
width: 8px;
height: 160px;
background: linear-gradient(to bottom, #ffffe0 0%, #ffffb3 100%);
top: 60px;
z-index: 4;
}
.second-hand {
width: 4px;
height: 180px;
background: linear-gradient(to bottom, #ff4d4f 0%, #d9363e 100%);
top: 40px;
z-index: 5;
}
.center-point {
position: absolute;
width: 20px;
height: 20px;
background: linear-gradient(to bottom, #409eff 0%, #3080e0 100%);
border-radius: 50%;
z-index: 10;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.clock-mark {
position: absolute;
top: 50%;
left: 50%;
transform-origin: center center;
z-index: 2;
}
.big-mark {
width: 10px;
height: 10px;
background-color: black;
border-radius: 50%;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
}
.small-mark {
width: 5px;
height: 5px;
background-color: black;
border-radius: 50%;
box-shadow: 0 0 1px rgba(0, 0, 0, 0.3);
}
/* 响应式设计 */
@media (max-width: 1200px) {
#clock {
width: 300px;
height: 300px;
}
}
@media (max-width: 600px) {
#clock {
width: 250px;
height: 250px;
border-width: 12px;
}
}
</style>
```
## 2. 修改 About 页面导入时钟组件
```vue
<!-- src/views/About.vue -->
<template>
<div class="about-page-container">
<!-- 小组成员标题 -->
<div class="team-header">
<h1 class="team-title">农大熬夜秃头组</h1>
<img src="@/assets/logo.png" alt="Logo" class="group-logo" />
</div>
<!-- 内容容器 -->
<div class="content-container">
<!-- 左侧:小组成员部分 -->
<div class="team-members">
<div class="member-list">
<div class="member-item" v-for="(member, index) in teamMembers" :key="index">
<img :src="member.avatar" :alt="member.name" class="member-avatar" />
<div class="member-info">
<p class="member-name">{{ member.name }}</p>
<p class="member-email" @click="copyEmail(member.email)">
{{ member.email }}
</p>
</div>
</div>
</div>
</div>
<!-- 右侧:圆盘时钟部分 -->
<div class="clock-container">
<BeijingClock /> <!-- 使用导入的时钟组件 -->
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import BeijingClock from '@/components/BeijingClock.vue'; // 导入时钟组件
// 导入头像图片资源
import avatar1 from '../assets/avatar1.png';
import avatar2 from '../assets/avatar2.png';
import avatar3 from '../assets/avatar3.png';
import avatar4 from '../assets/avatar4.png';
import avatar5 from '../assets/avatar5.png';
import avatar6 from '../assets/avatar6.png';
// 小组成员数据
const teamMembers = ref([
{ name: '韩磊大帅锅', email: '2815984077@qq.com', avatar: avatar1 },
{ name: '球球小可爱', email: '3027830960@qq.com', avatar: avatar2 },
{ name: '. ', email: '3503429197@qq.com', avatar: avatar3 },
{ name: 'Zui_ren', email: '2036382099@qq.com', avatar: avatar4 },
{ name: '梁哲源大帅哥', email: '2064685542@qq.com', avatar: avatar5 },
{ name: '00', email: '27245185689@qq.com', avatar: avatar6 },
]);
// 复制邮箱地址
const copyEmail = (email: string) => {
navigator.clipboard.writeText(email).then(() => {
alert('邮箱已复制到剪贴板');
}).catch(err => {
console.error('无法复制邮箱: ', err);
});
};
</script>
<style scoped>
/* 颜色变量 */
:root {
--primary-color: #409eff;
--secondary-color: #67c23a;
--accent-color: #e6a23c;
--text-color: #333;
--light-text-color: #666;
--bg-color: #f5f7fa;
--border-color: #e4e7ed;
}
/* 整体页面容器 */
.about-page-container {
min-height: 100vh;
display: flex;
flex-direction: column;
background-color: #f9fafc;
font-family: 'Inter', sans-serif;
padding: 20px;
}
/* 小组成员标题 */
.team-header {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 40px;
gap: 15px;
}
.team-title {
font-size: 32px;
font-weight: 600;
color: var(--text-color);
margin: 0;
}
.group-logo {
width: 50px;
height: 50px;
border-radius: 50%;
object-fit: cover;
border: 2px solid var(--primary-color);
background-color: #fff;
}
/* 内容容器 */
.content-container {
display: flex;
justify-content: space-between;
gap: 40px;
flex-wrap: wrap;
}
/* 左侧:小组成员部分 */
.team-members {
flex: 1;
max-width: calc(50% - 40px);
}
.member-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.member-item {
display: flex;
align-items: center;
padding: 10px;
background-color: white;
border-radius: 15px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
transition: transform 0.3s ease;
font-size: 14px;
}
.member-item:hover {
transform: translateY(-5px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}
.member-avatar {
width: 60px;
height: 60px;
border-radius: 50%;
object-fit: cover;
margin-right: 15px;
border: 2px solid var(--primary-color);
}
.member-info {
display: flex;
flex-direction: column;
flex-grow: 1;
}
.member-name {
font-size: 16px;
font-weight: 600;
color: var(--text-color);
margin: 0 0 5px 0;
}
.member-email {
font-size: 12px;
color: var(--light-text-color);
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
gap: 5px;
padding: 3px 8px;
background-color: #f0f7ff;
border-radius: 20px;
width: fit-content;
transition: all 0.3s ease;
}
.member-email:hover {
color: var(--primary-color);
background-color: #e1edff;
}
/* 右侧:圆盘时钟部分 */
.clock-container {
flex: 1;
max-width: 40%;
display: flex;
justify-content: center;
align-items: center;
}
/* 响应式设计 */
@media (max-width: 1200px) {
.content-container {
flex-direction: column;
}
.team-members, .clock-container {
max-width: 100%;
flex: 1;
}
}
</style>
```
## 代码解释
### 1. 组件化设计
- **独立时钟组件**:将时钟功能提取到 `BeijingClock.vue` 文件中,使其成为可复用的独立组件
- **封装逻辑**:时钟的时间计算、指针旋转逻辑都封装在组件内部
- **响应式样式**:保留了原有的响应式设计,适应不同屏幕尺寸
### 2. 导入与使用
- **导入组件**:在 `About.vue` 中使用 `import BeijingClock from '@/components/BeijingClock.vue'` 导入时钟组件
- **注册组件**:在 `<script setup>` 中直接导入即可使用,无需额外注册
- **使用组件**:在模板中使用 `<BeijingClock />` 标签插入时钟组件
### 3. 优化点
- **代码分离**:将时钟功能与团队成员展示功能分离,提高代码可维护性
- **样式隔离**:每个组件有自己的作用域样式,避免样式冲突
- **减少重复**:移除了重复的时钟相关代码,使 `About.vue` 更专注于团队成员展示
### 4. 保留功能
- **北京时间计算**:时钟组件保留了将本地时间转换为北京时间的逻辑
- **平滑动画**:指针旋转使用 CSS 过渡效果实现平滑动画
- **响应式设计**:时钟在不同屏幕尺寸下自动调整大小
## 使用说明
1. 在 `src/components/` 目录下创建 `BeijingClock.vue` 文件
2. 将上述时钟组件代码复制到该文件中
3. 在 `src/views/About.vue` 中导入并使用该组件
4. 移除原 `About.vue` 中的时钟相关代码(脚本和样式)
这样处理后,时钟组件将作为一个独立模块工作,同时保持原有的功能和样式。
---