diff --git a/.gitignore b/.gitignore
index b87bd9f..a136a55 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
node_modules
/dist
/dev-dist
+/data
# local env files
.env.local
diff --git a/.trae/specs/ui-improvements-spec/checklist.md b/.trae/specs/ui-improvements-spec/checklist.md
new file mode 100644
index 0000000..ba4fb13
--- /dev/null
+++ b/.trae/specs/ui-improvements-spec/checklist.md
@@ -0,0 +1,10 @@
+- [x] Announcement Settings Dialog Markdown toolbar wraps correctly
+- [x] Announcement Settings Dialog Preview works
+- [x] Announcement Widget is hidden when no announcements exist
+- [x] Clicking Announcement Widget opens detail popup
+- [x] "Self Correction" tag in Homework Grid is Green
+- [x] "Add Item" button in Homework Edit Dialog is White and distinct
+- [x] Metadata buttons in Homework Edit Dialog match Preset chips size/shape
+- [x] Refresh button in Floating Toolbar does not stick in loading state
+- [x] "Save Success" green notification appears after saving Homework
+- [x] "Save Success" green notification appears after saving Announcement
diff --git a/.trae/specs/ui-improvements-spec/spec.md b/.trae/specs/ui-improvements-spec/spec.md
new file mode 100644
index 0000000..0530228
--- /dev/null
+++ b/.trae/specs/ui-improvements-spec/spec.md
@@ -0,0 +1,55 @@
+# UI and Functional Improvements Spec
+
+## Why
+User reported several UI inconsistencies, visual bugs, and missing feedback mechanisms that affect the usability and aesthetic quality of the application. Specifically, the announcement feature needs polish, font colors need adjustment for dark mode, and feedback for save operations is missing.
+
+## What Changes
+
+### Announcement Feature
+- **Settings Dialog (`AnnouncementSettingsDialog.vue`)**:
+ - Fix Markdown toolbar layout to prevent conflicts/overflow (use `flex-wrap`).
+ - Fix Preview functionality (ensure `parseMarkdown` works).
+ - Add "Save Success" feedback when adding/modifying announcements.
+- **Widget (`AnnouncementWidget.vue`)**:
+ - **Hide the widget completely** when there are no active/upcoming announcements (User requirement: "only shows when there are announcements").
+ - Make the widget clickable to open a detail popup of the current announcement.
+- **Detail Popup (`AnnouncementOverlay.vue` or new)**:
+ - Ensure manually clicking the widget opens the detail view.
+
+### Homework UI
+- **Homework Grid (`HomeworkGrid.vue`)**:
+ - Change "Self Correction" (自批) tag color from Orange/Warning to **Green/Success**.
+- **Homework Edit Dialog (`HomeworkEditDialog.vue`)**:
+ - **Add Item Button**: Change style to be more obvious (White background, black text).
+ - **Metadata Buttons** (Submission Time, Self Correction): Adjust height and border-radius to match the "Preset" chips on the left for visual consistency.
+
+### General UI
+- **Font Colors**:
+ - Change gray text to **White** in key locations (e.g., Homework Edit dialog controls, Announcement widget text) for better contrast in dark mode.
+- **Floating Toolbar**:
+ - Fix the "Refresh" button loading state (it appears stuck).
+- **Feedback**:
+ - Implement a "Save Success" (green) notification (Snackbar/Toast) that triggers after successful save operations (Homework save, Announcement save, etc.).
+
+## Impact
+- **Affected Components**: `AnnouncementSettingsDialog`, `AnnouncementWidget`, `HomeworkGrid`, `HomeworkEditDialog`, `FloatingToolbar`, `index.vue`.
+- **User Experience**: Improved visual consistency, better feedback on actions, and cleaner interface when no announcements are present.
+
+## ADDED Requirements
+### Requirement: Save Feedback
+The system SHALL display a green "Save Success" notification (Snackbar) when:
+- A homework entry is saved (auto-save or manual).
+- An announcement is added or modified.
+- Attendance is saved.
+
+### Requirement: Announcement Widget Behavior
+- **WHEN** there are no active announcements, **THEN** the widget on the main board SHALL be hidden.
+- **WHEN** the widget is visible and clicked, **THEN** it SHALL open the announcement detail popup.
+
+## MODIFIED Requirements
+### Requirement: Visual Styles
+- **Self Correction Tag**: SHALL be Green.
+- **Add Item Button**: SHALL be White (filled).
+- **Metadata Buttons**: SHALL match Preset Chips in size and shape.
+- **Refresh Button**: SHALL NOT show loading state indefinitely.
+
diff --git a/.trae/specs/ui-improvements-spec/tasks.md b/.trae/specs/ui-improvements-spec/tasks.md
new file mode 100644
index 0000000..b3c8b68
--- /dev/null
+++ b/.trae/specs/ui-improvements-spec/tasks.md
@@ -0,0 +1,27 @@
+# Tasks
+
+- [x] Task 1: Update Announcement System
+ - [x] SubTask 1.1: Fix `AnnouncementSettingsDialog.vue`:
+ - Fix Markdown toolbar layout (flex-wrap).
+ - Ensure Preview works (check `parseMarkdown` usage).
+ - Add "Save Success" notification trigger on save.
+ - [x] SubTask 1.2: Update `AnnouncementWidget.vue`:
+ - Add logic to hide component if `notifications` is empty.
+ - Add click handler to open detail popup (emit event or call method).
+ - [x] SubTask 1.3: Update `index.vue` to handle widget click and show detail popup.
+
+- [x] Task 2: Update Homework UI
+ - [x] SubTask 2.1: Update `HomeworkGrid.vue`:
+ - Change "Self Correction" chip color to `success`.
+ - [x] SubTask 2.2: Update `HomeworkEditDialog.vue`:
+ - Style "Add Item" button (color="white", variant="flat").
+ - Style "Submission Time" and "Self Correction" buttons to match `v-chip` style (height, rounded).
+ - Ensure text colors are white where appropriate.
+
+- [x] Task 3: Fix General UI & Feedback
+ - [x] SubTask 3.1: Fix `FloatingToolbar.vue` refresh button loading state (debug `loading` prop).
+ - [x] SubTask 3.2: Implement Global "Save Success" Snackbar in `index.vue` and expose method/event to trigger it from child components.
+ - [x] SubTask 3.3: specific font color adjustments (Grey to White) in `AttendanceSidebar` and `HomeworkGrid` as requested.
+
+# Task Dependencies
+- Task 3.2 (Global Snackbar) should be done before Task 1.1 and 1.3 to ensure feedback works.
diff --git a/CHWB.png b/CHWB.png
new file mode 100644
index 0000000..7241cdc
Binary files /dev/null and b/CHWB.png differ
diff --git a/README.md b/README.md
index 44a3d93..294becc 100644
--- a/README.md
+++ b/README.md
@@ -8,8 +8,6 @@
适用于班级大屏的作业板小工具
-请打开 [https://cs.houlangs.com](https://cs.houlang.cloud) 立刻使用
-
## 交流
QQ:[964979747](https://qm.qq.com/q/4RX45b1Oac)
diff --git a/docker-server/.dockerignore b/docker-server/.dockerignore
new file mode 100644
index 0000000..5a045a3
--- /dev/null
+++ b/docker-server/.dockerignore
@@ -0,0 +1,3 @@
+node_modules
+data
+.env
diff --git a/docker-server/Dockerfile b/docker-server/Dockerfile
new file mode 100644
index 0000000..07f1db5
--- /dev/null
+++ b/docker-server/Dockerfile
@@ -0,0 +1,22 @@
+FROM node:18-alpine
+
+WORKDIR /app
+
+# Copy package files and install production dependencies
+COPY package*.json ./
+RUN npm install --production
+
+# Copy server code
+COPY server.js ./
+
+# Copy the pre-built frontend files
+COPY dist ./dist
+
+# Create a data directory to store JSON files locally
+RUN mkdir -p data
+
+# Expose port
+EXPOSE 3001
+
+# Start the server
+CMD ["npm", "start"]
diff --git a/docker-server/README.md b/docker-server/README.md
new file mode 100644
index 0000000..8307e87
--- /dev/null
+++ b/docker-server/README.md
@@ -0,0 +1,43 @@
+# ClassHomeworkBoard - Docker 部署版本
+
+这是一个专门为服务器部署设计的 Node.js 后端程序,它包含了 Express 服务器和构建好的前端静态文件。此版本去除了对 Windows 特有功能(如通过 `user32.dll` 抓取 PPT 和 WPS 窗口)的依赖,完美适配 Linux 和 Docker 环境。
+
+## 包含内容
+
+- `server.js`: 提供 `/api/local-file/*` 本地存储接口的 Express 后端。
+- `dist/`: 已经构建好的前端静态资源文件。
+- `Dockerfile`: 用于构建 Docker 镜像的配置文件。
+- `package.json`: 服务器的 npm 依赖配置文件。
+
+## 如何使用 Docker 部署
+
+1. **构建 Docker 镜像**
+ 在 `docker-server` 目录下运行以下命令:
+ ```bash
+ docker build -t classhomeworkboard .
+ ```
+
+2. **运行 Docker 容器**
+ ```bash
+ docker run -d -p 3001:3001 -v chwb_data:/app/data --name chwb classhomeworkboard
+ ```
+ > 这里的 `-v chwb_data:/app/data` 表示将容器内的 `/app/data`(存储 JSON 数据的文件夹)挂载到外部卷,防止数据随容器销毁而丢失。
+
+3. **访问服务**
+ 打开浏览器,访问 `http://<服务器IP>:3001` 即可使用。
+
+## 如果你需要更新前端代码
+
+如果你在主项目中修改了 Vue 前端代码:
+1. 在项目根目录运行 `npm run build`(或 `pnpm run build`)重新打包前端。
+2. 将打包后生成的 `dist` 文件夹覆盖到本目录的 `dist` 文件夹。
+3. 重新执行 Docker 镜像的构建与部署步骤。
+
+## 本地(不使用 Docker)直接运行
+
+如果你想直接在服务器的 Node 环境中运行:
+```bash
+npm install
+npm start
+```
+默认会在 3001 端口启动。
\ No newline at end of file
diff --git a/docker-server/package.json b/docker-server/package.json
new file mode 100644
index 0000000..3e4f89d
--- /dev/null
+++ b/docker-server/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "classhomeworkboard-server",
+ "version": "1.0.0",
+ "description": "Dockerized server for ClassHomeworkBoard",
+ "main": "server.js",
+ "type": "module",
+ "scripts": {
+ "start": "node server.js"
+ },
+ "dependencies": {
+ "express": "^4.18.2",
+ "cors": "^2.8.5"
+ }
+}
diff --git a/docker-server/server.js b/docker-server/server.js
new file mode 100644
index 0000000..86edab8
--- /dev/null
+++ b/docker-server/server.js
@@ -0,0 +1,123 @@
+import express from 'express';
+import cors from 'cors';
+import path from 'path';
+import fs from 'fs';
+import { fileURLToPath } from 'url';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+const app = express();
+const port = process.env.PORT || 3001;
+
+// Middlewares
+app.use(cors());
+app.use(express.json({ limit: '50mb' }));
+
+// Data Directory for JSON files
+const dataDir = path.resolve(__dirname, 'data');
+if (!fs.existsSync(dataDir)) {
+ fs.mkdirSync(dataDir, { recursive: true });
+}
+
+// =======================
+// API Routes
+// =======================
+
+// Mock PPT APIs (Windows GUI features are not supported on Linux/Docker)
+app.get('/api/ppt/check', (req, res) => {
+ res.json({ success: true, hasPpt: false, ppts: [] });
+});
+
+app.post('/api/ppt/control', (req, res) => {
+ res.json({ success: false, error: 'Not supported on server environment' });
+});
+
+// Local File Storage APIs
+app.post('/api/local-file/save', (req, res) => {
+ try {
+ const { key, data } = req.body;
+ if (!key || !data) throw new Error('Missing key or data');
+
+ // Sanitize key
+ const safeKey = String(key).replace(/[^a-zA-Z0-9_-]/g, '_');
+ const filePath = path.join(dataDir, `${safeKey}.json`);
+
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
+ res.json({ success: true });
+ } catch (e) {
+ res.status(500).json({ success: false, error: e.message });
+ }
+});
+
+app.get('/api/local-file/load', (req, res) => {
+ try {
+ const key = req.query.key;
+ if (!key) {
+ return res.status(400).json({ success: false, error: 'Missing key' });
+ }
+
+ const safeKey = String(key).replace(/[^a-zA-Z0-9_-]/g, '_');
+ const filePath = path.join(dataDir, `${safeKey}.json`);
+
+ if (fs.existsSync(filePath)) {
+ const data = fs.readFileSync(filePath, 'utf-8');
+ res.json({ success: true, data: JSON.parse(data) });
+ } else {
+ res.status(404).json({ success: false, error: 'Not found' });
+ }
+ } catch (e) {
+ res.status(500).json({ success: false, error: e.message });
+ }
+});
+
+app.get('/api/local-file/keys', (req, res) => {
+ try {
+ const files = fs.readdirSync(dataDir);
+ const keys = files
+ .filter(f => f.endsWith('.json'))
+ .map(f => f.replace('.json', ''));
+ res.json({ success: true, keys });
+ } catch (e) {
+ res.status(500).json({ success: false, error: e.message });
+ }
+});
+
+// =======================
+// Static Files & SPA
+// =======================
+const staticDir = path.resolve(__dirname, 'dist');
+
+if (fs.existsSync(staticDir)) {
+ // Serve static files
+ app.use(express.static(staticDir, {
+ setHeaders: (res, path, stat) => {
+ // Prevent caching for sw.js and index.html
+ if (path.endsWith('sw.js') || path.endsWith('index.html')) {
+ res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
+ res.setHeader('Pragma', 'no-cache');
+ res.setHeader('Expires', '0');
+ } else {
+ // Cache other static assets
+ res.setHeader('Cache-Control', 'public, max-age=3600');
+ }
+ }
+ }));
+
+ // Catch-all route for SPA fallback
+ app.get('*', (req, res) => {
+ res.sendFile(path.join(staticDir, 'index.html'));
+ });
+} else {
+ // Fallback if dist doesn't exist yet
+ app.get('*', (req, res) => {
+ res.send('
Frontend not found!
Please build the Vue frontend (npm run build) and copy the dist folder into the docker-server directory before running.
');
+ });
+}
+
+// Start Server
+app.listen(port, () => {
+ console.log(`ClassHomeworkBoard Server running on port ${port}`);
+ console.log(`Data directory: ${dataDir}`);
+ console.log(`Static files directory: ${staticDir}`);
+});
diff --git a/index.html b/index.html
index 6fdc3cb..9380e8d 100644
--- a/index.html
+++ b/index.html
@@ -2,24 +2,20 @@
-
- Classworks 作业板
+ CHWB作业板
-
-
-
@@ -28,7 +24,6 @@
-
diff --git a/package.json b/package.json
index db47c8a..d556994 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
{
- "name": "classworks",
+ "name": "classhomeworkboard",
"private": true,
"type": "module",
- "version": "0.0.0",
+ "version": "2.1.0",
"scripts": {
"dev": "vite --host",
"build": "vite build",
@@ -13,13 +13,13 @@
"dependencies": {
"@fingerprintjs/fingerprintjs": "^5.0.1",
"@mdi/font": "7.4.47",
- "@microsoft/clarity": "^1.0.2",
- "@sentry/vue": "^10.36.0",
+ "@vue-office/pptx": "^1.0.1",
"@vueuse/core": "^14.1.0",
"axios": "^1.13.2",
"idb": "^8.0.3",
"js-base64": "^3.7.8",
"lucide-vue-next": "^0.555.0",
+ "markdown-it": "^14.1.1",
"pinyin-pro": "^3.27.0",
"ratelimit-header-parser": "^0.1.0",
"roboto-fontface": "*",
@@ -28,7 +28,8 @@
"uuid": "^13.0.0",
"vue": "^3.5.25",
"vue-sonner": "^2.0.9",
- "vuetify": "^3.11.0"
+ "vuetify": "^3.11.0",
+ "xlsx": "^0.18.5"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
diff --git a/public/CHWB.png b/public/CHWB.png
new file mode 100644
index 0000000..7241cdc
Binary files /dev/null and b/public/CHWB.png differ
diff --git a/public/favicon.ico b/public/favicon.ico
deleted file mode 100644
index e0db255..0000000
Binary files a/public/favicon.ico and /dev/null differ
diff --git a/public/pwa/image/favicon.ico b/public/pwa/image/favicon.ico
deleted file mode 100644
index e0db255..0000000
Binary files a/public/pwa/image/favicon.ico and /dev/null differ
diff --git a/public/pwa/image/logo.svg b/public/pwa/image/logo.svg
deleted file mode 100644
index f11a8e5..0000000
--- a/public/pwa/image/logo.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
diff --git a/public/sounds/Teams Ping.mp3 b/public/sounds/Teams Ping.mp3
deleted file mode 100644
index 5f3e1c2..0000000
Binary files a/public/sounds/Teams Ping.mp3 and /dev/null differ
diff --git a/public/sounds/Teams Remix.mp3 b/public/sounds/Teams Remix.mp3
deleted file mode 100644
index f50f267..0000000
Binary files a/public/sounds/Teams Remix.mp3 and /dev/null differ
diff --git a/public/sounds/Teams bounce.mp3 b/public/sounds/Teams bounce.mp3
deleted file mode 100644
index 0b4e136..0000000
Binary files a/public/sounds/Teams bounce.mp3 and /dev/null differ
diff --git a/public/sounds/Teams incoming-ringtone-level30.mp3 b/public/sounds/Teams incoming-ringtone-level30.mp3
deleted file mode 100644
index ce9c3b8..0000000
Binary files a/public/sounds/Teams incoming-ringtone-level30.mp3 and /dev/null differ
diff --git a/public/sounds/Teams incoming-ringtone-level40.mp3 b/public/sounds/Teams incoming-ringtone-level40.mp3
deleted file mode 100644
index 41e1ef5..0000000
Binary files a/public/sounds/Teams incoming-ringtone-level40.mp3 and /dev/null differ
diff --git a/public/sounds/Teams meetup_ring.mp3 b/public/sounds/Teams meetup_ring.mp3
deleted file mode 100644
index 16ff8f7..0000000
Binary files a/public/sounds/Teams meetup_ring.mp3 and /dev/null differ
diff --git a/public/sounds/Teams screenshare_ring.mp3 b/public/sounds/Teams screenshare_ring.mp3
deleted file mode 100644
index 0e19996..0000000
Binary files a/public/sounds/Teams screenshare_ring.mp3 and /dev/null differ
diff --git a/public/sounds/Teams teams_meet_up_reminder.mp3 b/public/sounds/Teams teams_meet_up_reminder.mp3
deleted file mode 100644
index 0e19996..0000000
Binary files a/public/sounds/Teams teams_meet_up_reminder.mp3 and /dev/null differ
diff --git a/public/sounds/Teams teams_notification.mp3 b/public/sounds/Teams teams_notification.mp3
deleted file mode 100644
index f4a0a3f..0000000
Binary files a/public/sounds/Teams teams_notification.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 优先处理.mp3 b/public/sounds/Teams 优先处理.mp3
deleted file mode 100644
index 910efd0..0000000
Binary files a/public/sounds/Teams 优先处理.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 共鸣.mp3 b/public/sounds/Teams 共鸣.mp3
deleted file mode 100644
index 7f46f4e..0000000
Binary files a/public/sounds/Teams 共鸣.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 召唤.mp3 b/public/sounds/Teams 召唤.mp3
deleted file mode 100644
index 0b4aae2..0000000
Binary files a/public/sounds/Teams 召唤.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 叮铃.mp3 b/public/sounds/Teams 叮铃.mp3
deleted file mode 100644
index a099399..0000000
Binary files a/public/sounds/Teams 叮铃.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 增强.mp3 b/public/sounds/Teams 增强.mp3
deleted file mode 100644
index 03f7249..0000000
Binary files a/public/sounds/Teams 增强.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 尤里卡.mp3 b/public/sounds/Teams 尤里卡.mp3
deleted file mode 100644
index fb5cdb8..0000000
Binary files a/public/sounds/Teams 尤里卡.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 弹拨.mp3 b/public/sounds/Teams 弹拨.mp3
deleted file mode 100644
index a31cd6c..0000000
Binary files a/public/sounds/Teams 弹拨.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 提醒.mp3 b/public/sounds/Teams 提醒.mp3
deleted file mode 100644
index fce78c9..0000000
Binary files a/public/sounds/Teams 提醒.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 摇摆.mp3 b/public/sounds/Teams 摇摆.mp3
deleted file mode 100644
index 65fd935..0000000
Binary files a/public/sounds/Teams 摇摆.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 时空.mp3 b/public/sounds/Teams 时空.mp3
deleted file mode 100644
index bcf62e7..0000000
Binary files a/public/sounds/Teams 时空.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 气泡(大声).mp3 b/public/sounds/Teams 气泡(大声).mp3
deleted file mode 100644
index 212db3a..0000000
Binary files a/public/sounds/Teams 气泡(大声).mp3 and /dev/null differ
diff --git a/public/sounds/Teams 气泡.mp3 b/public/sounds/Teams 气泡.mp3
deleted file mode 100644
index 92896fe..0000000
Binary files a/public/sounds/Teams 气泡.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 波普.mp3 b/public/sounds/Teams 波普.mp3
deleted file mode 100644
index ee69f22..0000000
Binary files a/public/sounds/Teams 波普.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 波纹.mp3 b/public/sounds/Teams 波纹.mp3
deleted file mode 100644
index 966701b..0000000
Binary files a/public/sounds/Teams 波纹.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 滴水.mp3 b/public/sounds/Teams 滴水.mp3
deleted file mode 100644
index e61ad75..0000000
Binary files a/public/sounds/Teams 滴水.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 点击.mp3 b/public/sounds/Teams 点击.mp3
deleted file mode 100644
index fadcfa5..0000000
Binary files a/public/sounds/Teams 点击.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 蜂鸣声.mp3 b/public/sounds/Teams 蜂鸣声.mp3
deleted file mode 100644
index 765e504..0000000
Binary files a/public/sounds/Teams 蜂鸣声.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 警报.mp3 b/public/sounds/Teams 警报.mp3
deleted file mode 100644
index c5da548..0000000
Binary files a/public/sounds/Teams 警报.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 赋予希望.mp3 b/public/sounds/Teams 赋予希望.mp3
deleted file mode 100644
index 7562489..0000000
Binary files a/public/sounds/Teams 赋予希望.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 轻弹.mp3 b/public/sounds/Teams 轻弹.mp3
deleted file mode 100644
index f509a4e..0000000
Binary files a/public/sounds/Teams 轻弹.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 进阶.mp3 b/public/sounds/Teams 进阶.mp3
deleted file mode 100644
index 9c1d68e..0000000
Binary files a/public/sounds/Teams 进阶.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 重复振铃.mp3 b/public/sounds/Teams 重复振铃.mp3
deleted file mode 100644
index 7cd5fcf..0000000
Binary files a/public/sounds/Teams 重复振铃.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 颤振.mp3 b/public/sounds/Teams 颤振.mp3
deleted file mode 100644
index 9ff2793..0000000
Binary files a/public/sounds/Teams 颤振.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 高分.mp3 b/public/sounds/Teams 高分.mp3
deleted file mode 100644
index 53d2891..0000000
Binary files a/public/sounds/Teams 高分.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 默认.mp3 b/public/sounds/Teams 默认.mp3
deleted file mode 100644
index 639c092..0000000
Binary files a/public/sounds/Teams 默认.mp3 and /dev/null differ
diff --git a/public/sounds/Teams 默认通话铃.mp3 b/public/sounds/Teams 默认通话铃.mp3
deleted file mode 100644
index 36d569f..0000000
Binary files a/public/sounds/Teams 默认通话铃.mp3 and /dev/null differ
diff --git a/public/sounds/universfield-new-notification-024-370048.mp3 b/public/sounds/universfield-new-notification-024-370048.mp3
new file mode 100644
index 0000000..dc8ee33
Binary files /dev/null and b/public/sounds/universfield-new-notification-024-370048.mp3 differ
diff --git a/scripts/generate-sound-list.js b/scripts/generate-sound-list.js
index a4507bb..45e6efc 100644
--- a/scripts/generate-sound-list.js
+++ b/scripts/generate-sound-list.js
@@ -34,6 +34,7 @@ function getSoundFiles() {
// 生成文件内容
function generateSoundList() {
const soundFiles = getSoundFiles();
+ const defaultSound = 'universfield-new-notification-024-370048.mp3';
const fileContent = `/**
* 自动生成的音频文件列表
@@ -45,10 +46,10 @@ function generateSoundList() {
export const soundFiles = ${JSON.stringify(soundFiles, null, 2)};
// 默认的单次通知铃声
-export const defaultSingleSound = 'Teams 默认.mp3';
+export const defaultSingleSound = '${defaultSound}';
// 默认的持续通知铃声
-export const defaultUrgentSound = 'Teams 默认通话铃.mp3';
+export const defaultUrgentSound = '${defaultSound}';
// 获取音频文件的完整路径
export function getSoundPath(filename) {
diff --git a/src/App.vue b/src/App.vue
index cd7b1bb..6118c99 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -2,8 +2,14 @@
-
-
+
+
diff --git a/src/assets/cslogo.png b/src/assets/cslogo.png
deleted file mode 100644
index 94c3996..0000000
Binary files a/src/assets/cslogo.png and /dev/null differ
diff --git a/src/assets/favicon.ico b/src/assets/favicon.ico
deleted file mode 100644
index 8fe5a93..0000000
Binary files a/src/assets/favicon.ico and /dev/null differ
diff --git a/src/assets/logo.png b/src/assets/logo.png
deleted file mode 100644
index a5f23ae..0000000
Binary files a/src/assets/logo.png and /dev/null differ
diff --git a/src/assets/logo.svg b/src/assets/logo.svg
deleted file mode 100644
index df5fbd0..0000000
--- a/src/assets/logo.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-
diff --git a/src/components/AppHeader.vue b/src/components/AppHeader.vue
index 6a62425..7e625f6 100644
--- a/src/components/AppHeader.vue
+++ b/src/components/AppHeader.vue
@@ -1,7 +1,7 @@
-
-
+
+
作业
diff --git a/src/components/CacheManager.vue b/src/components/CacheManager.vue
index 793d33d..31bec12 100644
--- a/src/components/CacheManager.vue
+++ b/src/components/CacheManager.vue
@@ -2,48 +2,85 @@
缓存管理
-
-
+
+
清除所有缓存
-
+
mdi-refresh
-
+
Service Worker 未激活,缓存管理功能不可用。
-
+
{{ message }}
-
+
{{ formatCacheName(cache.name) }}
- {{ cache.urls.length }} 个文件
+
+ {{ cache.urls.length }} 个文件
+
-
+
清除此缓存
-
+
{{ getFileName(url) }}
{{ url }}
-
-
+
+
mdi-delete
@@ -53,9 +90,15 @@
-
+
-
+
没有找到缓存数据。
diff --git a/src/components/ExamConfigEditor.vue b/src/components/ExamConfigEditor.vue
index 065e8eb..c26bfa0 100644
--- a/src/components/ExamConfigEditor.vue
+++ b/src/components/ExamConfigEditor.vue
@@ -11,7 +11,9 @@
@click:close="error = ''"
>
- mdi-alert-circle
+
+ mdi-alert-circle
+
{{ error }}
@@ -27,7 +29,9 @@
@click:close="success = ''"
>
- mdi-check-circle
+
+ mdi-check-circle
+
{{ success }}
@@ -43,29 +47,49 @@
配置验证失败,请检查以下问题:
-
+
-
- mdi-circle-small
+
+
+ mdi-circle-small
+
- {{ error }}
+
+ {{ error }}
+
-
+
-
+
-
+
删除配置
- 预览
- 编辑
+
+ 预览
+
+
+ 编辑
+
-
+
{{ localConfig.examName || "未设置考试名称" }}
{{ localConfig.message || "未设置考试提示" }}
-
- mdi-home
+
+
+ mdi-home
+
考场:{{ localConfig.room }}
@@ -178,20 +220,29 @@
lg="4"
md="6"
>
-
+
- mdi-book-open-page-variant
+
+ mdi-book-open-page-variant
+
{{ examInfo.name || "未设置科目" }}
- mdi-clock-start
-
+ mdi-clock-start
+
开始时间
@@ -200,10 +251,13 @@
- mdi-clock-end
-
+ mdi-clock-end
+
结束时间
@@ -215,29 +269,50 @@
-
-
+
+
mdi-calendar-blank
-
暂无考试科目安排
+
+ 暂无考试科目安排
+
点击上方"添加科目"按钮开始配置考试时间表
-
- mdi-plus
+
+
+ mdi-plus
+
立即添加
-
+
- mdi-code-json
+
+ mdi-code-json
+
配置预览
-
+
-
+ />
-
-
+
+
{{ formattedStorageJson }}
@@ -271,14 +351,23 @@
-
+
- mdi-information
+
+ mdi-information
+
基本信息
-
+
+ />
-
+
+ />
- mdi-message-text
+ mdi-message-text
考试提示
+ />
-
+
- mdi-plus
+
+ mdi-plus
+
{{ tip.substring(0, 20) }}...
- mdi-lightbulb-outline
+
+ mdi-lightbulb-outline
+
点击上方选项快速添加常用考试提示
@@ -358,11 +466,17 @@
-
+
- mdi-format-list-bulleted
+
+ mdi-format-list-bulleted
+
考试科目安排
-
+
@@ -373,7 +487,7 @@
hide-details
@change="toggleAlertTimeMode"
>
-
+
自定义提醒时间
@@ -408,14 +522,19 @@
variant="tonal"
class="mr-3"
>
- mdi-numeric-{{ index + 1 }}-circle
+
+ mdi-numeric-{{ index + 1 }}-circle
+
第 {{ index + 1 }} 科目
-
+
- 上移
+
+ 上移
+
- 下移
+
+ 下移
+
- 删除
+
+ 删除
+
-
+
-
+
-
+
{{ customSubjectInput }}
-
+
-
+
-
+
-
+
mdi-calendar-clock
- mdi-clock-start
+
+ mdi-clock-start
+
选择开始时间
-
+
+ />
+ />
-
+
-
+
-
+
-
+
mdi-calendar-clock
- mdi-clock-end
+
+ mdi-clock-end
+
选择结束时间
-
+
+ />
+ />
-
+
-
-
-
+
+
+
-
-
-
-
+
+
+
+
-
-
+
+
mdi-calendar-blank
-
暂无考试科目安排
+
+ 暂无考试科目安排
+
点击上方"添加科目"按钮开始配置
@@ -695,10 +870,18 @@
-
+
- mdi-delete-alert
+
+ mdi-delete-alert
+
确认删除配置
@@ -706,7 +889,7 @@
此操作不可撤销,将会删除所有相关数据
-
+
(a.order ?? 0) - (b.order ?? 0))
.map(subject => ({
name: subject.name,
diff --git a/src/components/FloatingToolbar.vue b/src/components/FloatingToolbar.vue
index 1431ad1..b1aac7c 100644
--- a/src/components/FloatingToolbar.vue
+++ b/src/components/FloatingToolbar.vue
@@ -7,7 +7,10 @@
elevation="4"
rounded="xl"
>
-
+
-
+
-
+
- 复制到今天
+ class="side-action-container"
+ >
+
+ 复制到今天
+
+
@@ -138,9 +153,8 @@ export default {
left: 0;
width: 100%;
height: 0;
+ overflow: visible;
z-index: 100;
- display: flex;
- justify-content: center;
pointer-events: none;
}
@@ -191,12 +205,15 @@ export default {
transform: scale(0.95);
}
-.side-action-btn {
+.side-action-container {
position: absolute;
bottom: 24px;
right: 24px;
pointer-events: auto;
z-index: 101;
+}
+
+.side-action-btn {
background: rgba(255, 255, 255, 0.9) !important;
backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.2);
diff --git a/src/components/GlobalMessage.vue b/src/components/GlobalMessage.vue
index 73518f5..57d7649 100644
--- a/src/components/GlobalMessage.vue
+++ b/src/components/GlobalMessage.vue
@@ -8,14 +8,28 @@
variant="tonal"
>
-
+
-
{{ message?.title }}
-
{{ message?.content }}
+
+ {{ message?.title }}
+
+
+ {{ message?.content }}
+
-
+
diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue
deleted file mode 100644
index 79b3ce0..0000000
--- a/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,164 +0,0 @@
-
-
-
-
-
-
-
Welcome to
-
-
Vuetify
-
-
-
-
-
-
-
-
-
-
-
-
- Get started
-
-
-
-
- Replace this page by removing
- {{ `
-
- ` }}
-
- in
- pages/index.vue
- .
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/components/HitokotoCard.vue b/src/components/HitokotoCard.vue
index 6cbf3fd..e680e12 100644
--- a/src/components/HitokotoCard.vue
+++ b/src/components/HitokotoCard.vue
@@ -69,7 +69,7 @@ export default {
loading: false,
timer: null,
unwatch: null,
- fontSize: 28 // Will be updated in mounted()
+ fontSize: 28
}
},
computed: {
@@ -111,7 +111,7 @@ export default {
loadLocalSettings() {
this.enabled = SettingsManager.getSetting('hitokoto.enabled')
this.refreshInterval = SettingsManager.getSetting('hitokoto.refreshInterval')
- this.fontSize = SettingsManager.getSetting('font.size')
+ this.fontSize = 28
},
async loadKvSettings() {
try {
diff --git a/src/components/HitokotoSettings.vue b/src/components/HitokotoSettings.vue
index 26555e7..609796e 100644
--- a/src/components/HitokotoSettings.vue
+++ b/src/components/HitokotoSettings.vue
@@ -1,15 +1,26 @@
-
+
-
- 以下配置将同步到云端,对所有连接此班级的设备生效。
+
+
+ 以下配置将同步到云端,对所有连接此班级的设备生效。
+
- 启用数据源
+
+ 启用数据源
+
- mdi-alert
+
+ mdi-alert
+
一言(Hitokoto)数据源已收到关于存在负面内容的大量反馈,请用户谨慎启用。
@@ -99,39 +116,62 @@
- 已启用的数据源将在获取一言时随机尝试,直到成功获取内容为止。
- 敏感词过滤会将包含任意敏感词的句子过滤掉,避免显示不当内容。
+ 已启用的数据源将在获取一言时随机尝试,直到成功获取内容为止。
+ 敏感词过滤会将包含任意敏感词的句子过滤掉,避免显示不当内容。
-
+
-
-
+
+
正在同步配置...
-
-
-
-
+
+
-
+
-
-
+
+
- Token
-
+
+ Token
+
+
{{ testResultData.data.token }}
@@ -141,20 +181,36 @@
-
-
+
+
- IP 地址
- {{ testResultData.data.ip }}
+
+ IP 地址
+
+
+ {{ testResultData.data.ip }}
+
-
-
+
+
- 地区
- {{ testResultData.data.region }}
+
+ 地区
+
+
+ {{ testResultData.data.region }}
+
@@ -165,59 +221,123 @@
-
+
-
+
-
温度
-
{{ testResultData.data.weatherData.temperature }}°C
+
+ 温度
+
+
+ {{ testResultData.data.weatherData.temperature }}°C
+
-
+
-
+
-
天气
-
{{ testResultData.data.weatherData.weather }}
+
+ 天气
+
+
+ {{ testResultData.data.weatherData.weather }}
+
-
+
-
+
-
湿度
-
{{ testResultData.data.weatherData.humidity }}%
+
+ 湿度
+
+
+ {{ testResultData.data.weatherData.humidity }}%
+
-
+
-
+
-
风向/风力
+
+ 风向/风力
+
{{ testResultData.data.weatherData.windDirection }} {{ testResultData.data.weatherData.windPower }}级
-
+
-
+
-
PM2.5
-
{{ testResultData.data.weatherData.pm25 }}
+
+ PM2.5
+
+
+ {{ testResultData.data.weatherData.pm25 }}
+
-
+
-
+
-
能见度
-
{{ testResultData.data.weatherData.visibility }}
+
+ 能见度
+
+
+ {{ testResultData.data.weatherData.visibility }}
+
@@ -227,7 +347,9 @@
-
环境标签
+
+ 环境标签
+
-
-
+
+
北京时间: {{ new Date(testResultData.data.beijingTime).toLocaleString() }}
@@ -254,8 +380,6 @@
-
-
diff --git a/src/components/HomeworkEditDialog.vue b/src/components/HomeworkEditDialog.vue
index 8d5ec17..bbff799 100644
--- a/src/components/HomeworkEditDialog.vue
+++ b/src/components/HomeworkEditDialog.vue
@@ -1,250 +1,304 @@
-# 创建新的作业编辑对话框组件
-
-
- {{ title }}
+
+
+
+
+ {{ title }}
+
-
-
-
- {{ autoSave ? autoSavePromptText : manualSavePromptText }}
-
-
-
-
-
-
-
-
+
- {{ helpContent.title }}
+
+ {{ helpContent.title }}
+
- {{ helpContent.message }}
+
+ {{ helpContent.message }}
+
-
- 我知道了
+
+
+ 我知道了
+
diff --git a/src/components/RandomPicker.vue b/src/components/RandomPicker.vue
index 32714df..704813f 100644
--- a/src/components/RandomPicker.vue
+++ b/src/components/RandomPicker.vue
@@ -5,16 +5,32 @@
max-width="600"
persistent
>
-
+
-
+
随机点名
-
-
+
+
-
- 请选择抽取人数
+
+
+ 请选择抽取人数
+
- 姓名模式
- 学号模式
+
+ 姓名模式
+
+
+ 学号模式
+
-
-
学号范围设置
+
+
+ 学号范围设置
+
-
+
没有可抽取的学生,请调整过滤选项
@@ -109,8 +143,11 @@
当前可抽取学生: {{ filteredStudents.length }}人
-
-
+
+
-
+
-
-
+
+
-
-
抽取结果
+
+
+ 抽取结果
+
-
+
-
+
请求频率超限
- 您的请求过于频繁,请稍后再试。
+
+ 您的请求过于频繁,请稍后再试。
+
-
+
-
+
等待时间:
{{
@@ -27,12 +44,11 @@
{{ request.method }} {{ request.path }}
-
+
+ />
@@ -42,8 +58,14 @@
-
- 我知道了
+
+
+ 我知道了
+
diff --git a/src/components/ReadOnlyTokenWarning.vue b/src/components/ReadOnlyTokenWarning.vue
index 839a1d6..fa64582 100644
--- a/src/components/ReadOnlyTokenWarning.vue
+++ b/src/components/ReadOnlyTokenWarning.vue
@@ -9,7 +9,7 @@
@click:close="dismissed = true"
>
-
+
当前使用只读 Token
diff --git a/src/components/SettingsCard.vue b/src/components/SettingsCard.vue
index c78143e..5843a55 100644
--- a/src/components/SettingsCard.vue
+++ b/src/components/SettingsCard.vue
@@ -1,5 +1,8 @@
-
+
- {{ title }}
+
+ {{ title }}
+
@@ -18,11 +23,14 @@
color="primary"
indeterminate
/>
-
+
-
-
+
+
diff --git a/src/components/SettingsLinkGenerator.vue b/src/components/SettingsLinkGenerator.vue
index 35e163a..6a5239c 100644
--- a/src/components/SettingsLinkGenerator.vue
+++ b/src/components/SettingsLinkGenerator.vue
@@ -1,9 +1,16 @@
-
+
-
+
统一链接生成器
@@ -13,15 +20,23 @@
-
+
- mdi-account-key
+
+ mdi-account-key
+
预配置认证信息
-
+
-
+
- 预配置信息:
-
- mdi-identifier
+
+ 预配置信息:
+
+
+
+ mdi-identifier
+
命名空间: {{ preconfigForm.namespace }}
- mdi-lock
+
+ mdi-lock
+
认证码: {{ preconfigForm.authCode.length > 8 ? preconfigForm.authCode.substring(0, 8) + "..." :
- preconfigForm.authCode }}
+ preconfigForm.authCode }}
-
- mdi-lock-open
+
+
+ mdi-lock-open
+
无认证码
- {{
- preconfigForm.autoExecute ? "mdi-play-circle" : "mdi-hand-back-left"
+
+ {{
+ preconfigForm.autoExecute ? "mdi-play-circle" : "mdi-hand-back-left"
}}
{{ preconfigForm.autoExecute ? "自动认证" : "手动认证" }}
@@ -99,9 +146,14 @@
-
+
- mdi-cog-transfer
+
+ mdi-cog-transfer
+
设置分享(可选)
@@ -152,7 +204,10 @@
-
+
已选 {{ selectedItems.length }} 项设置
@@ -183,7 +238,9 @@
- {{ expanded ? 'mdi-chevron-up' : 'mdi-chevron-down' }}
+
+ {{ expanded ? 'mdi-chevron-up' : 'mdi-chevron-down' }}
+
显示设置列表详情
@@ -261,9 +318,14 @@
-
+
- mdi-link
+
+ mdi-link
+
生成的统一链接
@@ -317,10 +379,20 @@
type="success"
variant="tonal"
>
- 链接包含内容:
+
+ 链接包含内容:
+
-
- mdi-account-key
+
+
+ mdi-account-key
+
预配置认证
- mdi-cog
+
+ mdi-cog
+
{{ selectedItems.length }} 项设置
-
- mdi-cog-off
+
+
+ mdi-cog-off
+
无额外设置
@@ -341,8 +427,13 @@
-
- ⚠️ 安全提醒
+
+
+ ⚠️ 安全提醒
+
- 认证码和设置信息会在URL中传输,请谨慎分发
- 建议仅在受信任的网络环境中使用
@@ -509,6 +600,45 @@ export default {
},
},
+ watch: {
+ // 监听选择变化,自动生成统一链接
+ selectedItems: {
+ handler() {
+ if (this.preconfigForm.namespace.trim()) {
+ this.generateUnifiedLink();
+ }
+ },
+ deep: true,
+ },
+
+ // 监听预配置表单变化,自动生成统一链接
+ "preconfigForm.namespace": {
+ handler() {
+ if (this.preconfigForm.namespace.trim()) {
+ this.generateUnifiedLink();
+ } else {
+ this.unifiedLink = "";
+ }
+ },
+ },
+
+ "preconfigForm.authCode": {
+ handler() {
+ if (this.preconfigForm.namespace.trim()) {
+ this.generateUnifiedLink();
+ }
+ },
+ },
+
+ "preconfigForm.autoExecute": {
+ handler() {
+ if (this.preconfigForm.namespace.trim()) {
+ this.generateUnifiedLink();
+ }
+ },
+ },
+ },
+
methods: {
/**
* 处理表格选择变化
@@ -709,7 +839,7 @@ export default {
// 转换为JSON并进行base64编码
const jsonString = JSON.stringify(configObj);
- const utf8Encoder = new TextEncoder();
+ const utf8Encoder = new window.TextEncoder();
const utf8Bytes = utf8Encoder.encode(jsonString);
const base64String = btoa(
Array.from(utf8Bytes)
@@ -782,44 +912,5 @@ export default {
this.linkCopied = false;
},
},
-
- watch: {
- // 监听选择变化,自动生成统一链接
- selectedItems: {
- handler() {
- if (this.preconfigForm.namespace.trim()) {
- this.generateUnifiedLink();
- }
- },
- deep: true,
- },
-
- // 监听预配置表单变化,自动生成统一链接
- "preconfigForm.namespace": {
- handler() {
- if (this.preconfigForm.namespace.trim()) {
- this.generateUnifiedLink();
- } else {
- this.unifiedLink = "";
- }
- },
- },
-
- "preconfigForm.authCode": {
- handler() {
- if (this.preconfigForm.namespace.trim()) {
- this.generateUnifiedLink();
- }
- },
- },
-
- "preconfigForm.autoExecute": {
- handler() {
- if (this.preconfigForm.namespace.trim()) {
- this.generateUnifiedLink();
- }
- },
- },
- },
};
diff --git a/src/components/StudentNameManager.vue b/src/components/StudentNameManager.vue
index fe8742c..4973592 100644
--- a/src/components/StudentNameManager.vue
+++ b/src/components/StudentNameManager.vue
@@ -35,7 +35,6 @@
-
-
+
+
diff --git a/src/components/announcement/AnnouncementSettingsDialog.vue b/src/components/announcement/AnnouncementSettingsDialog.vue
new file mode 100644
index 0000000..85221dd
--- /dev/null
+++ b/src/components/announcement/AnnouncementSettingsDialog.vue
@@ -0,0 +1,579 @@
+
+
+
+
+
+ 公告板设置
+
+
+
+
+
+
+
+
+
+
+
+ 公告板设置
+
+
+ 设置定时弹出的公告和提示音
+
+
+
+
+
+
+
+
+
+
+ 定时公告列表
+
+
+ 添加公告
+
+
+
+
+ 暂无定时公告
+
+
+
+
+
+ {{ note.time }}
+
+
+ {{ note.content || '无内容' }}
+
+
+ {{ note.duration }}秒
+
+
+
+
+
+
+
+
+
+
+ 关闭
+
+
+
+
+
+
+
+
+ {{ isEditing ? '编辑公告' : '添加公告' }}
+
+
+ 使用帮助
+
+
+
+
+
+
+
+ 时间 (HH:mm:ss)
+
+
+
+
+
+ 持续时间 (秒, 0为不自动关闭)
+
+
+
+
+
+
+
+
+
编辑内容 (Markdown)
+
+
+
+
+
+
+
+
+ ', '')"
+ />
+
+
+
+
+
+
+ 预览区域
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+
+
+ 保存
+
+
+
+
+
+
+
+
+
+
+ mdi-school
+
+ 如何使用公告板
+
+
+
+
+
+
+
+
+ mdi-clock-outline
+
+ 基础设置
+
+
+
+
+ 设定时间
+
+
+ 设置公告自动弹出的时间(24小时制)。到达该时间时,全屏会显示此公告。
+
+
+
+
+
+ 持续时间
+
+
+ 公告显示的秒数。设为 0 则不会自动关闭,需要手动点击屏幕关闭。
+
+
+
+
+
+
+
+ mdi-format-paint
+
+ 排版与美化
+
+
+
+
+ 加粗 / 斜体
+
+
+ 选中文字点击按钮,强调重要信息
+
+
+
+
+
+ 大标题 / 小标题
+
+
+ 使用 H1/H2 区分不同板块
+
+
+
+
+
+ 高亮标记 (Mark)
+
+
+ 先选中文字,再点击颜色按钮,会自动包裹。
+
+
+
+
+
+ 分割线
+
+
+ 插入水平线分隔内容
+
+
+
+
+
+
+
+
+
+ 实时预览:右侧窗口会实时显示最终效果,所见即所得。
+
+
+
+ 我学会了
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/announcement/AnnouncementWidget.vue b/src/components/announcement/AnnouncementWidget.vue
new file mode 100644
index 0000000..532a37a
--- /dev/null
+++ b/src/components/announcement/AnnouncementWidget.vue
@@ -0,0 +1,224 @@
+
+
+
+
+ mdi-bullhorn
+
+ 公告板
+
+
+
+
+
+
+
+
+
+
+ mdi-clock-outline
+
+ {{ note.time }}
+
+
+ NEXT
+
+
+
+
+
+
+
+ 暂无公告安排
+
+
+
+
+
+
+
+
+ mdi-bullhorn-outline
+
+ 公告详情
+
+
+ {{ selectedNote?.time }}
+
+
+
+
+
+
+
+
+ 关闭
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/attendance/AttendanceManagementDialog.vue b/src/components/attendance/AttendanceManagementDialog.vue
index 33b904b..c9756dd 100644
--- a/src/components/attendance/AttendanceManagementDialog.vue
+++ b/src/components/attendance/AttendanceManagementDialog.vue
@@ -6,25 +6,42 @@
max-width="900"
@update:model-value="$emit('update:modelValue', $event)"
>
-
+
-
+
考勤
-
- {{ dateString }}
-
-
+
+ 修改名单
+
+
-
+
-
+
@@ -137,11 +157,13 @@
class="mr-2"
size="24"
>
-
{{ getStudentStatusIcon(student) }}
+
+ {{ getStudentStatusIcon(student) }}
- {{ student }}
+
+ {{ student }}
+
@@ -183,10 +205,19 @@
-
-
+
+
- 批量操作
+
+ 批量操作
+
-
-
@@ -280,8 +278,8 @@
也可以手动前往 Classworks KV 控制台获取认证信息:
@@ -293,13 +291,13 @@
- 请访问 {{ kvserverurl=='https://kv.houlang.cloud'? 'Classworks KV' : '自定义的 Classworks KV 实例 ' }} 控制台
+ 请访问 Classworks KV 控制台
- {{ kvserverurl }}
+ {{ kvserverurl || '未配置' }}
- {{ kvserverurl=='https://kv.houlang.cloud'? '此实例由 Classworks KV 官方提供' : '此链接由您的实例、预配代码或管理员管理,当前可能不是 Classworks KV 官方的实例地址。' }}
+ 此链接由您的实例、预配代码或管理员管理。
@@ -553,7 +551,7 @@
上一步
-
+
-
+
系统将自动为您创建设备并获取访问令牌,无需手动配置
@@ -52,7 +52,7 @@
variant="tonal"
>
-
+
设备注册成功!已自动获取访问令牌
@@ -60,7 +60,7 @@
-
+
设备名称
{{ deviceInfo.deviceName }}
@@ -68,7 +68,7 @@
-
+
设备 UUID
@@ -83,7 +83,7 @@
variant="tonal"
>
-
+
您可以点击下方按钮访问云端控制台来设置密码和管理高级功能
@@ -97,7 +97,7 @@
variant="tonal"
>
-
+
{{ errorMessage }}
@@ -105,7 +105,7 @@
-
+
-
+
-
-
+
+
diff --git a/src/components/common/HomeSkeleton.vue b/src/components/common/HomeSkeleton.vue
index 117d790..82c4a3c 100644
--- a/src/components/common/HomeSkeleton.vue
+++ b/src/components/common/HomeSkeleton.vue
@@ -1,6 +1,9 @@
-
+
-
-
-
-
-
出现了错误
-
-
404
+
+
+
+ mdi-alert-circle-outline
+
-
+
+
+ 出现了错误
+
+
+
+ 404
+
+
+
+
@@ -21,15 +35,19 @@
variant="outlined"
>
-
+
- 为什么会出现此错误?
+
+ 为什么会出现此错误?
+
- 大概是页面未找到
+
+ 大概是页面未找到
+
{{ exam?.examName || "加载中..." }}
-
-
+
+
+ />
@@ -26,7 +32,11 @@
-->
-
+
@@ -39,7 +49,10 @@
'text-grey': isPast(info.end),
}"
>
-
+
{{ info.name }}
-
无法加载
+
+ 无法加载
+
diff --git a/src/components/home/ExamScheduleCard.vue b/src/components/home/ExamScheduleCard.vue
index ab93c38..f1f52e5 100644
--- a/src/components/home/ExamScheduleCard.vue
+++ b/src/components/home/ExamScheduleCard.vue
@@ -1,15 +1,35 @@
-
+
- mdi-calendar-clock
+
+ mdi-calendar-clock
+
{{ exam?.examName || '加载中...' }}
-
-
+
+
-
-
-
+
+
+
@@ -24,12 +44,24 @@
{{ exam.message }}
-
-
+
+
-
- {{ info.name.charAt(0) }}
-
+
+ {{ info.name.charAt(0) }}
+
@@ -38,20 +70,40 @@
- mdi-clock-start
- {{ formatTime(info.start) }}
+
+ mdi-clock-start
+
+ {{ formatTime(info.start) }}
- mdi-clock-end
- {{ formatTime(info.end) }}
+
+ mdi-clock-end
+
+ {{ formatTime(info.end) }}
-
-
mdi-alert-circle-outline
+
+
+ mdi-alert-circle-outline
+
无法加载考试信息
diff --git a/src/components/home/HomeActions.vue b/src/components/home/HomeActions.vue
index f517616..f9fec13 100644
--- a/src/components/home/HomeActions.vue
+++ b/src/components/home/HomeActions.vue
@@ -10,7 +10,12 @@
>
上传
-
+
同步完成
-
+
屏幕保护技术已启用
diff --git a/src/components/home/HomeworkGrid.vue b/src/components/home/HomeworkGrid.vue
index 8051c2d..b35406f 100644
--- a/src/components/home/HomeworkGrid.vue
+++ b/src/components/home/HomeworkGrid.vue
@@ -1,5 +1,8 @@
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
- 出勤统计
-
-
-
- 应到/实到
-
- {{ item.data.total - item.data.exclude.length }}/{{
- item.data.total -
- item.data.absent.length -
- (!getSetting("display.lateStudentsArePresent")) * item.data.late.length -
- item.data.exclude.length
- }}
-
-
-
-
-
-
请假 ({{ item.data.absent.length }})
-
-
- {{ name }}
-
-
-
-
-
-
迟到 ({{ item.data.late.length }})
-
-
- {{ name }}
-
-
-
-
-
-
不参与 ({{ item.data.exclude.length }})
-
-
- {{ name }}
-
-
-
-
-
- 全勤
-
-
-
-
-
+
{{ item.name }}
@@ -123,23 +59,96 @@
- {{ item.name }}
-
-
-
+ {{ item.name }}
+
+
+
+ {{ line.text }}
+
+
- {{ text }}
-
-
+ {{ formatSubmissionTime(line.submissionTime).replace('上交|', '') }}
+ 上交
+
+
+
+
+ 交给: {{ line.submitTo }}
+
+
+
+ 选做
+
+
+
+
+ 不上交
+
+
+
+
+ 自批
+
+
@@ -149,7 +158,10 @@
-
+
-
+
{{ isReadOnlyToken ? 'mdi-cancel' : 'mdi-plus' }}
{{ subject.name }}
@@ -166,7 +181,10 @@
-
+
-
+
-
+
{{ subject.name }}
- mdi-cancel
- 当日无作业
+
+ mdi-cancel
+
+
+ 当日无作业
+
- mdi-plus
- 点击添加作业
+
+ mdi-plus
+
+
+ 点击添加作业
+
@@ -210,20 +246,13 @@
diff --git a/src/components/settings/cards/EditSettingsCard.vue b/src/components/settings/cards/EditSettingsCard.vue
index 37103b3..f4569a7 100644
--- a/src/components/settings/cards/EditSettingsCard.vue
+++ b/src/components/settings/cards/EditSettingsCard.vue
@@ -1,28 +1,30 @@
-
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
+
+
diff --git a/src/components/settings/cards/HomeworkTemplateCard.vue b/src/components/settings/cards/HomeworkTemplateCard.vue
index 4339766..4f148d7 100644
--- a/src/components/settings/cards/HomeworkTemplateCard.vue
+++ b/src/components/settings/cards/HomeworkTemplateCard.vue
@@ -48,8 +48,15 @@
-
-
+
+
-
-
+
+
-
+
addBookType(subject)"
/>
-
+
-
-
+
+
-
+
-
-
+
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
{{ dialog.title }}
@@ -235,22 +276,43 @@
/>
-
- 所属科目
- {{ dialog.editedItem.subject }}
+
+
+ 所属科目
+
+
+ {{ dialog.editedItem.subject }}
+
-
+
- 需完成部分
+
+ 需完成部分
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
数据库状态
{{ connectionStatus }}
-
+
刷新
-
+
-
+
数据条目
共 {{ kvData.length }} 条记录
-
+
加载数据
-
-
+
+
新建
-
+
从本地迁移
@@ -43,11 +70,18 @@
-
+
-
+
KV数据列表
-
+
-
+
-
+
-
+
查看数据
-
-
+
+
@@ -132,12 +179,21 @@
-
-
-
+
+
+
复制数据
-
+
关闭
@@ -145,13 +201,23 @@
-
+
-
+
编辑数据
-
-
+
+
@@ -171,8 +237,11 @@
-
-
+
+
取消
-
+
-
+
新建数据
-
-
+
+
@@ -215,15 +294,18 @@
:error-messages="isValidNewJson ? [] : ['请输入有效的JSON格式']"
class="font-monospace"
label="数据内容 (JSON格式)"
- placeholder='请输入JSON数据,如:{"name": "value"}'
+ placeholder="请输入JSON数据,如:{"name": "value"}"
rows="15"
variant="outlined"
/>
-
-
+
+
取消
-
+
-
+
获取云端访问地址
-
-
+
+
@@ -254,19 +346,43 @@
-
+
{{ cloudUrlError }}
-
+
云端地址获取成功
-
-
+
+
数据已从本地迁移到云端
-
@@ -283,10 +399,16 @@
@click:append-inner="copyCloudUrl"
/>
-
+
-
+
高级选项
@@ -307,7 +429,10 @@
variant="tonal"
@click="refreshCloudUrl"
>
-
+
重新获取
@@ -316,8 +441,11 @@
-
-
+
+
关闭
-
+
在新窗口打开
@@ -334,24 +465,37 @@
-
+
-
+
确认删除
确定要删除键名为 {{ itemToDelete?.key }} 的数据吗?
-
+
此操作不可撤销,请谨慎操作!
-
-
+
+
取消
-
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/src/components/settings/cards/RefreshSettingsCard.vue b/src/components/settings/cards/RefreshSettingsCard.vue
index 9dabc08..e5725b9 100644
--- a/src/components/settings/cards/RefreshSettingsCard.vue
+++ b/src/components/settings/cards/RefreshSettingsCard.vue
@@ -1,13 +1,21 @@
-
+
-
-
+
+
-
+
-
diff --git a/src/components/settings/cards/ServerSettingsCard.vue b/src/components/settings/cards/ServerSettingsCard.vue
index 7758ae5..1b1644f 100644
--- a/src/components/settings/cards/ServerSettingsCard.vue
+++ b/src/components/settings/cards/ServerSettingsCard.vue
@@ -118,6 +118,7 @@
import SettingsCard from "@/components/SettingsCard.vue";
import CloudNamespaceInfoCard from "./CloudNamespaceInfoCard.vue";
import {getSetting, setSetting, watchSettings} from "@/utils/settings";
+import dataProvider from "@/utils/dataProvider";
export default {
name: "ServerSettingsCard",
@@ -132,7 +133,7 @@ export default {
serverSettings: {
provider: getSetting("server.provider"),
domain: getSetting("server.domain"),
- classNumber: getSetting("server.classNumber"),
+ classNumber: "加载中...",
kvToken: getSetting("server.kvToken"),
},
// 用于监听设置变化时刷新 UI
@@ -186,19 +187,41 @@ export default {
},
methods: {
// 从全局设置加载所有设置到本地
- loadAllSettings() {
+ async loadAllSettings() {
this.serverSettings = {
provider: getSetting("server.provider"),
domain: getSetting("server.domain"),
- classNumber: getSetting("server.classNumber"),
+ classNumber: "加载中...",
kvToken: getSetting("server.kvToken"),
};
+
+ try {
+ const res = await dataProvider.loadData("classworks-config-basic");
+ if (res && res.success !== false && res.classNumber) {
+ this.serverSettings.classNumber = res.classNumber;
+ } else {
+ this.serverSettings.classNumber = "我的班级";
+ }
+ } catch (e) {
+ this.serverSettings.classNumber = "我的班级";
+ }
},
// 保存所有本地设置到全局
saveAllSettings() {
Object.entries(this.serverSettings).forEach(([key, value]) => {
const settingKey = `server.${key}`;
+
+ if (key === 'classNumber') {
+ // Sync to server data so it's consistent
+ dataProvider.saveData("classworks-config-basic", { classNumber: value }).then(() => {
+ console.log(`班级名称已同步到服务器: ${value}`);
+ }).catch(e => {
+ console.error("无法保存班级到服务器配置", e);
+ });
+ return; // Skip saving classNumber to localStorage
+ }
+
const currentValue = getSetting(settingKey);
// 只有当值发生变化时才进行设置
diff --git a/src/components/settings/cards/SubjectManagementCard.vue b/src/components/settings/cards/SubjectManagementCard.vue
index ab6f6f7..654d737 100644
--- a/src/components/settings/cards/SubjectManagementCard.vue
+++ b/src/components/settings/cards/SubjectManagementCard.vue
@@ -58,10 +58,16 @@
-
+
-
+
-
+
-
+
({
+ this.subjects = dataArray.map((subject, index) => ({
name: subject.name,
order: subject.order ?? index
})).sort((a, b) => a.order - b.order);
@@ -203,7 +213,9 @@ export default {
// 数据不存在,使用空数组
this.subjects = [];
this.originalSubjects = [];
- this.showMessage('使用默认配置', 'info');
+ if (response && response.success === false) {
+ this.showMessage('未找到已有配置,使用默认配置', 'info');
+ }
}
} catch (error) {
console.error('Failed to load config:', error);
diff --git a/src/components/settings/cards/ThemeSettingsCard.vue b/src/components/settings/cards/ThemeSettingsCard.vue
index 2fe3c9b..82b71c3 100644
--- a/src/components/settings/cards/ThemeSettingsCard.vue
+++ b/src/components/settings/cards/ThemeSettingsCard.vue
@@ -1,9 +1,15 @@
-
+
-
+
主题模式
选择明亮或暗黑主题
@@ -14,11 +20,17 @@
density="comfortable"
>
-
+
明亮
-
+
暗黑
@@ -37,6 +49,11 @@ export default {
name: 'ThemeSettingsCard',
components: {SettingsCard},
+ setup() {
+ const theme = useTheme();
+ return {theme};
+ },
+
data() {
return {
localTheme: getSetting('theme.mode')
@@ -50,11 +67,6 @@ export default {
}
},
- setup() {
- const theme = useTheme();
- return {theme};
- },
-
methods: {
updateTheme(mode) {
this.theme.global.name.value = mode;
diff --git a/src/main.js b/src/main.js
index 46d581a..1ae1254 100644
--- a/src/main.js
+++ b/src/main.js
@@ -27,35 +27,3 @@ app.component('GlobalMessage', GlobalMessage)
app.mount('#app')
// ====== 以下全部异步,不阻塞首屏渲染 ======
-
-// 异步初始化 Sentry(延迟到首帧渲染完成后,防止 errorHandler 与渲染周期冲突)
-setTimeout(() => {
- import('./utils/sentry').then(({ initSentry }) => {
- const router = app.config.globalProperties.$router
- initSentry(app, router)
- }).catch((err) => {
- console.warn('Sentry 初始化失败:', err)
- })
-}, 1000)
-
-// 异步加载 Clarity(在页面完全加载后)
-const loadClarity = async () => {
- try {
- const { getVisitorId } = await import('./utils/visitorId')
- const Clarity = (await import('@microsoft/clarity')).default
- Clarity.init('rhp8uqoc3l')
-
- const visitorId = await getVisitorId()
- console.log('Visitor ID:', visitorId)
- Clarity.identify(visitorId)
- Clarity.setTag('fingerprintjs', visitorId)
- } catch (error) {
- console.warn('Clarity 加载或标识设置失败:', error)
- }
-}
-
-if (document.readyState === 'complete') {
- loadClarity()
-} else {
- window.addEventListener('load', loadClarity, { once: true })
-}
diff --git a/src/pages/404.vue b/src/pages/404.vue
index f599be7..74e17c4 100644
--- a/src/pages/404.vue
+++ b/src/pages/404.vue
@@ -1,5 +1,5 @@
-
+
diff --git a/src/pages/debug-init.vue b/src/pages/debug-init.vue
index 0a9a87e..6154ee7 100644
--- a/src/pages/debug-init.vue
+++ b/src/pages/debug-init.vue
@@ -26,7 +26,7 @@
label="server.authDomain"
/>
-
+
{{ currentDataKey }}
-
+
-
+
-
- 聊天室消息
+
+
+ 聊天室消息
+
-
+
事件日志
-
+
- 访客 ID
+
+ 访客 ID
+
{{ visitorId || '加载中...' }}
-
+
Refresh
diff --git a/src/pages/exam-editor/[id].vue b/src/pages/exam-editor/[id].vue
index ed6cb88..c1b5d98 100644
--- a/src/pages/exam-editor/[id].vue
+++ b/src/pages/exam-editor/[id].vue
@@ -11,7 +11,7 @@
编辑考试配置
-
+
-
-
-
-
-
- mdi-calendar-check
- 考试看板
-
-
- 不只是考试看板。
-
-
-
-
-
- mdi-alert-circle
- {{ error }}
-
-
-
-
-
-
- mdi-check-circle
- {{ success }}
-
-
-
-
-
-
-
- 新建配置
-
-
- 导入配置
-
-
- AI生成
-
-
- 刷新
-
-
-
- {{ configs.length }} 个配置
-
-
-
-
-
-
-
-
-
-
-
-
-
- mdi-format-list-bulleted
- 配置列表
-
-
-
-
-
- mdi-calendar-text
-
-
-
-
- {{ config.examName || `配置 ${config.id}` }}
-
-
-
- mdi-information-outline
- {{ config.message || '无描述' }}
-
-
- mdi-book-multiple
- {{ config.examInfos ? config.examInfos.length : 0 }} 堂考试
-
-
-
-
-
-
- mdi-pencil
-
-
-
- mdi-eye
-
-
-
-
-
-
-
-
-
-
-
-
-
- mdi-calendar-blank
-
- 暂无配置
-
- 点击"新建配置"按钮创建您的第一个考试配置
-
-
- 新建配置
-
-
-
-
-
-
-
-
-
-
-
-
- mdi-rename-box
- 重命名配置
-
-
-
-
-
-
-
- 取消
-
-
- 确认
-
-
-
-
-
-
-
-
-
- mdi-pencil
- 编辑考试配置
-
-
- ID: {{ editingConfig.id }}
-
-
-
- mdi-close
-
-
-
-
-
-
-
- 关闭
-
-
-
- 保存配置
-
-
-
-
-
-
-
-
-
- mdi-import
- 导入考试配置
-
-
- mdi-close
-
-
-
-
- {{ importError }}
-
-
-
-
-
-
-
提示:
-
- - 日期格式支持: YYYY/MM/DD HH:mm 或 YYYY-MM-DD HH:mm:ss
- - 虚拟日期格式: 0000-00-01 (表示第1天), 0000-00-02 (第2天)...
- - 如使用虚拟日期,系统会要求您指定起始日期
- - 缺省字段将自动填充默认值
-
-
-
-
-
-
- 取消
-
-
-
- 导入
-
-
-
-
-
-
-
-
-
- mdi-calendar
- 选择起始日期
-
-
-
- 检测到配置中使用了虚拟日期格式 (0000-00-XX),请选择第一天的日期,系统将自动推算其他日期。
-
-
-
-
-
-
- 检测到 {{ virtualDateInfo.count }} 个虚拟日期,跨度 {{ virtualDateInfo.span }} 天
-
-
-
-
-
- 取消
-
-
-
- 确认
-
-
-
-
-
-
-
-
-
- mdi-brain
- AI生成考试配置
-
-
- mdi-close
-
-
-
-
-
-
- 复制下方提示词到任意AI工具(如ChatGPT、Claude、Copilot等),描述您的考试安排,AI将生成符合格式的JSON配置。生成后复制JSON内容,通过“导入配置”按钮导入即可。
-
-
-
-
-
-
-
提示词模板
-
- {{ copied ? '已复制' : '复制提示词' }}
-
-
-
-
- {{ aiPrompt }}
-
-
-
-
-
-
-
- 关闭
-
-
-
- 去导入配置
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/index.vue b/src/pages/index.vue
index b0494d8..e4b96de 100644
--- a/src/pages/index.vue
+++ b/src/pages/index.vue
@@ -1,227 +1,809 @@
-
-
+
+
+
+
+
+ ClassHomeWorkBoard 班级作业看板
+
+
+
+
+
+ ClassHomeWorkBoard
+
+
+ 本项目由 高二七班(原12班)李昊睿 制作
+
+
+ 同时在宝一外提供技术支持!
+
+
+
+
+
+ 进入看板 ({{ splashCountdown }}s)
+
+
+
+
+
+
+
{{ titleText }}
-
-
- 只读
-
-
-
-
- {{ tokenDisplayInfo.text }}
-
-
发送通知
-
-
-
+ prepend-icon="mdi-help-circle-outline"
+ class="mr-2"
+ @click="showHelpDialog = true"
+ >
+ 我不会用
+
+
+
+ mdi-bell
+
+
+ mdi-bell-outline
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
+
+
+ {{ notification.message }}
+
+
+
+
+
+
- {{ notification.message }}
-
-
-
-
-
-
+
-
-
-
-
-
-
- {{ currentNotification.isUrgent ? '强调通知' : '通知详情' }}
-
-
-
-
-
-
-
-
-
- {{ currentNotification.message }}
-
-
- 发布时间:{{ formatTime(currentNotification.timestamp) }}
-
-
-
-
-
-
+
- 删除通知
+ 随机点名
-
+
- 关闭
+ 全屏显示
-
-
-
-
+
+ 抄笔记
+
+
+
+ 公告设置
+
-
+
-
+
+ 我不会使用
+
+
-
-
-
-
检测到未来两天内有以下考试:
-
- {{ exam.examName }}
-
+ 若需要抄写笔记等PPT或图片内容。欢迎使用“抄笔记”模式并将PPT投影至页面。
+
+
+
+
+
-
-
- 一键添加
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ 点击添加PPT/图片/窗口捕获
+
+
+
+ 添加图片/PPT
+
+
+ 窗口捕获
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mdi-file-question
+
+
+ {{ noteSourceContent.name }}
+
+
+ 该文件无法直接预览
+
+
+ 下载文件
+
+
+
+
+
+
+
+
+
+
+
+ mdi-presentation-play
+
+
+ mdi-stop-circle
+
+
+ mdi-chevron-left
+
+
+ mdi-chevron-right
+
+
+
+
+
+
+ mdi-chevron-left
+
+ {{ notePage + 1 }} / {{ noteSourceContent.length }}
+
+ mdi-chevron-right
+
+
+
+
+
+
+ mdi-magnify-minus
+
+ {{ Math.round(noteZoom * 100) }}%
+
+ mdi-magnify-plus
+
+
+
+
+
+
+ mdi-close
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mdi-format-list-checks
+
+ 今日作业量: {{ totalHomeworkCount }} 项
+
+
+
+ mdi-close
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
{{ state.snackbarText }}
-
-
@@ -254,38 +830,16 @@
@refresh="downloadData"
@zoom="zoom"
@open-messages="$refs.messageLog.drawer = true"
- @open-settings="$router.push('/settings')"
@date-select="handleDateSelect"
@prev-day="navigateDay(-1)"
@next-day="navigateDay(1)"
@copy-to-today="copyHomeworkToToday"
/>
-
-
-
-
-
-
-
-
-
-
-
-
- 确认保存
-
- 您正在修改 {{ state.dateString }} 的数据,确定要保存吗?
-
-
-
-
- 取消
-
- 确认保存
-
-
-
+
+
-
-
-
- 确认应用URL配置
-
- 以下配置将应用于当前班级:
-
-
+
+
+
+
+ mdi-school
+
+ 如何使用班级作业看板
+
+
+
+
+
+
-
-
-
-
- {{ change.name }}
- {{ change.description || change.key }}
-
-
-
- {{ change.oldValue }}
-
- {{
- change.newValue
- }}
-
-
-
+
+
+ mdi-pencil-box-outline
+
+ 基础操作流程
+
+
+
+
+
+ 1. 打开作业卡片
+
+
+ 点击对应科目(如“语文”、“数学”)的作业卡片,即可打开编辑窗口。
+
+
+
+
+
+ 2. 输入作业内容
+
+
+ 在文本框中输入具体的作业内容。如果有多项作业,建议分条目记录。
+
+
+
+
+
+ 3. 使用快捷标签
+
+
+ 点击下方的快捷标签(如“卷”、“A”、“正面”)可快速将常用词汇添加到当前光标位置。
+
+
+
+
+
+ 4. 自动保存
+
+
+ 编辑完成后,直接点击空白处或右上角的关闭按钮,内容将自动保存并同步。
+
+
+
+
+
+
+
+
+ mdi-star-outline
+
+ 高级功能指南
+
+
+
+
+
+ mdi-clock-outline
+
+
+
+
+ 设置上交时间
+
+
+ 点击“上交时间”按钮,在日历中选择日期,系统会自动计算并显示对应的时间和星期(如:明天、周五)。
+
+
+
+
+
+
+
+ mdi-check-circle-outline
+
+
+
+
+ 自主批改/不上交标记
+
+
+ 点击“自主批改”按钮,该条目会变绿并显示
+ mdi-check-circle
+ 图标。点击“不上交”按钮,该条目会显示黄色“不上交”标签,并自动清除截止时间。
+
+
+
+
+
+
+
+ mdi-account-arrow-right
+
+
+
+
+ 指定收交人
+
+
+ 点击“交给谁”按钮,可从学生名单中指定负责收该项作业的课代表,让同学们明确上交去向。
+
+
+
+
+
+
+
+ mdi-playlist-plus
+
+
+
+
+ 多条目管理
+
+
+ 如果同一科目有多项作业(如:一张试卷 + 一页练习册),请点击“添加条目”分开记录,清晰明了。
+
+
+
+
+
+
+
+ mdi-notebook-outline
+
+
+
+
+ 抄笔记模式
+
+
+ 点击“抄笔记”按钮进入专注模式。左侧可导入PPT/图片或捕获窗口,右侧显示今日作业列表。作业列表会自动滚动,方便查看。
+
+
+
+
+
+
+
+ mdi-toolbox-outline
+
+
+
+
+ 班级辅助工具
+
+
+ 主界面上方提供“随机点名”、“全屏显示”等实用工具;右侧边栏可便捷设置班级公告及进行出勤统计。
+
+
+
+
+
+
+
+
+ 小贴士
+
+
+ 所有修改都会实时显示在屏幕上,请确认内容准确。
+
+
+
+
+
+
+
+
+
+ 我学会了
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mdi-presentation-play
+ 发现正在运行的PPT
+
+
+
+ 是否要投放以下内容?
+
+
+
+
+
+
+ mdi-information
+
+ 点击确认后,请在弹出的系统选择框中手动选择该PPT窗口。
+
取消
-
- 确认应用
+
+ 投放内容
-
-
-
-
- 编辑考试配置
-
-
+
+
+
+
+ {{ currentNotification.isUrgent ? '强调通知' : '通知详情' }}
-
-
+
+ {{ currentNotification.message }}
-
-
+
- 移除卡片
+ 删除
-
+
关闭
@@ -378,106 +1235,66 @@
-
-
+
+
- 预览考试看板
-
-
-
-
- mdi-calendar-text
-
-
-
-
-
-
-
- 暂无考试配置
-
-
-
-
- 关闭
-
-
-
-
-
-
-
- {{ currentNotification.isUrgent ? '强调通知' : '通知详情' }}
+
+ 欢迎使用!请设置您的班级
-
- {{ currentNotification.message }}
+
+
+ 这是您第一次启动,请输入您的班级名称。此设置将保存到服务器。
+
+
-
- 删除
-
- 关闭
+
+
+
+ 确定
+
-
+
+
+
+
diff --git a/src/pages/list/[id].vue b/src/pages/list/[id].vue
index b232171..4d6f6bd 100644
--- a/src/pages/list/[id].vue
+++ b/src/pages/list/[id].vue
@@ -7,11 +7,20 @@
@click="$router.push('/')"
/>
- {{ list.name }}
- 列表
+
+ {{ list.name }}
+
+
+ 列表
+
-
{{ list.name }}
-
+
mdi-pencil
-
+
-
+ />
+
mdi-check
-
+
mdi-close
@@ -51,10 +77,14 @@
-
+
项目列表
-
+
-
+
-
+
添加新项目
-
+
列表排序
-
-
+
+
{{ deleteDialog.title }}
{{ deleteDialog.text }}
-
-
+
+
取消
-
+
确认删除
@@ -170,8 +222,14 @@
-
-
+
+
项目详情
编辑项目
@@ -181,13 +239,16 @@
- {{ itemDialog.item.name }}
+
+ {{ itemDialog.item.name }}
{{ itemDialog.item.id }}
- 状态
+
+ 状态
+
- 描述
+
+ 描述
+
{{ itemDialog.item.description }}
-
-
+
+ />
+ />
+ />
-
+
-
+
编辑
-
+
删除
-
+
关闭
-
+
保存
-
+
取消
-
diff --git a/src/pages/list/index.vue b/src/pages/list/index.vue
index 709fdab..acaf6a3 100644
--- a/src/pages/list/index.vue
+++ b/src/pages/list/index.vue
@@ -7,12 +7,16 @@
@click="$router.push('/')"
/>
-
列表
+
+ 列表
+
-
-
-
+
现有列表
暂无列表,请创建新列表
@@ -27,7 +31,10 @@
{{ list.name }}
-
+
-
+ />
+
mdi-check
-
+
mdi-close
-
+
mdi-pencil
-
+
mdi-delete
@@ -58,30 +85,49 @@
-
+
创建新列表
-
+ />
+
创建列表
-
+
删除列表
{{ deleteDialog.text }}
-
-
+
+
取消
-
+
确认删除
@@ -134,8 +180,11 @@ export default {
try {
// 从classworks-list-main加载学生列表数据
const response = await dataProvider.loadData("classworks-list-main");
- if (response && Array.isArray(response)) {
- this.studentList = response;
+
+ const dataArray = Array.isArray(response) ? response : (response && Array.isArray(response.value) ? response.value : null);
+
+ if (response && response.success !== false && dataArray) {
+ this.studentList = dataArray;
} else {
this.studentList = [];
}
diff --git a/src/pages/settings.vue b/src/pages/settings.vue
index 0b720da..4714365 100644
--- a/src/pages/settings.vue
+++ b/src/pages/settings.vue
@@ -11,10 +11,11 @@
icon="mdi-menu"
variant="text"
@click="drawer = !drawer"
-
/>
-
设置
+
+ 设置
+
@@ -43,14 +44,23 @@
direction="vertical"
style="width: 100%"
>
-
-
+
+
-
在寻找 Classworks KV ?
+
+ 在寻找 Classworks KV ?
+
文档形键值数据库
@@ -71,50 +81,53 @@
-
+
Classworks
+ >
+ Classworks
是开源免费的软件,官方没有提供任何形式的付费支持服务,源代码仓库地址在
https://github.com/ZeroCatDev/Classworks。如果您通过有偿协助等付费方式取得本应用,在遇到问题时请在与卖家约定的服务框架下,优先向卖家求助。如果卖家没有提供您预期的服务,请退款或通过其它形式积极维护您的合法权益。
-
+ >https://github.com/ZeroCatDev/Classworks。如果您通过有偿协助等付费方式取得本应用,在遇到问题时请在与卖家约定的服务框架下,优先向卖家求助。如果卖家没有提供您预期的服务,请退款或通过其它形式积极维护您的合法权益。
+
请不要使用浏览器清除缓存功能,否则会导致配置丢失。
- 恶意的操作可能导致您受到贵校教师的处理
-
-
+ 请不要使用浏览器清除缓存功能,否则会导致配置丢失。
+ 恶意的操作可能导致您受到贵校教师的处理
+
+
- 请不要使用包括但不限于360极速浏览器、360安全浏览器、夸克浏览器、QQ浏览器等浏览器使用
- Classworks
- ,这些浏览器过时且存在严重的一致性问题。在Windows上,使用新版
- Microsoft Edge 浏览器是最推荐的选择。
-
+ >
+
+ 请不要使用包括但不限于360极速浏览器、360安全浏览器、夸克浏览器、QQ浏览器等浏览器使用
+ Classworks
+ ,这些浏览器过时且存在严重的一致性问题。在Windows上,使用新版
+ Microsoft Edge 浏览器是最推荐的选择。
+
上述浏览器商标为其所属公司所有,Classworks™
与上述浏览器所属公司无竞争关系。
-
+
下载 Microsoft Edge(微软边缘浏览器)
-
-
+ 下载 Microsoft Edge(微软边缘浏览器)
+
+
-
+
@@ -138,16 +150,32 @@
border
@saved="onSettingsSaved"
/>
-
-
+
+
-
-
+
+
-
+
@@ -191,27 +219,35 @@
-
+
-
-
-
+
+
+
-
-
+
+
-
+
启用开发者选项
- 启用后可以查看和修改开发者设置
-
+
+ 启用后可以查看和修改开发者设置
+
-
+
-
+
所有设置
浏览和修改所有可用设置
-
+
-
+
-
-
+
+
-
+
@@ -319,7 +367,7 @@ export default {
const settings = {
server: {
domain: getSetting("server.domain"),
- classNumber: getSetting("server.classNumber"),
+ classNumber: "加载中...",
provider: getSetting("server.provider"),
},
refresh: {
@@ -488,8 +536,23 @@ export default {
},
},
- mounted() {
+ async mounted() {
this.loadAllSettings();
+
+ try {
+ const dataProvider = (await import("@/utils/dataProvider")).default;
+ const res = await dataProvider.loadData("classworks-config-basic");
+ if (res && res.success !== false && res.classNumber) {
+ this.settings.server.classNumber = res.classNumber;
+ } else {
+ this.settings.server.classNumber = "我的班级";
+ }
+ } catch (e) {
+ this.settings.server.classNumber = "我的班级";
+ }
+
+ this.originalSettings = JSON.parse(JSON.stringify(this.settings));
+
this.unwatchSettings = watchSettings(() => {
this.loadAllSettings();
});
@@ -508,8 +571,49 @@ export default {
window.open(getSetting("server.authDomain"), "_blank");
},
loadAllSettings() {
+ // Keep existing classNumber or set to 加载中
+ const currentClassNumber = this.settings ? this.settings.server.classNumber : "加载中...";
+
+ this.settings = {
+ server: {
+ domain: getSetting("server.domain"),
+ classNumber: currentClassNumber,
+ provider: getSetting("server.provider"),
+ },
+ refresh: {
+ auto: getSetting("refresh.auto"),
+ interval: getSetting("refresh.interval"),
+ },
+ font: {
+ size: getSetting("font.size"),
+ },
+ edit: {
+ autoSave: getSetting("edit.autoSave"),
+ blockNonTodayAutoSave: getSetting("edit.blockNonTodayAutoSave"),
+ confirmNonTodaySave: getSetting("edit.confirmNonTodaySave"),
+ refreshBeforeEdit: getSetting("edit.refreshBeforeEdit"),
+ },
+ display: {
+ emptySubjectDisplay: getSetting("display.emptySubjectDisplay"),
+ dynamicSort: getSetting("display.dynamicSort"),
+ showRandomButton: getSetting("display.showRandomButton"),
+ showFullscreenButton: getSetting("display.showFullscreenButton"),
+ },
+ developer: {
+ enabled: getSetting("developer.enabled"),
+ showDebugConfig: getSetting("developer.showDebugConfig"),
+ },
+ message: {
+ showSidebar: getSetting("message.showSidebar"),
+ maxActiveMessages: getSetting("message.maxActiveMessages"),
+ timeout: getSetting("message.timeout"),
+ saveHistory: getSetting("message.saveHistory"),
+ },
+ };
+
Object.keys(this.settings).forEach((section) => {
Object.keys(this.settings[section]).forEach((key) => {
+ if (section === "server" && key === "classNumber") return;
this.settings[section][key] = getSetting(`${section}.${key}`);
});
});
diff --git a/src/pages/socket-debugger.vue b/src/pages/socket-debugger.vue
index 1e1f288..576d618 100644
--- a/src/pages/socket-debugger.vue
+++ b/src/pages/socket-debugger.vue
@@ -5,10 +5,18 @@
- mdi-network
+
+ mdi-network
+
Socket.IO 连接调试器
-
+
{{ connectionStatus.text }}
@@ -16,7 +24,10 @@
-
+
连接信息
@@ -43,7 +54,10 @@
传输方式
-
+
{{ transport || '未知' }}
@@ -113,40 +127,71 @@
-
+
统计信息
-
+
- {{ stats.eventsReceived }}
- 接收事件
+
+ {{ stats.eventsReceived }}
+
+
+ 接收事件
+
-
+
- {{ stats.eventsSent }}
- 发送事件
+
+ {{ stats.eventsSent }}
+
+
+ 发送事件
+
-
+
- {{ stats.errors }}
- 错误次数
+
+ {{ stats.errors }}
+
+
+ 错误次数
+
-
+
- {{ stats.reconnects }}
- 重连次数
+
+ {{ stats.reconnects }}
+
+
+ 重连次数
+
@@ -175,14 +220,26 @@
-
+
事件监控
- {{ activeListeners.size }} 个监听器
+
+ {{ activeListeners.size }} 个监听器
+
-
+
暂无活动监听器
@@ -193,7 +250,9 @@
:key="listener"
>
- mdi-eye
+
+ mdi-eye
+
{{ listener }}
@@ -203,7 +262,10 @@
-
+
发送自定义事件
@@ -272,9 +334,15 @@
[{{ log.time }}]
[{{ log.type.toUpperCase() }}]
{{ log.message }}
- {{ log.data }}
+ {{ log.data }}
-
@@ -288,7 +356,10 @@
连接诊断
-
+
-
+
-
+
data;
@@ -10,264 +7,32 @@ export const formatError = (message, code = "UNKNOWN_ERROR") => ({
error: {code, message},
});
-// Main data provider with simplified API
+// Main data provider with simplified API - Local File Storage
export default {
// Provider API methods
loadData: async (key) => {
- const provider = getSetting("server.provider");
- const useServer =
- provider === "kv-server" || provider === "classworkscloud";
-
- if (useServer) {
- return kvServerProvider.loadData(key);
- } else {
- return kvLocalProvider.loadData(key);
- }
+ return kvFileProvider.loadData(key);
},
saveData: async (key, data) => {
- const provider = getSetting("server.provider");
- const useServer =
- provider === "kv-server" || provider === "classworkscloud";
-
- if (useServer) {
- return kvServerProvider.saveData(key, data);
- } else {
- return kvLocalProvider.saveData(key, data);
- }
+ return kvFileProvider.saveData(key, data);
},
/**
* 获取键名列表
- * @param {Object} options - 查询选项
- * @param {string} options.sortBy - 排序字段,默认为 "key"
- * @param {string} options.sortDir - 排序方向,"asc" 或 "desc",默认为 "asc"
- * @param {number} options.limit - 每页返回的记录数,默认为 100
- * @param {number} options.skip - 跳过的记录数,默认为 0
- * @returns {Promise