WXL
4 天以前 4d9da000fbe74d344e0e4580b138e79d4ad98ede
components/attachment/index.vue
@@ -1,55 +1,32 @@
<template>
  <view class="attachment-upload">
    <!-- 附件悬浮按钮 -->
    <view
      class="attachment-btn"
      :style="{
        right: position.right,
        bottom: position.bottom,
        backgroundColor: bgColor,
        display: showButton ? 'flex' : 'none',
      }"
      @click="togglePopup"
    >
    <view class="attachment-btn" :style="{
      right: position.right,
      bottom: position.bottom,
      backgroundColor: bgColor,
      display: showButton ? 'flex' : 'none',
    }" @click="togglePopup">
      <text>附件</text>
      <text class="badge" v-if="totalFileCount > 0">{{ totalFileCount }}</text>
    </view>
    <!-- 附件弹层 -->
    <uni-popup
      ref="popup"
      type="bottom"
      :safe-area="false"
      @change="onPopupChange"
    >
    <uni-popup ref="popup" type="bottom" :safe-area="false" @change="onPopupChange">
      <view class="attachment-popup">
        <!-- 弹层标题 -->
        <view class="popup-header">
          <text class="title">附件管理</text>
          <uni-icons
            v-if="!readonly"
            type="plus"
            size="24"
            :color="mainColor"
            @click="chooseFile"
          />
          <uni-icons v-if="!readonly" type="plus" size="24" :color="mainColor" @click="chooseFile" />
          <uni-icons type="close" size="24" color="#999" @click="closePopup" />
        </view>
        <!-- 标签页切换 -->
        <view v-if="showGradeSlip" class="attachment-tabs">
          <view
            class="tab-item"
            :class="{ active: currentTab === 'base' }"
            @click="currentTab = 'base'"
          >
          <view class="tab-item" :class="{ active: currentTab === 'base' }" @click="currentTab = 'base'">
            <text>基础附件</text>
          </view>
          <view
            class="tab-item"
            :class="{ active: currentTab === 'grade' }"
            @click="currentTab = 'grade'"
          >
          <view class="tab-item" :class="{ active: currentTab === 'grade' }" @click="currentTab = 'grade'">
            <text>成绩单附件</text>
            <text class="required-mark" v-if="isGradeRequired">*</text>
          </view>
@@ -59,17 +36,9 @@
        <scroll-view scroll-y class="file-list">
          <!-- 基础附件列表 -->
          <template v-if="currentTab === 'base' || !showGradeSlip">
            <view
              class="file-item"
              v-for="(file, index) in baseFiles"
              :key="'base-' + index"
            >
            <view class="file-item" v-for="(file, index) in baseFiles" :key="'base-' + index">
              <view class="file-icon" @click="previewFile(file)">
                <uni-icons
                  :type="getFileIcon(file.type)"
                  size="24"
                  :color="getFileColor(file.type)"
                />
                <uni-icons :type="getFileIcon(file.type)" size="24" :color="getFileColor(file.type)" />
              </view>
              <view class="file-info" @click="previewFile(file)">
                <text class="file-name">{{ file.originalFilename || file.name }}</text>
@@ -77,29 +46,16 @@
                <text class="file-status" v-if="file.status === 'uploading'">上传中...</text>
                <text class="file-status error" v-else-if="file.status === 'error'">上传失败</text>
              </view>
              <uni-icons
                v-if="!readonly"
                type="trash"
                size="20"
                color="#ff4d4f"
                @click="(e) => removeFile('base', index, e)"
              />
              <uni-icons v-if="!readonly" type="trash" size="20" color="#ff4d4f"
                @click="(e) => removeFile('base', index, e)" />
            </view>
          </template>
          <!-- 成绩单附件列表 -->
          <template v-if="currentTab === 'grade' && showGradeSlip">
            <view
              class="file-item"
              v-for="(file, index) in gradeFiles"
              :key="'grade-' + index"
            >
            <view class="file-item" v-for="(file, index) in gradeFiles" :key="'grade-' + index">
              <view class="file-icon" @click="previewFile(file)">
                <uni-icons
                  :type="getFileIcon(file.type)"
                  size="24"
                  :color="getFileColor(file.type)"
                />
                <uni-icons :type="getFileIcon(file.type)" size="24" :color="getFileColor(file.type)" />
              </view>
              <view class="file-info" @click="previewFile(file)">
                <text class="file-name">{{ file.originalFilename || file.name }}</text>
@@ -107,13 +63,8 @@
                <text class="file-status" v-if="file.status === 'uploading'">上传中...</text>
                <text class="file-status error" v-else-if="file.status === 'error'">上传失败</text>
              </view>
              <uni-icons
                v-if="!readonly"
                type="trash"
                size="20"
                color="#ff4d4f"
                @click="(e) => removeFile('grade', index, e)"
              />
              <uni-icons v-if="!readonly" type="trash" size="20" color="#ff4d4f"
                @click="(e) => removeFile('grade', index, e)" />
            </view>
          </template>
