前端文件上传组件最全封装+删除+下载+预览

news2024/11/24 0:18:55

前言:使用的是若依的框架+element ui+vue2封装的。如果有不对的地方欢迎指出。后台管理使用,文件需要上传。回显列表,详情也需要回显+预览

// 开始封装组件:封装在 src/components/FileUpload/index.vue中
<template>
  <div class="upload-file">
    <el-upload
      multiple
      name="multipartFile"
      :action="uploadFileUrl"
      :data="{ 上传时附带的额外参数 }"
      :limit="limit"
      :file-list="fileList"
      :before-upload="handleBeforeUpload"
      :on-exceed="handleExceed"
      :on-error="handleUploadError"
      :on-success="handleUploadSuccess"
      :show-file-list="false"
      :headers="headers"
      class="upload-file-uploader"
      ref="fileUpload"
    >
      <!-- 上传按钮 -->
      <el-button size="mini" type="primary" plain v-if="!disabled">选取文件</el-button>
      <!-- 上传提示 -->
      <div class="el-upload__tip" slot="tip" v-if="showTip && !disabled">
        请上传
        <template v-if="fileSize">
          大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
        </template>
        <template v-if="fileType">
          格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
        </template>
        的文件
      </div>
    </el-upload>

    <!-- 文件列表,我们功能需要 删除文件、下载文件、预览文件功能 -->
    <transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul" style="min-width: 300px">
      <li class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList" :key="file.fileId">
        <el-link class="link" :href="`${baseUrl}${file.filePath}`" :underline="false" target="_blank">
          <span class="el-icon-document" style="padidng-left: 10px">
            {{ getFileName(file.fileName) }}
          </span>
        </el-link>
        <div class="controls">
          <div class="ele-upload-list__item-content-action" style="width: 50px; text-align: center">
            <el-link :underline="false" @click="handleDelete(index)" type="danger" v-if="!disabled">删除</el-link>
          </div>
          <div class="ele-upload-list__item-content-action" style="width: 50px; text-align: center">
            <el-link :underline="false" @click="handlePreview(file)" type="danger">预览</el-link>
          </div>
          <div class="ele-upload-list__item-content-action" style="width: 50px; text-align: center">
            <el-link :underline="false" @click="handleDownload(file)" type="danger">下载</el-link>
          </div>
        </div>
      </li>
    </transition-group>

	<!-- 文件预览功能,点击文件列表后的预览按钮,需要预览文档,pdf,excel,照片,视频,音频。其他没有封装,所以就不支持 -->
    <el-dialog title="文件预览" :visible.sync="preview.open" append-to-body :before-close="previewCancel">
      <vue-office-docx v-if="fileSuffix == 'docx'" :src="preview.url" style="height: 100vh" />
      <vue-office-excel v-else-if="fileSuffix == 'xlsx'" :src="preview.url" style="width: auto; height: 100vh" />
      <vue-office-pdf v-else-if="fileSuffix == 'pdf'" :src="preview.url" style="height: 100vh" />
      <div style="text-align: center" v-else-if="fileSuffix == 'img'">
        <el-image style="width: 500px" :src="preview.url" fit="fill" :preview-src-list="[preview.url]"></el-image>
      </div>
      <div style="text-align: center" v-else-if="fileSuffix == 'mp3'">
        <audio controls loop ref="myAudio" autoplay class="my-audio">
          <source :src="preview.url" />
        </audio>
      </div>
      <div style="text-align: center" v-else-if="fileSuffix == 'mp4'">
        <video-app :src="preview.url" :second="1"></video-app>
      </div>
      <div style="text-align: center" v-else>暂不支持该文件预览,请下载预览</div>
    </el-dialog>
  </div>
</template>

<script>
import { getToken } from "@/utils/auth";
import { 查看和下载的接口,后端给的 } from "";

//引入VueOfficeDocx组件,需要npm安装
import VueOfficeDocx from "@vue-office/docx";
import "@vue-office/docx/lib/index.css";

//引入VueOfficeExcel组件,需要npm安装
import VueOfficeExcel from "@vue-office/excel";
import "@vue-office/excel/lib/index.css";

//引入VueOfficePdf组件,需要npm安装
import VueOfficePdf from "@vue-office/pdf";

