fuliqi
2024-06-18 2729bd4b9b375466cc02ab4ea8edb136f5acfde1
教学资源
3个文件已修改
3个文件已添加
526 ■■■■■ 已修改文件
package-lock.json 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/educationResource.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/UploadC.vue 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/education/resource/list.vue 322 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json
@@ -9,6 +9,7 @@
            "version": "3.9.0",
            "license": "AGPL-3.0",
            "dependencies": {
                "@vue-office/pdf": "^2.0.2",
                "axios": "^0.19.2",
                "clipboard": "^2.0.11",
                "codemirror": "^5.65.16",
@@ -2289,6 +2290,22 @@
            "dev": true,
            "engines": {
                "node": ">= 8"
            }
        },
        "node_modules/@vue-office/pdf": {
            "version": "2.0.2",
            "resolved": "https://registry.npmmirror.com/@vue-office/pdf/-/pdf-2.0.2.tgz",
            "integrity": "sha512-bQFqGxSOnKbvCS7OoJniYoTz1VIm1XOrRD27Msorxny9NFJ8RsQK1A4uhlnISJRFMaUwp1qlcVE9jMAhsiIyjg==",
            "hasInstallScript": true,
            "peerDependencies": {
                "@vue/composition-api": "^1.7.1",
                "vue": "^2.0.0 || >=3.0.0",
                "vue-demi": "^0.14.6"
            },
            "peerDependenciesMeta": {
                "@vue/composition-api": {
                    "optional": true
                }
            }
        },
        "node_modules/@vue/babel-helper-vue-jsx-merge-props": {
@@ -17408,6 +17425,32 @@
            "resolved": "https://registry.npmmirror.com/vue-count-to/-/vue-count-to-1.0.13.tgz",
            "integrity": "sha512-6R4OVBVNtQTlcbXu6SJ8ENR35M2/CdWt3Jmv57jOUM+1ojiFmjVGvZPH8DfHpMDSA+ITs+EW5V6qthADxeyYOQ=="
        },
        "node_modules/vue-demi": {
            "version": "0.14.8",
            "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.8.tgz",
            "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
            "hasInstallScript": true,
            "peer": true,
            "bin": {
                "vue-demi-fix": "bin/vue-demi-fix.js",
                "vue-demi-switch": "bin/vue-demi-switch.js"
            },
            "engines": {
                "node": ">=12"
            },
            "funding": {
                "url": "https://github.com/sponsors/antfu"
            },
            "peerDependencies": {
                "@vue/composition-api": "^1.0.0-rc.1",
                "vue": "^3.0.0-0 || ^2.6.0"
            },
            "peerDependenciesMeta": {
                "@vue/composition-api": {
                    "optional": true
                }
            }
        },
        "node_modules/vue-eslint-parser": {
            "version": "5.0.0",
            "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz",
@@ -20555,6 +20598,12 @@
                    "dev": true
                }
            }
        },
        "@vue-office/pdf": {
            "version": "2.0.2",
            "resolved": "https://registry.npmmirror.com/@vue-office/pdf/-/pdf-2.0.2.tgz",
            "integrity": "sha512-bQFqGxSOnKbvCS7OoJniYoTz1VIm1XOrRD27Msorxny9NFJ8RsQK1A4uhlnISJRFMaUwp1qlcVE9jMAhsiIyjg==",
            "requires": {}
        },
        "@vue/babel-helper-vue-jsx-merge-props": {
            "version": "1.4.0",
@@ -32599,6 +32648,13 @@
            "resolved": "https://registry.npmmirror.com/vue-count-to/-/vue-count-to-1.0.13.tgz",
            "integrity": "sha512-6R4OVBVNtQTlcbXu6SJ8ENR35M2/CdWt3Jmv57jOUM+1ojiFmjVGvZPH8DfHpMDSA+ITs+EW5V6qthADxeyYOQ=="
        },
        "vue-demi": {
            "version": "0.14.8",
            "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.8.tgz",
            "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
            "peer": true,
            "requires": {}
        },
        "vue-eslint-parser": {
            "version": "5.0.0",
            "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz",
package.json
@@ -35,6 +35,7 @@
        ]
    },
    "dependencies": {
        "@vue-office/pdf": "^2.0.2",
        "axios": "^0.19.2",
        "clipboard": "^2.0.11",
        "codemirror": "^5.65.16",
src/api/educationResource.js
New file
@@ -0,0 +1,9 @@
import { post, get } from '@/utils/request'
export default {
  page: query => get('/api/admin/education/resource/page', query),
  add: data => post('/api/admin/education/resource', data),
  update: data => post('/api/admin/education/resource/edit', data),
  list: () => post('/api/admin/education/resource/list'),
  remove: (data) => post('/api/admin/education/resource/remove', data)
}
src/components/UploadC.vue
New file
@@ -0,0 +1,132 @@
<template>
  <div>
    <el-upload :action="uploadUrl" :show-file-list="true" :limit="uploadNum" :accept="acceptList" multiple
      :file-list="fileUrl" :on-remove="handleRemove" :before-remove="beforeRemove" :on-success="handleUploadSuccess"
      :before-upload="beforeUpload">
      <el-button size="small" type="primary">点击上传</el-button>
      <div slot="tip" class="el-upload__tip">只能上传pdf、mp4、avi、png、jpg、jpge文件,且不超过{{ fileSizeLimitM }}M</div>
      <div v-if="fileUrl && fileUrl.length > 0 && uploadNum === 1">
        <video controls class="returnShow" v-if="fileType === 'video'" :src="'/api/files/' + fileUrl[0].url"></video>
        <img class="returnShow" v-if="fileType === 'img'" :src="'/api/files/' + fileUrl[0].url" />
      </div>
    </el-upload>
  </div>
</template>
<script>
export default {
  name: "UploadC",
  props: {
    uploadNum: {
      required: false,
      default: 1,
      type: Number
    },
    fileType: {
      required: false,
      type: String
    },
    fileSizeLimitM: {
      required: false,
      default: 1,
      type: Number
    },
    fileUrl: {
      type: Array,
      required: true,
      default: () => []
    }
  },
  data() {
    return {
      uploadUrl: "http://localhost:8000/api/upload/upload",
      fileTypeList: {
        'video': ['mp4', 'avi'],
        'img': ['jpg', 'png', 'jpeg'],
        'pdf': ['pdf'],
        'file': ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'png', 'jpg', 'jpeg', 'pdf'],
      }
    };
  },
  methods: {
    clearFile() {
      this.fileUrl = [];
    },
    beforeRemove(file, fileList) {
      if (file && file.status === "success") {
        return this.$confirm(`确定移除 ${file.name}?`);
      }
    },
    handleRemove(file, fileList) {
      this.$emit('removeFile', this.fileUrl, file.name);
    },
    handleUploadSuccess(res, file) {
      this.fileUrl.push(res.data);
      this.$emit('getUploadUrl', this.fileUrl);
    },
    beforeUpload(file) {
      const { type } = file;
      const typeList = this.fileTypeList[this.fileType];
      let limitType = true;
      if (typeList) {
        const tempType = typeList.find(item => {
          if (type.includes(item)) {
            return true;
          } else {
            return false;
          }
        });
        if (!tempType) {
          this.$message.error(`请上传正确的文件格式!`);
          limitType = false;
        }
      }
      const limit = file.size / 1024 / 1024 < this.fileSizeLimitM;
      if (!limit) {
        this.$message.error(`上传文件大小不能超过 ${this.fileSizeLimitM}MB!`);
      }
      return limitType && limit;
    },
  },
  computed: {
    acceptList() {
      let temp = '.*';
      if (this.fileTypeList[this.fileType]) {
        temp = this.fileTypeList[this.fileType].map(item => '.' + item).join(',');
      }
      return temp;
    }
  }
};
</script>
<style scoped>
.returnShow {
  width: 300px;
  height: 200px;
}
.avatar-uploader {
  text-align: center;
  width: 100%
}
.avatar-uploader-icon:hover {
  border-color: #409EFF;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  text-align: center;
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
}
.avatar {
  display: block;
}
</style>
src/router.js
@@ -243,6 +243,12 @@
        name: 'EducationSubjectEditPage',
        meta: { title: '学科编辑', noCache: true, activeMenu: '/education/subject/list' },
        hidden: true
      },
      {
        path: 'resource/list',
        component: () => import('@/views/education/resource/list'),
        name: 'EducationResourcePage',
        meta: { title: '教学资源', noCache: true }
      }
    ]
  },
