lex 11 tháng trước cách đây
mục cha
commit
b8f247e1da

+ 72 - 73
src/components/CBreadcrumb/index.tsx

@@ -1,73 +1,72 @@
-import { defineComponent, ref, watch } from 'vue';
-import styles from './index.module.less';
-import {
-  NIcon,
-  NImage,
-  NDatePicker,
-  NSelect,
-  NSpace,
-  NBreadcrumb,
-  NBreadcrumbItem
-} from 'naive-ui';
-import icon_back from './images/icon_back.png';
-import icon_separator from './images/icon_separator.png';
-import activeArrow from './images/activeArrow.png';
-import arrow from './images/arrow.png';
-import { useRoute, useRouter } from 'vue-router';
-export default defineComponent({
-  props: {
-    list: {
-      type: Array,
-      required: true,
-      default: [] as any
-    }
-  },
-  name: 'CBreadcrumb',
-  setup(props, { emit, attrs }) {
-    const router = useRouter();
-    const route = useRoute();
-    const lastNum = ref(props.list?.length || 0);
-    const list = ref(props.list as any);
-    watch(
-      () => props.list,
-      (value: any) => {
-        console.log('list', value);
-        list.value = value;
-      },
-      { deep: true, immediate: true }
-    );
-    return () => (
-      <>
-        <div class={styles.CBreadcrumb}>
-          <NSpace align="center" wrapItem={false} size={16}>
-            <img
-              style={{ cursor: 'pointer' }}
-              src={icon_back}
-              class={styles.icon_back}
-              onClick={() => router.go(-1)}
-            />
-            <NBreadcrumb separator="">
-              {list.value &&
-                list.value.map((item: any, index: number) => (
-                  <>
-                    <NBreadcrumbItem
-                      onClick={() =>
-                        router.push({
-                          path: item.path,
-                          query: { ...route.query }
-                        })
-                      }>
-                      {item.name}
-                    </NBreadcrumbItem>
-                    {index != lastNum.value - 1 ? (
-                      <img class={styles.separator} src={icon_separator} />
-                    ) : null}
-                  </>
-                ))}
-            </NBreadcrumb>
-          </NSpace>
-        </div>
-      </>
-    );
-  }
-});
+import { defineComponent, ref, watch } from 'vue';
+import styles from './index.module.less';
+import {
+  NIcon,
+  NImage,
+  NDatePicker,
+  NSelect,
+  NSpace,
+  NBreadcrumb,
+  NBreadcrumbItem
+} from 'naive-ui';
+import icon_back from './images/icon_back.png';
+import icon_separator from './images/icon_separator.png';
+import activeArrow from './images/activeArrow.png';
+import arrow from './images/arrow.png';
+import { useRoute, useRouter } from 'vue-router';
+export default defineComponent({
+  props: {
+    list: {
+      type: Array,
+      required: true,
+      default: [] as any
+    }
+  },
+  name: 'CBreadcrumb',
+  setup(props, { emit, attrs }) {
+    const router = useRouter();
+    const route = useRoute();
+    const lastNum = ref(props.list?.length || 0);
+    const list = ref(props.list as any);
+    watch(
+      () => props.list,
+      (value: any) => {
+        list.value = value;
+      },
+      { deep: true, immediate: true }
+    );
+    return () => (
+      <>
+        <div class={styles.CBreadcrumb}>
+          <NSpace align="center" wrapItem={false} size={16}>
+            <img
+              style={{ cursor: 'pointer' }}
+              src={icon_back}
+              class={styles.icon_back}
+              onClick={() => router.go(-1)}
+            />
+            <NBreadcrumb separator="">
+              {list.value &&
+                list.value.map((item: any, index: number) => (
+                  <>
+                    <NBreadcrumbItem
+                      onClick={() =>
+                        router.push({
+                          path: item.path,
+                          query: { ...route.query }
+                        })
+                      }>
+                      {item.name}
+                    </NBreadcrumbItem>
+                    {index != lastNum.value - 1 ? (
+                      <img class={styles.separator} src={icon_separator} />
+                    ) : null}
+                  </>
+                ))}
+            </NBreadcrumb>
+          </NSpace>
+        </div>
+      </>
+    );
+  }
+});

