创新项目实训开发日志5
一、开发简介
核心工作内容:logo切换、计时时钟实现、小窗实现
工作时间:第十周
二、logo切换
1.制作logo
2.利用piana保存主题风格
import { defineStore } from 'pinia';/*** theme 主题管理模块* 切换主题*/
export const useThemeStore = defineStore('theme', {state: () => ({logoUrl: new URL('@/assets/images/logo.png', import.meta.url).href,dayOrNight: false // 默认白天}),actions: {toggleTheme() {this.dayOrNight = !this.dayOrNight;if (this.dayOrNight) {// 切换到夜晚模式this.logoUrl = new URL('@/assets/images/logo2.png', import.meta.url).href;} else {// 切换到白天模式this.logoUrl = new URL('@/assets/images/logo.png', import.meta.url).href;}}}
});
3.切换主题——组件
<!--* @FileDescription: 白天模式与深色模式的切换* @Author: Nevertheless* @Date: 2025.4.4* @LastEditors: Nevertheless* @LastEditTime: 2025.4.26--><script setup>
import {ref} from "vue";
import {useThemeStore} from '@/stores/theme.js'/*** 变量初始化* */
const dayOrNight = ref(false);
const baseColor = "#232323";
const contrastColor = "#FFFFFF";
const themeStore = useThemeStore();/*** @description: 改变背景颜色* */
const changeBackground = () => {if (dayOrNight.value) {console.log("切换至夜晚");document.getElementsByTagName('body')[0].style.setProperty('--global-background-color', baseColor);document.getElementsByTagName('body')[0].style.setProperty('--global-background-color-contrast', contrastColor);document.getElementsByTagName('body')[0].style.setProperty('--global-font-color', contrastColor);document.getElementsByTagName('body')[0].style.setProperty('$global-font-color-contrast', baseColor);themeStore.toggleTheme();} else {console.log("切换至白天");document.getElementsByTagName('body')[0].style.setProperty('--global-background-color', contrastColor);document.getElementsByTagName('body')[0].style.setProperty('--global-background-color-contrast', baseColor);document.getElementsByTagName('body')[0].style.setProperty('--global-font-color', baseColor);document.getElementsByTagName('body')[0].style.setProperty('$global-font-color-contrast', contrastColor);themeStore.toggleTheme();}}
</script><template><span><el-switchstyle="--el-switch-on-color: #2F2F2F; --el-switch-off-color: #F1F1F1"v-model="dayOrNight"active-text="晚上"inactive-text="白天"@change="changeBackground"></el-switch></span>
</template><style scoped lang="scss"></style>
4.资源绑定
import {useThemeStore} from '@/stores/theme.js'
import { computed } from 'vue';
const themeStore = useThemeStore();
const logoUrl = computed(() => themeStore.logoUrl);
三、计时时钟实现
1.页面设计
2.路由添加
{ path: '/clock', component: ClockVue },
3.状态管理
import { defineStore } from 'pinia'
import { ref } from 'vue'
import router from "@/router";/*** smallWindow 小窗管理模块* 显示时间*/
export const useSmallWindowStore = defineStore('smallWindow', () => {const showSmallWindow = ref(false)const clockTime = ref(0)const isRunning = ref(false)let timer = nullconst startTimer = () => {if (!isRunning.value) {timer = setInterval(() => {clockTime.value++}, 1000)isRunning.value = true}}const pauseTimer = () => {clearInterval(timer)isRunning.value = false}const handleDoubleClick = () => {setTimeout(() => {showSmallWindow.value = falserouter.push('/clock')}, 300) // 先缩小动画 300ms,再跳转}const resetTimer = () => {clearInterval(timer)clockTime.value = 0isRunning.value = false}return {showSmallWindow,clockTime,isRunning,startTimer,pauseTimer,resetTimer,handleDoubleClick,}
})
4.页面实现
<!--* @FileDescription: 时钟* @Author: Nevertheless* @Date: 2025.4.26* @LastEditors: Nevertheless* @LastEditTime: 2025.4.26-->
<script setup>
/**导入组件**/
import {computed, ref} from 'vue'
import {useRouter} from 'vue-router'/**导入函数**/
import {useSmallWindowStore} from '@/stores/smallWindow'/**初始化**/
// 科目选择
const subjects = ['数据结构', '计算机网络', '计算机组成原理', '操作系统']
const selectedSubject = ref(subjects[0])const smallWindowStore = useSmallWindowStore()
const router = useRouter()/**定义函数**/
// 计时格式化
const formattedTime = computed(() => {const minutes = Math.floor(smallWindowStore.clockTime / 60)const seconds = smallWindowStore.clockTime % 60return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`
})// 计时控制
const startTimer = () => {smallWindowStore.startTimer()
}const pauseTimer = () => {smallWindowStore.pauseTimer()
}const resetTimer = () => {console.log(`结束:${selectedSubject.value},用时 ${formattedTime.value}`)smallWindowStore.resetTimer()
}const clearTimer = () => {smallWindowStore.clockTime = 0
}// 最小化窗口并跳转到首页
const minimizeWindow = () => {smallWindowStore.showSmallWindow = truerouter.push('/home')
}
</script><template><!-- 主界面 --><el-card v-if="!smallWindowStore.showSmallWindow" class="timer-container"><!-- 头部 --><div class="header"><h2>408 科目训练</h2><el-selectclass="subject-select"v-model="selectedSubject"placeholder="选择科目"size="large"style="width: 240px"><el-optionv-for="subject in subjects":key="subject":label="subject":value="subject"/></el-select></div><!-- 标题 --><div class="title"><h1>训练计时</h1></div><!-- 时间 --><div class="timer-display"><p class="time-text">{{ formattedTime }}</p></div><div class="controls"><button @click="startTimer" :disabled="smallWindowStore.isRunning">开始</button><button @click="pauseTimer" :disabled="!smallWindowStore.isRunning">暂停</button><button @click="resetTimer" :disabled="smallWindowStore.clockTime === 0">结束</button><button @click="clearTimer">重置</button></div><div class="footer"><button class="minimize-btn" @click="minimizeWindow">返回首页 / 最小化</button></div></el-card>
</template><style lang="scss" scoped>
.timer-container {@include hold(99, 99);text-align: center;font-family: 'Segoe UI', sans-serif;
}/* 头部 */
.header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 20px;
}h2 {font-size: 22px;margin: 0;
}.subject-select {font-size: 16px;padding: 5px 10px;}/* 标题 */
.title {margin-top: 20px;
}.title h1 {font-size: 32px;font-weight: bold;margin: 20px 0;
}/* 时间显示 */
.timer-display {margin: 20px 0;
}.time-text {font-size: 120px;font-weight: bold;
}/* 控制按钮 */
.controls {display: flex;justify-content: space-around;margin-top: 20px;
}.controls button {width: 120px;padding: 10px 16px;border: none;border-radius: 8px;font-size: 16px;cursor: pointer;transition: all 0.2s ease;background-color: #409eff;color: white;
}.controls button:disabled {background-color: #bbb;cursor: not-allowed;
}.footer {margin-top: 30px;
}.minimize-btn {background-color: #f56c6c;padding: 8px 20px;border-radius: 8px;border: none;font-size: 16px;color: white;cursor: pointer;
}
</style>
四、小窗实现
1.小窗组件
<!--* @FileDescription: 窗口* @Author: Nevertheless* @Date: 2025.4.26* @LastEditors: Nevertheless* @LastEditTime: 2025.4.26-->
<script setup>
/**导入组件**/
import {computed, ref} from 'vue'
import {useRouter} from 'vue-router'/**导入函数**/
import {useSmallWindowStore} from '@/stores/smallWindow'/**初始化**/
const router = useRouter()
const smallWindowStore = useSmallWindowStore()
const windowRef = ref(null)let dragging = false
let offsetY = 0/**定义函数**/
// 显示时间
const formattedTime = computed(() => {const minutes = Math.floor(smallWindowStore.clockTime / 60)const seconds = smallWindowStore.clockTime % 60return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`
})// 开始拖动(上下)
const startDrag = (e) => {dragging = trueoffsetY = e.clientY - windowRef.value.getBoundingClientRect().topconst onMouseMove = (e) => {if (!dragging) returnconst newTop = e.clientY - offsetYwindowRef.value.style.top = `${newTop}px`}const onMouseUp = () => {dragging = falsewindow.removeEventListener('mousemove', onMouseMove)window.removeEventListener('mouseup', onMouseUp)}window.addEventListener('mousemove', onMouseMove)window.addEventListener('mouseup', onMouseUp)
}// 双击逻辑
const handleDoubleClick = () => {smallWindowStore.handleDoubleClick()
}
</script><template><divv-if="smallWindowStore.showSmallWindow"class="small-window"@mousedown="startDrag"@dblclick="handleDoubleClick"ref="windowRef">⏱️ {{ formattedTime }}</div>
</template><style scoped lang="scss">
.small-window {position: fixed;top: 100px;right: 20px;width: 100px;height: 50px;background-color: #2c3e50;color: white;display: flex;justify-content: center;align-items: center;font-size: 18px;border-radius: 12px;box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);user-select: none;cursor: grab;z-index: 1000;transition: all 0.3s ease;&:hover {background-color: #34495e;}
}</style>
2.组件配置到Layout
<!-- 浮动小窗 -->
<small-window></small-window>
3.主页跳转
<!--* @FileDescription: 主页* @Author: Nevertheless* @Date: 2025.4.3* @LastEditors: Nevertheless* @LastEditTime: 2025.4.26--><script setup>
/**导入组件**/
import { useRouter } from 'vue-router'
import {useSmallWindowStore} from "@/stores/smallWindow";/**导入函数**//**初始化**/
const router = useRouter()
const smallWindowStore = useSmallWindowStore()/**定义函数**/
// 跳转到 clock 页面
const goToClock = () => {if(!smallWindowStore.showSmallWindow) {router.push('/clock') // 跳转到 clock 页面}else {smallWindowStore.handleDoubleClick()}
}/*** @description: 测试Postman的后端是否能正常运行* @return void*/
const testPostmanSeverFunction = async () => {// let result = await testPostmanSever();// console.log(result)
}// testPostmanSeverFunction()
</script><template><el-card class="card">这是一个卡片<hr><button @click="goToClock">跳转到 Clock 页面</button></el-card>
</template><style lang="scss" scoped>
.card {@include content-center-flex;@include hold(99, 99);@include position-center-box("column,row");background-color: $global-background-color;color: $global-font-color;
}
</style>