src/views/education/resource/list.vue
New file
@@ -0,0 +1,322 @@
<template>
  <div class="warp">
    <div class="search">
      <el-form :inline="true" :model="searchForm" class="demo-form-inline">
        <el-form-item label="主题">
          <el-input v-model="searchForm.introduction" size="small" placeholder="主题内容" clearable @clear="page"></el-input>
        </el-form-item>
        <el-form-item label="科目">
          <el-select v-model="searchForm.subjectId" clearable @clear="page" @change="page" placeholder="科目">
            <el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id">
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="page" size="small">查询</el-button>
        </el-form-item>
      </el-form>
      <div>
        <el-button type="primary" @click="handlerAdd" size="small">添加</el-button>
        <el-button type="danger" @click="batchRemove" size="small" style="margin-left: 5px">删除</el-button>
      </div>
    </div>
    <el-table :data="tableData" border @selection-change="handleSelectionChange" style="width: 100%">
      <el-table-column type="selection" width="55">
      </el-table-column>
      <el-table-column fixed prop="introduction" label="主题简介">
      </el-table-column>
      <el-table-column prop="typeName" label="科目">
      </el-table-column>
      <el-table-column prop="contentType" :formatter="typeFormatter" label="文件类型">
      </el-table-column>
      <el-table-column label="文件内容" width="240">
        <template slot-scope="scope">
          <video controls v-if="scope.row.contentType === 'video'" :src="'/api/files/' + scope.row.contentUrl.url"
                 class="showContent" />
          <el-image v-if="scope.row.contentType === 'img'" :src="'/api/files/' + scope.row.contentUrl.url"
                    class="showContent"></el-image>
          <!-- <img v-if="scope.row.contentType === 'img'" :src="'/api/files/' + scope.row.contentUrl.url"
            class="showContent" /> -->
          <el-link type="primary" v-if="scope.row.contentType === 'pdf'" class="showContent"
                   @click="checkPdf('/api/files/' + scope.row.contentUrl.url)">点击查看</el-link>
        </template>
      </el-table-column>
      <el-table-column prop="attachment" label="附件">
        <template slot-scope="scope">
          <div v-for="item in scope.row.attachment" :key="item.url">
            <el-link type="primary" :href="'/api/upload/download?url=' + item.url +'&fileName=' + item.name" >{{ item.name }}</el-link>
          </div>
        </template>
      </el-table-column>
      <el-table-column fixed="right" label="操作" width="140">
        <template slot-scope="scope">
          <el-button @click="handleUpdate(scope.row)" type="primary" size="small"
                     style="margin-right: 5px">修改</el-button>
          <el-popconfirm :title="getTitle(scope.row.typeName)" @confirm="remove(scope.row.id)">
            <el-button slot="reference" type="danger" size="small">删除</el-button>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>
    <pagination v-show="total > 0" :total="total" :page.sync="searchForm.pageNum" :limit.sync="searchForm.pageSize"
                @pagination="page" />
    <el-dialog :title="dialogTitle" :visible.sync="open" width="600px" :close-on-click-modal="false"
               :before-close="handleClose">
      <el-form label-position="left" label-width="120px" ref="form" :rules="rules" :model="form">
        <el-form-item label="主题简介" prop="introduction">
          <el-input v-model="form.introduction" placeholder="主题内容"></el-input>
        </el-form-item>
        <el-form-item label="科目" prop="subjectId">
          <el-select v-model="form.subjectId" placeholder="科目">
            <el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id">
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="文件类型" prop="contentType">
          <el-select v-model="form.contentType" placeholder="不同类型的文件阅览方式不同,多余文件请以附件形式上传" @change="fileChange">
            <el-option label="视频" value="video"></el-option>
            <el-option label="PDF" value="pdf"></el-option>
            <el-option label="图片" value="img"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="上传文件(一个)" prop="contentUrl">
          <upload v-show="form.contentType" ref="upload" :fileUrl="form.contentUrl" :fileType="form.contentType"
                  :fileSizeLimitM="1024" :uploadNum="1" @getUploadUrl="getUploadUrl" @removeFile="removeFile" />
        </el-form-item>
        <el-form-item label="附件(最多3个)" prop="attachment">
          <upload :fileSizeLimitM="1024" :uploadNum="3" :fileUrl="form.attachment"
                  @getUploadUrl="getUploadAttachmentUrl" @removeFile="removeAttachmentFile" />
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="open = false">取 消</el-button>
        <el-button type="primary" @click="handlerSubmit">保 存</el-button>
      </span>
    </el-dialog>
    <el-dialog title="PDF查看" :visible.sync="pdfDialog" width="80%" :before-close="closePdfDialog">
      <vue-office-pdf :src="pdf" @rendered="rendered" style="min-height: 400px; max-height: 800px;"/>
    </el-dialog>
  </div>