import VideoApp from "./video.vue"; // 这个是我封装的视频预览的组件

export default {
  name: "FileUpload",
  props: {
    // 数量限制
    limit: {
      type: Number,
      default: 5,
    },
    // 大小限制(MB)
    fileSize: {
      type: Number,
      default: 20,
    },
    // 文件类型, 例如['png', 'jpg', 'jpeg']
    fileType: {
      type: Array,
      default: () => ["docx", "pptx", "pdf"],
    },
    // 是否显示提示
    isShowTip: {
      type: Boolean,
      default: true,
    },
    formFileList: {
      type: Array,
      default: () => [],
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    // 培训记录附件的类型
    busiType: {
      type: String,
    },
  },
  components: {
    VueOfficeDocx,
    VueOfficeExcel,
    VueOfficePdf,
    VideoApp,
  },
  data() {
    return {
      number: 0,
      uploadList: [],
      baseUrl: // 地址 ,
      uploadFileUrl: , // 上传文件服务器地址
      headers: {
        Authorization: "Bearer " + getToken(),
      },
      fileList: [],

      lookFile: false,
      url: "",
      // 文件预览
      preview: {
        open: false,
        url: "",
      },
      fileSuffix: "",
    };
  },
  watch: {
  	// 编辑和详情的回显fileList
    formFileList: {
      handler(val) {
        if (val !== undefined) {
          this.fileList = val;
        }
        if (val == null) {
          this.fileList = [];
          return;
        }
      },
      deep: true,
      immediate: true,
    },
  },
  computed: {
    // 是否显示提示
    showTip() {
      return this.isShowTip && (this.fileType || this.fileSize);
    },
  },
  methods: {
    // 上传前校检格式和大小
    handleBeforeUpload(file) {
      // 校检文件类型
      if (this.fileType) {
        const fileName = file.name.split(".");
        const fileExt = fileName[fileName.length - 1];
        const isTypeOk = this.fileType.length ? this.fileType.indexOf(fileExt) >= 0 : [];
        if (!isTypeOk) {
          this.$modal.msgError(`文件格式不正确, 请上传${this.fileType.join("/")}格式文件!`);
          return false;
        }
      }
      // 校检文件大小
      if (this.fileSize) {
        const isLt = file.size / 1024 / 1024 < this.fileSize;
        if (!isLt) {
          this.$modal.msgError(`上传文件大小不能超过 ${this.fileSize} MB!`);
          return false;
        }
      }
      this.$modal.loading("正在上传文件,请稍候...");
      this.number++;
      return true;
    },
    // 文件个数超出
    handleExceed() {
      this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);
    },
    // 上传失败
    handleUploadError(err) {
      this.number--;
      this.$modal.msgError("上传文件失败,请重试");
      this.$modal.closeLoading();
    },
    // 上传成功回调
    handleUploadSuccess(res, file) {
      if (res.code === 200) {
        this.uploadList.push(res.data);
        this.$modal.closeLoading();
        this.uploadedSuccessfully();
      } else {
        this.number--;
        this.$modal.closeLoading();
        this.$modal.msgError(res.msg);
        this.$refs.fileUpload.handleRemove(file);
        this.uploadedSuccessfully();
      }
    },
    // 删除文件
    handleDelete(index) {
      this.fileList.splice(index, 1);
      this.$emit("input", this.listToString(this.fileList));
    },
    // 上传结束处理
    uploadedSuccessfully() {
      if (this.number > 0 && this.uploadList.length === this.number) {
        this.fileList = this.fileList.concat(this.uploadList);
        this.uploadList = [];
        this.number = 0;
        this.$emit("input", this.listToString(this.fileList));
        this.$modal.closeLoading();
        this.$emit("fileUploadSuccess", this.fileList);
      }
    },
    // 获取文件名称
    getFileName(name) {
      if (name?.lastIndex/Of("/") > -1) {
        return name.slice(name.lastIndexOf("/") + 1);
      } else {
        return name;
      }
    },
    // 对象转成指定字符串分隔
    listToString(list, separator) {
      let strs = "";
      separator = separator || ",";
      for (let i in list) {
        strs += list[i].url + separator;
      }
      return strs != "" ? strs.substr(0, strs.length - 1) : "";
    },
    resetFileList() {
      this.fileList = [];
    },
    // 下载
    handleDownload(file) {
    	
      downloadFile(file.fileId).then((res) => {
        this.exportFunction(res, file.fileName, file.fileType);
      });
    },
    exportFunction(response, name, type) {
      const link = document.createElement("a");
      const blob = new Blob([response], { type });
      link.style.display = "none";
      link.href = URL.createObjectURL(blob);
      link.setAttribute("download", name, type);
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    },
    // 预览
    handlePreview(file) {
      this.preview.url = "";
      if (file.fileType.indexOf("image") !== -1) {
        this.fileSuffix = "img";
      } else {
        this.fileSuffix = file.suffix;
      }
      
      showFileURL(file.fileId).then((res) => {
        this.preview.url = defaultSettings.minioUrl + res;
        if (file.suffix == "mp3") {
          this.$nextTick((res) => {
            this.$refs.myAudio.load();
            this.$refs.myAudio.play();
          });
        }
        this.preview.open = true;
      });
    },
    previewCancel() {
      this.preview.open = false;
      this.preview.url = "";
      if (this.fileSuffix == "mp3") {
        this.$refs.myAudio.pause();
      }
    },
  },
};
</script>

