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:
lijing
2026-09-14 18:40:20 +08:00
commit d75aab8d07
344 changed files with 35558 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
<template>
<div>
<ul class="public-upload-img fix">
<li v-for="(item, index) in uploadList">
<template v-if="item.status === 'finished'">
<img :src="item.url" alt="" />
<div class="upload-list-cover">
<Icon
type="ios-eye-outline"
size="24"
title="预览图片"
@click.native="handleView(item.url)"
></Icon>
<Icon
size="24"
class="ml-10"
title="删除图片"
type="ios-trash-outline"
@click.native="handleRemove(index)"
></Icon>
</div>
</template>
<template v-else>
<Progress v-if="item.showProgress" :percent="item.percentage" class="mt-24" hide-info></Progress>
</template>
</li>
<li class="add-li" v-show="uploadList.length < uploadCount">
<Upload
ref="upload"
:show-upload-list="false"
:on-success="handleSuccess"
:format="['jpg', 'jpeg', 'png', 'gif']"
accept="image/gif,image/jpeg,image/jpg,image/png"
:max-size="20480"
:on-format-error="handleFormatError"
:on-exceeded-size="handleMaxSize"
:default-file-list="defaultList"
:headers="uploadHeader"
type="drag"
:action="uploadUrl"
:data="data"
>
<div class="add-box">
<Icon type="ios-add" size="50"></Icon>
</div>
</Upload>
</li>
</ul>
<Modal title="图片预览" v-model="visible" footer-hide>
<img :src="imgName" v-if="visible" style="width: 100%" />
</Modal>
</div>
</template>
<script>
export default {
props: {
imgList: { type: Array, default: [] },
uploadUrl: { type: String, default: "" },
uploadCount: { type: Number, default: 1 },
data: { type: Object, default: null },
},
data() {
return {
imgName: "",
visible: false,
uploadList: [],
defaultList: [],
};
},
created() {
this.setDefaultList();
this.$watch("imgList", val => {
this.setDefaultList();
});
},
mounted() {
console.log(this.$refs.upload.fileList);
this.uploadList = this.$refs.upload.fileList;
},
computed: {
uploadHeader() {
const bschUtil = BschLoadUtil.getInstance();
if (!bschUtil.getIsSelf()) {
return {
ticket: sessionStorage.getItem("bschTicket"),
platform: "grzx",
};
} else return null;
},
},
methods: {
handleView(name) {
this.imgName = name;
this.visible = true;
},
handleRemove(index) {
this.$refs.upload.fileList.splice(index, 1);
this.imgList.splice(index, 1);
},
handleSuccess(res, file) {
console.log(res, file);
file.url = res.data.fileurl;
this.imgList.push(res.data.fileurl);
},
handleFormatError(file) {
this.$Homework.info({
title: "不支持的文件类型!",
desc: "请上传指定的文件类型",
});
},
handleMaxSize(file) {
this.$Homework.warning({
title: "图片过大",
desc: "图片最大上传大小为20M.",
});
},
setDefaultList() {
let arr = [];
this.imgList.forEach(element => {
arr.push({
name: "",
url: element,
});
});
this.defaultList = arr;
},
},
};
</script>
<style></style>