feat(M5): P0 契约层 + 审批/应用设置下线
- types/homeworkV2.ts:V2 全量类型契约(五态/枚举/矩阵/版本/通知)
- teacherHomework/studentHomework/notification 三客户端(28 端点)
- §6 审批功能下线:删 ApprovalList/ApprovalSetting/ApprovalFlowEditor,
types/api/menu/menuRoute/App/mock 同步清理,DROPPED_MOD_IDS={5,6}
- §7 应用设置下线:删 HomeworkSetting/TypeSetBox/UserCheckInput,
SettingApi 与 dict/contacts 死代码清理,DROPPED_MOD_IDS 扩 {21}
- 新增 V2 组件:TeacherHomeworks(已发布+草稿箱)、ReviewModal(批阅弹窗)、
AttachmentUpload(附件上传)——尚未挂路由,页面切换随后续阶段
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<!-- V2 附件上传:emit AttachmentItem[](fileId/fileUrl/fileType/fileName/sort)。
|
||||
fileUrl 统一存 /api/file/Preview?id=(浏览器可直接渲染;下载走 Down 直链)。 -->
|
||||
<div class="attachment-upload">
|
||||
<Upload
|
||||
:on-success="onSuccess"
|
||||
:on-remove="onRemove"
|
||||
:default-file-list="fileList"
|
||||
:action="uploadUrl"
|
||||
:accept="accept"
|
||||
:multiple="multiple"
|
||||
:format="formatArr"
|
||||
:max-size="204800"
|
||||
:on-format-error="onFormatError"
|
||||
:on-exceeded-size="onExceeded"
|
||||
>
|
||||
<Button><Icon type="ios-cloud-upload-outline"></Icon>{{ buttonText || (imageOnly ? "拍照/上传图片" : "选择文件") }}</Button>
|
||||
</Upload>
|
||||
<span class="upload-tip">大小不超过 200MB</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Message } from "view-ui-plus";
|
||||
import config from "@/assets/config/host.config";
|
||||
import type { AttachmentItem } from "@/types/homeworkV2";
|
||||
|
||||
/** base/FileUpload(form-data: file)返回体:{code,msg,data:{fileid,name,fileurl,host,type,size}} */
|
||||
interface FileUploadResp {
|
||||
code: number;
|
||||
msg?: string;
|
||||
data: { fileid: number; name: string; fileurl: string; host: string; type: string; size: number };
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "AttachmentUpload",
|
||||
props: {
|
||||
/** 已选附件(v-model) */
|
||||
modelValue: { type: Array as () => AttachmentItem[], default: () => [] },
|
||||
/** 仅图片(拍照提交场景) */
|
||||
imageOnly: { type: Boolean, default: false },
|
||||
multiple: { type: Boolean, default: true },
|
||||
/** 留空时按 imageOnly 自动取「拍照/上传图片」或「选择文件」 */
|
||||
buttonText: { type: String, default: "" },
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
data() {
|
||||
return {
|
||||
uploadUrl: (config.isMock ? "" : config.serviceHost) + "/api/file/Upload",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
accept(): string {
|
||||
return this.imageOnly ? "image/*" : "";
|
||||
},
|
||||
formatArr(): string[] {
|
||||
return this.imageOnly
|
||||
? ["jpg", "jpeg", "gif", "png", "bmp", "webp"]
|
||||
: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "xls", "doc", "txt", "docx", "xlsx", "zip", "rar", "pdf", "ppt", "pptx", "wps", "mp3", "mp4"];
|
||||
},
|
||||
/** iview Upload 回显列表(只读 name;移除按项过滤) */
|
||||
fileList(): any[] {
|
||||
return (this.modelValue || []).filter((a) => a.fileName).map((a) => ({ name: a.fileName, url: this.previewUrl(a), _item: a }));
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
previewUrl(a: AttachmentItem): string {
|
||||
return a.fileId ? `${config.isMock ? "" : config.serviceHost}/api/file/Preview?id=${a.fileId}` : a.fileUrl;
|
||||
},
|
||||
onSuccess(resp: FileUploadResp, file: any) {
|
||||
if (!resp || resp.code !== 0 || !resp.data) {
|
||||
Message.error(resp?.msg || "上传失败");
|
||||
return;
|
||||
}
|
||||
const d = resp.data;
|
||||
const item: AttachmentItem = {
|
||||
fileId: d.fileid,
|
||||
fileUrl: `/api/file/Preview?id=${d.fileid}`,
|
||||
fileType: this.imageType(d) ? "image" : "file",
|
||||
fileName: d.name || file.name,
|
||||
sort: this.modelValue.length,
|
||||
};
|
||||
this.$emit("update:modelValue", [...this.modelValue, item]);
|
||||
},
|
||||
imageType(d: FileUploadResp["data"]): boolean {
|
||||
const t = String(d.type || "").toLowerCase();
|
||||
if (t.startsWith("image")) return true;
|
||||
return /\.(jpe?g|gif|png|bmp|webp)$/i.test(d.name || d.fileurl || "");
|
||||
},
|
||||
onRemove(file: any) {
|
||||
// 回显项挂 _item;刚上传项从 response.data.fileid 匹配
|
||||
const target: AttachmentItem | undefined = file._item || (file.response?.data ? { fileId: file.response.data.fileid } as AttachmentItem : undefined);
|
||||
if (!target) return;
|
||||
const arr = this.modelValue.filter((a) => a !== target && (target.fileId ? a.fileId !== target.fileId : a.fileUrl !== target.fileUrl));
|
||||
this.$emit("update:modelValue", arr.map((a, i) => ({ ...a, sort: i })));
|
||||
},
|
||||
onFormatError() {
|
||||
Message.error(this.imageOnly ? "仅支持图片文件" : "不支持的上传类型!");
|
||||
},
|
||||
onExceeded() {
|
||||
Message.warning("文件大小超过 200MB 限制");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.attachment-upload {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
:deep(.ivu-upload) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
:deep(.ivu-upload-list) {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
}
|
||||
:deep(.ivu-upload-list-file) {
|
||||
padding: 4px 0;
|
||||
}
|
||||
.upload-tip {
|
||||
margin-left: 10px;
|
||||
color: #f90;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user