This commit is contained in:
Guwan 2025-07-31 00:42:47 +08:00
commit d22dde58fc
28 changed files with 2959 additions and 0 deletions

28
.gitignore vendored Normal file
View File

@ -0,0 +1,28 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
package-lock.json
yarn.lock
**/cert/

296
CHANGELOG.md Normal file
View File

@ -0,0 +1,296 @@
# 项目改动日志
## 版本: v1.2.0 - 界面全面美化与作业管理 (2024-01-30)
### 🎉 新增功能
#### 1. 小学作业管理系统
- **完整的作业管理界面** - 基于AboutView重构
- **作业统计面板** - 4个统计卡片展示完成情况
- **作业列表管理** - 支持筛选、添加、完成、删除操作
- **预设作业内容** - 包含数学、语文、英语、科学四个科目
- **响应式设计** - 适配不同屏幕尺寸的美观界面
#### 2. 美观对话框系统
- **精美提示框** - 复杂功能展示的大型对话框
- **简单提示框** - 轻量级确认对话框
- **美化表单对话框** - 添加作业的高级表单界面
### 🎨 界面美化升级
#### 1. 数据表格全面美化
- **渐变表头** - 蓝紫色渐变背景,白色文字
- **圆角设计** - 12px圆角现代化外观
- **悬停效果** - 行悬停时轻微放大和彩色阴影
- **图标装饰** - 日期、姓名、邮箱列都添加了对应图标
- **操作按钮美化** - 渐变色按钮,悬停上浮效果
- **标签美化** - 部门标签使用圆角胶囊设计
#### 2. 添加作业对话框美化
- **渐变头部设计** - 蓝紫色渐变 + 脉冲动画图标
- **表单输入美化** - 圆角输入框,悬停和聚焦效果
- **科目选择器** - 带图标的选项设计
- **难度按钮组** - 渐变激活状态,悬停动画
- **底部按钮** - 渐变背景,上浮动画效果
#### 3. 作业管理界面设计
- **渐变背景** - 整体页面使用柔和渐变
- **统计卡片** - 彩色图标 + 悬停上浮效果
- **作业项目** - 左侧彩色边框,悬停右移效果
- **科目标签** - 每个科目不同的渐变色彩
- **筛选按钮** - 活跃状态高亮显示
### 🔧 交互体验提升
#### 1. 导航系统完善
- **路由跳转** - 菜单项正确连接到对应页面
- **自动高亮** - 根据当前路由自动高亮菜单项
- **前进后退支持** - 浏览器导航按钮正确更新菜单状态
#### 2. 用户反馈系统
- **消息提示** - 所有操作都有即时反馈
- **状态变化** - 作业完成状态实时更新
- **视觉反馈** - 按钮点击、悬停的视觉效果
#### 3. 表格交互增强
- **行点击事件** - 点击表格行显示信息提示
- **按钮防冲突** - 操作按钮阻止行点击事件冒泡
- **图标表意** - 每列都有对应的图标说明
### 📱 响应式设计改进
#### 1. 作业管理页面
- **弹性布局** - 统计卡片自适应排列
- **移动端优化** - 小屏幕下的合理间距
- **卡片悬停** - 不同设备的交互适配
#### 2. 对话框适配
- **居中显示** - 所有对话框完美居中
- **宽度自适应** - 根据内容和屏幕自动调整
- **触摸友好** - 移动设备上的操作体验
### 🎯 样式系统重构
#### 1. 全局宽度优化 (已完成)
- **移除宽度限制** - 应用可以使用全屏宽度
- **响应式间距** - 不同屏幕尺寸的合适内边距
- **布局优化** - 移除网格限制,使用正常流布局
#### 2. 组件样式统一
- **颜色主题** - 统一的蓝紫色主题色彩
- **圆角标准** - 12-16px的统一圆角规范
- **阴影层次** - 不同深度的阴影效果
- **动画规范** - 0.3s的统一过渡时间
### 🛠️ 技术优化
#### 1. Vue 3 组合式API应用
- **响应式数据** - 使用ref和computed管理状态
- **生命周期** - 合理使用watch监听路由变化
- **类型支持** - TypeScript类型定义完善
#### 2. ElementPlus深度定制
- **样式穿透** - 使用:deep()修改组件内部样式
- **主题定制** - 覆盖默认样式实现个性化设计
- **组件扩展** - 在ElementPlus基础上增强功能
### 📦 新增依赖和配置
```json
{
"features": {
"作业管理": "完整的学生作业管理系统",
"表格美化": "全面美化的数据表格组件",
"对话框美化": "多种风格的美观对话框",
"导航增强": "完善的路由导航系统"
}
}
```
### 🚀 用户体验提升
- **视觉冲击力** - 现代化的渐变色彩和动画效果
- **操作便捷性** - 直观的图标和清晰的交互反馈
- **功能完整性** - 从数据展示到作业管理的完整流程
- **响应速度** - 流畅的动画和即时的状态更新
### 🎨 设计亮点
1. **色彩搭配** - 蓝紫色主题 + 功能性彩色图标
2. **动画效果** - 悬停上浮、脉冲动画、渐变过渡
3. **层次结构** - 通过阴影和颜色建立清晰的视觉层次
4. **一致性** - 统一的设计语言和交互模式
### 📋 功能清单
- ✅ 用户数据表格美化
- ✅ 作业管理系统完成
- ✅ 多种风格对话框
- ✅ 导航路由修复
- ✅ 响应式布局优化
- ✅ 交互动画增强
- ✅ 主题色彩统一
### 🎯 下一步计划
- [ ] 添加设置页面功能
- [ ] 数据持久化存储
- [ ] 用户权限管理
- [ ] 暗黑模式支持
- [ ] 国际化多语言
- [ ] 移动端App化
- [ ] 数据导出功能
---
## 版本: v1.0.0 - ElementPlus 集成 (2024-01-30)
### 🎉 新增功能
#### 1. ElementPlus 组件库集成
- **安装依赖包**
- `element-plus` - Vue 3 的企业级 UI 组件库
- `@element-plus/icons-vue` - ElementPlus 官方图标库
#### 2. 全新的用户界面设计
- **导航栏**: 使用 ElementPlus 水平菜单组件
- **卡片布局**: 响应式卡片展示系统特性
- **数据表格**: 完整的用户管理表格,支持编辑和删除
- **表单组件**: 带验证的用户添加表单
- **统计面板**: 数据统计和进度展示
- **时间线**: 系统通知时间线展示
### 🔧 文件修改详情
#### 1. `src/main.ts` - 应用入口配置
```typescript
// 新增内容:
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
// 注册所有 ElementPlus 图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(ElementPlus)
```
#### 2. `src/App.vue` - 主应用组件重构
- **移除原有**: `HelloWorld` 组件引用
- **新增组件**:
- `el-menu` - 水平导航菜单
- `el-card` - 欢迎卡片和功能展示
- `el-button` - 各种类型的按钮组件
- `el-alert` - 成功提示组件
- `el-row/el-col` - 响应式栅格布局
- `el-icon` - 图标组件集成
#### 3. `src/views/HomeView.vue` - 首页完全重构
- **移除**: `TheWelcome` 组件
- **新增功能模块**:
**数据表格模块**:
- `el-table` - 用户数据展示表格
- `el-tag` - 部门标签显示
- 编辑和删除操作按钮
**用户管理模块**:
- `el-dialog` - 添加用户对话框
- `el-form` - 表单组件与验证
- `el-input` - 输入框组件
- `el-select` - 下拉选择器
- 完整的表单验证规则
**统计面板模块**:
- `el-statistic` - 数据统计组件
- `el-progress` - 进度条显示
- `el-steps` - 项目步骤展示
- `el-timeline` - 系统通知时间线
### 🎨 样式系统重构
#### 1. `src/assets/main.css` - 全局样式优化
**问题修复**: 界面宽度受限问题
**修改前**:
```css
#app {
max-width: 1280px; /* 限制最大宽度 */
margin: 0 auto;
padding: 2rem;
}
@media (min-width: 1024px) {
#app {
display: grid;
grid-template-columns: 1fr 1fr; /* 强制两列布局 */
padding: 0 2rem;
}
}
```
**修改后**:
```css
#app {
width: 100%; /* 使用全屏宽度 */
min-height: 100vh; /* 最小高度为视窗高度 */
margin: 0;
padding: 0;
}
@media (min-width: 1024px) {
#app {
display: block; /* 正常块级布局 */
width: 100%;
padding: 0;
}
}
```
#### 2. 组件样式优化
- **App.vue**: 减少内边距,添加响应式设计
- **HomeView.vue**: 优化容器宽度,确保表格全宽显示
### 📱 响应式设计改进
- **移动端**: 优化小屏幕显示效果
- **桌面端**: 充分利用大屏幕空间
- **自适应**: 不同尺寸下的合适内边距
### 🛠️ 开发体验提升
- **TypeScript 支持**: 完整的类型定义
- **组件复用**: 模块化的组件结构
- **代码规范**: 统一的编码风格
- **注释完善**: 详细的中文注释
### 📦 依赖变更
```json
{
"dependencies": {
"element-plus": "^2.x.x",
"@element-plus/icons-vue": "^2.x.x"
}
}
```
### 🚀 使用说明
1. **安装依赖**: `npm install`
2. **启动开发**: `npm run dev`
3. **构建生产**: `npm run build`
### 🔧 主要组件功能
- **导航菜单**: 支持页面切换和路由跳转
- **用户管理**: 完整的增删改查操作
- **数据展示**: 美观的表格和统计图表
- **表单验证**: 实时验证和错误提示
- **消息反馈**: 操作成功/失败的消息提示
### 📋 技术栈
- **Vue 3**: 最新的响应式框架
- **TypeScript**: 类型安全的JavaScript超集
- **ElementPlus**: 企业级UI组件库
- **Vue Router**: 官方路由管理器
- **Pinia**: 现代化状态管理
- **Vite**: 快速的构建工具
---
**开发者**: AI Assistant
**最新更新**: 2024-01-30
**当前版本**: v1.2.0