<style scoped lang="scss">
::v-deep .el-button--primary.is-plain {
  color: var(--select-selected-color);
  background: var(--table-content-bColor);
  border-color: var(--search-back);
}
::v-deep .el-button--primary.is-plain:hover,
::v-deep .el-button--primary.is-plain:focus {
  border-color: var(--select-selected-color);
  background-color: var(--select-selected-color);
  color: #fff !important;
}

.upload-file-uploader {
  margin-bottom: 5px;
}
.upload-file-list .el-upload-list__item {
  border: 1px solid #e4e7ed;
  line-height: 2;
  margin-bottom: 10px;
  position: relative;
}
.upload-file-list .ele-upload-list__item-content {
  display: flex;
  color: inherit;
  .link {
    flex: 1;
    display: block;
    margin-left: 2px;
    ::v-deep .el-icon-document {
      width: 310px;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
  }
  .controls {
    display: flex;
  }
}
.ele-upload-list__item-content-action .el-link {
  margin-right: 10px;
}
::v-deep {
  .x-spreadsheet-table {
    width: auto !important;
  }
}
.my-audio {
  width: 100%;
}
</style>

video组件封装:


<template>
  <div class="m-video" :class="{'u-video-hover': !hidden}" :style="`width: ${width}px; height: ${height}px;`">
    <video
      ref="veo"
      :style="`object-fit: ${zoom};`"
      :src="src"
      :poster="veoPoster"
      :width="width"
      :height="height"
      :autoplay="autoplay"
      :controls="!originPlay&&controls"
      :loop="loop"
      :muted="autoplay || muted"
      :preload="preload"
      crossorigin="anonymous"
      @loadeddata="poster ? () => false : getPoster()"
      @pause="showPlay ? onPause() : () => false"
      @playing="showPlay ? onPlaying() : () => false"
      @click.prevent.once="onPlay"
      v-bind="$attrs">
      您的浏览器不支持video标签。
    </video>
    <svg v-show="originPlay || showPlay" class="u-play" :class="{'hidden': hidden}" :style="`width: ${playWidth}px; height: ${playWidth}px;`" viewBox="0 0 24 24">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4.75 6.75C4.75 5.64543 5.64543 4.75 6.75 4.75H17.25C18.3546 4.75 19.25 5.64543 19.25 6.75V17.25C19.25 18.3546 18.3546 19.25 17.25 19.25H6.75C5.64543 19.25 4.75 18.3546 4.75 17.25V6.75Z"></path>
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M15.25 12L9.75 8.75V15.25L15.25 12Z"></path>
    </svg>
  </div>
</template>
<script>
export default {
  name: 'Video',
  props: {
    src: { // 视频文件url,必传,支持网络地址 https 和相对地址 require('@/assets/files/Bao.mp4')
      type: String,
      required: true,
      default: ''
    },
    poster: { // 视频封面url,支持网络地址 https 和相对地址 require('@/assets/images/Bao.jpg')
      type: String,
      default: ''
    },
    second: { // 在未设置封面时,自动截取视频第 second 秒对应帧作为视频封面
      type: Number,
      default: 0.5
    },
    width: { // 视频播放器宽度
      type: Number,
      default: 800
    },
    height: { // 视频播放器高度
      type: Number,
      default: 450
    },
    autoplay: { // 视频就绪后是否马上播放
      type: Boolean,
      default: false
    },
    controls: { // 是否向用户显示控件,比如进度条,全屏
      type: Boolean,
      default: true
    },
    loop: { // 视频播放完成后,是否循环播放
      type: Boolean,
      default: false
    },
    muted: { // 是否静音
      type: Boolean,
      default: false
    },
    preload: { // 是否在页面加载后载入视频,如果设置了autoplay属性,则preload将被忽略;
      type: String,
      default: 'auto' // auto:一旦页面加载,则开始加载视频; metadata:当页面加载后仅加载视频的元数据 none:页面加载后不应加载视频
    },
    showPlay: { // 播放暂停时是否显示播放器中间的暂停图标
      type: Boolean,
      default: true
    },
    playWidth: { // 中间播放暂停按钮的边长
      type: Number,
      default: 96
    },
    zoom: { // video的poster默认图片和视频内容缩放规则
      type: String,
      default: 'contain' // none:(默认)保存原有内容,不进行缩放; fill:不保持原有比例,内容拉伸填充整个内容容器; contain:保存原有比例,内容以包含方式缩放; cover:保存原有比例,内容以覆盖方式缩放
    }
  },
  data () {
    return {
      veoPoster: this.poster,
      originPlay: true,
      hidden: false
    }
  },
  mounted () {
    if (this.autoplay) {
      this.hidden = true
      this.originPlay = false
    }
    /*
      自定义设置播放速度,经测试:
      在vue2中需设置:this.$refs.veo.playbackRate = 2
      在vue3中需设置:veo.value.defaultPlaybackRate = 2
    */
    // this.$refs.veo.playbackRate = 2
  },
  methods: {
    /*
      loadeddata 事件在媒体当前播放位置的视频帧(通常是第一帧)加载完成后触发
      preload为none时不会触发
    */
    getPoster () { // 在未设置封面时,自动获取视频0.5s对应帧作为视频封面
      // 由于不少视频第一帧为黑屏,故设置视频开始播放时间为0.5s,即取该时刻帧作为封面图
      this.$refs.veo.currentTime = this.second
      // 创建canvas元素
      const canvas = document.createElement('canvas')
      const ctx = canvas.getContext('2d')
      // canvas画图
      canvas.width = this.$refs.veo.videoWidth
      canvas.height = this.$refs.veo.videoHeight
      ctx.drawImage(this.$refs.veo, 0, 0, canvas.width, canvas.height)
      // 把canvas转成base64编码格式
      this.veoPoster = canvas.toDataURL('image/png')
    },
    onPlay () {
      if (this.originPlay) {
        this.$refs.veo.currentTime = 0
        this.originPlay = false
      }
      if (this.autoplay) {
        this.$refs.veo.pause()
      } else {
        this.hidden = true
        this.$refs.veo.play()
      }
    },
    onPause () {
      this.hidden = false
    },
    onPlaying () {
      this.hidden = true
    }
  }
}
</script>
<style lang="scss" scoped>
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
.m-video {
  display: inline-block;
  position: relative;
  background: #000;
  cursor: pointer;
  .u-play {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    margin: auto;
    fill: none;
    color: #FFF;
    pointer-events: none;
    opacity: 0.7;
    transition: opacity .3s;
    path {
      stroke: #FFF;
    }
  }
  .hidden {
    opacity: 0;
  }
}
.u-video-hover {
  &:hover {
    .u-play {
      opacity: 0.9;
    }
  }
}
</style>

使用组件:

import FileUpload= from "@/components/FileUpload=";
<el-form-item label="文件上传">
  <file-upload
    ref="fileResetRef"
    @fileUploadSuccess="fileUploadSuccessHandle"
    :formFileList="form.files" // 回显的数据文件列表
    :disabled="isReadonly" // 区分编辑还是查看
    :file-type="[ // 支持的类型
      'png',
      'jpg',
      'docx',
      'xlsx',
      'pptx',
      'pdf',
      'mp3',
      'mp4',
      'zip',
    ]"
  >
  </file-upload>
