From 9a6cee214ccbba90c3421a775adca8c569a0648d Mon Sep 17 00:00:00 2001 From: bai <1643359946@qq.com> Date: Sat, 25 May 2024 18:50:53 +0800 Subject: [PATCH 1/4] =?UTF-8?q?2024=E5=B9=B45=E6=9C=8825=E6=97=A5=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kc/detection/KcDetectionMain.data.ts | 86 ++- .../components/KcDetectionMainModal.vue | 23 +- src/views/kc/detection/zyZhjsList.vue | 87 ++- src/views/zy/jiaoXueDanYuanNeiRong/index2.vue | 321 +++++---- .../jiaoXueDanYuanNeiRong/index2.vue.allSave | 663 ++++++++++++++++++ .../zy/jiaoXueDanYuanNeiRong/stuIndex.vue | 13 +- 6 files changed, 1005 insertions(+), 188 deletions(-) create mode 100644 src/views/zy/jiaoXueDanYuanNeiRong/index2.vue.allSave diff --git a/src/views/kc/detection/KcDetectionMain.data.ts b/src/views/kc/detection/KcDetectionMain.data.ts index 914e0e3..effc533 100644 --- a/src/views/kc/detection/KcDetectionMain.data.ts +++ b/src/views/kc/detection/KcDetectionMain.data.ts @@ -12,21 +12,26 @@ export const columns: BasicColumn[] = [ align: "center", dataIndex: 'xnxq' }, - { - title: '授课日期', - align: "center", - dataIndex: 'createTime' - }, { title: '课程名称', align: "center", dataIndex: 'kcmc' }, { - title: '节次', + title: '授课日期', + align: "center", + dataIndex: ['ketangbiaoInfo', 'skrq'] + }, + { + title: '授课节次', align: "center", dataIndex: ['ketangbiaoInfo', 'hh'] }, + { + title: '选课人数', + align: "center", + dataIndex: ['ketangbiaoInfo', 'xkrs'], + }, // { // title: '任务编号', @@ -49,25 +54,20 @@ export const columns: BasicColumn[] = [ // dataIndex: 'detectionUrl' // }, { - title: '检测次数', + title: '抓取次数', align: "center", dataIndex: 'detectionNum' }, { - title: '人数(累加)', + title: '累计抓取人数', align: "center", dataIndex: 'allNum' }, { - title: '平均数', + title: '平均抓取人数', align: "center", dataIndex: 'averageNum', }, - { - title: '选课人数', - align: "center", - dataIndex: ['ketangbiaoInfo', 'xkrs'], - }, { title: '出勤率', align: "center", @@ -108,18 +108,22 @@ export const formSchema: FormSchema[] = [ field: 'xnxq', component: 'Input', }, - { - label: '授课日期', - field: 'createTime', - component: 'Input', - }, { label: '课程名称', field: 'kcmc', component: 'Input', }, { - label: '节次', + label: '授课日期', + field: 'createTime', + component: 'Input', + render: ({ values }) => { + let text = values?.ketangbiaoInfo?.skrq; + return h(Input, { value: text, disabled: true }); + } + }, + { + label: '授课节次', field: 'ketangbiaoInfo', component: 'Input', render: ({ values }) => { @@ -152,17 +156,17 @@ export const formSchema: FormSchema[] = [ show: false, }, { - label: '检测次数', + label: '抓取次数', field: 'detectionNum', component: 'InputNumber', }, { - label: '人数(累加)', + label: '累计抓取人数', field: 'allNum', component: 'InputNumber', }, { - label: '平均数', + label: '平均抓取人数', field: 'averageNum', component: 'InputNumber', }, @@ -257,26 +261,52 @@ export const detectionDetailedListColumns: BasicColumn[] = [ // dataIndex: 'jsbh' // }, { - title: '检测序号', + title: '序号', align: "center", - dataIndex: 'detectionNum' + dataIndex: 'detectionNum', + customRender: ({ index }) => { + return index+1; + }, + }, + // { + // title: '序号', + // align: "center", + // dataIndex: 'detectionNum', + // }, + { + title: '授课地点', + align: "center", + dataIndex: 'none1', + slots: { customRender: 'skkd' }, }, { - title: '截取图片', + title: '抓取图片', align: "center", dataIndex: 'detectionOutImgUrl', slots: { customRender: 'imgSlot' }, }, { - title: '检测时间', + title: '抓取时间', align: "center", dataIndex: 'createTime' }, { - title: '人数', + title: '选课人数', + align: "center", + dataIndex: 'none2', + slots: { customRender: 'xkrs' }, + }, + { + title: 'AI识别人数', align: "center", dataIndex: 'num' }, + { + title: '到课识别率', + align: "center", + dataIndex: 'none3', + slots: { customRender: 'dksbl' }, + }, // { // title: '人数计算结果', // align: "center", diff --git a/src/views/kc/detection/components/KcDetectionMainModal.vue b/src/views/kc/detection/components/KcDetectionMainModal.vue index 22372f6..5f03b6f 100644 --- a/src/views/kc/detection/components/KcDetectionMainModal.vue +++ b/src/views/kc/detection/components/KcDetectionMainModal.vue @@ -8,6 +8,9 @@ 无图片 + + + @@ -27,7 +30,7 @@ import { saveOrUpdate } from '../KcDetectionMain.api'; const emit = defineEmits(['register', 'success']); const isUpdate = ref(true); //表单配置 -const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({ +const [registerForm, { setProps, getFieldsValue, resetFields, setFieldsValue, validate }] = useForm({ //labelWidth: 150, schemas: formSchema, showActionButtonGroup: false, @@ -49,7 +52,7 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data setProps({ disabled: !data?.showFooter }) }); //设置标题 -const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑')); +const title = computed(() => '查看'); //表单提交事件 async function handleSubmit(v) { try { @@ -66,6 +69,22 @@ async function handleSubmit(v) { } } +function calcAverageNum(record){ + let xkrs = getFieldsValue('ketangbiaoInfo')?.ketangbiaoInfo?.xkrs; + let { num: averageNum } = record; + let xkrsNum = Number(xkrs); + if(!isNaN(xkrsNum) && xkrsNum != 0 && averageNum != 0) { + let num = averageNum / xkrsNum * 100; + return num.toFixed(2) + '%'; + }else{ + if(averageNum == 0){ + return '0.00' + '%'; + }else{ + return ''; + } + } +} + /** * 获取预览图片 */ diff --git a/src/views/kc/detection/zyZhjsList.vue b/src/views/kc/detection/zyZhjsList.vue index 5a257a4..f190367 100644 --- a/src/views/kc/detection/zyZhjsList.vue +++ b/src/views/kc/detection/zyZhjsList.vue @@ -1,10 +1,40 @@ -
+
视频
文档
@@ -97,76 +97,12 @@ {{ three.title }} - + + - - - - - -
-
- -
-
- - - - -
-
-
@@ -220,7 +156,7 @@ import { defHttp } from '/@/utils/http/axios'; import { useMessage } from "/@/hooks/web/useMessage"; import { useRouter } from 'vue-router'; - import { randomString, simpleDebounce } from '/@/utils/common/compUtils' + import { randomString, simpleDebounce, getFileAccessHttpUrl } from '/@/utils/common/compUtils' import draggable from 'vuedraggable'; import JUpload from '/@/components/Form/src/jeecg/components/JUpload/JUpload.vue'; @@ -270,16 +206,41 @@ }); enum Api { - list = '/teachingunitcontent/kcTeachingUnitContentOne/allList', - edit = '/teachingunitcontent/kcTeachingUnitContentOne/edit', -} + list = '/teachingunitcontent/kcTeachingUnitContentOne/allList', + editAll = '/teachingunitcontent/kcTeachingUnitContentOne/editAll', + + addOne = '/teachingunitcontent/kcTeachingUnitContentOne/add', + editOne = '/teachingunitcontent/kcTeachingUnitContentOne/edit', + delOne = '/teachingunitcontent/kcTeachingUnitContentOne/delete', + + addTwo = '/teachingunitcontent/kcTeachingUnitContentTwo/add', + editTwo = '/teachingunitcontent/kcTeachingUnitContentTwo/edit', + delTwo = '/teachingunitcontent/kcTeachingUnitContentTwo/delete', + + addThree = '/teachingunitcontent/kcTeachingUnitContentThree/add', + editThree = '/teachingunitcontent/kcTeachingUnitContentThree/edit', + delThree = '/teachingunitcontent/kcTeachingUnitContentThree/delete', + + } /** * 列表接口 * @param params */ const listAll = (params) => defHttp.get({ url: Api.list, params }); - const editAll = (params) => defHttp.post({ url: Api.edit, params }, { isTransformResponse: true }); + const editAll = (params) => defHttp.post({ url: Api.editAll, params }, { isTransformResponse: true }); + + const addOneFetch = (params) => defHttp.post({ url: Api.addOne, params }, { isTransformResponse: false }); + const editOneFetch = (params) => defHttp.put({ url: Api.editOne, params }, { isTransformResponse: false }); + const delOneFetch = (params) => defHttp.delete({ url: Api.delOne, params }, { isTransformResponse: false, joinParamsToUrl: true }); + + const addTwoFetch = (params) => defHttp.post({ url: Api.addTwo, params }, { isTransformResponse: false }); + const editTwoFetch = (params) => defHttp.put({ url: Api.editTwo, params }, { isTransformResponse: false }); + const delTwoFetch = (params) => defHttp.delete({ url: Api.delTwo, params }, { isTransformResponse: false, joinParamsToUrl: true }); + + const addThreeFetch = (params) => defHttp.post({ url: Api.addThree, params }, { isTransformResponse: false }); + const editThreeFetch = (params) => defHttp.put({ url: Api.editThree, params }, { isTransformResponse: false }); + const delThreeFetch = (params) => defHttp.delete({ url: Api.delThree, params }, { isTransformResponse: false, joinParamsToUrl: true }); function reload() { @@ -332,51 +293,6 @@ dataRecursion(dataSource.value, (x, i) => x.sort = i+1); } - //创建新的节点 - function createNoneData(){ - let data = { - //临时ID,兼容新增和修改 - _id: randomString(32), - title: null, - sort: 0, - childrenList: [], - } - return data; - } - - function stop(e) { - e?.stopPropagation(); - } - - function changeInput(e, pdata, key) { - let { target } = e; - let { value } = target; - pdata[key] = value; - } - - function addOne(){ - dataSource.value.push(createNoneData()); - createMessage.success('添加标题成功!'); - nextTick(() => { - refreshDataSort(); - }) - } - - function delOne(e, one){ - stop(e); - let index = dataSource.value.findIndex(x => x == one); - if(index == -1) return; - dataSource.value.splice(index, 1); - createMessage.success('删除标题成功!'); - nextTick(() => { - refreshDataSort(); - }) - } - - // function insertOne(index){ - // dataSource.value.splice(index,0, createNoneData()); - // } - //获取祖宗级对象 function getBtnEle(e, className){ if(e){ @@ -398,6 +314,74 @@ } } + //创建新的节点 + function createNoneData(){ + let data = { + //临时ID,兼容新增和修改 + _id: randomString(32), + rwbh, + xqxn, + title: null, + sort: 0, + childrenList: [], + } + return data; + } + + function stop(e) { + e?.stopPropagation(); + } + + function changeInput(e, pdata, key) { + let { target } = e; + let { value } = target; + pdata[key] = value; + } + + function addOne(){ + //调用后台保存(创建个新的) + let nowData = createNoneData(); + dataSource.value.push(nowData); + refreshDataSort(); + addOneFetch(nowData).then(res => { + if(res.success){ + nowData.id = res.result.id; + createMessage.success('添加标题成功!'); + // nextTick(() => { + // refreshDataSort(); + // }); + } + }); + } + + function editOne(one){ + editOneFetch(one).then(res => { + if(res.success){ + createMessage.success('修改标题成功!'); + } + }); + } + + function delOne(e, one){ + stop(e); + //删除 + delOneFetch({ id: one.id }).then(res => { + if(res.success){ + let index = dataSource.value.findIndex(x => x == one); + if(index == -1) return; + dataSource.value.splice(index, 1); + createMessage.success('删除标题成功!'); + nextTick(() => { + refreshDataSort(); + }) + } + }); + } + + // function insertOne(index){ + // dataSource.value.splice(index,0, createNoneData()); + // } + function addTwo(e, one){ let btn = getBtnEle(e.target, 'twoBtn'); if(btn == null){ @@ -418,27 +402,44 @@ }else{ stop(e); } + let nowData = createNoneData(); + nowData.pid = one.id; if(one.childrenList){ - one.childrenList.push(createNoneData()); + one.childrenList.push(nowData); }else{ - one.childrenList = [ createNoneData() ]; + one.childrenList = [ nowData ]; } + refreshDataSort(); + addTwoFetch(nowData).then(res => { + if(res.success){ + nowData.id = res.result.id; + createMessage.success('添加章节成功!'); + } + }); + } - createMessage.success('添加章节成功!'); - nextTick(() => { - refreshDataSort(); - }) + function editTwo(two){ + editTwoFetch(two).then(res => { + if(res.success){ + createMessage.success('修改章节成功!'); + } + }); } function delTwo(e, one, two){ stop(e); - let index = one.childrenList.findIndex(x => x == two); - if(index == -1) return; - one.childrenList.splice(index, 1); - createMessage.success('删除章节成功!'); - nextTick(() => { - refreshDataSort(); - }) + delTwoFetch({ id: two.id }).then(res => { + if(res.success){ + let index = one.childrenList.findIndex(x => x == two); + if(index == -1) return; + one.childrenList.splice(index, 1); + createMessage.success('删除章节成功!'); + nextTick(() => { + refreshDataSort(); + }) + } + }); + } // function insertTwo(one, index){ @@ -479,16 +480,24 @@ function delThree(e, two, three){ stop(e); - let index = two.childrenList.findIndex(x => x == three); - if(index == -1) return; - two.childrenList.splice(index, 1); + delThreeFetch({ id: three.id }).then(res => { + if(res.success){ + let index = two.childrenList.findIndex(x => x == three); + if(index == -1) return; + two.childrenList.splice(index, 1); - createMessage.success('删除成功!'); - nextTick(() => { - refreshDataSort(); - }) + createMessage.success('删除成功!'); + nextTick(() => { + refreshDataSort(); + }) + } + }); } + function downloadFile(three) { + let url = getFileAccessHttpUrl(three.filePath); + window.open(url,"_blank"); + } //移动结束时触发,如果未移动则不触发,刷新排序, function endDraggable(){ @@ -523,23 +532,32 @@ } } - if(!threeIndex){ + if(!threeIndex && threeIndex !== 0){ + three.pid = two.id; //新增 if(two.childrenList){ two.childrenList.push(three); }else{ two.childrenList = [ three ]; } - createMessage.success('新增成功!'); + refreshDataSort(); + addThreeFetch(three).then(res => { + if(res.success){ + three.id = res.result.id; + createMessage.success('添加成功!'); + } + }); }else{ //修改 two.childrenList[threeIndex] = three; - createMessage.success('修改成功!'); + refreshDataSort(); + editThreeFetch(three).then(res => { + if(res.success){ + createMessage.success('修改成功!'); + } + }); } - nextTick(() => { - refreshDataSort(); - }) threePageOpen.value = false; @@ -657,7 +675,14 @@ overflow: auto; } -:deep(.maxDiv) .ant-collapse-header{ - align-items: center; +:deep(.maxDiv){ + .ant-collapse-header{ + align-items: center; + padding-bottom: 0; + } + .ant-card-body { + padding-top: 0; + } } + diff --git a/src/views/zy/jiaoXueDanYuanNeiRong/index2.vue.allSave b/src/views/zy/jiaoXueDanYuanNeiRong/index2.vue.allSave new file mode 100644 index 0000000..4794734 --- /dev/null +++ b/src/views/zy/jiaoXueDanYuanNeiRong/index2.vue.allSave @@ -0,0 +1,663 @@ + + + + + diff --git a/src/views/zy/jiaoXueDanYuanNeiRong/stuIndex.vue b/src/views/zy/jiaoXueDanYuanNeiRong/stuIndex.vue index 7910352..e34c92d 100644 --- a/src/views/zy/jiaoXueDanYuanNeiRong/stuIndex.vue +++ b/src/views/zy/jiaoXueDanYuanNeiRong/stuIndex.vue @@ -75,7 +75,10 @@ {{ three.title }} - + + + + @@ -176,7 +179,7 @@ import { defHttp } from '/@/utils/http/axios'; import { useMessage } from "/@/hooks/web/useMessage"; import { useRouter } from 'vue-router'; - import { randomString, simpleDebounce } from '/@/utils/common/compUtils' + import { randomString, simpleDebounce, getFileAccessHttpUrl } from '/@/utils/common/compUtils' import draggable from 'vuedraggable'; import JUpload from '/@/components/Form/src/jeecg/components/JUpload/JUpload.vue'; @@ -451,6 +454,12 @@ threePageOpen.value = false; } + function downloadFile(three) { + let url = getFileAccessHttpUrl(three.filePath); + window.open(url,"_blank"); + } + + //移动结束时触发,如果未移动则不触发,刷新排序, function endDraggable(){ From d2827f642fd43c2648c52eed99fac34f5ffb1de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=B9=E7=A3=8A?= <45566618@qq.com> Date: Sat, 25 May 2024 23:25:35 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=95=99=E5=AD=A6?= =?UTF-8?q?=E5=A4=A7=E7=BA=B2=E9=99=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/site/renKeJiaoCheng/checkKecheng/dqxqkcDetail.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/site/renKeJiaoCheng/checkKecheng/dqxqkcDetail.vue b/src/views/site/renKeJiaoCheng/checkKecheng/dqxqkcDetail.vue index fd327d8..8614c92 100644 --- a/src/views/site/renKeJiaoCheng/checkKecheng/dqxqkcDetail.vue +++ b/src/views/site/renKeJiaoCheng/checkKecheng/dqxqkcDetail.vue @@ -123,7 +123,7 @@ const baseApiUrl = globSetting.domainUrl; //教学大纲提交附件 function jxdgChange(record){ console.log(`🚀 ~ jxdgChange ~ record:`, record) - var model = {id:jxdgInfo.value.id,filePath:record} + var model = {id:jxdgInfo.value.id,filePath:record,rwbh:rwbh} defHttp.post({ url: '/zyJxdg/zyJxdg/jxdgScfj', params: model }).then((res) => { getKcjsJxdg(); }); From c3e2294d590245193d472fbf1fd089c901cdefa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=B9=E7=A3=8A?= <45566618@qq.com> Date: Sat, 25 May 2024 23:42:12 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=95=99=E5=AD=A6?= =?UTF-8?q?=E5=A4=A7=E7=BA=B2=E9=99=84=E4=BB=B6,=E5=9C=A8=E7=BC=96?= =?UTF-8?q?=E5=86=99=E8=AF=BE=E7=A8=8B=E7=AE=80=E4=BB=8B=E5=87=BA=E7=8E=B0?= =?UTF-8?q?2=E6=9D=A1=E6=95=B0=E6=8D=AE=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/site/renKeJiaoCheng/checkKecheng/dqxqkcDetail.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/site/renKeJiaoCheng/checkKecheng/dqxqkcDetail.vue b/src/views/site/renKeJiaoCheng/checkKecheng/dqxqkcDetail.vue index 8614c92..eb3462c 100644 --- a/src/views/site/renKeJiaoCheng/checkKecheng/dqxqkcDetail.vue +++ b/src/views/site/renKeJiaoCheng/checkKecheng/dqxqkcDetail.vue @@ -123,7 +123,7 @@ const baseApiUrl = globSetting.domainUrl; //教学大纲提交附件 function jxdgChange(record){ console.log(`🚀 ~ jxdgChange ~ record:`, record) - var model = {id:jxdgInfo.value.id,filePath:record,rwbh:rwbh} + var model = {id:jxdgInfo.value.id,filePath:record,rwbh:rwbh,xqxn:xqxn} defHttp.post({ url: '/zyJxdg/zyJxdg/jxdgScfj', params: model }).then((res) => { getKcjsJxdg(); }); From bbf7de0cc496b0b217aad2d8a01eb35fbea12569 Mon Sep 17 00:00:00 2001 From: bai <1643359946@qq.com> Date: Mon, 27 May 2024 01:09:54 +0800 Subject: [PATCH 4/4] =?UTF-8?q?2024=E5=B9=B45=E6=9C=8827=E6=97=A5=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=88=87=E7=89=87=E4=B8=8A=E4=BC=A0=EF=BC=8C?= =?UTF-8?q?=E5=85=BC=E5=AE=B9ftp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/jeecg/components/JUpload/JUpload.vue | 349 ++++++++- src/router/routes/modules/zy/zy.ts | 2 +- src/views/zy/jiaoXueDanYuanNeiRong/index.vue | 497 ++++++++++--- .../jiaoXueDanYuanNeiRong/index.vue.oneType | 393 ++++++++++ src/views/zy/jiaoXueDanYuanNeiRong/index2.vue | 688 ------------------ 5 files changed, 1132 insertions(+), 797 deletions(-) create mode 100644 src/views/zy/jiaoXueDanYuanNeiRong/index.vue.oneType delete mode 100644 src/views/zy/jiaoXueDanYuanNeiRong/index2.vue diff --git a/src/components/Form/src/jeecg/components/JUpload/JUpload.vue b/src/components/Form/src/jeecg/components/JUpload/JUpload.vue index f01f3ca..db9957e 100644 --- a/src/components/Form/src/jeecg/components/JUpload/JUpload.vue +++ b/src/components/Form/src/jeecg/components/JUpload/JUpload.vue @@ -1,9 +1,8 @@ - + {{ text }} +
@@ -29,7 +29,7 @@ import { ref, reactive, computed, watch, nextTick, createApp, unref } from 'vue'; import { Icon } from '/@/components/Icon'; import { getToken } from '/@/utils/auth'; - import { uploadUrl } from '/@/api/common/api'; + import { uploadUrl, baseUploadUrl } from '/@/api/common/api'; import { propTypes } from '/@/utils/propTypes'; import { useMessage } from '/@/hooks/web/useMessage'; import { createImgPreview } from '/@/components/Preview/index'; @@ -38,6 +38,9 @@ import { UploadTypeEnum } from './upload.data'; import { getFileAccessHttpUrl } from '/@/utils/common/compUtils'; import UploadItemActions from './components/UploadItemActions.vue'; + import { defHttp } from '/@/utils/http/axios'; + import { buildUUID } from '/@/utils/uuid'; + import CryptoJS from 'crypto-js'; const { createMessage, createConfirm } = useMessage(); const { prefixCls } = useDesign('j-upload'); @@ -70,9 +73,16 @@ forceBeforeUploadFn: propTypes.bool.def(true), //后加的,强制验证accept,目前仅支持*.jpg,*.gif之类的扩展名验证,video/*,audio/*,image/jpeg之类的暂不支持 forceAcceptVerify: propTypes.bool.def(false), + //后加的,是否查询biz + isGetBiz: propTypes.bool.def(false), + //后加的,查询biz参数 + getBizParam: propTypes.object.def({}), disabled: propTypes.bool.def(false), }); + const otherData = ref({}); + const otherClass = ref({}); + const headers = reactive({ 'X-Access-Token': getToken(), }); @@ -89,8 +99,8 @@ const bind: any = Object.assign({}, props, unref(attrs)); bind.name = 'file'; bind.listType = isImageMode.value ? 'picture-card' : 'text'; - bind.class = [bind.class, { 'upload-disabled': props.disabled }]; - bind.data = { biz: props.bizPath, ...bind.data }; + bind.class = [bind.class, { 'upload-disabled': props.disabled, ...unref(otherClass) }]; + bind.data = { biz: props.bizPath, ...bind.data, ...unref(otherData) }; //update-begin-author:taoyan date:20220407 for: 自定义beforeUpload return false,并不能中断上传过程 if (!bind.beforeUpload) { bind.beforeUpload = onBeforeUpload; @@ -124,6 +134,41 @@ watch(fileList, () => nextTick(() => addActionsListener()), { immediate: true }); + const loadBizFn = (params) => defHttp.get({ url: '/ktgl/kcKetangbiao/getBizPath', params }, { isTransformResponse: false }) + + function loadBiz(param){ + otherData.value.loading = true; + // otherClass.value = { 'upload-disabled': true, } + loadBizFn(param).then(res => { + //otherData.value + if(res.success){ + otherData.value.biz = res.result; + }else{ + delete otherData.value.biz; + } + // otherClass.value = {} + delete otherData.value.loading; + }); + } + + + watch(() => props.getBizParam, + (param, oldParam) => { + if(JSON.stringify(param) != JSON.stringify(oldParam)){ + nextTick(() => { + console.log(`🚀 ~ props.isGetBiz:`, props.isGetBiz, bindProps.value.disabled, loadBiz); + if(props.isGetBiz && !bindProps.value.disabled){ + //非被禁用状态,查询 + nextTick(() => { + loadBiz(param); + }); + } + }); + } + }, + { immediate: true } + ); + const antUploadItemCls = 'ant-upload-list-item'; // Listener @@ -299,7 +344,7 @@ }); } } else if (info.file.status === 'error') { - createMessage.error(`${info.file.name} 上传失败.`); + createMessage.error(`${info.file.name} 上传失败. ${info.file.response.message}`); } fileList.value = fileListTemp; if (info.file.status === 'done' || info.file.status === 'removed') { @@ -372,6 +417,296 @@ return path.substring(path.lastIndexOf('/') + 1); } + // //获取文件md5(仅支持小文件,,,弃用) + // function calculateFileMd5(file) { + // return new Promise((resolve, reject) => { + // const reader = new FileReader(); + // // 当文件被成功读取时调用onload事件 + // reader.onload = function (event) { + // const arrayBufferView = event.target.result; + // // 将ArrayBuffer转换为WordArray对象 + // const wordArray = CryptoJS.lib.WordArray.create(arrayBufferView); + // // 计算MD5哈希值 + // const md5Hash = CryptoJS.MD5(wordArray).toString(); + // resolve(md5Hash); + // }; + // // 当发生错误时调用onerror事件 + // reader.onerror = function () { + // reject('Failed to read file'); + // }; + // // 开始读取文件内容 + // reader.readAsArrayBuffer(file); + // }); + // } + + /** + * 计算文件md5(分批次计算,挺慢的,支持大文件) + */ + function calFileMd5(file){ + return new Promise((resolve, reject) => { + let md5 = CryptoJS.algo.MD5.create(); + //获取文件对象 + let reader = new FileReader(); + let step = 1024* 1024; + let total = file.size; + let cuLoaded = 0; + let time=1; + console.info("文件大小:" + file.size); + //读取一段成功 + reader.onload = (e) => { + //console.log("开始读取第"+time+"段"); + //处理读取的结果 + let wordArray = CryptoJS.lib.WordArray.create(reader.result); + md5.update(wordArray); + cuLoaded += e.loaded; + //如果没有读完,继续 + if (cuLoaded < total) { + readBlob(cuLoaded); + } else { + cuLoaded = total; + let hash = md5.finalize().toString(); + //document.getElementById("result").innerText=hash; + resolve(hash + buildUUID()); + } + time++; + } + reader.onerror = (e) => { + console.error('读取文件MD5出现错误 =>',e); + reject(e); + } + //开始读取 + readBlob(0); + //指定开始位置,分块读取文件 + function readBlob(start) { + //指定开始位置和结束位置读取文件 + let blob = file.slice(start, start + step); + reader.readAsArrayBuffer(blob); + } + }); + } + + function calFileUid(file){ + return new Promise((resolve, reject) => { + resolve(buildUUID()); + }); + } + + + + + //请求接口的数据返回 +// function upSearch(data){ +// return new Promise((resolve,reject)=>{ +// setTimeout(()=>{ +// console.log(data) +// resolve(data) +// },2000) +// }) +// } + +// function limitPromise(promiseList, limit){ + +// let init = Math.min(limit, promiseList.length) +// let promiseList = [] +// for(let i=0; i{ +// console.log('全部完成') +// }) +// } + +//limitUp([1,2,3,4,5,6,7,8], 3) + + + async function limitPromise(promises, limit) { + const results = [] + + for(let i=0; i<=promises.length; i+=limit) { + const tasks = [] + // 切片 Promise.all 的参数 + for(const promise of promises.slice(i, i + limit)) { + tasks.push(promise) + } + // 用 await 以确保后面的任务不会在执行当前任务时执行 + const result = await Promise.all(tasks) + results.push(...result) + } + + return results + } + + //分片大小 5m + const chunkSize = 5 * 1024 * 1024; + + const runBatchNum = 20; + + const uploadAxiosHttpConfig = { + //超时时间,分钟 * 秒 * 毫秒 60分钟 + timeout: 60 * 60 * 1000, + //不自动拼接前缀 + joinPrefix: false, + //自定义前缀 + apiUrl: baseUploadUrl, + //不自动处理 + isTransformResponse: false + } + + const defUploadFn = (params) => defHttp.post({ url: '/sys/common/upload', params }, uploadAxiosHttpConfig ); + + const bigFileUploadInit = (params) => defHttp.post({ url: '/sys/common/sectionUpload/init', params }, uploadAxiosHttpConfig ); + + const bigFileUploadUpload = (params) => defHttp.post({ url: '/sys/common/sectionUpload/upload', params }, uploadAxiosHttpConfig ); + + const bigFileUploadEnd = (params) => defHttp.post({ url: '/sys/common/sectionUpload/end', params }, uploadAxiosHttpConfig) + + /** + * 自定义文件上传方法,小于5m的走默认上传,大于5m的走切片上传 + */ + function uploadFn(customRequestData){ + const { data, file, onProgress, onSuccess, onError } = customRequestData; // 解构出a-upload的内置的方法(进度条,成功失败等) + + let fileSize = file.size; + let uploadPromiseList = []; + + if(fileSize <= chunkSize){ + let formData = new FormData(); + Object.keys(data).forEach(key => { + formData.append(key, data[key]); + }); + formData.append('file', file); + //传统直传 + defUploadFn(formData).then(res => { + console.log(`🚀 ~ 传统直传 - defUploadFn ~ res:`, res); + if(res.success){ + onProgress({ percent: 100 }, file) // 进度条 + onSuccess(res, file) // 上传文件的状态 + } else { + onError(res, res, file); + } + }).catch(err => { + console.log(`🚀 ~ 传统直传 - defUploadFn ~ err:`, err); + onError(err, err, file); + }); + } else { + //计算当前选择文件需要的分片数量 + const chunkCount = Math.ceil(fileSize / chunkSize); + console.log("文件大小:",(file.size / 1024 / 1024) + "Mb","分片数:",chunkCount); + //获取文件md5 + console.log('第一步,计算文件的md5',calFileMd5, file); + calFileMd5(file).then(fileMd5 => { + console.log(`🚀 ~ 文件的md5是:`, fileMd5); + const initUploadParams = { chunkCount, fileMd5 , ...data, }; + let successNum = 0; + + let taskAllList = []; + //切片上传(开始) + console.log('第二步,根据md5创建文件夹', initUploadParams); + bigFileUploadInit(initUploadParams).then(res => { + console.log('第三步,准备切片文件', res); + if(res.success){ + onProgress({ percent: 0 }, file); // 进度条 + //后台创建成功,下一步,分批次上传 + for (let i = 1; i <= chunkCount; i++) { + let uploadParams = { + partNumber: i, + ...initUploadParams, + file: null, + } + //分片开始位置 + let start = (i - 1) * chunkSize + //分片结束位置 + let end = Math.min(fileSize, start + chunkSize) + //取文件指定范围内的byte,从而得到分片数据 + let _chunkFile = file.slice(start, end) + console.log("第四步,开始准备文件第" + i + "个分片" + ",切片范围从" + start + "到" + end) + + uploadParams.file = _chunkFile; + let uploadFormData = new FormData(); + Object.keys(uploadParams).forEach(key => { + uploadFormData.append(key, uploadParams[key]); + }); + + console.log('第五步,准备切片文件上传参数', uploadFormData); + taskAllList.push(new Promise((resolve, reject) => { + console.log('第六步,准备切片文件上传前,', uploadFormData); + bigFileUploadUpload(uploadFormData).then(res => { + console.log('第七步,切片文件上传后,', uploadFormData, res); + if(res.success) { + successNum++; + let percent = Number(Number(successNum/chunkCount*100).toFixed(0)) + onProgress({ percent }, file) // 进度条 + resolve(res); + }else{ + reject(res); + } + }).catch(err => { + reject(err) + }); + })); + } + //任务分片,一次20片 + console.log('第八步,全部切片,根据指定线程数执行,', limitPromise, taskAllList); + limitPromise(taskAllList, runBatchNum).then(resList => { + console.log('第九步,全部切片,根据指定线程数执行完成后的结果,', resList); + //完成后再执行一个合并 + let endUploadParams = { + ...initUploadParams, + fileName: file.name, + } + console.log('第十步,全部切片,发生合并指令,', endUploadParams); + bigFileUploadEnd(endUploadParams).then(res => { + console.log(`第十一步 ~ 合并返回值 defHttp.post ~ res:`, res); + //返回跟单个上传一样的返回值, + if(res.success){ + onProgress({ percent: 100 }, file) // 进度条 + onSuccess(res, file) // 上传文件的状态 + } else { + onError(res, res, file); + } + + }).catch(err => { + console.error(`🚀 ~ 合并出现错误! defHttp.post ~ err:`, err); + onError(err, err, file); + }) + }).catch(err => { + console.error(`🚀 ~ 全部完成后,其中有错误limitPromise ~ err:`, err); + //有错误,, + onError(err, err, file); + }); + + } + //return + }) + + }) + + + } + + + // Promise.all(uploadPromiseList).then(resList => { + + // }); + } + defineExpose({ addActionsListener, }); diff --git a/src/router/routes/modules/zy/zy.ts b/src/router/routes/modules/zy/zy.ts index d6158dc..a454f52 100644 --- a/src/router/routes/modules/zy/zy.ts +++ b/src/router/routes/modules/zy/zy.ts @@ -65,7 +65,7 @@ const zuoye: AppRouteModule = { { path: 'jiaoXueDanYuanNeiRong', name: 'jiaoXueDanYuanNeiRong', - component: () => import('/@/views/zy/jiaoXueDanYuanNeiRong/index2.vue'), + component: () => import('/@/views/zy/jiaoXueDanYuanNeiRong/index.vue'), meta: { title: '教学单元内容', }, diff --git a/src/views/zy/jiaoXueDanYuanNeiRong/index.vue b/src/views/zy/jiaoXueDanYuanNeiRong/index.vue index 442729b..29dccf2 100644 --- a/src/views/zy/jiaoXueDanYuanNeiRong/index.vue +++ b/src/views/zy/jiaoXueDanYuanNeiRong/index.vue @@ -1,90 +1,108 @@