feat: 新建课程作业模块(基于校园公告模板),实现 v1 教师端与 v2 学生/家长端
以 CampusAnnouncement 为模板建立课程作业模块,重命名标识符与文案(notice→homework、公告→作业), 并保留其顶栏/左栏外壳,按课程作业业务重搭数据模型与页面。 - mock(src/assets/mock/homeworkapi.ts):作业化数据模型(学科/截止/提交方式/流程状态), 班级花名册→学生个体提交记录与批改(确定性分布),端点 List/SendList/Submissions/ SubmissionDetail/Grade/Remind/Statis 等;左栏菜单按角色返回。 - v1 教师端:布置作业(新增学科/截止/提交方式)、我布置的(状态标签+提交/批改进度)、 提交情况(Submissions,含内联批改)、批改中心(Grade 逐份批改)、作业统计(成绩分布+进度)。 - v2 学生/家长端(端分离):SetRole + viewerRole 切换身份;StudentHomeworks.vue 承担 今日作业/待订正(学生可提交/重交)与孩子作业(家长只读+提醒);新增 MyHomeworks/Submit/ MySubmission/ParentNudge 接口与类型。 - 类型与 api 模块同步;dev:mock 三角色 curl 端到端验证通过。
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
# 开发环境 (dev):仅 vite dev --mode dev 加载;build 产物不读任何 .env.*
|
||||||
|
# (.env.base/.env.ld/.env.prod 已删,线上由 /api/env-config?target=front 运行时下发)。
|
||||||
|
# ⚠️ VITE_* 仅本地开发生效。
|
||||||
|
# API 走 /api 同源相对路径(vite proxy 转发到本地后端 8101),
|
||||||
|
# 规避后端 CAS 过滤器不回 CORS 头导致的跨域拦截;SSO/基础平台用真实环境
|
||||||
|
VITE_BASE_HOST=http://base.yunhaixingtong.com
|
||||||
|
VITE_SSO_HOST=http://sso.yunhaixingtong.com
|
||||||
|
VITE_SERVICE_HOST=http://base.yunhaixingtong.com/campusnotice/
|
||||||
|
VITE_ZIYUANKU_HOST=https://base.bestsch.com/BschZyk
|
||||||
|
VITE_ZHUANXIANG_HOST=https://base.bestsch.com/BschZncpd
|
||||||
|
VITE_IS_MOCK=false
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# 本地纯前端调试模式:仅 vite dev --mode mock 加载(build 产物不读任何 .env.*,
|
||||||
|
# 线上由 /api/env-config?target=front 运行时下发)。后端 SSO/base 不可达时启用内置 mock
|
||||||
|
# 启动:npm run dev:mock
|
||||||
|
VITE_BASE_HOST=
|
||||||
|
VITE_SSO_HOST=
|
||||||
|
VITE_SERVICE_HOST=http://localhost:8101
|
||||||
|
VITE_ZIYUANKU_HOST=
|
||||||
|
VITE_ZHUANXIANG_HOST=
|
||||||
|
VITE_IS_MOCK=true
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
name: build
|
||||||
|
|
||||||
|
# 前端应用 CI:构建 → 打包 zip → 推 Nexus raw → 通知 projman 建档
|
||||||
|
# 触发:push 到 main / 手动 Run workflow
|
||||||
|
# 链路:clone 源码 -> node 容器 npm ci + npm run build -> 打包 dist 为 zip -> 推 Nexus raw -> 通知 projman 建档
|
||||||
|
# 依赖仓库 Secrets:DEPLOY_TOKEN(webhook 鉴权)、NEXUS_USER / NEXUS_PASSWORD(上传 zip 到 Nexus raw)
|
||||||
|
# 注意:CI 构建后自动建档(status=pending),前端发布仍由用户在「前端发布」Tab 点部署触发
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: docker:24
|
||||||
|
options: --network infra-infra-net
|
||||||
|
steps:
|
||||||
|
- name: 安装 git / curl / zip(alpine)
|
||||||
|
run: apk add --no-cache git curl zip
|
||||||
|
|
||||||
|
# 内网 Gitea 无法访问 GitHub 拉 actions/checkout,手动 clone 本仓库
|
||||||
|
- name: 拉取源码(内网无 GitHub,手动 clone 本仓库)
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
git clone --depth 1 --branch main "http://x-access-token:${GITHUB_TOKEN}@gitea.yunhaixingtong.com/${{ github.repository }}.git" /tmp/src
|
||||||
|
cp -a /tmp/src/. .
|
||||||
|
rm -rf /tmp/src
|
||||||
|
# 取提交备注首行,脱去换行/双引号/反斜杠(避免破坏 webhook JSON),截断到 200 字符
|
||||||
|
COMMIT_MSG=$(git log -1 --pretty=%s | tr '\n' ' ' | sed 's/["\\]/ /g' | cut -c1-200)
|
||||||
|
echo "COMMIT_MSG=${COMMIT_MSG}" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
# 前端构建:起独立 node 容器(通过 docker.sock 调宿主 dockerd)
|
||||||
|
# node:20 镜像预装 npm,npm ci 按 lockfile 安装依赖
|
||||||
|
- name: 构建前端(node 容器)
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
docker rm -f frontbuild 2>/dev/null || true
|
||||||
|
docker run -d --name frontbuild -w /build node:20 sleep 3600
|
||||||
|
docker cp "${PWD}/." frontbuild:/build/
|
||||||
|
docker exec frontbuild sh -c '
|
||||||
|
set -e
|
||||||
|
cd /build
|
||||||
|
if [ -f .npmrc ]; then sed -i "/SASS_BINARY_PATH/d" .npmrc; fi
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
'
|
||||||
|
echo "=== dist 产物 ==="
|
||||||
|
docker exec frontbuild sh -c 'ls -la /build/dist/ | head -20'
|
||||||
|
# 从容器 cp dist 到宿主工作目录
|
||||||
|
docker cp frontbuild:/build/dist ./dist
|
||||||
|
docker rm -f frontbuild
|
||||||
|
echo "BUILD CHECK OK"
|
||||||
|
|
||||||
|
# 打包 dist → zip(vite outDir 已简化为直出 dist/,无中间层;进入 dist 打包,
|
||||||
|
# zip 根目录直接是 index.html/css/js。此前 outDir=dist/base 时曾误打 dist/ 整层,
|
||||||
|
# zip 根只有 base/ 目录,projman 前端发布校验报「zip 根目录缺少 html 文件」)
|
||||||
|
- name: 打包产物
|
||||||
|
run: |
|
||||||
|
TAG=$(echo "$GITHUB_SHA" | cut -c1-8)
|
||||||
|
ZIP_FILE="notice-front-${TAG}.zip"
|
||||||
|
cd dist && zip -r "../${ZIP_FILE}" . && cd ..
|
||||||
|
echo "ZIP_FILE=${ZIP_FILE}" >> "$GITHUB_ENV"
|
||||||
|
echo "TAG=${TAG}" >> "$GITHUB_ENV"
|
||||||
|
ls -la "${ZIP_FILE}"
|
||||||
|
|
||||||
|
# 上传到 Nexus raw 仓库
|
||||||
|
- name: 上传产物到 Nexus
|
||||||
|
env:
|
||||||
|
NEXUS_USER: ${{ secrets.NEXUS_USER }}
|
||||||
|
NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
NEXUS_URL="http://nexus.yunhaixingtong.com:8081/repository/front-hosted/${ZIP_FILE}"
|
||||||
|
curl -sSf -X PUT \
|
||||||
|
-u "${NEXUS_USER}:${NEXUS_PASSWORD}" \
|
||||||
|
--upload-file "${ZIP_FILE}" \
|
||||||
|
"${NEXUS_URL}"
|
||||||
|
echo "NEXUS_URL=${NEXUS_URL}" >> "$GITHUB_ENV"
|
||||||
|
echo "uploaded to ${NEXUS_URL}"
|
||||||
|
|
||||||
|
# 通知 projman:webhook 收到后建档(type=front, status=pending),部署由用户在「前端发布」Tab 手动触发
|
||||||
|
- name: 通知 projman 建档
|
||||||
|
run: |
|
||||||
|
curl -sSf -X POST "http://192.168.10.160:8090/projman/api/deploy/webhook" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-Deploy-Token: ${{ secrets.DEPLOY_TOKEN }}" \
|
||||||
|
-d "{\"app\":\"notice\",\"front\":\"front\",\"tag\":\"${TAG}\",\"sha\":\"${GITHUB_SHA}\",\"message\":\"${COMMIT_MSG}\",\"nexusUrl\":\"${NEXUS_URL}\"}"
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
yarn.lock
|
||||||
|
.npmrc
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
# scratch
|
||||||
|
.temp
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: ["airbnb", "prettier", "prettier/react"],
|
||||||
|
singleQuote: false, // 是否强制使用单引号
|
||||||
|
printWidth: 120, // 超过最大值换行
|
||||||
|
tabWidth: 4,
|
||||||
|
htmlWhitespaceSensitivity: "ignore",
|
||||||
|
semi: true, // 结尾是否使用分号
|
||||||
|
//disableLanguages: ["vue"], // 不格式化vue文件,vue文件的格式化单独设置
|
||||||
|
endOfLine: "auto",
|
||||||
|
arrowParens: "avoid", // 箭头函数参数括号 默认avoid 可选 avoid| always
|
||||||
|
};
|
||||||
|
//如需显示webpack目录,运行以下命令
|
||||||
|
// npm run eject
|
||||||
|
//如需配合使eslint生效,运行以下命令
|
||||||
|
//npm install --save-dev eslint-plugin-prettier
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# 课程作业前端(Vue3 + Vite + view-ui-plus)
|
||||||
|
|
||||||
|
## 本地启动
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1)纯前端调试(推荐,后端不可用时)
|
||||||
|
|
||||||
|
本地连不上 `sso.yunhaixingtong.com` / `base.yunhaixingtong.com` 时,后端 CAS 过滤器会拦截
|
||||||
|
`/api/*` 请求导致所有接口失败。此时使用内置 mock 调试前端页面:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev:mock
|
||||||
|
```
|
||||||
|
|
||||||
|
- 启用 `src/assets/mock/homeworkapi.ts` 中的内存 mock(vite-plugin-mock),覆盖全部页面接口:
|
||||||
|
用户/菜单、发文部门字典、作业增删改查、阅读情况、联系组、选择用户树、附件上传等。
|
||||||
|
- mock 数据为内存态,刷新页面即还原;修改 mock 文件后需重启 dev server 才生效。
|
||||||
|
- mock 模式下请求走 vite 同源(`host.config.ts` 中 `serviceHost` 为空串)。
|
||||||
|
|
||||||
|
### 2)连接后端调试
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
- `.env.dev`:`VITE_SERVICE_HOST=http://localhost:8070/homework`(Java 后端,见 `../service`)。
|
||||||
|
- 后端需要能访问 CAS(`yh.sso.server-url-prefix`)与用户中心(`yh.open.*`),否则接口 302/失败。
|
||||||
|
|
||||||
|
## 主要页面
|
||||||
|
|
||||||
|
| 路由 | 页面 |
|
||||||
|
| --- | --- |
|
||||||
|
| `/homework/newhomework` | 作业发送(新建/编辑 `?serid=`/转发 `?fid=`,wangeditor 富文本) |
|
||||||
|
| `/homework/homework_list` | 已发作业 |
|
||||||
|
| `/homework/homework_accept` | 已收作业(左列表 + 右详情,打印/转发/附件) |
|
||||||
|
| `/homework/manage` | 作业列表(管理端) |
|
||||||
|
| `/homework/setting` | 应用设置(发文部门卡片 + 自定义组) |
|
||||||
|
| `/homework/homework_details` | 作业详情(`type=0` 管理视角含阅读进度/编辑/打印,`type=1` 查看) |
|
||||||
|
|
||||||
|
## 关键组件
|
||||||
|
|
||||||
|
- `components/UserCheckModal.vue`:选择用户弹窗(组织树勾选 + 已选列表,移植自旧前端
|
||||||
|
`oldfront/src/components/userCheck.vue`)。
|
||||||
|
- `components/UserCheckInput.vue`:作业对象选择(全部用户/指定用户,移植自旧
|
||||||
|
`user_check_input.vue`)。
|
||||||
|
- `components/HomeworkUpload.vue`:作业附件上传(选择文件 + 200MB 限制,移植自旧
|
||||||
|
`uploadFile.vue`)。
|
||||||
|
- 发文部门卡片:见 `views/homework/HomeworkSetting.vue`,样式与交互对齐 UserBase「应用类型」
|
||||||
|
(`UserBase/front/src/views/app/AppCategory.vue`),额外支持停用/启用。
|
||||||
|
|
||||||
|
## 统计说明
|
||||||
|
|
||||||
|
统计相关前端已按需求移除(`Homework_statis` 接口封装、菜单、图标等),后期统计不在本应用内做。
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import type { GeneratorConfig } from "@alova/wormhole";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
generator: [
|
||||||
|
{
|
||||||
|
input: "http://base.yunhaixingtong.com/homework/v3/api-docs",
|
||||||
|
// 文档平台类型
|
||||||
|
platform: "swagger",
|
||||||
|
|
||||||
|
// 生成代码的输出目录
|
||||||
|
output: "src/api",
|
||||||
|
// 响应数据的媒体类型
|
||||||
|
responseMediaType: "application/json",
|
||||||
|
// 请求体数据的媒体类型
|
||||||
|
bodyMediaType: "application/json",
|
||||||
|
// 生成的 API 版本
|
||||||
|
version: 1,
|
||||||
|
// 生成代码类型
|
||||||
|
type: "typescript",
|
||||||
|
// 全局 API 名称
|
||||||
|
global: "Apis",
|
||||||
|
splitImplementation: true,
|
||||||
|
implementation: "inline",
|
||||||
|
// API 处理函数
|
||||||
|
handleApi: apiDescriptor => {
|
||||||
|
// 过滤掉已废弃的 API
|
||||||
|
if (apiDescriptor.deprecated) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const newOpId = apiDescriptor.summary?.trim() || apiDescriptor.operationId;
|
||||||
|
const url = apiDescriptor.url.replace(/^\/{platform}\//, "/api/");
|
||||||
|
|
||||||
|
return {
|
||||||
|
...apiDescriptor,
|
||||||
|
operationId: newOpId, // 关键:改掉它
|
||||||
|
url: apiDescriptor.url.replace(/^\/{platform}\//, "/api/"),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as GeneratorConfig;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
//铃铛debug
|
||||||
|
window.conf = {
|
||||||
|
baseHost: "http://basedebug.bestsch.com",
|
||||||
|
ssoHost: "http://ssodebug.bestsch.com",
|
||||||
|
serviceHost: "http://basedebug.bestsch.com/QualityEvaluate",
|
||||||
|
};
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
# 课程作业模块 · 功能清单与原型说明(v1 / 任课教师视角 · 核心闭环)
|
||||||
|
|
||||||
|
> 说明:本模块只复用校园公告模板的**外壳**——顶栏 + 左栏菜单机制、登录/SSO、班级树选人、富文本、附件上传、列表分页、统计页骨架。内部页面与数据按“课程作业”真实业务重搭,不再套用公告的“发布→接收→已读→审批”模型。
|
||||||
|
>
|
||||||
|
> v1 范围:**仅任课教师端核心闭环** = 布置作业 → 我布置的 → 提交情况(已交/未交) → 批改中心 → 作业统计。
|
||||||
|
> v1 暂不做:作业审核(审批流)、分层/周期性作业、学生端与家长端、拍照/附件真实上传(批改读取用 mock)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. 角色与左栏菜单(v1 = 教师视角)
|
||||||
|
|
||||||
|
登录身份 = 任课教师(沿用 `GetUserMod` 数据驱动左栏)。左栏菜单项:
|
||||||
|
|
||||||
|
| 序号 | 菜单名 | 路由 | 对应页面 | 复用/新建 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| 1 | 布置作业 | `/assign` | 新建/编辑作业表单 | 改造自现 `NewHomework.vue` |
|
||||||
|
| 2 | 我布置的 | `/list` | 作业列表(按状态分组) | 改造自现 `HomeworkList.vue` |
|
||||||
|
| 3 | 提交情况 | `/submissions` | 单作业的已交/未交名单 | 新建(可并入详情) |
|
||||||
|
| 4 | 批改中心 | `/grade` | 逐份批改工作台 | 新建 |
|
||||||
|
| 5 | 作业统计 | `/stats` | 提交率/正确率/分布 | 改造自现 `HomeworkStats.vue` |
|
||||||
|
|
||||||
|
> “我布置的”行内入口:查看详情、编辑、复制、置顶、撤回、催交;从某条作业进入「提交情况」和「批改中心」。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 核心业务闭环(状态机)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────── 我布置的(列表,筛选/状态标签) ────────────┐
|
||||||
|
│ │
|
||||||
|
布置作业(草稿) ─发布→ 收集中 ──到截止──> 已截止 ──全部批改完──> 已完成
|
||||||
|
│ │ │
|
||||||
|
│ 催交(未交) 批改中心(逐份 打分/评语/驳回)
|
||||||
|
│ │ │
|
||||||
|
└──── 编辑/复制/置顶/撤回 订正 ── 学生重交 ── 再批改
|
||||||
|
```
|
||||||
|
|
||||||
|
- **作业状态**:草稿 / 收集中 / 已截止 / 已完成(v1 去掉 pending/rejected 审批态)。
|
||||||
|
- **提交状态**(每个学生×每份作业):未交 / 已交(待批改) / 已批改 / 待订正 / 已订正。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 页面详规
|
||||||
|
|
||||||
|
### 2.1 布置作业(`/assign`)
|
||||||
|
目的:教师创建并即时发布一份作业给若干班级。
|
||||||
|
|
||||||
|
表单项:
|
||||||
|
- 学科(下拉,来自作业设置/字典)
|
||||||
|
- 作业标题(必填)
|
||||||
|
- 布置班级(多选,复用 `ClassCheckInput` 班级树;支持“全部班级”)
|
||||||
|
- 截止时间(日期时间;用于“已截止”判定与倒计时)
|
||||||
|
- 提交方式(多选/单选:文本、图片、附件、无需提交仅查阅)——决定学生端能力与批改界面
|
||||||
|
- 作业正文(富文本,复用 `@wangeditor`)
|
||||||
|
- 附件/题目(复用现附件上传组件 `HomeworkUpload`)
|
||||||
|
- 是否开启短信提醒(复用 `smsFlag`)
|
||||||
|
- 操作:保存草稿 / 发布
|
||||||
|
|
||||||
|
交互要点:发布即进入“收集中”,生成面向所选班级的可见作业;编辑草稿不改变状态。v1 无审批直发。
|
||||||
|
|
||||||
|
API:`POST /api/homework/Create`、`POST /api/homework/Edit`(复用,字段扩展见 §3)。
|
||||||
|
|
||||||
|
### 2.2 我布置的(`/list`)
|
||||||
|
目的:教师管理自己发布的所有作业。
|
||||||
|
|
||||||
|
- 顶部状态标签页:全部 / 收集中 / 已截止 / 已完成 / 草稿
|
||||||
|
- 列表列:标题、学科、布置班级、截止时间、提交进度(已交 x/应交 y)、批改进度(已批 m/已交 n)、状态、操作
|
||||||
|
- 操作:查看详情、编辑、复制、置顶、撤回、催交、进入批改
|
||||||
|
- 支持关键词/学科/时间筛选(复用 `applyFilters` 思路)
|
||||||
|
|
||||||
|
API:`GET /api/homework/SendList?scope=user`(改造为“我布置的”,返回体新增 progress 字段)。
|
||||||
|
|
||||||
|
### 2.3 提交情况(`/submissions?serid=`)
|
||||||
|
目的:看某份作业的收交名单。
|
||||||
|
|
||||||
|
- 头部:作业标题、截止倒计时、提交率、已交/未交/待订正 汇总
|
||||||
|
- 名单(按班级分组,复用 `ReadDetail` 的年级→班级分组):每个学生 提交时间 / 状态 / 快捷“批改”“催交”
|
||||||
|
- 批量:对未交一键催交(短信/站内)
|
||||||
|
|
||||||
|
API:`GET /api/homework/Submissions?serid=`(新增,替代现 `ReadDetail` 的“已读/未读”语义)。
|
||||||
|
|
||||||
|
### 2.4 批改中心(`/grade?serid=&subId=`)
|
||||||
|
目的:逐份高效批改。
|
||||||
|
|
||||||
|
- 左:提交列表(已交待批优先);右:某学生提交内容(文本/图片/附件预览,复用 `@open-file-viewer`/pdfjs)
|
||||||
|
- 批改区:评分方式二选一——分数(0–100)或等级(A/B/C 或 优/良/待改进);文字评语;(可选)图片圈划标注
|
||||||
|
- 动作:保存批改、标为优秀作业、驳回要求订正、(批量)一键通过
|
||||||
|
- 键盘快捷键上一条/下一条,提升批改效率
|
||||||
|
|
||||||
|
API:`GET /api/homework/SubmissionDetail?subId=`、`POST /api/homework/Grade`(新增)。
|
||||||
|
|
||||||
|
### 2.5 作业统计(`/stats`)
|
||||||
|
目的:教师看学情。
|
||||||
|
|
||||||
|
- 单作业维度:提交率、按时率、批改进度、分数/等级分布、订正完成率、共性易错点(v1 可仅展示分布)
|
||||||
|
- 总览维度:我布置的作业数、平均提交率、待批改数量
|
||||||
|
- 图表复用现统计页骨架
|
||||||
|
|
||||||
|
API:`GET /api/homework/Statis`(改造为作业口径)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 数据模型(v1 新增字段,mock 需对齐)
|
||||||
|
|
||||||
|
**Homework(作业)**
|
||||||
|
- 新增:`subject`(学科)、`deadline`(截止)、`submitType`(提交方式数组)、`status ∈ {draft, collecting, closed, done}`
|
||||||
|
- 去除/停用:`approvalEnabled/approvalStatus/approvalFlow`(v1 不用)、`classRead`(改为按提交统计)
|
||||||
|
|
||||||
|
**Submission(提交)** — 新概念
|
||||||
|
```
|
||||||
|
{ id, homeworkId, studentId, studentName, classId, className,
|
||||||
|
submittedAt|null, submitStatus: 'unsubmitted'|'submitted'|'graded'|'needRedo'|'redone',
|
||||||
|
text, images[], attachments[],
|
||||||
|
grade: { score|null, level|null, comment, isExcellent, markedImages[] } }
|
||||||
|
```
|
||||||
|
|
||||||
|
学生名单来源:班级 → 学生(现 mock 只有班级维度 `studentCount`,v1 需扩到**学生个体**才能做提交/批改;生成每班的 N 个 mock 学生即可)。
|
||||||
|
|
||||||
|
**统计**:提交率=已交/应交;按时率=截止前交/应交;批改进度=已批/已交;分数分布分桶。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 与现有“公告换皮”代码的差异(改造清单)
|
||||||
|
|
||||||
|
| 现有(公告语义) | v1 课程作业 | 处理 |
|
||||||
|
|---|---|---|
|
||||||
|
| 已收到 / 已发送 | 我布置的(+提交情况) | 改列表口径与字段 |
|
||||||
|
| 已读 / 未读(班级维度) | 已交 / 未交(学生个体维度) | 提交名单需下到学生 |
|
||||||
|
| 审批 pending/rejected | v1 不做 | 关闭审批入口与状态 |
|
||||||
|
| 收件箱(学生视角) | v1 不做(属学生端) | 暂隐藏 |
|
||||||
|
| NewHomework/Detail/Stats | 布置作业/详情/统计 | 改造复用 |
|
||||||
|
| ClassCheckInput/富文本/附件/TablePage | 同名复用 | 直接用 |
|
||||||
|
|
||||||
|
Mock 层:在 `src/assets/mock/homeworkapi.ts` 里 ①补 `subject/deadline/submitType`;②加学生个体表;③新增端点 `Submissions / SubmissionDetail / Grade / Remind`;④`Statis` 改作业口径;⑤去掉 `SeedFlows/approval` 相关种子与分支(或保留但不进菜单)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. v1 之后(预留,不实现,仅登记)
|
||||||
|
|
||||||
|
- 学生端 / 家长端(今日作业、提交、看批改、订正补交、提醒)
|
||||||
|
- 作业审核(教研组/教务抽查,复用现审批流,可选开启)
|
||||||
|
- 分层作业(A/B 组不同题量)、周期性作业(每周固定模板自动生成)
|
||||||
|
- 拍照提交 + 手写圈划批改、错题本、年级作业负担看板、优秀作业库
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 建议下一步
|
||||||
|
|
||||||
|
1. 你按本页圈改功能/字段(尤其 §2 每页要哪些、§3 提交/批改字段够不够)。
|
||||||
|
2. 确认后我先改造 `mock`(补学生与提交/批改数据 + 新端点),再逐页替换 `views`。
|
||||||
|
3. 先做「布置作业 → 我布置的 → 提交情况」这条能看到数据的链,批改中心随后。
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="../../favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<!-- 运行时配置引导:在任何应用模块执行前同步拉取 /<contextPath>/api/env-config
|
||||||
|
(后端免登录端点,projman 配置管理在线维护),替代生产构建环境变量。
|
||||||
|
必须同步:host.config.ts 在模块求值期就读 window.__APP_CONFIG__;失败/本地开发
|
||||||
|
(根路径,段数不足 2)则不设置:本地走 .env.dev/.env.mock(VITE_*),线上走同域推导兜底。
|
||||||
|
target = URL 第 2 段目录名:本应用产物部署于 /{contextPath}/front/,故为 front,
|
||||||
|
对应后端配置键 yh.env-config.source.front(环境变量 YH_ENV_CONFIG_SOURCE_FRONT)。
|
||||||
|
注意:同步 XHR 不支持 timeout 属性(赋值会抛 InvalidAccessError),依赖内网快速失败 -->
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
try {
|
||||||
|
var seg = location.pathname.split("/").filter(Boolean);
|
||||||
|
if (seg.length < 2) return; // 本地开发(根路径):走 .env.dev / .env.mock
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", "/" + seg[0] + "/api/env-config?target=" + encodeURIComponent(seg[1]), false);
|
||||||
|
xhr.send(null);
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
var conf = JSON.parse(xhr.responseText);
|
||||||
|
if (conf && typeof conf === "object") window.__APP_CONFIG__ = conf;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
/* 拉取失败保持未设置,走同域推导兜底 */
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<title><%- title %></title>
|
||||||
|
<link rel="stylesheet" href="./libs/ztree/zTreeStyle.css" />
|
||||||
|
<script src="./libs/jquery.min.js"></script>
|
||||||
|
<script src="./libs/lodash.min.js"></script>
|
||||||
|
<script src="./libs/ztree/jquery.ztree.all.min.js"></script>
|
||||||
|
<%- injectScript %>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Generated
+12915
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"name": "course-homework",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"nodeversion": "20.19.4",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --mode dev",
|
||||||
|
"dev:mock": "vite --mode mock",
|
||||||
|
"build": "vite build --mode base",
|
||||||
|
"build:base": "vite build --mode base",
|
||||||
|
"build:ld": "vite build --mode ld",
|
||||||
|
"build:prod": "vite build --mode prod",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"api:gen": "alova gen -f"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@open-file-viewer/core": "^0.1.43",
|
||||||
|
"@open-file-viewer/vue": "^0.1.43",
|
||||||
|
"@types/lodash": "^4.14.196",
|
||||||
|
"@wangeditor/editor": "^5.1.23",
|
||||||
|
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||||
|
"alova": "^3.4.0",
|
||||||
|
"axios": "^0.24.0",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"mitt": "^3.0.1",
|
||||||
|
"mockjs": "^1.1.0",
|
||||||
|
"node-sass": "latest",
|
||||||
|
"pdfjs-dist": "^4.10.38",
|
||||||
|
"pinia": "^2.1.4",
|
||||||
|
"sass": "^1.64.1",
|
||||||
|
"view-ui-plus": "^1.3.16",
|
||||||
|
"vite-plugin-mock": "2.9.6",
|
||||||
|
"vue": "^3.4.19",
|
||||||
|
"vue-router": "4",
|
||||||
|
"vuedraggable": "^4.1.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@alova/wormhole": "^1.5.0",
|
||||||
|
"@types/node": "^20.4.4",
|
||||||
|
"@unocss/preset-rem-to-px": "^66.6.0",
|
||||||
|
"@unocss/preset-uno": "^66.6.0",
|
||||||
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
|
"fast-glob": "^3.3.2",
|
||||||
|
"typescript": "^5.2.2",
|
||||||
|
"unocss": "^66.6.0",
|
||||||
|
"unplugin-auto-import": "0.18.3",
|
||||||
|
"vite": "^5.1.4",
|
||||||
|
"vite-plugin-html": "^3.2.2",
|
||||||
|
"vite-plugin-svg-icons": "^2.0.1",
|
||||||
|
"vue-tsc": "^1.8.27"
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
|
|||||||
|
àRCopyright 1990-2009 Adobe Systems Incorporated.
|
||||||
|
All rights reserved.
|
||||||
|
See ./LICENSEáCNS2-H
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
|
|||||||
|
àRCopyright 1990-2009 Adobe Systems Incorporated.
|
||||||
|
All rights reserved.
|
||||||
|
See ./LICENSEá ETen-B5-H` ^
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,4 @@
|
|||||||
|
àRCopyright 1990-2009 Adobe Systems Incorporated.
|
||||||
|
All rights reserved.
|
||||||
|
See ./LICENSE!!�º]aX!!]`�21�> �p�z�$]‚�"R‚d�-Uƒ7�*„␍4„%�+ „Z „{�/…%…<�9K…b�1]†.�"‡‰`]‡,�"]ˆ
|
||||||
|
�"]ˆh�"]‰F�"]Š$�"]‹�"]‹`�"]Œ>�"]��"]�z�"]ŽX�"]�6�"]��"]�r�"]‘P�"]’.�"]“�"]“j�"]”H�"]•&�"]–�"]–b�"]—@�"]˜�"]˜|�"]™Z�"]š8�"]›�"]›t�"]œR�"]�0�"]ž�"]žl�"]ŸJ�"] (�"]¡�"]¡d�"]¢B�"]£ �"X£~�']¤W�"]¥5�"]¦�"]¦q�"]§O�"]¨-�"]©�"]©i�"]ªG�"]«%�"]¬�"]¬a�"]?�"]®�"]®{�"]¯Y�"]°7�"]±�"]±s�"]²Q�"]³/�"]´␍�"]´k�"]µI�"]¶'�"]·�"]·c�"]¸A�"]¹�"]¹}�"]º[�"]»9
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user