</el-form-item>
// 文件上传成功的展示
fileUploadSuccessHandle1(fileList) {
  this.form.files = fileList;
},

上传组件效果图:
在这里插入图片描述
上传的文件列表:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1350631.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

诺派克ROPEX热封控制器维修RES-408/400VAC

德国希尔科诺派克ROPEX热封控制器维修型号包括&#xff1a;RES-401&#xff0c;RES-402&#xff0c;RES-403&#xff0c;RES-406&#xff0c;RES-407&#xff0c;RES-408&#xff0c;RES-409&#xff0c;RES-420&#xff0c;RES-440&#xff0c;MSW-2&#xff0c;PEX-W3&#x…

三层架构概述

三层架构就是把整个软件的代码分为三个层次&#xff0c;分层的目的是&#xff1a;规范代码&#xff0c;大型软件需要团队配合的时候问题就来了&#xff0c;由于每个程序员风格不一样&#xff0c;而开发软件大量的代码风格不统一就会造成后期调试和维护出现问题&#xff0c;然而…

Axure骚操作:【制作可暂停与不可暂停进度加载条】

目录 一、不可暂停进度条 1.1 前期准备 1.2 效果假想 1.3 适用场景 1.4 实现步骤 &#xff08;1&#xff09;除按钮外的元件设置隐藏 &#xff08;2&#xff09;给按钮添加交互 &#xff08;3&#xff09;给变量值文本标签添加交互 &#xff08;4&#xff09;给进度条矩…