+ 5 - 5
src/components/card-preview/song-modal/index.module.less

@@ -47,7 +47,7 @@
   background: rgba(0, 0, 0, 0.6);
   backdrop-filter: blur(26px);
   height: 80px;
-  padding: 0 26px 0 26px !important;
+  padding: 0 24px 0 24px !important;
   transition: all 0.3s;
   display: flex;
   align-items: center;
@@ -56,7 +56,7 @@
     display: flex;
     justify-content: space-between;
     color: #fff;
-    padding: 4px 12px 4px;
+    padding: 4px 12px 4px 18px;
     font-size: 24px;
     font-weight: 600;
     line-height: 33px;
@@ -112,8 +112,8 @@
     height: 36px;
     background-color: transparent;
     cursor: pointer;
-    margin-left: 22px;
-    margin-right: 10px;
+    margin-left: 12px;
+    margin-right: 12px;
 
     &>img {
       width: 100%;
@@ -146,7 +146,7 @@
   .iconDownload {
     width: 48px;
     height: 48px;
-    margin-left: 22px;
+    margin-left: 14px;
     border-radius: 6px;
     background-color: transparent;
     cursor: pointer;

+ 27 - 4
src/components/card-preview/song-modal/index.tsx

@@ -1,4 +1,11 @@
-import { defineComponent, reactive, ref, nextTick } from 'vue';
+import {
+  defineComponent,
+  reactive,
+  ref,
+  nextTick,
+  onMounted,
+  onUnmounted
+} from 'vue';
 import styles from './index.module.less';
 import iconplay from '@views/attend-class/image/icon-pause.png';
 import iconpause from '@views/attend-class/image/icon-play.png';
@@ -121,11 +128,12 @@ export default defineComponent({
       const filename =
         props.item.title +
         (props.item.studentName ? '-' + props.item.studentName : '');
+      const sfixx = fileUrl.substring(fileUrl.lastIndexOf('.'));
       // 发起Fetch请求
       fetch(fileUrl)
         .then(response => response.blob())
         .then(blob => {
-          saveAs(blob, filename || new Date().getTime() + '.mp3');
+          saveAs(blob, (filename || new Date().getTime()) + sfixx);
         })
         .catch(() => {
           message.error('下载失败');
@@ -173,13 +181,11 @@ export default defineComponent({
       if (el) {
         if (isElementFullscreen(el)) {
           exitFullscreen();
-          audioForms.isFullScreen = false;
         } else {
           (el.requestFullscreen && el.requestFullscreen()) ||
             (el.mozRequestFullScreen && el.mozRequestFullScreen()) ||
             (el.webkitRequestFullscreen && el.webkitRequestFullscreen()) ||
             (el.msRequestFullscreen && el.msRequestFullscreen());
-          audioForms.isFullScreen = true;
         }
         // audioForms.isFullScreen = isElementFullscreen(el);
       }
@@ -202,6 +208,23 @@ export default defineComponent({
       }, 3000);
     };
 
+    const onFullScreenChange = () => {
+      if (document.fullscreenElement) {
+        audioForms.isFullScreen = true;
+      } else {
+        audioForms.isFullScreen = false;
+      }
+    };
+
+    onMounted(() => {
+      console.log(props.item, 'props.item');
+      window.addEventListener('fullscreenchange', onFullScreenChange);
+    });
+
+    onUnmounted(() => {
+      window.removeEventListener('fullscreenchange', onFullScreenChange);
+    });
+
     return () => (
       <div class={styles.audioWrap} id={videoId} onClick={setModelOpen2}>
         <div class={styles.audioContainer}>

+ 3 - 3
src/components/card-preview/video-modal/index.module.less

@@ -58,7 +58,7 @@
       display: flex;
       justify-content: space-between;
       color: #fff;
-      padding: 4px 0 4px 12px;
+      padding: 4px 12px 4px 26px;
       font-size: max(24px, 14Px);
       font-weight: 600;
       line-height: 33px;
@@ -130,7 +130,7 @@
       height: 36px;
       background-color: transparent;
       cursor: pointer;
-      margin: 0 18px;
+      margin: 0 14px 0 12px;
 
       &>img {
         width: 100%;
@@ -163,7 +163,7 @@
     .iconDownload {
       width: 48px;
       height: 48px;
-      margin-left: 22px;
+      margin-left: 14px;
       border-radius: 6px;
       background-color: transparent;
       cursor: pointer;

+ 23 - 4
src/components/card-preview/video-modal/index.tsx

@@ -1,4 +1,11 @@
-import { defineComponent, nextTick, onMounted, reactive, toRefs } from 'vue';
+import {
+  defineComponent,
+  nextTick,
+  onMounted,
+  onUnmounted,
+  reactive,
+  toRefs
+} from 'vue';
 // import 'plyr/dist/plyr.css';
 // import Plyr from 'plyr';
 import { ref } from 'vue';
@@ -194,25 +201,36 @@ export default defineComponent({
       if (el) {
         if (isElementFullscreen(el)) {
           exitFullscreen();
-          videoFroms.isFullScreen = false;
         } else {
           (el.requestFullscreen && el.requestFullscreen()) ||
             (el.mozRequestFullScreen && el.mozRequestFullScreen()) ||
             (el.webkitRequestFullscreen && el.webkitRequestFullscreen()) ||
             (el.msRequestFullscreen && el.msRequestFullscreen());
-          videoFroms.isFullScreen = true;
         }
         // videoFroms.isFullScreen = isElementFullscreen(el);
       }
     };
 
+    const onFullScreenChange = () => {
+      if (document.fullscreenElement) {
+        videoFroms.isFullScreen = true;
+      } else {
+        videoFroms.isFullScreen = false;
+      }
+    };
+
     onMounted(() => {
       videoItem.value = TCPlayer(videoID, {
         appID: '',
         controls: false
       }); // player-container-id 为播放器容器 ID,必须与 html 中一致
       __init();
+      window.addEventListener('fullscreenchange', onFullScreenChange);
+    });
+    onUnmounted(() => {
+      window.removeEventListener('fullscreenchange', onFullScreenChange);
     });
+
     expose({
       // changePlayBtn,
       toggleHideControl
@@ -259,7 +277,8 @@ export default defineComponent({
 
                 <div
                   class={styles.actionBtnSpeed}
-                  onClick={() => {
+                  onClick={(e: any) => {
+                    e.stopPropagation();
                     videoFroms.speedControl = !videoFroms.speedControl;
                   }}>
                   <img src={iconSpeed} />

+ 394 - 395
src/components/upload-file/index.tsx

@@ -1,395 +1,394 @@
-import {
-  NButton,
-  NModal,
-  NUpload,
-  UploadCustomRequestOptions,
-  UploadFileInfo,
-  useMessage
-} from 'naive-ui';
-import { defineComponent, watch, PropType, reactive, ref } from 'vue';
-import { policy } from './api';
-import Copper from './copper';
-import axios from 'axios';
-import {
-  getUploadSign,
-  onFileUpload,
-  onOnlyFileUpload
-} from '/src/helpers/oss-file-upload';
-
-export default defineComponent({
-  name: 'upload-file',
-  props: {
-    fileList: {
-      type: String,
-      default: ''
-    },
-    imageList: {
-      type: Array,
-      default: () => []
-    },
-    accept: {
-      // 支持类型
-      type: String,
-      default: '.jpg,.png,.jpeg,.gif'
-    },
-    listType: {
-      type: String as PropType<'image' | 'image-card'>,
-      default: 'image-card'
-    },
-    showType: {
-      type: String as PropType<'default' | 'custom'>,
-      default: 'default'
-    },
-    showFileList: {
-      type: Boolean,
-      default: true
-    },
-    // width: {
-    //   type: Number,
-    //   default: 96
-    // },
-    // height: {
-    //   type: Number,
-    //   default: 96
-    // },
-    text: {
-      type: String as PropType<string>,
-      default: '上传文件'
-    },
-    size: {
-      // 文件大小
-      type: Number as PropType<number>,
-      default: 5
-    },
-    max: {
-      type: Number as PropType<number>,
-      default: 1
-    },
-    multiple: {
-      type: Boolean as PropType<boolean>,
-      default: false
-    },
-    disabled: {
-      type: Boolean as PropType<boolean>,
-      default: false
-    },
-    tips: {
-      type: String as PropType<string>,
-      default: ''
-    },
-    bucketName: {
-      type: String,
-      default: 'gyt'
-    },
-    path: {
-      type: String,
-      default: ''
-    },
-    fileName: {
-      type: String,
-      default: ''
-    },
-    cropper: {
-      // 是否裁切, 只有图片才支持
-      type: Boolean as PropType<boolean>,
-      default: false
-    },
-    options: {
-      type: Object,
-      default: () => {
-        return {
-          viewMode: 0,
-          autoCrop: true, //是否默认生成截图框
-          enlarge: 1, //  图片放大倍数
-          autoCropWidth: 200, //默认生成截图框宽度
-          autoCropHeight: 200, //默认生成截图框高度
-          fixedBox: false, //是否固定截图框大小 不允许改变
-          previewsCircle: true, //预览图是否是原图形
-          title: '上传图片'
-        };
-      }
-    }
-  },
-  // readFileInputEventAsArrayBuffer 只会在文件的时间回调
-  emits: [
-    'update:fileList',
-    'close',
-    'readFileInputEventAsArrayBuffer',
-    'remove'
-  ],
-  setup(props, { emit, expose, slots }) {
-    const ossUploadUrl = `https://${props.bucketName}.ks3-cn-beijing.ksyuncs.com/`;
-    const message = useMessage();
-    const visiable = ref<boolean>(false);
-    const btnLoading = ref<boolean>(false);
-    const tempFiileBuffer = ref();
-    const uploadRef = ref();
-    // const state = reactive({
-    //   policy: '',
-    //   signature: '',
-    //   key: '',
-    //   KSSAccessKeyId: '',
-    //   acl: 'public-read',
-    //   name: ''
-    // }) as any;
-    const state = reactive([]) as any;
-
-    const fileListRef = ref<UploadFileInfo[]>([]);
-    const initFileList = () => {
-      if (props.fileList) {
-        const splitName = props.fileList.split('/');
-        fileListRef.value = [
-          {
-            id: new Date().getTime().toString(),
-            name: splitName[splitName.length - 1],
-            status: 'finished',
-            url: props.fileList
-          }
-        ];
-      } else if (Array.isArray(props.imageList)) {
-        const list: any = [];
-        props.imageList.forEach((n: any) => {
-          const splitName = n.split('/');
-          list.push({
-            id: Date.now().toString(),
-            name: splitName[splitName.length - 1],
-            status: 'finished',
-            url: n
-          });
-        });
-        fileListRef.value = list;
-      } else {
-        fileListRef.value = [];
-      }
-    };
-    initFileList();
-    watch(
-      () => props.imageList,
-      () => {
-        initFileList();
-      }
-    );
-    watch(
-      () => props.fileList,
-      () => {
-        console.log('list');
-        initFileList();
-      }
-    );
-    const handleClearFile = () => {
-      uploadRef.value?.clear();
-      console.log('清空', uploadRef.value);
-    };
-    expose({
-      handleClearFile
-    });
-
-    const CropperModal = ref();
-    const onBeforeUpload = async (options: any) => {
-      const file = options.file;
-      // 文件大小
-      let isLt2M = true;
-      if (props.size) {
-        isLt2M = file.file.size / 1024 / 1024 < props.size;
-        if (!isLt2M) {
-          message.error(`文件大小不能超过${props.size}M`);
-          return false;
-        }
-      }
-
-      if (!isLt2M) {
-        return isLt2M;
-      }
-      // 是否裁切
-      if (props.cropper) {
-        getBase64(file.file, (imageUrl: any) => {
-          const target = Object.assign({}, props.options, {
-            img: imageUrl,
-            name: file.file.name // 上传文件名
-          });
-          visiable.value = true;
-
-          setTimeout(() => {
-            CropperModal.value?.edit(target);
-            console.log(CropperModal.value, 'cropper');
-          }, 100);
-        });
-        return false;
-      }
-      try {
-        btnLoading.value = true;
-        console.log(props.path, file.file);
-        const name = file.file.name;
-        const suffix = name.slice(name.lastIndexOf('.'));
-        // const months = dayjs().format('MM')
-        const fileName = `${props.path}${
-          props.fileName || Date.now() + suffix
-        }`;
-        const obj = {
-          filename: fileName,
-          bucketName: props.bucketName,
-          postData: {
-            filename: fileName,
-            acl: 'public-read',
-            key: fileName,
-            unknowValueField: []
-          }
-        };
-        // const { data } = await policy(obj);
-
-        // state.policy = data.policy;
-        // state.signature = data.signature;
-        // state.key = fileName;
-        // state.KSSAccessKeyId = data.kssAccessKeyId;
-        // state.name = fileName;
-
-        // tempFiileBuffer.value = file.file;
-        const { data } = await getUploadSign(obj);
-        state.push({
-          id: file.id,
-          tempFiileBuffer: file.file,
-          policy: data.policy,
-          signature: data.signature,
-          acl: 'public-read',
-          key: fileName,
-          KSSAccessKeyId: data.kssAccessKeyId,
-          name: fileName
-        });
-      } catch {
-        //
-        // message.error('上传失败')
-        btnLoading.value = false;
-        return false;
-      }
-      return true;
-    };
-    const getBase64 = async (img: any, callback: any) => {
-      const reader = new FileReader();
-      reader.addEventListener('load', () => callback(reader.result));
-      reader.readAsDataURL(img);
-    };
-    const onFinish = (options: any) => {
-      const item = state.find((c: any) => c.id == options.file.id);
-      // const url = ossUploadUrl + state.key;
-      emit('update:fileList', options.file.url);
-      emit('readFileInputEventAsArrayBuffer', item.tempFiileBuffer);
-      // options.file.url = url;
-      visiable.value = false;
-      btnLoading.value = false;
-    };
-    const onRemove = async (options: any) => {
-      console.log('🚀 ~ options', options);
-      emit('update:fileList', '');
-      emit('remove');
-      btnLoading.value = false;
-    };
-
-    const onCustomRequest = ({
-      file,
-      // data,
-      // headers,
-      // withCredentials,
-      action,
-      onFinish,
-      onError,
-      onProgress
-    }: UploadCustomRequestOptions) => {
-      const item = state.find((c: any) => {
-        return c.id == file.id;
-      });
-
-      item.file = file;
-      onFileUpload({ file, action, data: item, onProgress, onFinish, onError });
-    };
-
-    // 裁切失败
-    // const cropperNo = () => {}
-    // 裁切成功
-    const cropperOk = async (blob: any) => {
-      try {
-        // const months = dayjs().format('MM')
-        const fileName = `${props.path}${
-          props.fileName || new Date().getTime() + '.png'
-        }`;
-        const obj = {
-          filename: fileName,
-          bucketName: props.bucketName,
-          postData: {
-            filename: fileName,
-            acl: 'public-read',
-            key: fileName,
-            unknowValueField: []
-          }
-        };
-        // const { data } = await policy(obj);
-        const { data } = await getUploadSign(obj);
-        const formData = {
-          policy: data.policy,
-          signature: data.signature,
-          acl: 'public-read',
-          key: fileName,
-          KSSAccessKeyId: data.kssAccessKeyId,
-          name: fileName,
-          file: blob
-        };
-
-        const res = await onOnlyFileUpload(ossUploadUrl, formData);
-        console.log(res, 'upload');
-        emit('update:fileList', res);
-        visiable.value = false;
-      } catch {
-        //
-        // message.error('上传失败');
-        return false;
-      }
-    };
-    return () => (
-      <div>
-        <NUpload
-          ref={uploadRef}
-          action={ossUploadUrl}
-          // data={state}
-          customRequest={onCustomRequest}
-          v-model:fileList={fileListRef.value}
-          listType={props.listType}
-          accept={props.accept}
-          multiple={props.multiple}
-          max={props.max}
-          disabled={props.disabled}
-          showFileList={props.showFileList}
-          showPreviewButton
-          onBeforeUpload={(options: any) => onBeforeUpload(options)}
-          onFinish={(options: any) => onFinish(options)}
-          onRemove={(options: any) => onRemove(options)}>
-          {props.showType === 'default' && props.listType === 'image' && (
-            <NButton loading={btnLoading.value} type="primary">
-              {props.text}
-            </NButton>
-          )}
-          {props.showType === 'custom' && slots.custom && slots.custom()}
-        </NUpload>
-        {props.tips && (
-          <p style="font-size: 13px; color: #666; padding-top: 4px;">
-            {props.tips}
-          </p>
-        )}
-
-        <NModal
-          v-model:show={visiable.value}
-          preset="dialog"
-          showIcon={false}
-          class={['modalTitle background']}
-          title="上传图片"
-          style={{ width: '800px' }}>
-          {/* @cropper-no="error" @cropper-ok="success" */}
-          <Copper
-            // ref="CropperModal"
-            ref={CropperModal}
-            onClose={() => (visiable.value = false)}
-            onCropperOk={cropperOk}
-          />
-        </NModal>
-      </div>
-    );
-  }
-});
+import {
+  NButton,
+  NModal,
+  NUpload,
+  UploadCustomRequestOptions,
+  UploadFileInfo,
+  useMessage
+} from 'naive-ui';
+import { defineComponent, watch, PropType, reactive, ref } from 'vue';
+import { policy } from './api';
+import Copper from './copper';
+import axios from 'axios';
+import {
+  getUploadSign,
+  onFileUpload,
+  onOnlyFileUpload
+} from '/src/helpers/oss-file-upload';
+
+export default defineComponent({
+  name: 'upload-file',
+  props: {
+    fileList: {
+      type: String,
+      default: ''
+    },
+    imageList: {
+      type: Array,
+      default: () => []
+    },
+    accept: {
+      // 支持类型
+      type: String,
+      default: '.jpg,.png,.jpeg,.gif'
+    },
+    listType: {
+      type: String as PropType<'image' | 'image-card'>,
+      default: 'image-card'
+    },
+    showType: {
+      type: String as PropType<'default' | 'custom'>,
+      default: 'default'
+    },
+    showFileList: {
+      type: Boolean,
+      default: true
+    },
+    // width: {
+    //   type: Number,
+    //   default: 96
+    // },
+    // height: {
+    //   type: Number,
+    //   default: 96
+    // },
+    text: {
+      type: String as PropType<string>,
+      default: '上传文件'
+    },
+    size: {
+      // 文件大小
+      type: Number as PropType<number>,
+      default: 5
+    },
+    max: {
+      type: Number as PropType<number>,
+      default: 1
+    },
+    multiple: {
+      type: Boolean as PropType<boolean>,
+      default: false
+    },
+    disabled: {
+      type: Boolean as PropType<boolean>,
+      default: false
+    },
+    tips: {
+      type: String as PropType<string>,
+      default: ''
+    },
+    bucketName: {
+      type: String,
+      default: 'gyt'
+    },
+    path: {
+      type: String,
+      default: ''
+    },
+    fileName: {
+      type: String,
+      default: ''
+    },
+    cropper: {
+      // 是否裁切, 只有图片才支持
+      type: Boolean as PropType<boolean>,
+      default: false
+    },
+    options: {
+      type: Object,
+      default: () => {
+        return {
+          viewMode: 0,
+          autoCrop: true, //是否默认生成截图框
+          enlarge: 1, //  图片放大倍数
+          autoCropWidth: 200, //默认生成截图框宽度
+          autoCropHeight: 200, //默认生成截图框高度
+          fixedBox: false, //是否固定截图框大小 不允许改变
+          previewsCircle: true, //预览图是否是原图形
+          title: '上传图片'
+        };
+      }
+    }
+  },
+  // readFileInputEventAsArrayBuffer 只会在文件的时间回调
+  emits: [
+    'update:fileList',
+    'close',
+    'readFileInputEventAsArrayBuffer',
+    'remove'
+  ],
+  setup(props, { emit, expose, slots }) {
+    const ossUploadUrl = `https://${props.bucketName}.ks3-cn-beijing.ksyuncs.com/`;
+    const message = useMessage();
+    const visiable = ref<boolean>(false);
+    const btnLoading = ref<boolean>(false);
+    const tempFiileBuffer = ref();
+    const uploadRef = ref();
+    // const state = reactive({
+    //   policy: '',
+    //   signature: '',
+    //   key: '',
+    //   KSSAccessKeyId: '',
+    //   acl: 'public-read',
+    //   name: ''
+    // }) as any;
+    const state = reactive([]) as any;
+
+    const fileListRef = ref<UploadFileInfo[]>([]);
+    const initFileList = () => {
+      if (props.fileList) {
+        const splitName = props.fileList.split('/');
+        fileListRef.value = [
+          {
+            id: new Date().getTime().toString(),
+            name: splitName[splitName.length - 1],
+            status: 'finished',
+            url: props.fileList
+          }
+        ];
+      } else if (Array.isArray(props.imageList)) {
+        const list: any = [];
+        props.imageList.forEach((n: any) => {
+          const splitName = n.split('/');
+          list.push({
+            id: Date.now().toString(),
+            name: splitName[splitName.length - 1],
+            status: 'finished',
+            url: n
+          });
+        });
+        fileListRef.value = list;
+      } else {
+        fileListRef.value = [];
+      }
+    };
+    initFileList();
+    watch(
+      () => props.imageList,
+      () => {
+        initFileList();
+      }
+    );
+    watch(
+      () => props.fileList,
+      () => {
+        initFileList();
+      }
+    );
+    const handleClearFile = () => {
+      uploadRef.value?.clear();
+      console.log('清空', uploadRef.value);
+    };
+    expose({
+      handleClearFile
+    });
+
+    const CropperModal = ref();
+    const onBeforeUpload = async (options: any) => {
+      const file = options.file;
+      // 文件大小
+      let isLt2M = true;
+      if (props.size) {
+        isLt2M = file.file.size / 1024 / 1024 < props.size;
+        if (!isLt2M) {
+          message.error(`文件大小不能超过${props.size}M`);
+          return false;
+        }
+      }
+
+      if (!isLt2M) {
+        return isLt2M;
+      }
+      // 是否裁切
+      if (props.cropper) {
+        getBase64(file.file, (imageUrl: any) => {
+          const target = Object.assign({}, props.options, {
+            img: imageUrl,
+            name: file.file.name // 上传文件名
+          });
+          visiable.value = true;
+
+          setTimeout(() => {
+            CropperModal.value?.edit(target);
+            console.log(CropperModal.value, 'cropper');
+          }, 100);
+        });
+        return false;
+      }
+      try {
+        btnLoading.value = true;
+        console.log(props.path, file.file);
+        const name = file.file.name;
+        const suffix = name.slice(name.lastIndexOf('.'));
+        // const months = dayjs().format('MM')
+        const fileName = `${props.path}${
+          props.fileName || Date.now() + suffix
+        }`;
+        const obj = {
+          filename: fileName,
+          bucketName: props.bucketName,
+          postData: {
+            filename: fileName,
+            acl: 'public-read',
+            key: fileName,
+            unknowValueField: []
+          }
+        };
+        // const { data } = await policy(obj);
+
+        // state.policy = data.policy;
+        // state.signature = data.signature;
+        // state.key = fileName;
+        // state.KSSAccessKeyId = data.kssAccessKeyId;
+        // state.name = fileName;
+
+        // tempFiileBuffer.value = file.file;
+        const { data } = await getUploadSign(obj);
+        state.push({
+          id: file.id,
+          tempFiileBuffer: file.file,
+          policy: data.policy,
+          signature: data.signature,
+          acl: 'public-read',
+          key: fileName,
+          KSSAccessKeyId: data.kssAccessKeyId,
+          name: fileName
+        });
+      } catch {
+        //
+        // message.error('上传失败')
+        btnLoading.value = false;
+        return false;
+      }
+      return true;
+    };
+    const getBase64 = async (img: any, callback: any) => {
+      const reader = new FileReader();
+      reader.addEventListener('load', () => callback(reader.result));
+      reader.readAsDataURL(img);
+    };
+    const onFinish = (options: any) => {
+      const item = state.find((c: any) => c.id == options.file.id);
+      // const url = ossUploadUrl + state.key;
+      emit('update:fileList', options.file.url);
+      emit('readFileInputEventAsArrayBuffer', item.tempFiileBuffer);
+      // options.file.url = url;
+      visiable.value = false;
+      btnLoading.value = false;
+    };
+    const onRemove = async (options: any) => {
+      console.log('🚀 ~ options', options);
+      emit('update:fileList', '');
+      emit('remove');
+      btnLoading.value = false;
+    };
+
+    const onCustomRequest = ({
+      file,
+      // data,
+      // headers,
+      // withCredentials,
+      action,
+      onFinish,
+      onError,
+      onProgress
+    }: UploadCustomRequestOptions) => {
+      const item = state.find((c: any) => {
+        return c.id == file.id;
+      });
+
+      item.file = file;
+      onFileUpload({ file, action, data: item, onProgress, onFinish, onError });
+    };
+
+    // 裁切失败
+    // const cropperNo = () => {}
+    // 裁切成功
+    const cropperOk = async (blob: any) => {
+      try {
+        // const months = dayjs().format('MM')
+        const fileName = `${props.path}${
+          props.fileName || new Date().getTime() + '.png'
+        }`;
+        const obj = {
+          filename: fileName,
+          bucketName: props.bucketName,
+          postData: {
+            filename: fileName,
+            acl: 'public-read',
+            key: fileName,
+            unknowValueField: []
+          }
+        };
+        // const { data } = await policy(obj);
+        const { data } = await getUploadSign(obj);
+        const formData = {
+          policy: data.policy,
+          signature: data.signature,
+          acl: 'public-read',
+          key: fileName,
+          KSSAccessKeyId: data.kssAccessKeyId,
+          name: fileName,
+          file: blob
+        };
+
+        const res = await onOnlyFileUpload(ossUploadUrl, formData);
+        console.log(res, 'upload');
+        emit('update:fileList', res);
+        visiable.value = false;
+      } catch {
+        //
+        // message.error('上传失败');
+        return false;
+      }
+    };
+    return () => (
+      <div>
+        <NUpload
+          ref={uploadRef}
+          action={ossUploadUrl}
+          // data={state}
+          customRequest={onCustomRequest}
+          v-model:fileList={fileListRef.value}
+          listType={props.listType}
+          accept={props.accept}
+          multiple={props.multiple}
+          max={props.max}
+          disabled={props.disabled}
+          showFileList={props.showFileList}
+          showPreviewButton
+          onBeforeUpload={(options: any) => onBeforeUpload(options)}
+          onFinish={(options: any) => onFinish(options)}
+          onRemove={(options: any) => onRemove(options)}>
+          {props.showType === 'default' && props.listType === 'image' && (
+            <NButton loading={btnLoading.value} type="primary">
+              {props.text}
+            </NButton>
+          )}
+          {props.showType === 'custom' && slots.custom && slots.custom()}
+        </NUpload>
+        {props.tips && (
+          <p style="font-size: 13px; color: #666; padding-top: 4px;">
+            {props.tips}
+          </p>
+        )}
+
+        <NModal
+          v-model:show={visiable.value}
+          preset="dialog"
+          showIcon={false}
+          class={['modalTitle background']}
+          title="上传图片"
+          style={{ width: '800px' }}>
+          {/* @cropper-no="error" @cropper-ok="success" */}
+          <Copper
+            // ref="CropperModal"
+            ref={CropperModal}
+            onClose={() => (visiable.value = false)}
+            onCropperOk={cropperOk}
+          />
+        </NModal>
+      </div>
+    );
+  }
+});

+ 15 - 0
src/views/homework-record/detail/index.module.less

@@ -248,4 +248,19 @@
 
 .wordDetailModel {
   width: 1012px;
+}
+
+.isok {
+  font-weight: 600;
+  color: #333333;
+}
+
+.ison {
+  font-weight: 600;
+  color: #ea4132;
+}
+
+.nosub {
+  font-weight: 600;
+  color: #aaa;
 }