33
README.md Normal file
View File

@ -0,0 +1,33 @@
# my-vue3-app
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
## Customize configuration
See [Vite Configuration Reference](https://vite.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Type-Check, Compile and Minify for Production
```sh
npm run build
```

1
env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

13
index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

35
package.json Normal file
View File

@ -0,0 +1,35 @@
{
"name": "my-vue3-app",
"version": "0.0.0",
"private": true,
"type": "module",
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --build"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"element-plus": "^2.10.4",
"pinia": "^3.0.3",
"vue": "^3.5.18",
"vue-router": "^4.5.1"
},
"devDependencies": {
"@tsconfig/node22": "^22.0.2",
"@types/node": "^22.16.5",
"@vitejs/plugin-vue": "^6.0.1",
"@vitejs/plugin-vue-jsx": "^5.0.1",
"@vue/tsconfig": "^0.7.0",
"npm-run-all2": "^8.0.4",
"typescript": "~5.8.0",
"vite": "^7.0.6",
"vite-plugin-vue-devtools": "^8.0.0",
"vue-tsc": "^3.0.4"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

214
src/App.vue Normal file
View File

@ -0,0 +1,214 @@
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import { ref, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
const activeIndex = ref('1')
//
const updateActiveIndex = () => {
switch(route.path) {
case '/':
activeIndex.value = '1'
break
case '/about':
activeIndex.value = '2'
break
default:
activeIndex.value = '1'
}
}
//
updateActiveIndex()
//
watch(() => route.path, () => {
updateActiveIndex()
})
const handleSelect = (key: string, keyPath: string[]) => {
console.log(key, keyPath)
//
switch(key) {
case '1':
router.push('/')
break
case '2':
router.push('/about')
break
case '3':
//
router.push('/')
break
}
}
</script>
<template>
<div class="app-container">
<!-- ElementPlus 导航栏 -->
<el-menu
:default-active="activeIndex"
class="el-menu-demo"
mode="horizontal"
@select="handleSelect"
>
<el-menu-item index="1">
<el-icon><house /></el-icon>
<span>首页</span>
</el-menu-item>
<el-menu-item index="2">
<el-icon><document /></el-icon>
<span>作业</span>
</el-menu-item>
<el-menu-item index="3">
<el-icon><setting /></el-icon>
<span>设置</span>
</el-menu-item>
</el-menu>
<!-- 主要内容区域 -->
<div class="main-content">
<div class="welcome-section">
<el-card class="welcome-card">
<template #header>
<div class="card-header">
<span>欢迎使用 Vue 3 + ElementPlus</span>
<el-button type="primary" class="button">
<el-icon><plus /></el-icon>
新建项目
</el-button>
</div>
</template>
<div class="demo-section">
<el-row :gutter="20">
<el-col :span="8">
<el-card shadow="hover">
<template #header>
<div class="demo-header">
<el-icon><star /></el-icon>
<span>组件丰富</span>
</div>
</template>
<p>ElementPlus 提供了丰富的组件库满足各种业务需求</p>
<el-button type="primary" size="small">了解更多</el-button>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover">
<template #header>
<div class="demo-header">
<el-icon><magic-stick /></el-icon>
<span>设计精美</span>
</div>
</template>
<p>基于现代设计理念提供美观的用户界面</p>
<el-button type="success" size="small">查看设计</el-button>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover">
<template #header>
<div class="demo-header">
<el-icon><cpu /></el-icon>
<span>高性能</span>
</div>
</template>
<p>基于Vue 3构建享受最新的响应式系统带来的性能提升</p>
<el-button type="warning" size="small">性能测试</el-button>
</el-card>
</el-col>
</el-row>
</div>
<div class="demo-components">
<h3>组件演示</h3>
<el-divider />
<div class="component-demo">
<el-space wrap>
<el-button>默认按钮</el-button>
<el-button type="primary">主要按钮</el-button>
<el-button type="success">成功按钮</el-button>
<el-button type="info">信息按钮</el-button>
<el-button type="warning">警告按钮</el-button>
<el-button type="danger">危险按钮</el-button>
</el-space>
<el-divider />
<el-alert
title="恭喜ElementPlus 已成功引入到您的 Vue 3 项目中"
type="success"
:closable="false"
show-icon>
</el-alert>
</div>
</div>
</el-card>
</div>
<!-- 路由视图 -->
<div class="router-view">
<RouterView />
</div>
</div>
</div>
</template>
<style scoped>
.app-container {
min-height: 100vh;
width: 100%;
}
.main-content {
padding: 10px 15px; /* 减少内边距 */
max-width: 100%; /* 确保使用全宽 */
}
.welcome-card {
margin-bottom: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.demo-section {
margin: 20px 0;
}
.demo-header {
display: flex;
align-items: center;
gap: 8px;
}
.demo-components {
margin-top: 30px;
}
.component-demo {
margin-top: 20px;
}
.router-view {
margin-top: 20px;
}
/* 优化大屏幕显示 */
@media (min-width: 1200px) {
.main-content {
padding: 15px 30px; /* 大屏幕时稍微增加内边距 */
}
}
</style>

0
src/assets/base.css Normal file
View File

1
src/assets/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

After

Width:  |  Height:  |  Size: 276 B

38
src/assets/main.css Normal file
View File

@ -0,0 +1,38 @@
@import './base.css';
#app {
/* 移除最大宽度限制,让界面可以使用全屏宽度 */
width: 100%;
min-height: 100vh;
margin: 0;
padding: 0;
font-weight: normal;
}
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
padding: 3px;
}
@media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}
@media (min-width: 1024px) {
body {
margin: 0;
padding: 0;
}
#app {
/* 移除网格布局,使用正常的块级布局 */
display: block;
width: 100%;
padding: 0;
}
}