计算机网络——传输层(五)

前言&#xff1a; 最重要的网络层我们已经学习完了&#xff0c;下面让我们再往上一层&#xff0c;对网络层的上一层传输层进行一个学习与了解&#xff0c;学习网络层的基本概念和网络层中的TCP协议和UDP协议 目录 ​编辑一、传输层的概述&#xff1a; 1.传输层&#xff1a; …

二叉树的前序遍历 、二叉树的最大深度、平衡二叉树、二叉树遍历【LeetCode刷题日志】

目录 一、二叉树的前序遍历 方法一&#xff1a;全局变量记录节点个数 方法二&#xff1a;传址调用记录节点个数 二、二叉树的最大深度 三、平衡二叉树 四、二叉树遍历 一、二叉树的前序遍历 方法一&#xff1a;全局变量记录节点个数 计算树的节点数: 函数TreeSize用于…

LLM之RAG理论(四)| RAG高级数据索引技术

本文将重新审视分块技术以及其他方法&#xff0c;包括查询增强、层次结构和知识图谱。 一、简单RAG架构快速概览 在2023年年初&#xff0c;我的主要关注点集中在Vector DB及其在更广泛的设计领域中的表现上。然而&#xff0c;随着2023年的收尾&#xff0c;这一领域出现了重大进…

Spring MVC - Controller的创建与使用

控制器Controller是处理器&#xff0c;是真正处理请求的组件 1 创建Controller 一般在src/main/java/com/qdu下建立一个controller包用来存放所有控制器。当创建一个控制器时&#xff0c;首先要记得使用Controller标记将该类注册成为一个控制器类。 然后在SpringMVCConfig类…

项目 杂碎 知识点 汇总!!!

Vue !!! setup生命周期 使用 nextTick &#xff01;&#xff01;获取节点 onMounted中可以使用JS&#xff0c;获取节点&#xff0c;setup生命周期无法获取节点 vue实现文本粘贴复制 Vue遍历对象 1、使用v-for指令&#xff1a;可以直接遍历对象的键和值 2、使用 Object.keys…

git基础概念和常用命令(日常开发收藏备用)

目录 ### 常用命令 ### 远程仓库与克隆 ### 分支管理 ### 子模块&#xff08;Submodule&#xff09; ### 其他高级操作 ### 交互式暂存&#xff08;Interactive Staging&#xff09; ### cherry-pick ### rebase ### reflog与reset ### 子树合并&#xff08;Subtree …

【Maven】<scope>provided</scope>

