Merge pull request #37 from Jursin/master

优化
This commit is contained in:
MKStoler 2024-12-02 16:41:48 +08:00 committed by GitHub
commit 61de0dcbb5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 316 additions and 159 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

BIN
.Screenshots/HomePage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

BIN
.Screenshots/InfoPage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

View File

@ -1,5 +1,5 @@
name: Bug 反馈 name: Bug 反馈
description: 在使用考试看板 Next 的过程中遇到了 Bug description: 在使用考试看板 Next 的过程中遇到了 Bug
title: (在这里输入你的标题) title: (在这里输入你的标题)
labels: ['Bug', '未阅读'] labels: ['Bug', '未阅读']
body: body:
@ -9,9 +9,10 @@ body:
感谢您进行 Bug 反馈。请在上面的文本框⬆️起一个能够清晰描述您的问题的标题,便于开发者解决您的问题。 感谢您进行 Bug 反馈。请在上面的文本框⬆️起一个能够清晰描述您的问题的标题,便于开发者解决您的问题。
> [!important] > [!important]
> 重要:如果要提出**多个 Bug**,请为每一个 Bug 开一个单独的 issue。 >
> 如果要提出**多个 Bug **,请为每一个 Bug 开一个单独的 issue。
> >
> 如果您不知道如何有效、精准地表述,我们建议先阅读《提问的智慧》[链接](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md) > 如果您不知道如何有效、精准地表述,建议先阅读[《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md)。
- type: checkboxes - type: checkboxes
id: checklist id: checklist
@ -19,7 +20,7 @@ body:
label: 检查清单 label: 检查清单
description: 在开始反馈这个问题之前,请先检查: description: 在开始反馈这个问题之前,请先检查:
options: options:
- label: 我已更新到最新版 ![最新的版本号](https://img.shields.io/github/v/release/ProjectCampus-CH/dsz-exam-showboard-next?include_prereleases&style=flat-square&label=) ,并看过[最新提交](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/commits/dev/),确认这一 Bug 还没有修复。 - label: 我已更新到最新版 ![最新的版本号](https://img.shields.io/github/v/release/ProjectCampus-CH/dsz-exam-showboard-next?include_prereleases&style=flat) ,并看过[最新提交](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/commits/dev/),确认这一 Bug 还没有修复。
required: true required: true
- label: 我已在 [Issues](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/issues?q=label%3ABug) 中检索,确认这一 Bug 未被提交过。 - label: 我已在 [Issues](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/issues?q=label%3ABug) 中检索,确认这一 Bug 未被提交过。
required: true required: true
@ -50,7 +51,7 @@ body:
attributes: attributes:
label: 重现步骤 label: 重现步骤
description: | description: |
详细描述怎么操作能再次触发这个 Bug。 详细描述怎么操作能再次触发这个 Bug
placeholder: | placeholder: |
1. 首先…… 1. 首先……
2. 然后…… 2. 然后……
@ -61,14 +62,14 @@ body:
id: stacktrace id: stacktrace
attributes: attributes:
label: 堆栈跟踪(可选) label: 堆栈跟踪(可选)
description: 如果在遇到这个 Bug 时发生了崩溃(弹出崩溃提示),或者产生了错误日志,请将产生的堆栈跟踪信息粘贴到此处,便于开发者定位 Bug description: 如果在遇到这个 Bug 时发生了崩溃(弹出崩溃提示),或者产生了错误日志,请将产生的堆栈跟踪信息粘贴到此处,便于开发者定位 Bug
render: shell render: shell
- type: input - type: input
id: app_version id: app_version
attributes: attributes:
label: 应用版本 label: 应用版本
description: 您当前使用的考试看板 Next 版本号,可以在【关于】中查看。 description: 您当前使用的考试看板 Next 版本号,可以在【关于】中查看。
placeholder: 1.1.1 placeholder: 1.2.0
validations: validations:
required: true required: true
- type: input - type: input
@ -86,4 +87,4 @@ body:
description: 回顾您的回答 description: 回顾您的回答
options: options:
- label: 我认为上述的描述已经足以详细,以允许开发人员能复现该问题。如果我的 issue 没有按照上述的要求填写,可能会被无条件关闭。 - label: 我认为上述的描述已经足以详细,以允许开发人员能复现该问题。如果我的 issue 没有按照上述的要求填写,可能会被无条件关闭。
required: true required: true

View File

@ -0,0 +1,17 @@
on:
push:
branches:
- master
jobs:
contrib-readme-job:
runs-on: ubuntu-latest
name: A job to automate contrib in readme
permissions:
contents: write
pull-requests: write
steps:
- name: Contribute List
uses: akhilmhdh/contributors-readme-action@v2.3.10
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

158
README.md
View File

@ -1,26 +1,30 @@
<div align="center"> <div align="center">
# <image src="resources/icon.png" height="28"/> ExamShowboard-Next <image src="resources/icon.png" height="128"/>
> 考试看板 Next —— 下一代考试看板 # ExamShowboard-Next下一代考试看板
![HomePage](/.Screenshots/HomePage.png)
![InfoPage](/.Screenshots/InfoPage.png)
[![stars](https://img.shields.io/github/stars/ProjectCampus-CH/dsz-exam-showboard-next?label=Stars)](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/stargazers)
[![forks](https://img.shields.io/github/forks/ProjectCampus-CH/dsz-exam-showboard-next?label=Forks)](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/forks)
[![Watchers](https://img.shields.io/github/watchers/ProjectCampus-CH/dsz-exam-showboard-next?style=social)](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/watchers)
[![Downloads](https://img.shields.io/github/downloads/ProjectCampus-CH/dsz-exam-showboard-next/total?style=social&label=Downloads&logo=github)](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/releases)
[![GitHub Issues](https://img.shields.io/github/issues-search/ProjectCampus-CH/dsz-exam-showboard-next?query=is%3Aopen&style=flat&logo=github&label=Issues&color=%233fb950)](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/issues)
[![GitHub Discussions](https://img.shields.io/github/discussions/ProjectCampus-CH/dsz-exam-showboard-next?style=flat&logo=Github&label=Discussions)](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/discussions)
[![Created At](https://img.shields.io/github/created-at/ProjectCampus-CH/dsz-exam-showboard-next)](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next)
[![Github Last Commit](https://img.shields.io/github/last-commit/ProjectCampus-CH/dsz-exam-showboard-next)](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/commits/master)
[![GitHub Language Count](https://img.shields.io/github/languages/count/ProjectCampus-CH/dsz-exam-showboard-next)](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next)
[![GitHub Top Language](https://img.shields.io/github/languages/top/ProjectCampus-CH/dsz-exam-showboard-next)](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next)
[![stars](https://img.shields.io/github/stars/MKStoler4096/dsz-exam-showboard-next?label=Stars)](https://github.com/MKStoler4096/dsz-exam-showboard-next/stargazers)
[![forks](https://img.shields.io/github/forks/MKStoler4096/dsz-exam-showboard-next?label=Forks)](https://github.com/MKStoler4096/dsz-exam-showboard-next/forks)
[![Watchers](https://img.shields.io/github/watchers/MKStoler4096/dsz-exam-showboard-next?style=social)](https://github.com/MKStoler4096/dsz-exam-showboard-next/watchers)
[![Downloads](https://img.shields.io/github/downloads/MKStoler4096/dsz-exam-showboard-next/total?style=social&label=Downloads&logo=github)](https://github.com/MKStoler4096/dsz-exam-showboard-next/releases)
[![GitHub Issues](https://img.shields.io/github/issues-search/MKStoler4096/dsz-exam-showboard-next?query=is%3Aopen&style=social-square&logo=github&label=Issues&color=%233fb950)](https://github.com/MKStoler4096/dsz-exam-showboard-next/issues)
[![GitHub Discussions](https://img.shields.io/github/discussions/MKStoler4096/dsz-exam-showboard-next?style=flat&logo=Github&label=Discussions)](https://github.com/MKStoler4096/dsz-exam-showboard-next/discussions)
[![Created At](https://img.shields.io/github/created-at/MKStoler4096/dsz-exam-showboard-next)](https://github.com/MKStoler4096/dsz-exam-showboard-next)
[![Github Last Commit](https://img.shields.io/github/last-commit/MKStoler4096/dsz-exam-showboard-next)](https://github.com/MKStoler4096/dsz-exam-showboard-next/commits/master)
[![GitHub Language Count](https://img.shields.io/github/languages/count/MKStoler4096/dsz-exam-showboard-next)](https://github.com/MKStoler4096/dsz-exam-showboard-next)
[![GitHub Top Language](https://img.shields.io/github/languages/top/MKStoler4096/dsz-exam-showboard-next)](https://github.com/MKStoler4096/dsz-exam-showboard-next)
[![LICENSE](https://img.shields.io/badge/License-GPL--3.0-red.svg 'LICENSE')](LICENSE) [![LICENSE](https://img.shields.io/badge/License-GPL--3.0-red.svg 'LICENSE')](LICENSE)
[![QQ群](https://img.shields.io/badge/-QQ%E7%BE%A4%EF%BD%9C901670561-blue?style=flat&logo=TencentQQ&logoColor=white)](https://qm.qq.com/q/zDiEipHsaI) [![QQ群](https://img.shields.io/badge/-QQ%E7%BE%A4%EF%BD%9C901670561-blue?style=flat&logo=TencentQQ&logoColor=white)](https://qm.qq.com/q/zDiEipHsaI)
一款显示当前时间与考试详细信息的看板类软件 一款显示当前时间与考试详细信息的看板类软件
| 下载 | [Releases](https://github.com/MKStoler4096/dsz-exam-showboard-next/releases) | [Actions](https://github.com/MKStoler4096/dsz-exam-showboard-next/actions) | | 下载 | [Releases](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/releases) | [Actions](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/actions) |
| ---- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------- | | - | - | - |
![WelcomePage](/.Screenshots/WelcomePage.jpg) ![WelcomePage](/.Screenshots/WelcomePage.jpg)
![ExamPage](/.Screenshots/ExamPage.jpg) ![ExamPage](/.Screenshots/ExamPage.jpg)
@ -28,16 +32,15 @@
</div> </div>
> [!tip] > [!tip]
> > **本软件使用 `Vue` + `TypeScript` + `JavaScript` 制作,使用 `Node.js` + `Electron` 完善系统级功能并打包。**
> **本软件使用`Vue` + `TypeScript` + `JavaScript`制作,使用`Node.js` + `Electron`完善系统级功能并打包。**
## 功能 ## 功能
- 起始页展示`打开配置``直接进入看板`按钮 - 起始页展示 `打开配置` `直接进入看板` 按钮
- 看板页面 - 看板页面
- 上方展示`考试标题``信息` - 上方展示 `考试标题` `信息`
- 左侧展示`当前时间``当前科目``考试时间``考试状态` - 左侧展示 `当前时间` `当前科目` `考试时间` `考试状态`
- 右侧展示考试科目列表,包括`科目``开始``结束``状态` - 右侧展示考试科目列表,包括 `科目` `开始` `结束` `状态`
- 考试结束前15分钟黄字提醒 - 考试结束前15分钟黄字提醒
- 集控功能(早期测试) - 集控功能(早期测试)
@ -45,57 +48,75 @@
- 下载安装程序并运行 - 下载安装程序并运行
默认安装`AppData\Local\Programs\dsz-exam-showboard` 默认安装路径 `AppData\Local\Programs\dsz-exam-showboard`
- 编写`json`配置文件 - 编写 `json` 配置文件
新建文件`exam_config.json`,模板如下 新建文件 `exam_config.json` ,模板如下
```json ```json
{ {
"examName": "考试名称", "examName": "考试名称",
"message": "信息", "message": "信息",
"room": "考场号",
"examInfos": [ "examInfos": [
{ {
"name": "科目", "name": "科目",
"start": "2024-10-01T07:00:00", "start": "2024-12-01T07:00:00",
"end": "2024-10-01T08:00:00" "end": "2024-12-01T08:00:00"
}, },
{ {
"name": "科目", "name": "科目/科目",
"start": "2024-10-01T09:00:00", "start": "2024-12-01T09:00:00",
"end": "2024-10-01T10:00:00" "end": "2024-12-01T10:00:00"
} }
] ]
} }
``` ```
- 打开软件,进入起始页面,点击`打开配置`按钮,选择配置文件,下次可点击`直接进入看板`按钮,将继续使用上次加载的配置。 > [!tip]
>
> `message``room` 为选填
>
> 如果有两个以"/"分隔的科目可以自动转化为双行显示
- 集控 - 打开软件,进入起始页面,点击 `打开配置` 按钮,选择配置文件
- 或使用集控(仿照 [`ClassIsland` 集控使用方法](https://docs.classisland.tech/management/tutorial-create-management-config.html)
- 新建 GitHub 公开存储仓库
- 上传上面提到的 `exam_config.json` 文件
- 复制 `Raw` 直链粘贴到应用`请求地址`文本框并保存
仿照 `ClassIsland` 的集控方法,把上面提到的 `exam_config.json` 传上去,获得 `raw` 直链粘贴回文本框并保存即可。 > [!tip]
>
> 可以在 `Raw 文件` 前加上镜像源,如
>
>```
>https://github.moeyy.xyz/https://raw.githubusercontent.com/{owner}/{repo}/refs/heads/main/exam_config.json
>```
- 点击`请求配置`加载配置文件并进入看板(下次可直接点击`直接进入看板`加载上次配置文件)
## 遇到问题 ## 遇到问题
💡 如果您遇到`Bug`,或需要提出`优化`建议或新的`功能`,请提交[`Issues`](https://github.com/MKStoler4096/dsz-exam-showboard-next/issues)或在[`Discussions`](https://github.com/MKStoler4096/dsz-exam-showboard-next/discussions)中讨论。 💡 如果您遇到 `Bug` ,或需要提出`优化`建议或新的`功能`,请提交 [`Issues`](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/issues) 或在 [`Discussions`](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/discussions) 中讨论。
👥 您也可以加入[`QQ群901670561`](https://qm.qq.com/q/zDiEipHsaI)获取帮助或交流讨论。 👥 您也可以加入 [`QQ群901670561`](https://qm.qq.com/q/zDiEipHsaI)获取帮助或交流讨论。
🛠️ 欢迎为本软件进行改进或编写新功能提交[`Pull Request`](https://github.com/MKStoler4096/dsz-exam-showboard-next/pulls) 🛠️ 欢迎为本软件进行改进或编写新功能提交 [`Pull Request`](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/pulls)
## 开发 ## 开发
### 推荐开发环境 ### 推荐开发环境
- [VSCode](https://code.visualstudio.com/) - [VSCode](https://code.visualstudio.com/)
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) - [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
- [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) - [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
> [!IMPORTANT] > [!Caution]
> >
> **必须使用Yarn包管理。Node版本要求为20。** > **必须使用 Yarn 包管理。Node 版本要求为20。**
### 工程构建 ### 工程构建
@ -113,11 +134,11 @@ $ yarn dev
$ yarn start $ yarn start
``` ```
> [!note] > [!important]
> >
> **如果dev模式页面不显示或按钮点击无效等问题请连续刷新至少3次后再进行操作。build后没有此问题。** > **如果 dev 模式页面不显示或按钮点击无效等问题请连续刷新至少3次后再进行操作。 build 后没有此问题。**
#### 生成 #### 构建
```bash ```bash
# For windows # For windows
@ -132,16 +153,63 @@ $ yarn build:linux
### 开发进度 ### 开发进度
- 正在[`master`](https://github.com/MKStoler4096/dsz-exam-showboard-next/commits/master)分支上维护`1.2-Yesod`版本。 - 正在 [`master`](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/commits/master) 分支上维护`1.2-Yesod`版本。
- 正在[`dev`](https://github.com/MKStoler4096/dsz-exam-showboard-next/commits/dev)分支上开发`1.3-HOD`版本。 - 正在 [`dev`](https://github.com/ProjectCampus-CH/dsz-exam-showboard-next/commits/dev) 分支上开发`1.3-HOD`版本。
## 贡献者
<!-- readme: collaborators,contributors -start -->
<table>
<tbody>
<tr>
<td align="center">
<a href="https://github.com/Jursin">
<img src="https://avatars.githubusercontent.com/u/127487914?v=4" width="100;" alt="Jursin"/>
<br />
<sub><b>Jursin</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/hello8693DSZ">
<img src="https://avatars.githubusercontent.com/u/88492699?v=4" width="100;" alt="hello8693DSZ"/>
<br />
<sub><b>Hello8693</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/MKStoler4096">
<img src="https://avatars.githubusercontent.com/u/178344462?v=4" width="100;" alt="MKStoler4096"/>
<br />
<sub><b>MKStoler</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/fhz08">
<img src="https://avatars.githubusercontent.com/u/152045732?v=4" width="100;" alt="fhz08"/>
<br />
<sub><b>fhz08</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/wjj-8283">
<img src="https://avatars.githubusercontent.com/u/82750345?v=4" width="100;" alt="wjj-8283"/>
<br />
<sub><b>wjj-8283</b></sub>
</a>
</td>
</tr>
<tbody>
</table>
<!-- readme: collaborators,contributors -end -->
## Stars 历史 ## Stars 历史
[![Stargazers over time](https://starchart.cc/ProjectCampus-CH/exam-showboard-next.svg?variant=adaptive)](https://starchart.cc/ProjectCampus-CH/exam-showboard-next)
<div align="center"> <div align="center">
[![Star 历史](https://starchart.cc/ProjectCampus-CH/dsz-exam-showboard-next.svg?variant=adaptive)](https://starchart.cc/ProjectCampus-CH/dsz-exam-showboard-next/stargazers)
如果这个项目对您有帮助,请点亮 Star [](#dsz-exam-showboard-next) 如果这个项目对您有帮助,请点亮 Star [](#dsz-exam-showboard-next)
</div> </div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

View File

@ -1,8 +1,8 @@
<template> <template>
<v-card class="mx-auto" max-width="600"> <v-card class="mx-auto" max-width="600" flat>
<v-container fluid> <v-container fluid class="pa-0">
<v-row justify="center"> <v-row justify="center" class="ma-0">
<v-col cols="12"> <v-col cols="12" class="pa-0">
<v-data-table <v-data-table
:items="groupedExams" :items="groupedExams"
:headers="headers" :headers="headers"
@ -11,29 +11,41 @@
dense dense
class="text-h5" class="text-h5"
> >
<template #item="{ item, index }"> <template #item="{ item }">
<tr :style="{ lineHeight: '2.5rem' }"> <tr :style="{ lineHeight: item.name.includes('/') ? '2.5rem' : '2.0rem' }">
<td v-if="item.showDate" class="text-h5 date-column" :rowspan="item.rowspan"> <td v-if="item.showDate" class="text-h5 date-column" :rowspan="item.rowspan">
{{ item.date }}<br />{{ item.period }} {{ item.date }}<br>{{ item.period }}
</td> </td>
<td :class="['text-h5', 'subject-column', getStatusClass(item)]"> <td class="text-h5 subject-column">
{{ item.name }} <div v-if="item.name.includes('/')">
<div>{{ item.name.split('/')[0] }}</div>
<div>{{ item.name.split('/')[1] }}</div>
</div>
<div v-else>{{ item.name }}</div>
</td> </td>
<td class="text-h5 time-column">{{ formatTime(item.start) }}</td> <td class="text-h5 time-column">{{ formatTime(item.start) }}</td>
<td class="text-h5 time-column">{{ formatTime(item.end) }}</td> <td class="text-h5 time-column">{{ formatTime(item.end) }}</td>
<td class="status-column">
<v-chip :color="getStatusColor(item)" dark class="exam-status-chip">
{{ getStatusText(item) }}
</v-chip>
</td>
</tr> </tr>
</template> </template>
<template #header.date> <template #header.date>
<span class="text-h5 font-weight-bold">日期</span> <span class="text-h5 font-weight-bold no-wrap">日期</span>
</template> </template>
<template #header.name> <template #header.name>
<span class="text-h5 font-weight-bold">科目</span> <span class="text-h5 font-weight-bold no-wrap">科目</span>
</template> </template>
<template #header.start> <template #header.start>
<span class="text-h5 font-weight-bold">开始</span> <span class="text-h5 font-weight-bold no-wrap">开始</span>
</template> </template>
<template #header.end> <template #header.end>
<span class="text-h5 font-weight-bold">结束</span> <span class="text-h5 font-weight-bold no-wrap">结束</span>
</template>
<template #header.status>
<span class="text-h5 font-weight-bold no-wrap">状态</span>
</template> </template>
</v-data-table> </v-data-table>
</v-col> </v-col>
@ -67,38 +79,27 @@ const groupedExams = computed(() => {
const grouped = []; const grouped = [];
let currentDate = ''; let currentDate = '';
let currentPeriod = ''; let currentPeriod = '';
sortedExams.value.forEach((exam, index) => { sortedExams.value.forEach((exam, index) => {
const examDate = const examDate = new Date(exam.start)
new Date(exam.start) .toLocaleDateString('zh-CN', { month: 'numeric', day: 'numeric' })
.toLocaleDateString('zh-CN', { .replace('/', '月') + '日';
month: 'numeric',
day: 'numeric'
})
.replace('/', '月') + '日';
const period = formatPeriod(exam.start); const period = formatPeriod(exam.start);
const showDate = examDate !== currentDate || period !== currentPeriod; const showDate = examDate !== currentDate || period !== currentPeriod;
if (showDate) { if (showDate) {
currentDate = examDate; currentDate = examDate;
currentPeriod = period; currentPeriod = period;
const rowspan = sortedExams.value.filter( const rowspan = sortedExams.value.filter(
(e) => (e) =>
new Date(e.start) new Date(e.start)
.toLocaleDateString('zh-CN', { month: 'numeric', day: 'numeric' }) .toLocaleDateString('zh-CN', { month: 'numeric', day: 'numeric' })
.replace('/', '月') + .replace('/', '月') + '日' === currentDate &&
'日' === formatPeriod(e.start) === currentPeriod
currentDate && formatPeriod(e.start) === currentPeriod
).length; ).length;
grouped.push({ ...exam, date: examDate, period, showDate, rowspan }); grouped.push({ ...exam, date: examDate, period, showDate, rowspan });
} else { } else {
grouped.push({ ...exam, date: examDate, period, showDate: false }); grouped.push({ ...exam, date: examDate, period, showDate: false });
} }
}); });
return grouped; return grouped;
}); });
@ -106,7 +107,8 @@ const headers = [
{ text: '日期', value: 'date', sortable: false, align: 'center' }, { text: '日期', value: 'date', sortable: false, align: 'center' },
{ text: '科目', value: 'name', align: 'center' }, { text: '科目', value: 'name', align: 'center' },
{ text: '开始', value: 'start', sortable: false, align: 'center' }, { text: '开始', value: 'start', sortable: false, align: 'center' },
{ text: '结束', value: 'end', sortable: false, align: 'center' } { text: '结束', value: 'end', sortable: false, align: 'center' },
{ text: '状态', value: 'status', sortable: false, align: 'center' }
]; ];
const formatTime = (isoString: string) => { const formatTime = (isoString: string) => {
@ -114,14 +116,26 @@ const formatTime = (isoString: string) => {
return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`; return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
}; };
function getStatusClass(item: any): string { function getStatusColor(item: any): string {
const now = new Date(); const now = new Date();
const startTime = new Date(item.start); const startTime = new Date(item.start);
const endTime = new Date(item.end); const endTime = new Date(item.end);
if (now < startTime) return 'orange';
else if (now >= startTime && now <= endTime) return 'green';
else return 'red';
}
if (now < startTime) return 'status-upcoming'; function getStatusText(item: any): string {
else if (now >= startTime && now <= endTime) return 'status-ongoing'; const now = new Date();
else return 'status-ended'; const startTime = new Date(item.start);
const endTime = new Date(item.end);
if (now < startTime) {
return '未开始';
} else if (now >= startTime && now <= endTime) {
return '进行中';
} else {
return '已结束';
}
} }
const sortedExams = computed(() => { const sortedExams = computed(() => {
@ -132,7 +146,7 @@ onMounted(() => {
const interval = setInterval(() => { const interval = setInterval(() => {
state.exams = state.exams.map((exam) => ({ state.exams = state.exams.map((exam) => ({
...exam, ...exam,
status: getStatusClass(exam) status: getStatusText(exam)
})); }));
}, 1000); }, 1000);
@ -152,44 +166,39 @@ onMounted(() => {
font-weight: bold; font-weight: bold;
} }
.v-card { .exam-status-chip {
overflow-x: auto; font-size: 1.5rem !important;
max-width: 100%; /* 防止表格超出边界 */ text-align: center;
padding-right: 8px; /* 调整右边距 */ }
/* 强制标题行文字不换行 */
.no-wrap {
white-space: nowrap;
} }
/* 列样式 */ /* 列样式 */
.date-column, .date-column,
.subject-column, .subject-column,
.time-column { .time-column,
.status-column {
white-space: nowrap; white-space: nowrap;
text-align: center; text-align: center;
padding-left: 4px; padding: 5px 2px;
padding-right: 4px;
} }
.date-column { .date-column {
width: 100px; width: 60px;
} }
.subject-column { .subject-column {
width: 140px; width: 50px;
} }
.time-column { .time-column {
width: 80px; width: 50px;
} }
/* 状态样式 */ .status-column {
.status-upcoming { width: 60px;
color: orange;
} }
</style>
.status-ongoing {
color: #00ff00;
}
.status-ended {
color: red;
}
</style>

View File

@ -1,23 +1,24 @@
<template> <template>
<v-card v-if="exam" class="mx-auto pa-4 subject-info-card" max-width="600" elevation="12"> <v-card v-if="exam" class="mx-auto pa-4 subject-info-card" max-width="600" elevation="12">
<v-card-text> <v-card-text>
<div class="text-h5"> <div class="text-h5 line-item">
当前科目:<span class="text-h5 ml-2">{{ exam.name }}</span> 当前科目:<span class="text-h5 ml-2">{{ exam.name }}</span>
</div> </div>
<div class="text-h5"> <div class="text-h5 line-item">
考试时间: {{ formatDateTime(exam.start) }} ~ {{ formatDateTime(exam.end) }} 考试时间: {{ formatDateTime(exam.start) }} {{ formatDateTime(exam.end) }}
</div> </div>
<div class="text-h5 mt-4"> <div class="text-h5 line-item">
考试状态: <span :class="statusColor">{{ statusText }}</span> 考试状态: <span :class="statusColor">{{ statusText }}</span>
</div> </div>
<div v-if="isWarning" class="text-h5 text--warning">考试即将结束</div> <div v-if="showCountdown" class="text-h5 text--info line-item">开考倒计时: {{ countdown }}</div>
<div v-if="showRemainingTime" class="text-h5 text--info">剩余时间: {{ remainingTime }}</div> <div v-if="showRemainingTime" :class="['text-h5', remainingTimeColorClass, 'line-item']">
<div v-if="showCountdown" class="text-h5 text--info">倒计时: {{ countdown }}</div> 考试剩余时间: {{ remainingTime }}
</div>
</v-card-text> </v-card-text>
</v-card> </v-card>
<v-card v-else class="mx-auto pa-4 subject-info-card" max-width="600" elevation="12"> <v-card v-else class="mx-auto pa-4 subject-info-card" max-width="600" elevation="12">
<v-card-title class="headline grey lighten-2"> 考试已结束 </v-card-title> <v-card-title class="headline grey lighten-2 text-center text--ended">考试已结束</v-card-title>
</v-card> </v-card>
</template> </template>
@ -45,10 +46,8 @@ const statusColor = computed(() => {
const start = new Date(props.exam.start); const start = new Date(props.exam.start);
const end = new Date(props.exam.end); const end = new Date(props.exam.end);
const fifteenMinutesBeforeStart = new Date(start.getTime() - 15 * 60 * 1000);
if (now.value < fifteenMinutesBeforeStart) return 'status-before'; if (now.value < start) return 'status-before';
if (now.value >= fifteenMinutesBeforeStart && now.value < start) return 'status-soon';
if (now.value >= start && now.value < end) return 'status-middle'; if (now.value >= start && now.value < end) return 'status-middle';
if (now.value >= end) return 'status-after'; if (now.value >= end) return 'status-after';
}); });
@ -58,23 +57,12 @@ const statusText = computed(() => {
const start = new Date(props.exam.start); const start = new Date(props.exam.start);
const end = new Date(props.exam.end); const end = new Date(props.exam.end);
const fifteenMinutesBeforeStart = new Date(start.getTime() - 15 * 60 * 1000);
if (now.value < fifteenMinutesBeforeStart) return '未开始'; if (now.value < start) return '未开始';
if (now.value >= fifteenMinutesBeforeStart && now.value < start) return '即将开始';
if (now.value >= start && now.value < end) return '进行中'; if (now.value >= start && now.value < end) return '进行中';
if (now.value >= end) return '已结束'; if (now.value >= end) return '已结束';
}); });
const isWarning = computed(() => {
if (!props.exam) return false;
const end = new Date(props.exam.end);
const fifteenMinutesBeforeEnd = new Date(end.getTime() - 15 * 60 * 1000);
return now.value >= fifteenMinutesBeforeEnd && now.value < end;
});
const showRemainingTime = computed(() => { const showRemainingTime = computed(() => {
if (!props.exam) return false; if (!props.exam) return false;
@ -115,6 +103,16 @@ const countdown = computed(() => {
return `${minutes}${seconds}`; return `${minutes}${seconds}`;
}); });
//
const remainingTimeColorClass = computed(() => {
if (!props.exam) return 'text--default';
const end = new Date(props.exam.end);
const fifteenMinutesBeforeEnd = new Date(end.getTime() - 15 * 60 * 1000);
return now.value >= fifteenMinutesBeforeEnd && now.value < end ? 'text--warning' : 'text--default';
});
// Update the current time every second // Update the current time every second
const updateNow = () => { const updateNow = () => {
now.value = new Date(); now.value = new Date();
@ -128,24 +126,33 @@ updateNow();
font-size: 2.5rem !important; font-size: 2.5rem !important;
} }
.line-item {
margin-bottom: 16px;
}
.text--default {
color: white !important;
}
.text--warning { .text--warning {
color: #ffc107 !important; /* Vuetify's default warning color */ color: #ff0000 !important;
} }
.text--info { .text--info {
color: #17a2b8 !important; /* Info color */ color: #ffff00 !important;
}
.text--ended {
font-size: 2.5rem !important;
color: #666;
} }
.status-before { .status-before {
color: orange; color: orange;
} }
.status-soon {
color: #ff9800; /* 即将开始的颜色 */
}
.status-middle { .status-middle {
color: #00ff00; color: green;
} }
.status-after { .status-after {
@ -155,4 +162,8 @@ updateNow();
.subject-info-card { .subject-info-card {
margin-top: 20px; margin-top: 20px;
} }
</style>
.text-center {
text-align: center;
}
</style>

View File

@ -7,5 +7,6 @@ export interface TimeSlot {
export interface ExamSchedule { export interface ExamSchedule {
examName: string; examName: string;
message: string; message: string;
room: string;
examInfos: TimeSlot[]; examInfos: TimeSlot[];
} }

View File

@ -1,21 +1,25 @@
<template> <template>
<v-container class="main-container" fill-height> <v-container class="main-container" fill-height>
<v-row justify="center" align="center"> <v-row justify="center" align="center">
<v-col cols="12" md="8"> <v-col cols="12" md="10">
<v-card class="pa-4" outlined> <v-card class="pa-4" outlined>
<v-card-title class="text-h4">关于考试展</v-card-title> <v-card-title class="text-h3">关于考试看</v-card-title>
<v-card-text> <v-card-text>
<p> <p class="text-lg">
欢迎来到考试展板应用程序这是一个用于展示考试信息的工具帮助考生更好地了解考试安排和状态 欢迎使用考试看板这是一款用于展示考试信息的工具帮助考生更好地了解考试信息与状态
</p>
<p class="text-lg">本软件旨在为考生提供便捷的考试信息查看体验</p>
<p class="text-lg developer">
开发者
<a href="https://github.com/hello8693DSZ" target="_blank" class="developer-name">Hello8963</a>
<span> </span>
<a href="https://github.com/MKStoler4096" target="_blank" class="developer-name">Mkstoler4096</a>
</p> </p>
<p>本应用程序旨在为考生提供便捷的考试信息查看体验</p>
<p>版本号 1.1.4-Malkuth 正式版</p>
<p>开发者Hello8963 & Mkstoler4096</p>
<v-btn <v-btn
href="https://github.com/MKStoler4096/dsz-exam-showboard-next" href="https://github.com/ProjectCampus-CH/dsz-exam-showboard-next"
target="_blank" target="_blank"
color="primary" color="primary"
class="mt-4" class="mt-4 normal-case"
> >
GitHub 上查看源代码 GitHub 上查看源代码
</v-btn> </v-btn>
@ -32,16 +36,50 @@
display: flex; display: flex;
align-items: center; align-items: center;
} }
.v-card { .v-card {
width: 100%; width: 100%;
max-width: 600px; max-width: 800px;
margin: auto; margin: auto;
transition: transform 0.3s ease-in-out; transition: transform 0.3s ease-in-out;
} }
.v-card:hover { .v-card:hover {
transform: scale(1.05); transform: scale(1.05);
} }
/* 调整字体大小 */
.text-h3 {
font-size: 2.5em !important;
}
.text-lg {
font-size: 1.2em;
}
.v-btn { .v-btn {
height: 48px; height: 48px;
font-size: 1.1em;
} }
</style>
.normal-case {
text-transform: none !important;
}
/* 开发者名字背景框 */
.developer-name {
background-color: #f0f0f0;
color: black;
padding: 0 6px;
border-radius: 4px;
display: inline-block;
line-height: 1.4em;
font-size: 1.2em;
margin: 0 5px;
text-decoration: none;
}
.developer-name:hover {
text-decoration: underline;
}
</style>

View File

@ -1,9 +1,12 @@
<template> <template>
<v-container class="main-area"> <v-container class="main-area">
<v-row> <v-row>
<v-col cols="12"> <v-col cols="12" class="d-flex justify-space-between align-center">
<h1 class="large-title">{{ globalStore.examName }}</h1> <h1 class="large-title">{{ globalStore.examName }}</h1>
<h2 class="medium-title">{{ globalStore.message }}</h2> <h2 class="room">{{ globalStore.room }}</h2>
</v-col>
<v-col cols="12" v-if="globalStore.message">
<h3 class="medium-title text-left">{{ globalStore.message }}</h3>
</v-col> </v-col>
</v-row> </v-row>
@ -43,7 +46,7 @@ const scheduleNextUpdate = () => {
if (nextExam) { if (nextExam) {
const nextEndTime = new Date(nextExam.end).getTime(); const nextEndTime = new Date(nextExam.end).getTime();
const now = Date.now(); const now = Date.now();
const delay = nextEndTime - now + 60000; // + 1 const delay = nextEndTime - now + 60000; // Next exam end time + 1 minute
timeout = setTimeout(() => { timeout = setTimeout(() => {
updateCurrentExam(); updateCurrentExam();
@ -71,10 +74,18 @@ onUnmounted(() => {
} }
.large-title { .large-title {
font-size: 3em; /* 放大h1文字 */ font-size: 3em;
} }
.medium-title { .medium-title {
font-size: 1em; /* 略小一点的h2文字 */ font-size: 2.0em;
color: gray;
text-align: left;
} }
</style>
.room {
font-size: 3em;
text-align: right;
}
</style>

View File

@ -9,7 +9,7 @@
<v-btn block color="deep-purple accent-4" dark class="mt-2" @click="openDialog" <v-btn block color="deep-purple accent-4" dark class="mt-2" @click="openDialog"
>打开配置</v-btn >打开配置</v-btn
> >
<p class="mt-2 text-center">打开 JSON 配置文件</p> <p class="mt-2 text-center">打开 Json 配置文件</p>
</v-card> </v-card>
</v-col> </v-col>
<v-col cols="12" md="4" class="d-flex flex-column"> <v-col cols="12" md="4" class="d-flex flex-column">

View File

@ -6,6 +6,7 @@ export const useProfileStore = defineStore('app', {
examName: '考试名称', examName: '考试名称',
appHeader: '考试看板', appHeader: '考试看板',
message: '考试提醒信息', message: '考试提醒信息',
room: '考场号',
examInfos: [] examInfos: []
}), }),
persist: true persist: true