View File

@ -0,0 +1,41 @@
<script setup lang="ts">
defineProps<{
msg: string
}>()
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<h3>
Youve successfully created a project with
<a href="https://vite.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. What's next?
</h3>
</div>
</template>
<style scoped>
h1 {
font-weight: 500;
font-size: 2.6rem;
position: relative;
top: -10px;
}
h3 {
font-size: 1.2rem;
}
.greetings h1,
.greetings h3 {
text-align: center;
}
@media (min-width: 1024px) {
.greetings h1,
.greetings h3 {
text-align: left;
}
}
</style>

View File

@ -0,0 +1,94 @@
<script setup lang="ts">
import WelcomeItem from './WelcomeItem.vue'
import DocumentationIcon from './icons/IconDocumentation.vue'
import ToolingIcon from './icons/IconTooling.vue'
import EcosystemIcon from './icons/IconEcosystem.vue'
import CommunityIcon from './icons/IconCommunity.vue'
import SupportIcon from './icons/IconSupport.vue'
const openReadmeInEditor = () => fetch('/__open-in-editor?file=README.md')
</script>
<template>
<WelcomeItem>
<template #icon>
<DocumentationIcon />
</template>
<template #heading>Documentation</template>
Vues
<a href="https://vuejs.org/" target="_blank" rel="noopener">official documentation</a>
provides you with all information you need to get started.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<ToolingIcon />
</template>
<template #heading>Tooling</template>
This project is served and bundled with
<a href="https://vite.dev/guide/features.html" target="_blank" rel="noopener">Vite</a>. The
recommended IDE setup is
<a href="https://code.visualstudio.com/" target="_blank" rel="noopener">VSCode</a>
+
<a href="https://github.com/vuejs/language-tools" target="_blank" rel="noopener">Vue - Official</a>. If
you need to test your components and web pages, check out
<a href="https://vitest.dev/" target="_blank" rel="noopener">Vitest</a>
and
<a href="https://www.cypress.io/" target="_blank" rel="noopener">Cypress</a>
/
<a href="https://playwright.dev/" target="_blank" rel="noopener">Playwright</a>.
<br />
More instructions are available in
<a href="javascript:void(0)" @click="openReadmeInEditor"><code>README.md</code></a
>.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<EcosystemIcon />
</template>
<template #heading>Ecosystem</template>
Get official tools and libraries for your project:
<a href="https://pinia.vuejs.org/" target="_blank" rel="noopener">Pinia</a>,
<a href="https://router.vuejs.org/" target="_blank" rel="noopener">Vue Router</a>,
<a href="https://test-utils.vuejs.org/" target="_blank" rel="noopener">Vue Test Utils</a>, and
<a href="https://github.com/vuejs/devtools" target="_blank" rel="noopener">Vue Dev Tools</a>. If
you need more resources, we suggest paying
<a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">Awesome Vue</a>
a visit.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<CommunityIcon />
</template>
<template #heading>Community</template>
Got stuck? Ask your question on
<a href="https://chat.vuejs.org" target="_blank" rel="noopener">Vue Land</a>
(our official Discord server), or
<a href="https://stackoverflow.com/questions/tagged/vue.js" target="_blank" rel="noopener"
>StackOverflow</a
>. You should also follow the official
<a href="https://bsky.app/profile/vuejs.org" target="_blank" rel="noopener">@vuejs.org</a>
Bluesky account or the
<a href="https://x.com/vuejs" target="_blank" rel="noopener">@vuejs</a>
X account for latest news in the Vue world.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<SupportIcon />
</template>
<template #heading>Support Vue</template>
As an independent project, Vue relies on community backing for its sustainability. You can help
us by
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>.
</WelcomeItem>
</template>

View File

@ -0,0 +1,87 @@
<template>
<div class="item">
<i>
<slot name="icon"></slot>
</i>
<div class="details">
<h3>
<slot name="heading"></slot>
</h3>
<slot></slot>
</div>
</div>
</template>
<style scoped>
.item {
margin-top: 2rem;
display: flex;
position: relative;
}
.details {
flex: 1;
margin-left: 1rem;
}
i {
display: flex;
place-items: center;
place-content: center;
width: 32px;
height: 32px;
color: var(--color-text);
}
h3 {
font-size: 1.2rem;
font-weight: 500;
margin-bottom: 0.4rem;
color: var(--color-heading);
}
@media (min-width: 1024px) {
.item {
margin-top: 0;
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
}
i {
top: calc(50% - 25px);
left: -26px;
position: absolute;
border: 1px solid var(--color-border);
background: var(--color-background);
border-radius: 8px;
width: 50px;
height: 50px;
}
.item:before {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
bottom: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:after {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
top: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:first-of-type:before {
display: none;
}
.item:last-of-type:after {
display: none;
}
}
</style>

View File

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
/>
</svg>
</template>

View File

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
<path
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
/>
</svg>
</template>

View File

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
<path
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
/>
</svg>
</template>

View File

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
/>
</svg>
</template>

View File

@ -0,0 +1,19 @@
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true"
role="img"
class="iconify iconify--mdi"
width="24"
height="24"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 24 24"
>
<path
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
fill="currentColor"
></path>
</svg>
</template>

26
src/main.ts Normal file
View File

@ -0,0 +1,26 @@
import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
// 引入ElementPlus
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
// 引入ElementPlus图标
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
// 注册ElementPlus图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(createPinia())
app.use(router)
app.use(ElementPlus)
app.mount('#app')

23
src/router/index.ts Normal file
View File

@ -0,0 +1,23 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView,
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue'),
},
],
})
export default router

12
src/stores/counter.ts Normal file
View File

@ -0,0 +1,12 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})

970
src/views/AboutView.vue Normal file
View File