在Maven中&#xff0c;“provided”是一个常用的依赖范围&#xff0c;它表示某个依赖项在编译和测试阶段是必需的&#xff0c;但在运行时则由外部环境提供&#xff0c;不需要包含在最终的项目包中。下面是对Maven scope “provided”的详细解释&#xff1a; 编译和测试阶段可用…

Vue3全局属性app.config.globalProperties

文章目录 一、概念二、实践2.1、定义2.2、使用 三、最后 一、概念 一个用于注册能够被应用内所有组件实例访问到的全局属性的对象。点击【前往】访问官网 二、实践 2.1、定义 在main.ts文件中设置app.config.globalPropertie import {createApp} from vue import ElementPl…

.NET国产化改造探索(二)、银河麒麟安装人大金仓数据库

随着时代的发展以及近年来信创工作和…废话就不多说了&#xff0c;这个系列就是为.NET遇到国产化需求的一个闭坑系列。接下来&#xff0c;看操作。 上一篇介绍了如何安装银河麒麟操作系统&#xff0c;这篇文章详细介绍下如何在银河麒麟操作系统上安装人大金仓数据库。 准备安…

LangChain.js 实战系列:搭配 LangSmith 实现调试、监控、测试

&#x1f4dd; LangChain.js 是一个快速开发大模型应用的框架&#xff0c;它提供了一系列强大的功能和工具&#xff0c;使得开发者能够更加高效地构建复杂的应用程序。LangChain.js 实战系列文章将介绍在实际项目中使用 LangChain.js 时的一些方法和技巧。 LangSmith 是 LangCh…

RK3568平台开发系列讲解(Linux系统篇)PWM系统编程

🚀返回专栏总目录 文章目录 一、什么是PWM二、PWM相关节点三、PWM应用编程沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍 PWM 的系统编程。 一、什么是PWM PWM,即脉冲宽度调制(Pulse Width Modulation)

2023年下半年信息系统项目管理师考试

目录 引言 结果 论文 案例分析 综合知识 总结 引言 2023年下半年参加了信息系统项目管理师考试&#xff0c;考试结果情理之中&#xff0c;意料之外。论文压线&#xff0c;综合和案例差一分。从个人参加考试的整个过程来看&#xff0c;属于历史性的突破。以本文&#xff…

稳定币记录

稳定币&#xff1a; 稳定币&#xff08;Stablecoin&#xff09;是一种加密货币&#xff0c;其设计目的是维持相对稳定的价值&#xff0c;通常与某种法定货币&#xff08;如美元、欧元&#xff09;或其他资产&#xff08;如黄金&#xff09;挂钩。稳定币通过将加密货币与相应的…

详解Vue3中的鼠标事件mousemove、mouseover和mouseout

本文主要介绍Vue3中的常见鼠标事件mousemove、mouseover和mouseout。 目录 一、mousemove——鼠标移动事件二、mouseover——鼠标移入事件三、mouseout——鼠标移出事件 下面是Vue 3中常用的鼠标事件mousemove、mouseover和mouseout的详解。 一、mousemove——鼠标移动事件 鼠…

DevC++ easyx实现视口编辑--像素绘图板与贴图系统

到了最终成果阶段了&#xff0c;虽然中间有一些代码讲起来没有意思&#xff0c;纯靠debug,1-1解决贴图网格不重合问题&#xff0c;这次是一个分支结束。 想着就是把瓦片贴进大地图里。 延续这几篇帖子&#xff0c;开发时间也从2023年的4月16到了6月2号&#xff0c;80小时基本…

【K8S 部署】基于kubeadm搭建Kurbernetes集群

目录 一、基本架构 二、环境准备: 三、安装部署 1、所有节点安装docker 2、、所有节点安装kubeadm&#xff0c;kubelet和kubectl 3、配置网络--flannel 4、测试 pod 资源创建 四、安装部署与k8s集群对接的Harbor仓库 五、Dashboard安装部署&#xff1a; 一、基本架构…

jupyter notebook打开其他盘的文件

jupyter notebook打开其他盘文件 打开jupyter notebook打开terminal输入&#xff1a;jupyter-notebook 路径打开你想打开的工程的文件 打开jupyter notebook 打开terminal 输入&#xff1a;jupyter-notebook 路径 打开你想打开的工程的文件