@@ -134,17 +85,9 @@
    </uni-popup>
    <!-- 文件选择器 -->
    <uni-file-picker
      ref="filePicker"
      v-if="!readonly"
      :auto-upload="false"
      file-mediatype="all"
      :limit="maxCount - currentFileList.length"
      :image-styles="imageStyles"
      @select="onFileSelect"
      @delete="onFileDelete"
      style="display: none"
    />
    <uni-file-picker ref="filePicker" v-if="!readonly" :auto-upload="false" file-mediatype="all"
      :limit="maxCount - currentFileList.length" :image-styles="imageStyles" @select="onFileSelect"
      @delete="onFileDelete" style="display: none" />
  </view>
</template>
@@ -192,9 +135,9 @@
});
const emit = defineEmits([
  "update:files",
  "update:files",
  "update:gradesFiles", // 新增事件:更新成绩单附件
  "upload",
  "upload",
  "preview",
  "upload-grade", // 新增事件:上传成绩单附件
  "upload-base" // 新增事件:上传基础附件
@@ -332,7 +275,7 @@
  // 明确确定当前是基础附件还是成绩单附件
  const isGradeTab = props.showGradeSlip && currentTab.value === 'grade';
  const targetFiles = isGradeTab ? gradeFiles.value : baseFiles.value;
  const newFiles = e.tempFiles
    .filter((file) => {
      const fileExt = file.name ? file.name.split(".").pop().toLowerCase() : "";
@@ -379,7 +322,7 @@
  if (event) {
    event.stopPropagation();
  }
  // 严格按类型删除
  if (type === 'grade') {
    gradeFiles.value.splice(index, 1);
@@ -393,7 +336,7 @@
// 预览文件
const previewFile = (file) => {
  const fullUrl = getFullUrl(file.url);
  if (file.type && supportedImageTypes.includes(file.type)) {
    // 预览图片
    const allFiles = getAllFiles();
@@ -440,7 +383,7 @@
const uploadFile = (file, type) => {
  return new Promise((resolve, reject) => {
    const token = uni.getStorageSync('token');
    uni.uploadFile({
      url: '/api/common/upload',
      filePath: file.path || file.url,
@@ -451,8 +394,8 @@
      success: (res) => {
        if (res.statusCode === 200) {
          const data = JSON.parse(res.data);
          console.log(data,'文件');
          console.log(data, '文件');
          if (data.code === 200) {
            resolve({
              ...data,
@@ -500,11 +443,11 @@
  try {
    // 分别处理基础附件和成绩单附件的上传
    const pendingBaseFiles = baseFiles.value.filter(file =>
    const pendingBaseFiles = baseFiles.value.filter(file =>
      !file.url || file.status === 'pending');
    const pendingGradeFiles = gradeFiles.value.filter(file =>
    const pendingGradeFiles = gradeFiles.value.filter(file =>
      !file.url || file.status === 'pending');
    // 上传基础附件
    for (const file of pendingBaseFiles) {
      try {
@@ -516,7 +459,7 @@
          newFileName: res.newFileName,
          originalFilename: res.originalFilename,
          status: 'success',
          size:res.size
          size: res.size
        });
        emit("upload-base", file);
      } catch (error) {
@@ -528,7 +471,7 @@
        });
      }
    }
    // 上传成绩单附件
    for (const file of pendingGradeFiles) {
      try {
@@ -551,14 +494,14 @@
        });
      }
    }
    console.log(baseFiles.value,'1');
    console.log(gradeFiles.value,'2');
    console.log(baseFiles.value, '1');
    console.log(gradeFiles.value, '2');
    // 更新文件列表
    emit("update:files", baseFiles.value);
    emit("update:gradesFiles", gradeFiles.value);
    // emit("upload", allFiles);
    uni.showToast({
      title: '上传完成',
      icon: 'success'
@@ -593,7 +536,7 @@
    display: flex;
    border-bottom: 1px solid #eee;
    margin-bottom: 20rpx;
    .tab-item {
      flex: 1;
      text-align: center;
@@ -601,13 +544,13 @@
      position: relative;
      font-size: 28rpx;
      color: #666;
      &.active {
        color: #67AFAB;
        font-weight: bold;
        border-bottom: 4rpx solid #67AFAB;
      }
      .required-mark {
        color: red;
        position: absolute;
@@ -617,7 +560,7 @@
      }
    }
  }
  // 其他样式保持不变
  .attachment-btn {
    position: fixed;
@@ -677,6 +620,7 @@
      .uni-icons {
        padding: 10rpx;
        &:active {
          opacity: 0.7;
        }
@@ -721,12 +665,12 @@
            color: #999;
            display: block;
          }
          .file-status {
            font-size: 24rpx;
            color: #666;
            display: block;
            &.error {
              color: #ff4d4f;
            }
@@ -735,6 +679,7 @@
        .uni-icons {
          padding: 10rpx;
          &:active {
            opacity: 0.7;
          }