@ -0,0 +1,970 @@
<template>
<div class="homework-container">
<!-- 作业页面头部 -->
<div class="homework-header">
<el-card class="header-card">
<div class="header-content">
<div class="header-left">
<el-icon size="40" class="homework-icon"><reading /></el-icon>
<div class="header-text">
<h1 class="page-title">📚 小学作业管理</h1>
<p class="page-subtitle">让学习变得更有趣</p>
</div>
</div>
<div class="header-right">
<el-button type="primary" class="add-homework-btn" @click="showAddDialog = true">
<el-icon><plus /></el-icon>
添加作业
</el-button>
</div>
</div>
</el-card>
</div>
<!-- 作业统计卡片 -->
<el-row :gutter="20" class="stats-row">
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-icon completed">
<el-icon size="24"><select /></el-icon>
</div>
<div class="stat-text">
<div class="stat-number">{{ completedCount }}</div>
<div class="stat-label">已完成</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-icon pending">
<el-icon size="24"><clock /></el-icon>
</div>
<div class="stat-text">
<div class="stat-number">{{ pendingCount }}</div>
<div class="stat-label">待完成</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-icon total">
<el-icon size="24"><document /></el-icon>
</div>
<div class="stat-text">
<div class="stat-number">{{ homeworkList.length }}</div>
<div class="stat-label">总作业</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-icon progress">
<el-icon size="24"><trophy /></el-icon>
</div>
<div class="stat-text">
<div class="stat-number">{{ completionRate }}%</div>
<div class="stat-label">完成率</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 作业列表 -->
<el-card class="homework-list-card">
<template #header>
<div class="card-header">
<span class="header-title">
<el-icon><notebook /></el-icon>
作业清单
</span>
<el-button-group>
<el-button
:type="filterStatus === 'all' ? 'primary' : ''"
size="small"
@click="filterStatus = 'all'"
>
全部
</el-button>
<el-button
:type="filterStatus === 'pending' ? 'primary' : ''"
size="small"
@click="filterStatus = 'pending'"
>
待完成
</el-button>
<el-button
:type="filterStatus === 'completed' ? 'primary' : ''"
size="small"
@click="filterStatus = 'completed'"
>
已完成
</el-button>
</el-button-group>
</div>
</template>
<div class="homework-list">
<div
v-for="(homework, index) in filteredHomework"
:key="homework.id"
class="homework-item"
:class="{ 'completed': homework.completed }"
>
<div class="homework-content">
<div class="homework-left">
<div class="subject-badge" :class="homework.subject.toLowerCase()">
<el-icon size="20">
<component :is="getSubjectIcon(homework.subject)" />
</el-icon>
<span>{{ homework.subject }}</span>
</div>
<div class="homework-details">
<h3 class="homework-title">{{ homework.title }}</h3>
<p class="homework-desc">{{ homework.description }}</p>
<div class="homework-meta">
<span class="due-date">
<el-icon><calendar /></el-icon>
截止日期: {{ homework.dueDate }}
</span>
<span class="difficulty" :class="homework.difficulty">
<el-icon><star /></el-icon>
{{ getDifficultyText(homework.difficulty) }}
</span>
</div>
</div>
</div>
<div class="homework-actions">
<el-button
v-if="!homework.completed"
type="success"
size="small"
@click="markCompleted(homework.id)"
>
<el-icon><check /></el-icon>
完成
</el-button>
<el-button
v-else
type="info"
size="small"
disabled
>
<el-icon><select /></el-icon>
已完成
</el-button>
<el-button
type="primary"
size="small"
@click="viewDetails(homework)"
>
<el-icon><view /></el-icon>
查看
</el-button>
<el-button
type="danger"
size="small"
@click="deleteHomework(homework.id)"
>
<el-icon><delete /></el-icon>
删除
</el-button>
</div>
</div>
</div>
</div>
</el-card>
<!-- 添加作业对话框 -->
<el-dialog
v-model="showAddDialog"
width="600px"
center
:show-close="false"
class="add-homework-dialog"
>
<template #header>
<div class="dialog-header">
<div class="header-icon">
<el-icon size="32"><plus /></el-icon>
</div>
<div class="header-text">
<h2 class="dialog-title">📝 添加新作业</h2>
<p class="dialog-subtitle">为学习添加新的任务</p>
</div>
</div>
</template>
<div class="dialog-body">
<el-form :model="newHomework" label-width="0px" class="beautiful-form">
<div class="form-section">
<div class="form-item-wrapper">
<div class="form-label">
<el-icon><reading /></el-icon>
<span>选择科目</span>
</div>
<el-select
v-model="newHomework.subject"
placeholder="请选择科目"
class="form-input"
size="large"
>
<el-option label="📚 语文" value="语文">
<div class="option-content">
<el-icon><reading /></el-icon>
<span>语文</span>
</div>
</el-option>
<el-option label="🔢 数学" value="数学">
<div class="option-content">
<el-icon><calculator /></el-icon>
<span>数学</span>
</div>
</el-option>
<el-option label="🌍 英语" value="英语">
<div class="option-content">
<el-icon><chat-line-round /></el-icon>
<span>英语</span>
</div>
</el-option>
<el-option label="🔬 科学" value="科学">
<div class="option-content">
<el-icon><magic-stick /></el-icon>
<span>科学</span>
</div>
</el-option>
</el-select>
</div>
<div class="form-item-wrapper">
<div class="form-label">
<el-icon><edit /></el-icon>
<span>作业标题</span>
</div>
<el-input
v-model="newHomework.title"
placeholder="请输入作业标题"
class="form-input"
size="large"
/>
</div>
<div class="form-item-wrapper">
<div class="form-label">
<el-icon><document /></el-icon>
<span>作业描述</span>
</div>
<el-input
v-model="newHomework.description"
type="textarea"
placeholder="请详细描述作业内容和要求"
:rows="4"
class="form-input"
resize="none"
/>
</div>
<div class="form-row">
<div class="form-item-wrapper half">
<div class="form-label">
<el-icon><star /></el-icon>
<span>难度等级</span>
</div>
<el-radio-group v-model="newHomework.difficulty" class="difficulty-group">
<el-radio-button label="easy" class="difficulty-easy">
<el-icon><circle-check /></el-icon>
简单
</el-radio-button>
<el-radio-button label="medium" class="difficulty-medium">
<el-icon><warning /></el-icon>
中等
</el-radio-button>
<el-radio-button label="hard" class="difficulty-hard">
<el-icon><close /></el-icon>
困难
</el-radio-button>
</el-radio-group>
</div>
<div class="form-item-wrapper half">
<div class="form-label">
<el-icon><calendar /></el-icon>
<span>截止日期</span>
</div>
<el-date-picker
v-model="newHomework.dueDate"
type="date"
placeholder="选择截止日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
class="form-input"
size="large"
/>
</div>
</div>
</div>
</el-form>
</div>
<template #footer>
<div class="dialog-footer">
<el-button
size="large"
@click="showAddDialog = false"
class="cancel-btn"
>
<el-icon><close /></el-icon>
取消
</el-button>
<el-button
type="primary"
size="large"
@click="addHomework"
class="confirm-btn"
>
<el-icon><check /></el-icon>
添加作业
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { ElMessage } from 'element-plus'
//
const homeworkList = ref([
{
id: 1,
subject: '数学',
title: '口算练习',
description: '完成课本第15页的加减法口算题共20道题目',
dueDate: '2024-02-01',
difficulty: 'easy',
completed: false
},
{
id: 2,
subject: '语文',
title: '古诗背诵',
description: '背诵《静夜思》并默写,注意字体工整',
dueDate: '2024-02-02',
difficulty: 'medium',
completed: true
},
{
id: 3,
subject: '英语',
title: '单词练习',
description: '抄写Unit 1的新单词每个单词写5遍',
dueDate: '2024-02-03',
difficulty: 'easy',
completed: false
},
{
id: 4,
subject: '科学',
title: '观察日记',
description: '观察一种植物的生长变化记录3天的观察日记',
dueDate: '2024-02-05',
difficulty: 'hard',
completed: false
}
])
//
const filterStatus = ref('all')
//
const showAddDialog = ref(false)
const newHomework = ref({
subject: '',
title: '',
description: '',
difficulty: 'easy',
dueDate: ''
})
//
const completedCount = computed(() =>
homeworkList.value.filter(h => h.completed).length
)
const pendingCount = computed(() =>
homeworkList.value.filter(h => !h.completed).length
)
const completionRate = computed(() => {
if (homeworkList.value.length === 0) return 0
return Math.round((completedCount.value / homeworkList.value.length) * 100)
})
const filteredHomework = computed(() => {
if (filterStatus.value === 'all') return homeworkList.value
if (filterStatus.value === 'completed') return homeworkList.value.filter(h => h.completed)
if (filterStatus.value === 'pending') return homeworkList.value.filter(h => !h.completed)
return homeworkList.value
})
//
const getSubjectIcon = (subject: string) => {
const icons = {
'数学': 'Calculator',
'语文': 'Reading',
'英语': 'ChatLineRound',
'科学': 'MagicStick'
}
return icons[subject] || 'Document'
}
const getDifficultyText = (difficulty: string) => {
const texts = {
'easy': '简单',
'medium': '中等',
'hard': '困难'
}
return texts[difficulty] || '未知'
}
const markCompleted = (id: number) => {
const homework = homeworkList.value.find(h => h.id === id)
if (homework) {
homework.completed = true
ElMessage.success('作业已标记为完成!')
}
}
const viewDetails = (homework: any) => {
ElMessage.info(`查看作业: ${homework.title}`)
}
const deleteHomework = (id: number) => {
const index = homeworkList.value.findIndex(h => h.id === id)
if (index > -1) {
homeworkList.value.splice(index, 1)
ElMessage.success('作业已删除!')
}
}
const addHomework = () => {
if (!newHomework.value.subject || !newHomework.value.title) {
ElMessage.error('请填写完整信息')
return
}
const newId = Math.max(...homeworkList.value.map(h => h.id)) + 1
homeworkList.value.push({
id: newId,
...newHomework.value,
completed: false
})
//
newHomework.value = {
subject: '',
title: '',
description: '',
difficulty: 'easy',
dueDate: ''
}
showAddDialog.value = false
ElMessage.success('作业添加成功!')
}
</script>
<style scoped>
.homework-container {
padding: 20px;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 100vh;
}
.homework-header {
margin-bottom: 20px;
}
.header-card {
border-radius: 16px;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.header-left {
display: flex;
align-items: center;
gap: 20px;
}
.homework-icon {
color: #667eea;
}
.page-title {
margin: 0;
color: #2c3e50;
font-size: 28px;
font-weight: 700;
}
.page-subtitle {
margin: 5px 0 0 0;
color: #7f8c8d;
font-size: 14px;
}
.add-homework-btn {
background: linear-gradient(45deg, #667eea, #764ba2);
border: none;
border-radius: 10px;
padding: 12px 20px;
}
.stats-row {
margin-bottom: 20px;
}
.stat-card {
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.stat-content {
display: flex;
align-items: center;
gap: 15px;
}
.stat-icon {
width: 50px;
height: 50px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
}
.stat-icon.completed {
background: linear-gradient(45deg, #67c23a, #85ce61);
}
.stat-icon.pending {
background: linear-gradient(45deg, #e6a23c, #f7ba2a);
}
.stat-icon.total {
background: linear-gradient(45deg, #409eff, #67c23a);
}
.stat-icon.progress {
background: linear-gradient(45deg, #f56c6c, #ff7875);
}
.stat-number {
font-size: 24px;
font-weight: 700;
color: #2c3e50;
line-height: 1;
}
.stat-label {
font-size: 14px;
color: #7f8c8d;
}
.homework-list-card {
border-radius: 16px;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.header-title {
display: flex;
align-items: center;
gap: 8px;
font-size: 18px;
font-weight: 600;
color: #2c3e50;
}
.homework-item {
padding: 20px;
margin-bottom: 15px;
background: white;
border-radius: 12px;
border-left: 4px solid #667eea;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.homework-item:hover {
transform: translateX(5px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
}
.homework-item.completed {
border-left-color: #67c23a;
opacity: 0.8;
}
.homework-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.homework-left {
flex: 1;
}
.subject-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 600;
color: white;
margin-bottom: 10px;
}
.subject-badge.数学 {
background: linear-gradient(45deg, #f56c6c, #ff7875);
}
.subject-badge.语文 {
background: linear-gradient(45deg, #67c23a, #85ce61);
}
.subject-badge.英语 {
background: linear-gradient(45deg, #409eff, #67c23a);
}
.subject-badge.科学 {
background: linear-gradient(45deg, #e6a23c, #f7ba2a);
}
.homework-title {
margin: 0 0 8px 0;
font-size: 18px;
font-weight: 600;
color: #2c3e50;
}
.homework-desc {
margin: 0 0 10px 0;
color: #5a6c7d;
line-height: 1.5;
}
.homework-meta {
display: flex;
gap: 20px;
font-size: 14px;
}
.due-date, .difficulty {
display: flex;
align-items: center;
gap: 4px;
color: #7f8c8d;
}
.difficulty.easy {
color: #67c23a;
}
.difficulty.medium {
color: #e6a23c;
}
.difficulty.hard {
color: #f56c6c;
}
.homework-actions {
display: flex;
gap: 8px;
}
.homework-actions .el-button {
border-radius: 8px;
}
/* New styles for the dialog */
:deep(.add-homework-dialog) {
border-radius: 16px;
overflow: hidden;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
}
:deep(.add-homework-dialog .el-dialog__header) {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-bottom: none;
border-radius: 16px 16px 0 0;
padding: 25px 30px;
margin-bottom: 0;
}
.dialog-header {
display: flex;
align-items: center;
gap: 15px;
}
.header-icon {
background: rgba(255, 255, 255, 0.2);
border-radius: 12px;
padding: 12px;
display: flex;
align-items: center;
justify-content: center;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.header-icon .el-icon {
color: white;
}
.header-text {
color: white;
}
.dialog-title {
margin: 0;
font-size: 24px;
font-weight: 700;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.dialog-subtitle {
margin: 5px 0 0 0;
font-size: 14px;
opacity: 0.9;
}
:deep(.add-homework-dialog .el-dialog__body) {
padding: 30px;
background: #ffffff;
}
.form-section {
display: flex;
flex-direction: column;
gap: 25px;
}
.form-item-wrapper {
display: flex;
flex-direction: column;
gap: 10px;
}
.form-item-wrapper.half {
flex: 1;
}
.form-label {
display: flex;
align-items: center;
gap: 8px;
font-size: 16px;
font-weight: 600;
color: #2c3e50;
margin-bottom: 5px;
}
.form-label .el-icon {
color: #667eea;
font-size: 18px;
}
.form-row {
display: flex;
gap: 20px;
}
/* 输入框美化 */
:deep(.form-input .el-input__wrapper) {
border-radius: 12px;
border: 2px solid #e8ecf4;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
padding: 12px 16px;
}
:deep(.form-input .el-input__wrapper:hover) {
border-color: #667eea;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.1);
}
:deep(.form-input .el-input__wrapper.is-focus) {
border-color: #667eea;
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.2);
}
:deep(.form-input .el-textarea__inner) {
border-radius: 12px;
border: 2px solid #e8ecf4;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
padding: 12px 16px;
font-family: inherit;
}
:deep(.form-input .el-textarea__inner:hover) {
border-color: #667eea;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.1);
}
:deep(.form-input .el-textarea__inner:focus) {
border-color: #667eea;
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.2);
}
/* 选择器美化 */
:deep(.form-input .el-select .el-input .el-input__wrapper) {
border-radius: 12px;
}
.option-content {
display: flex;
align-items: center;
gap: 8px;
}
/* 难度按钮组美化 */
.difficulty-group {
display: flex;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
:deep(.difficulty-group .el-radio-button__inner) {
border: none;
border-radius: 0;
padding: 12px 20px;
font-size: 14px;
font-weight: 600;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 6px;
background: #f8f9fc;
color: #5a6c7d;
}
:deep(.difficulty-group .el-radio-button:first-child .el-radio-button__inner) {
border-radius: 12px 0 0 12px;
}
:deep(.difficulty-group .el-radio-button:last-child .el-radio-button__inner) {
border-radius: 0 12px 12px 0;
}
:deep(.difficulty-group .el-radio-button.is-active .el-radio-button__inner) {
background: linear-gradient(45deg, #667eea, #764ba2);
color: white;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
:deep(.difficulty-group .el-radio-button__inner:hover) {
background: #e8ecf4;
transform: translateY(-1px);
}
:deep(.difficulty-group .el-radio-button.is-active .el-radio-button__inner:hover) {
background: linear-gradient(45deg, #667eea, #764ba2);
}
/* 日期选择器美化 */
:deep(.form-input .el-date-editor .el-input__wrapper) {
border-radius: 12px;
}
/* 底部按钮美化 */
:deep(.add-homework-dialog .el-dialog__footer) {
background: linear-gradient(135deg, #f8f9fc 0%, #e8ecf4 100%);
border-top: 1px solid rgba(255, 255, 255, 0.5);
border-radius: 0 0 16px 16px;
padding: 25px 30px;
margin-top: 0;
}
.dialog-footer {
display: flex;
justify-content: center;
gap: 15px;
}
.cancel-btn {
background: linear-gradient(45deg, #95a5a6, #bdc3c7);
border: none;
border-radius: 12px;
padding: 12px 24px;
font-weight: 600;
color: white;
transition: all 0.3s ease;
min-width: 120px;
}
.cancel-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(149, 165, 166, 0.3);
}
.confirm-btn {
background: linear-gradient(45deg, #667eea, #764ba2);
border: none;
border-radius: 12px;
padding: 12px 24px;
font-weight: 600;
color: white;
transition: all 0.3s ease;
min-width: 120px;
}
.confirm-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
}
</style>

938
src/views/HomeView.vue Normal file
View File

@ -0,0 +1,938 @@
<script setup lang="ts">
import { ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage } from 'element-plus'
//
const tableData = ref([
{
date: '2024-01-20',
name: '张三',
email: 'zhangsan@example.com',
tag: '开发'
},
{
date: '2024-01-21',
name: '李四',
email: 'lisi@example.com',
tag: '设计'
},
{
date: '2024-01-22',
name: '王五',
email: 'wangwu@example.com',
tag: '测试'
},
{
date: '2024-01-23',
name: '赵六',
email: 'zhaoliu@example.com',
tag: '产品'
}
])
//
const formRef = ref<FormInstance>()
const form = ref({
name: '',
email: '',
type: '',
description: ''
})
const rules = ref<FormRules>({
name: [
{ required: true, message: '请输入姓名', trigger: 'blur' },
{ min: 2, max: 15, message: '长度在 2 到 15 个字符', trigger: 'blur' }
],
email: [
{ required: true, message: '请输入邮箱地址', trigger: 'blur' },
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
],
type: [
{ required: true, message: '请选择类型', trigger: 'change' }
]
})
//
const dialogVisible = ref(false)
//
const beautifulDialogVisible = ref(false)
//
const simpleDialogVisible = ref(false)
//
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
ElMessage.success('提交成功!')
console.log('表单数据:', form.value)
} else {
ElMessage.error('请检查表单输入')
console.log('验证失败:', fields)
}
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
}
//
const handleEdit = (index: number, row: any) => {
ElMessage.info(`编辑用户: ${row.name}`)
console.log('编辑:', index, row)
}
//
const handleDelete = (index: number, row: any) => {
ElMessage.warning(`删除用户: ${row.name}`)
tableData.value.splice(index, 1)
console.log('删除:', index, row)
}
//
const handleRowClick = (row: any) => {
ElMessage.info(`点击行: ${row.name}`)
console.log('点击行:', row)
}
</script>
<template>
<div class="home-container">
<el-row :gutter="20">
<!-- 数据表格区域 -->
<el-col :span="14">
<el-card>
<template #header>
<div class="card-header">
<span>用户数据表格</span>
<div class="header-buttons">
<el-button type="primary" @click="dialogVisible = true">
<el-icon><plus /></el-icon>
添加用户
</el-button>
<el-button
type="success"
class="beautiful-btn"
@click="beautifulDialogVisible = true"
>
<el-icon><magic-stick /></el-icon>
精美提示
</el-button>
<el-button
type="warning"
class="simple-btn"
@click="simpleDialogVisible = true"
>
<el-icon><bell /></el-icon>
简单提示
</el-button>
</div>
</div>
</template>
<el-table
:data="tableData"
style="width: 100%"
stripe
class="beautiful-table"
:header-cell-style="{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
color: 'white',
fontWeight: '600',
textAlign: 'center',
border: 'none'
}"
:cell-style="{ textAlign: 'center', padding: '12px 0' }"
:row-style="{ cursor: 'pointer' }"
@row-click="handleRowClick"
>
<el-table-column prop="date" label="日期" width="120">
<template #default="scope">
<div class="date-cell">
<el-icon class="date-icon"><calendar /></el-icon>
<span class="date-text">{{ scope.row.date }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="name" label="姓名" width="120">
<template #default="scope">
<div class="name-cell">
<el-icon class="user-icon"><user /></el-icon>
<span class="name-text">{{ scope.row.name }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="email" label="邮箱">
<template #default="scope">
<div class="email-cell">
<el-icon class="email-icon"><message /></el-icon>
<span class="email-text">{{ scope.row.email }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="tag" label="部门" width="100">
<template #default="scope">
<el-tag
:type="scope.row.tag === '开发' ? '' : scope.row.tag === '设计' ? 'success' : scope.row.tag === '测试' ? 'warning' : 'info'"
disable-transitions
>
{{ scope.row.tag }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="150">
<template #default="scope">
<div class="action-buttons">
<el-button
size="small"
type="primary"
class="edit-btn"
@click.stop="handleEdit(scope.$index, scope.row)"
>
<el-icon><edit /></el-icon>
编辑
</el-button>
<el-button
size="small"
type="danger"
class="delete-btn"
@click.stop="handleDelete(scope.$index, scope.row)"
>
<el-icon><delete /></el-icon>
删除
</el-button>
</div>
</template>
</el-table-column>
</el-table>
</el-card>
</el-col>
<!-- 侧边栏区域 -->
<el-col :span="10">
<el-card>
<template #header>
<span>快速统计</span>
</template>
<el-row :gutter="16">
<el-col :span="12">
<el-statistic title="总用户数" :value="tableData.length" />
</el-col>
<el-col :span="12">
<el-statistic title="活跃度" value="98.5%" />
</el-col>
</el-row>
<el-divider />
<el-progress :percentage="85" color="#409eff" />
<p style="margin-top: 10px; color: #666;">项目进度</p>
<el-divider />
<el-steps :active="2" finish-status="success">
<el-step title="项目初始化" />
<el-step title="功能开发" />
<el-step title="测试部署" />
<el-step title="上线运营" />
</el-steps>
</el-card>
<el-card style="margin-top: 20px;">
<template #header>
<span>系统通知</span>
</template>
<el-timeline>
<el-timeline-item timestamp="2024-01-20 10:18" type="primary">
<el-card>
<h4>ElementPlus 集成完成</h4>
<p>成功将 ElementPlus 组件库集成到项目中</p>
</el-card>
</el-timeline-item>
<el-timeline-item timestamp="2024-01-20 09:45" type="success">
<el-card>
<h4>Vue 3 项目创建</h4>
<p>使用最新的 Vue 3 + TypeScript 技术栈</p>
</el-card>
</el-timeline-item>
</el-timeline>
</el-card>
</el-col>
</el-row>
<!-- 添加用户对话框 -->
<el-dialog v-model="dialogVisible" title="添加新用户" width="500px">
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="80px"
>
<el-form-item label="姓名" prop="name">
<el-input v-model="form.name" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" />
</el-form-item>
<el-form-item label="部门" prop="type">
<el-select v-model="form.type" placeholder="请选择部门">
<el-option label="开发" value="开发" />
<el-option label="设计" value="设计" />
<el-option label="测试" value="测试" />
<el-option label="产品" value="产品" />
</el-select>
</el-form-item>
<el-form-item label="描述">
<el-input
v-model="form.description"
type="textarea"
placeholder="请输入描述"
:rows="3"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button @click="resetForm(formRef)">重置</el-button>
<el-button type="primary" @click="submitForm(formRef)">确定</el-button>
</span>
</template>
</el-dialog>
<!-- 新增美观提示框 -->
<el-dialog
v-model="beautifulDialogVisible"
:show-close="false"
width="600px"
center
class="beautiful-dialog"
>
<template #header>
<div class="dialog-header">
<div class="header-icon">
<el-icon size="40"><star-filled /></el-icon>
</div>
<h2 class="header-title">🎉 欢迎体验精美界面</h2>
</div>
</template>
<div class="dialog-content">
<div class="content-section">
<div class="feature-card">
<div class="feature-icon success">
<el-icon size="24"><check /></el-icon>
</div>
<div class="feature-text">
<h3>界面美观</h3>
<p>精心设计的UI界面带来愉悦的用户体验</p>
</div>
</div>
<div class="feature-card">
<div class="feature-icon primary">
<el-icon size="24"><lightning /></el-icon>
</div>
<div class="feature-text">
<h3>响应迅速</h3>
<p>基于Vue 3构建享受极速的响应体验</p>
</div>
</div>
<div class="feature-card">
<div class="feature-icon warning">
<el-icon size="24"><trophy /></el-icon>
</div>
<div class="feature-text">
<h3>功能丰富</h3>
<p>ElementPlus组件库提供丰富的功能组件</p>
</div>
</div>
</div>
<div class="message-box">
<p class="message-text">
这是一个精美设计的提示框示例展示了现代化的UI设计理念
通过渐变背景图标装饰和流畅动画为用户带来视觉愉悦的交互体验
</p>
</div>
</div>
<template #footer>
<div class="dialog-footer-beautiful">
<el-button
size="large"
@click="beautifulDialogVisible = false"
class="close-btn"
>
<el-icon><close /></el-icon>
关闭
</el-button>
<el-button
type="primary"
size="large"
class="action-btn"
@click="beautifulDialogVisible = false"
>
<el-icon><star /></el-icon>
太棒了
</el-button>
</div>
</template>
</el-dialog>
<!-- 新增简单美观提示框 -->
<el-dialog
v-model="simpleDialogVisible"
:show-close="false"
width="450px"
center
class="simple-dialog"
>
<template #header>
<div class="simple-header">
<div class="simple-icon">
<el-icon size="32"><info-filled /></el-icon>
</div>
<h3 class="simple-title">温馨提示</h3>
</div>
</template>
<div class="simple-content">
<p class="simple-message">
🌟 这是一个简洁而美观的提示框示例通过精心的色彩搭配和简约的设计风格
在保持功能性的同时为用户提供清爽的视觉体验简约不简单美观又实用
</p>
</div>
<template #footer>
<div class="simple-footer">
<el-button
size="default"
@click="simpleDialogVisible = false"
class="simple-cancel-btn"
>
取消
</el-button>
<el-button
type="primary"
size="default"
class="simple-confirm-btn"
@click="simpleDialogVisible = false"
>
确认
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<style scoped>
.home-container {
padding: 10px; /* 减少内边距 */
width: 100%;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.header-buttons {
display: flex;
gap: 10px; /* 按钮之间的间距 */
}
.beautiful-btn {
background-color: #67c23a; /* 设置按钮背景颜色 */
border-color: #67c23a; /* 设置按钮边框颜色 */
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
/* 确保表格可以充分利用空间 */
.el-table {
width: 100% !important;
}
/* 美化表格样式 */
:deep(.beautiful-table) {
border-radius: 12px;
overflow: hidden;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.08);
border: 1px solid #e8ecf4;
}
:deep(.beautiful-table .el-table__header-wrapper) {
border-radius: 12px 12px 0 0;
}
:deep(.beautiful-table .el-table__body-wrapper) {
background: #ffffff;
}
:deep(.beautiful-table .el-table__row) {
transition: all 0.3s ease;
}
:deep(.beautiful-table .el-table__row:hover) {
background-color: #f8f9ff !important;
transform: scale(1.01);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.1);
}
:deep(.beautiful-table .el-table__row--striped) {
background-color: #fafbfc;
}
:deep(.beautiful-table .el-table__row--striped:hover) {
background-color: #f8f9ff !important;
}
:deep(.beautiful-table .el-table__cell) {
border-bottom: 1px solid #f0f2f5;
padding: 16px 12px;
}
:deep(.beautiful-table .el-table__header .el-table__cell) {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
color: white !important;
font-weight: 600 !important;
text-align: center !important;
border: none !important;
padding: 18px 12px;
}
/* 表格单元格美化 */
.date-cell {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.date-icon {
color: #f56c6c;
font-size: 16px;
}
.date-text {
color: #5a6c7d;
font-weight: 500;
}
.name-cell {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.user-icon {
color: #667eea;
font-size: 16px;
}
.name-text {
font-weight: 600;
color: #2c3e50;
}
.email-cell {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.email-icon {
color: #67c23a;
font-size: 16px;
}
.email-text {
color: #5a6c7d;
}
/* 操作按钮美化 */
.action-buttons {
display: flex;
gap: 8px;
justify-content: center;
align-items: center;
}
.edit-btn {
background: linear-gradient(45deg, #409eff, #67c23a);
border: none;
border-radius: 8px;
transition: all 0.3s ease;
font-size: 12px;
padding: 8px 12px;
}
.edit-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(64, 158, 255, 0.3);
}
.delete-btn {
background: linear-gradient(45deg, #f56c6c, #ff7875);
border: none;
border-radius: 8px;
transition: all 0.3s ease;
font-size: 12px;
padding: 8px 12px;
}
.delete-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(245, 108, 108, 0.3);
}
/* 标签美化 */
:deep(.el-tag) {
border-radius: 20px;
padding: 6px 12px;
font-weight: 500;
border: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
:deep(.el-tag--success) {
background: linear-gradient(45deg, #67c23a, #85ce61);
color: white;
}
:deep(.el-tag--warning) {
background: linear-gradient(45deg, #e6a23c, #f7ba2a);
color: white;
}
:deep(.el-tag--info) {
background: linear-gradient(45deg, #909399, #b4bccc);
color: white;
}
:deep(.el-tag) {
background: linear-gradient(45deg, #409eff, #67c23a);
color: white;
}
/* 优化大屏幕显示 */
@media (min-width: 1200px) {
.home-container {
padding: 15px;
}
}
/* 美化对话框样式 */
:deep(.beautiful-dialog) {
border-radius: 20px;
overflow: hidden;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
}
:deep(.beautiful-dialog .el-dialog__header) {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 0;
margin: 0;
}
:deep(.beautiful-dialog .el-dialog__body) {
padding: 30px;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
}
:deep(.beautiful-dialog .el-dialog__footer) {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
padding: 20px 30px;
border-top: 1px solid rgba(255, 255, 255, 0.3);
}
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
padding: 30px;
color: white;
text-align: center;
}
.header-icon {
margin-right: 15px;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.header-title {
margin: 0;
font-size: 24px;
font-weight: 600;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.dialog-content {
text-align: center;
}
.content-section {
margin-bottom: 30px;
}
.feature-card {
display: flex;
align-items: center;
padding: 20px;
margin: 15px 0;
background: white;
border-radius: 15px;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
animation: slideInUp 0.6s ease-out;
}
.feature-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.15);
}
.feature-card:nth-child(1) { animation-delay: 0.1s; }
.feature-card:nth-child(2) { animation-delay: 0.2s; }
.feature-card:nth-child(3) { animation-delay: 0.3s; }
@keyframes slideInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.feature-icon {
width: 50px;
height: 50px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20px;
color: white;
flex-shrink: 0;
}
.feature-icon.success {
background: linear-gradient(45deg, #4CAF50, #81C784);
}
.feature-icon.primary {
background: linear-gradient(45deg, #2196F3, #64B5F6);
}
.feature-icon.warning {
background: linear-gradient(45deg, #FF9800, #FFB74D);
}
.feature-text {
text-align: left;
flex: 1;
}
.feature-text h3 {
margin: 0 0 8px 0;
font-size: 18px;
font-weight: 600;
color: #2c3e50;
}
.feature-text p {
margin: 0;
color: #7f8c8d;
line-height: 1.5;
}
.message-box {
background: white;
border-radius: 15px;
padding: 25px;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
border-left: 5px solid #667eea;
animation: fadeIn 0.8s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.message-text {
font-size: 16px;
line-height: 1.8;
color: #2c3e50;
margin: 0;
}
.dialog-footer-beautiful {
display: flex;
justify-content: center;
gap: 15px;
}
.close-btn {
background: #95a5a6;
border-color: #95a5a6;
color: white;
transition: all 0.3s ease;
}
.close-btn:hover {
background: #7f8c8d;
border-color: #7f8c8d;
transform: translateY(-2px);
}
.action-btn {
background: linear-gradient(45deg, #667eea, #764ba2);
border-color: #667eea;
transition: all 0.3s ease;
}
.action-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
}
.beautiful-btn {
background: linear-gradient(45deg, #11998e, #38ef7d);
border: none;
transition: all 0.3s ease;
}
.beautiful-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(17, 153, 142, 0.4);
}
/* 简单美观对话框样式 */
.simple-btn {
background: linear-gradient(45deg, #ff9a9e, #fecfef);
border: none;
transition: all 0.3s ease;
}
.simple-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(255, 154, 158, 0.4);
}
:deep(.simple-dialog) {
border-radius: 16px;
overflow: hidden;
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.12);
}
:deep(.simple-dialog .el-dialog__header) {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 0;
margin: 0;
}
:deep(.simple-dialog .el-dialog__body) {
padding: 25px 30px;
background: #ffffff;
}
:deep(.simple-dialog .el-dialog__footer) {
background: #f8f9fc;
padding: 20px 30px;
border-top: 1px solid #e8ecf4;
}
.simple-header {
display: flex;
align-items: center;
justify-content: center;
padding: 25px;
color: white;
}
.simple-icon {
margin-right: 12px;
opacity: 0.9;
}
.simple-title {
margin: 0;
font-size: 20px;
font-weight: 500;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
.simple-content {
text-align: center;
padding: 10px 0;
}
.simple-message {
font-size: 15px;
line-height: 1.6;
color: #5a6c7d;
margin: 0;
padding: 0 10px;
}
.simple-footer {
display: flex;
justify-content: center;
gap: 12px;
}
.simple-cancel-btn {
background: #f5f6fa;
border-color: #ddd;
color: #666;
transition: all 0.3s ease;
min-width: 80px;
}
.simple-cancel-btn:hover {
background: #e9ecef;
border-color: #ccc;
color: #555;
transform: translateY(-1px);
}
.simple-confirm-btn {
background: linear-gradient(45deg, #667eea, #764ba2);
border-color: #667eea;
transition: all 0.3s ease;
min-width: 80px;
}
.simple-confirm-btn:hover {
transform: translateY(-1px);
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.3);
}
</style>

12
tsconfig.app.json Normal file
View File

@ -0,0 +1,12 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"paths": {
"@/*": ["./src/*"]
}
}
}

11
tsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
}
]
}

19
tsconfig.node.json Normal file
View File

@ -0,0 +1,19 @@
{
"extends": "@tsconfig/node22/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*",
"eslint.config.*"
],
"compilerOptions": {
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}

20
vite.config.ts Normal file
View File

@ -0,0 +1,20 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import vueDevTools from 'vite-plugin-vue-devtools'
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
vueJsx(),
vueDevTools(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
})