</template>
<script>
import Pagination from '@/components/Pagination';
import EducationResourceAPI from '@/api/educationResource';
import SubjectAPI from '@/api/subject';
import Upload from '@/components/UploadC';
import VueOfficePdf from '@vue-office/pdf';
export default {
  name: 'type',
  components: { Upload, Pagination, VueOfficePdf },
  computed: {
    fileContentUrl: () => {
      return this.form ? this.form.contentUrl ? [this.form.contentUrl] : [] : [];
    }
  },
  data() {
    return {
      pdf: '',
      dialogTitle: '添加学习内容',
      ids: [],
      typeList: [],
      searchForm: {
        pageNum: 1,
        pageSize: 5,
        subjectId: null,
        introduction: null
      },
      total: 0,
      tableData: [],
      open: false,
      pdfDialog: false,
      form: {
        contentType: 'video',
        introduction: '',
        belongType: 2,
        contentUrl: [],
        attachment: [],
        temp: []
      },
      rules: {
        contentUrl: [
          { required: true, message: '请上传文件', trigger: 'blur' },
        ],
        introduction: [
          { required: true, message: '请输入主题内容', trigger: 'blur' },
        ],
        belongType: [
          { required: true, message: '请选择学习分类', trigger: 'change' }
        ],
        contentType: [
          { required: true, message: '请选择文件类型', trigger: 'change' }
        ]
      }
    };
  },
  methods: {
    checkPdf(url) {
      this.pdf = url;
      this.pdfDialog = true;
    },
    closePdfDialog() {
      this.pdfDialog = false;
    },
    rendered() {
    },
    fileChange() {
      this.form.contentUrl = [];
    },
    handleSelectionChange(val) {
      this.ids = val.map(item => item.id);
    },
    typeFormatter(row) {
      if (row.contentType === 'video') {
        return "视频";
      }
      if (row.contentType === 'img') {
        return "图片";
      }
      if (row.contentType === 'pdf') {
        return "PDF";
      }
    },
    clearFile() {
      this.form.contentUrl = [];
      this.$refs.upload.clearFile();
    },
    removeFile() {
      this.form.contentUrl = [];
    },
    removeAttachmentFile (fileList, fileName) {
      this.form.attachment = fileList.filter(item => item.name !== fileName);
    },
    getUploadAttachmentUrl (uploadData) {
      this.form.attachment = uploadData;
    },
    getUploadUrl (uploadData) {
      this.form.contentUrl = uploadData;
    },
    remove (id) {
      EducationResourceAPI.remove([id]).then(res => {
        if (res.code === 1) {
          this.$message.success('删除成功');
          this.page()
        }
      });
    },
    batchRemove() {
      if (this.ids.length < 1) {
        this.$message.warning("请选择要删除的数据");
      }
      EducationResourceAPI.remove(this.ids).then(res => {
        if (res.code === 1) {
          this.$message.success('删除成功');
          this.ids = [];
        }
      });
    },
    getTitle (typeName) {
      return '确定要删除' + typeName + '这个文件吗?';
    },
    handlerSubmit () {
      this.$refs['form'].validate((valid) => {
        if (valid) {
          const temp = JSON.parse(JSON.stringify(this.form));
          // this.form.contentUrl = this.form.contentUrl[0]
          temp.contentUrl = temp.contentUrl[0];
          if (temp.id) {
            EducationResourceAPI.update(temp).then(res => {
              if (res.code === 1) {
                this.$message.success('修改成功');
                this.open = false;
                this.page();
              }
            });
          } else {
            EducationResourceAPI.add(temp).then(res => {
              if (res.code === 1) {
                this.$message.success('添加成功');
                this.open = false;
                this.page();
              }
            });
          }
        }
      });
    },
    resetForm() {
      this.form = {
        contentType: 'video',
        introduction: '',
        belongType: 2,
        contentUrl: [],
        attachment: [],
        temp: []
      };
    },
    handleClose() {
      this.open = false;
      this.resetForm();
    },
    handlerAdd() {
      this.resetForm();
      this.open = true;
      this.dialogTitle = '添加学习内容';
    },
    page() {
      EducationResourceAPI.page(this.searchForm).then(res => {
        if (res.code === 1) {
          this.tableData = res.data;
          this.total = res.total;
        }
      });
    },
    handleUpdate(row) {
      this.form.id = row.id;
      this.form.contentType = row.contentType;
      this.form.contentUrl = [row.contentUrl] || [];
      this.form.attachment = row.attachment || [];
      this.form.introduction = row.introduction;
      this.form.belongType = row.belongType;
      this.dialogTitle = '修改学习内容';
      this.open = true;
    }
  },
  mounted() {
    this.page();
    SubjectAPI.list().then(res => {
      if (res.code === 1) {
        this.typeList = res.data;
      }
    });
  }
};
</script>
<style scoped>
.showContent {
  width: 100%;
  min-height: 80px;
  object-fit: contain;
}
.warp {
  margin: 40px 10px;
}
.search {
  margin-top: 10px;
}
::v-deep .el-image__error {
  position: absolute;
  top: 0;
  bottom: 0;
}
</style>