Vue3实战Easy云盘(三):文件删除+文件移动+目录导航+上传优化/文件过滤/搜索

一、文件删除

(1)选中了之后才可以删除,没有选中时就显示暗调删除按钮
(2)实现选中高亮功能
(3)单个删除
(4)批量删除

Main.vue中 

                <!-- 按钮3 -->
                <!-- 如果selectFileIdList数组的长度为0(即数组为空),则HTML元素的disabled属性将被设置为true,从而使该元素变为禁用状态 -->
                <el-button type="danger" @click="delFileBatch" :disabled="selectFileIdList.length == 0">
                    <span class="iconfont icon-del"></span>
                    批量删除
                </el-button>

.....

<span class="iconfont icon-del" @click="delFile(row)">
                                    删除
</span>

......

// 定义多选文件夹列表
const selectFileIdList = ref([]);
// 多选
const rowSelected = (rows) => {
    // 重置为一个空数组,清空数据
    selectFileIdList.value = [];
    // 遍历 rows 数组中的每一个元素
    rows.forEach((item) => {
        // 经遍历到的fileId添加到selectFileIdList中
        selectFileIdList.value.push(item.fileId);
    });
};
// 删除单个文件
const delFile = (row) => {
    // 调用Confirm
    proxy.Confirm(
        `你确定要删除【$row.fileName】吗?删除的文件可在 10 天内通过回收站还原`,
        async () => {
            let result = await proxy.Request({
                url: api.delFile,
                params: {
                    fileIds: row.fileId,
                },
            });
            if (!result) {
                return;
            }
            // 重新获取数据
            loadDataList();
        }
    );
};
// 批量删除文件
const delFileBatch = () => {
    // 如果要删除的长度为0就不执行
    if (selectFileIdList.value.length == 0) {
        return;
    }
    // 调用Confirm
    proxy.Confirm(
        // 第一个参数是一个字符串,用于显示给用户的确认消息
        `你确定要删除这些文件吗?删除的文件可在 10 天内通过回收站还原`,
        // 第二个参数,当用户点击确认按钮后,这个异步函数会被执行
        async () => {
            // 使用await关键字调用proxy.Request方法,发送一个HTTP请求到服务器
            // 接收proxy.Request方法的响应并将其存储在result变量中
            let result = await proxy.Request({
                // 请求的URL来自api.delFile,这可能是一个常量或配置对象中的属性,指向删除文件的API端点。
                url: api.delFile,
                // join将这个数组转换为一个由逗号分隔的字符串(因为批量删除多个ids所以要分开),作为查询参数发送的文件ID列表
                params: {
                    fileIds: selectFileIdList.value.join(","),
                },
            });
            // 处理响应,
            if (!result) {
                return;
            }
            // 重新获取数据
            loadDataList();
        }
    );
};

 二、文件移动

(1)定义全局组件,FolderSelect.vue

(因为文件移动到哪个文件夹,文件保存到哪个文件夹都要用到,所以封装一个全局组件)

别忘记在Main.js中引入

src/components/FolderSelect.vue(不全面,未添加导航)

<!-- 移动/保存到哪个文件夹组件 -->
<template>
    <div>
        <!-- 弹出的对话框组件Dialog -->
        <Dialog
          :show="dialogConfig.show"
          :title="dialogConfig.title"
          :buttons="dialogConfig.buttons"
          width="600px"
          :showCancel="true"
          @close="dialogConfig.show = false">
            <!-- 目录导航 -->
            <div class="navigation-panel"></div>
            <!-- 文件夹列表 -->
            <div class="folder-list" v-if="folderList.length > 0">
                <!-- 每一项文件夹 -->
                <div
                   class="folder-item"
                   v-for="item in folderList"
                   @click="selectFolder(item)">
                   <!-- 文件类型为0时即文件夹就显示文件夹的图标 -->
                   <Icon :fileType="0"></Icon>
                   <!-- 每个文件夹的名字 -->
                   <span class="file-name">{{ item.fileName }}</span>
               </div>
            </div>
            <!-- 判断 -->
            <div v-else class="tips">
                移动到 <span>{{ currentFolder.fileName }}</span> 文件夹
            </div>
        </Dialog>
    </div>
</template>

<script setup>
import { ref, reactive, getCurrentInstance } from "vue";
import { useRouter, useRoute } from "vue-router";

const { proxy } = getCurrentInstance();
const router = useRouter();
const route = useRoute();

const api = {
    loadAllFolder: "/file/loadAllFolder",
};

// 定义弹出框的属性
const dialogConfig = ref({
    show: false,
    // 对话框的标题
    title: "移动到",
    buttons: [
        {
            type: "primary",
            text: "移动到此",// 按钮上的文字
            // 当按钮被点击时触发的回调函数。在这个例子中,点击按钮会调用 folderSelect() 函数。
            click: (e) => {
                folderSelect();
            },
        },
    ],
});

// 目录列表
const folderList = ref([]);
// 父级ID
const filePid = ref("0");
// 当前目录ID
const currentFileIds = ref([]);
// 当前文件夹
const currentFolder = ref({});

// 获取所有的目录文件夹列表
const loadAllFolder = async () => {
    // API请求
    // 使用 await 等待 proxy.Request 方法的执行结果存储到result里
    let result = await proxy.Request({
        // 指定请求的url
        url: api.loadAllFolder,
        // 传递父id和当前文件夹id
        params: {
            filePid: filePid.value,
            currentFileIds: currentFileIds.value,
        },
    });
    // 判断结果
    if (!result) {
        return;
    }
    folderList.value = result.data;
};

// 展示弹出框对外的方法
const showFolderDialog = (currentFolder) => {
    dialogConfig.value.show = true;
    // 更新当前文件id数组
    currentFileIds.value = currentFolder;
    // 在加载一次获取到的目录文件夹列表
    loadAllFolder();
};

// 关闭弹出框
const close = () => {
    dialogConfig.value.show = false;
};
// 向外暴露这两个函数,使得父组件Main可以调用这两个函数
defineExpose({ showFolderDialog, close });

// 选择目录(目录导航)
const selectFolder = (data) => {
    navigationRef.value.openFolder(data);
};

// 确定选择要移动到的目录
// 将选定的文件目录参数传递给父组件 Main 中的 folderSelect 函数
const emit = defineEmits(["folderSelect"]);
// 此方法回调在父组件中
const folderSelect = () => {
    emit("folderSelect", filePid.value);
};

</script>

<style lang="scss" scoped>
.navigation-panel {
    padding-left: 10px;
    background: #f1f1f1;
}

.folder-list {
    .folder-item {
        cursor: pointer;
        display: flex;
        align-items: center;
        padding: 10px;

        .file-name {
            display: inline-block;
            margin-left: 10px;
        }

        &:hover {
            background: #f8f8f8;
        }
    }

    max-height: calc(100vh - 200px);
    min-height: 200px;
}

.tips {
    text-align: center;
    line-height: 200px;

    span {
        color: #06a7ff;
    }
}</style>

(2)Main.vue中调用

(不全面,未添加导航栏)

<!-- 引入组件 -->
        <FolderSelect
          ref="folderSelectRef"
          @folderSelect="moveFolderDone"
        ></FolderSelect>
// 移动目录
const folderSelectRef = ref();
// 当前要移动的文件(单个文件)
const currentMoveFile = ref({});
// 移动单个文件
const moveFolder = (data) => {
    // 存储当前要移动的单个文件的信息
    currentMoveFile.value = data;
    // 把当前文件id给showFolderDialog方法
    folderSelectRef.value.showFolderDialog(currentFolder.value.fileId);
};
// 移动批量文件
const moveFolderBatch = () => {
    // 清空当前要移动的文件数据
    currentMoveFile.value = {};
    // 把当前的文件夹id给showFolderDialog
    folderSelectRef.value.showFolderDialog(currentFolder.value.fileId);
};

// 点击按钮之后,移动文件操作
const moveFolderDone = async (folderId) => {
    // 如果要移动到当前目录,提醒无需移动
    if (
        // 如果当前移动的文件父级id等于此时要移动到的文件夹id或者当前文件夹的id等于要移动到的文件夹id
        currentMoveFile.value.filePid == folderId ||
        currentFolder.value.fileId == folderId
    ) {
        // 就提示无需移动
        proxy.Message.warning("文件正在当前目录,无需移动");
        return;
    }
    // 定义一个数组存放要移动的文件或者文件夹信息
    let fileIdsArray = [];
    // 如果是单个文件移动
    if (currentMoveFile.value.fileId) {
        // 就把当前移动的文件id传给这个数组
        fileIdsArray.push(currentMoveFile.value.fileId);
    } else {
        // 如果是多个文件移动
        // concat 连接多个数组
        // selectFileIdList 是指批量选择时选择的文件ID
        filedIdsArray = filedIdsArray.concat(selectFileIdList.value);
    }
    // 发请求并将结果存储
    let result = await proxy.Request({
        // 请求的url
        url: api.changeFileFolder,
        // 携带的参数
        params: {
            // 将 fileIdsArray 数组中的所有元素转换为一个由逗号分隔的字符串,赋值给fileIds
            fileIds: fileIdsArray.join(","),
            // 把folderId传到父文件id里面
            filePid: folderId,
        },
    });
    if (!result) {
        return;
    }
    // 调用子组件暴露的close方法,实现当前弹出框页面的关闭
    folderSelectRef.value.close();
    // 更新当前文件列表
    loadDataList();
};

三、目录导航(难点)

(1)导航栏组件(全局)

src/components/Navigation.vue
template结构

js
1.设置点击目录事件 openFolder:
2.暴露此事件供父组件使用:defineExpose({ openFolder });
3.设置当前路径 setpath:当点击后目录改变,路径也随之改变
4.获取当前路径的目录 getNavigationFolder
5.doCallback
6.监听路由
7.初始化设置init
8.setCurrentFolder 设置当前目录,点击导航跳转到所点击的目录
9.返回上一级 backParent

src/components/Navigation.vue

<template>
  <!-- 导航 -->
  <div class="top-navigation">
    <!-- 返回上一级 -->
    <template v-if="folderList.length > 0">
      <span class="back link" @click="backParent">返回上一级</span>
      <!-- 竖线 -->
      <el-divider direction="vertical" />
    </template>
    <!-- 全部文件:外面粗体的不能点 -->
    <span v-if="folderList.length == 0" class="all-file">全部文件</span>
    <!-- 全部文件:能点的 -->
    <span
      class="link"
      v-if="folderList.length > 0"
      @click="setCurrentFolder(-1)"
      >全部文件
    </span>
    <!-- 遍历文件列表 -->
    <template v-for="(item, index) in folderList">
      <!-- 图标 -->
      <span class="iconfont icon-right"></span>
      <!-- 文件名字可以点击 -->
      <span
        class="link"
        v-if="index < folderList.length - 1"
        @click="setCurrentFolder(index)"
        >{{ item.fileName }}
      </span>
      <!-- 文件名字不可以点击 -->
      <span class="text" v-if="index == folderList.length - 1">
          {{item.fileName}}
      </span>
    </template>
  </div>
</template>

<script setup>
import { ref, reactive, getCurrentInstance, watch } from "vue";
import { useRouter, useRoute } from "vue-router";

const { proxy } = getCurrentInstance();
const router = useRouter();
const route = useRoute();

// 定义父组件Main.vue传递过来的值
const props = defineProps({
  // 默认开启路由监听
  watchPath: {
    // 是否监听路由变化
    type: Boolean,
    default: true,
  },
  shareId: {
    type: String,
  },
  adminShow: {
    type: Boolean,
    default: false,
  },
});

const api = {
  // 首页 获取当前目录 获取列表 参数(path:完整路径)
  getFolderInfo: "/file/getFolderInfo",
  // 外部分享 获取目录信息 参数(shareId:分享id / path:完整路径)
  getFolderInfo4Share: "/showShare/getFolderInfo",
  // 管理员 获取当前目录 参数(path:完整路径)
  getFolderInfo4Admin: "/admin/getFolderInfo",
};

// 分类
const category = ref();
// 目录的集合
const folderList = ref([]);
// 当前目录
const currentFolder = ref({ fileId: "0" });

// 初始化
const init = () => {
  // 初始目录集合 设置为一个空数组
  folderList.value = [];
  // 初始当前目录设置
  currentFolder.value = { fileId: "0" };
  // 调用
  doCallback();
};

// 点击目录openFolder
// 父组件 Main/FolderSelect 中调用该方法,实现目录(文件及)的预览
const openFolder = (data) => {
  // 把data赋值给文件id和文件name
  const { fileId, fileName } = data;
  // 定义folder
  const folder = {
    fileName: fileName,
    fileId: fileId,
  };
  // 把folder push进目录集合
  folderList.value.push(folder);
  // 更新当前目录
  currentFolder.value = folder;
  // 设置当前路径,当点击后目录改变,路径也随之改变,调用setPath
  setPath();
};
defineExpose({ openFolder });

// 返回上一级
const backParent = () => {
  // 查找当前文件夹的索引
  let currentIndex = null;
  for (let i = 0; i < folderList.value.length; i++) {
    if (folderList.value[i].fileId == currentFolder.value.fileId) {
      currentIndex = i;
      break;
    }
  }
  // 设置当前文件夹为上一级目录
  setCurrentFolder(currentIndex - 1);
};

// 点击导航 设置当前目录(点击目录,跳转到所点击的目录)
const setCurrentFolder = (index) => {
  // 如果点的是全部文件
  if (index == -1) {
    // 初始化数组
    currentFolder.value = { fileId: "0" };
    folderList.value = [];
  } else {
    // 当前目录的值更新为目录集合数组为index的值
    currentFolder.value = folderList.value[index];
    // 删除从index+1开始的长度为目录集合数组的长度 的数组
    // 对于 splice(start, deleteCount, ...items) 方法:
    //  start(必需):开始更改数组的位置的索引。
    // deleteCount(必需):要删除的元素数量。如果设置为 0,则不会删除任何元素。
    // ...items(可选):要添加到数组中的新元素
    folderList.value.splice(index + 1, folderList.value.length);
  }
  setPath();
};

// 设置当前路径,当点击后目录改变,路径也随之改变
const setPath = () => {
  if (!props.watchPath) {
    // 设置不监听路由回调方法
    doCallback();
    return;
  }
  // 定义路径数组
  let pathArray = [];
  // 遍历目录集合的每一项
  folderList.value.forEach((item) => {
    // 把每一项的fileId push 到 路径数组里
    pathArray.push(item.fileId);
  });
  // 设置路由
  router.push({
    // 当前路径path
    path: route.path,
    // 参数
    query:
    // 如果pathArray长度为0,参数就为空,否则就把路径用/隔开加入到pathArray里面,更新path,添加到参数里面
      pathArray.length == 0
        ? ""
        : {
            path: pathArray.join("/"),
          },
  });
};

// 获取当前路径的目录
const getNavigationFolder = async (path) => {
  // 根据给定的 path 和一些其他属性(如 props.shareId 和 props.adminShow)来确定请求哪个 API,然后发送一个请求来获取该路径下的目录信息,并将获取到的目录信息存储在 folderList.value 中。
  let url = api.getFolderInfo;
  if (props.shareId) {
    url = api.getFolderInfo4Share;
  }
  if (props.adminShow) {
    url = api.getFolderInfo4Admin;
  }

  let result = await proxy.Request({
    url: url,
    showLoading: false,
    params: {
      path: path,
      shareId: props.shareId,
    },
  });
  if (!result) {
    return;
  }
  folderList.value = result.data;
};

// 回调 将当前的参数传递给父组件 Main
// 定义了一个名为 navChange 的事件。这允许子组件在需要时触发这个事件,并传递一些数据给父组件。
const emit = defineEmits(["navChange"]);
const doCallback = () => {
  emit("navChange", {
    categoryId: category.value,
    curFolder: currentFolder.value,
  });
};

// 监听路由
watch(
  () => route,
  // 它会在route的值变化时被调用。
  // newVal是route的新值,oldVal是route的旧值
  (newVal, oldVal) => {
    if (!props.watchPath) {
      return;
    }
    if (
      // 如果不在main路径里面就不用管
      newVal.path.indexOf("/main") === -1 &&
      newVal.path.indexOf("/settings/fileList") === -1 &&
      newVal.path.indexOf("/share") === -1
    ) {
      return;
    }
    // 把新携带的路径赋值给path,可以拿到?后面的一坨,query参数是?后面的一截
    const path = newVal.query.path;
    // params是route路由,category是在router里面定义的
    const categoryId = newVal.params.category;
    category.value = categoryId;
    if (path == undefined) {
      // 调用
      init();
    } else {
      // 调用
      getNavigationFolder(path);
      // 刷新的时候要把当前目录设置进来
      // 使用split("/")方法将path字符串分割为一个数组pathArray
      let pathArray = path.split("/");
      // fileId被赋值给currentFolder.value
      currentFolder.value = {
        // 它使用数组的最后一个元素(即path中的最后一个部分)作为fileId
        fileId: pathArray[pathArray.length - 1],
      };
      doCallback();
    }
  },
  { immediate: true, deep: true }
);
</script>

<style lang="scss" scoped>
.top-navigation {
  font-size: 13px;
  display: flex;
  align-items: center;
  line-height: 40px;
  .all-file {
    font-weight: bold;
  }
  .link {
    color: #06a7ff;
    cursor: pointer;
  }
  .icon-right {
    color: #06a7ff;
    padding: 0px 5px;
    font-size: 13px;
  }
}
</style>

(2)main.js中引入

import Navigation from '@/components/Navigation.vue';
app.component('Navigation', Navigation);

(3)Main.vue中使用组件

<!-- 导航 -->
<Navigation ref="navigationRef" @navChange="navChange"></Navigation>

Main.vue中绑定点击事件
 

<span @click="preview(row)">{{ row.fileName }}</span>
<span @click="preview(row)">{{ row.fileName }}</span>

preview回调,预览

// 预览
const previewRef = ref();
const preview = (data) => {
    // 如果是文件夹
    if (data.folderType == 1) {
        // 就调用Navigation组件中的openFolder方法,实现预览
        navigationRef.value.openFolder(data);
        return;
    }
    if (data.status != 2) {
        proxy.Message.warning("文件未完成转码,无法预览");
        return;
    }
    previewRef.value.showPreview(data, 0);
};

navChange回调

// 目录,展示目录
const navChange = (data) => {
    // 从传入的 data 对象中解构出 curFolder 和 categoryId 两个属性,并将它们的值分别赋给新定义的常量 curFolder 和 categoryId
    const { curFolder, categoryId } = data;
    // 将当前文件夹的值更新为传过来的文件夹
    currentFolder.value = curFolder;
    // 展示
    showLoading.value = true;
    // 更新category
    category.value = categoryId;
    loadDataList();
};

无文件上传时,Main.vue展示

<!-- 判断没有文件时 -->
        <div class="no-data" v-else>
          <div class="no-data-inner">
            <!-- 图片 -->
            <Icon iconName="no_data" :width="120" fit="fill"></Icon>
            <!-- 文字提示 -->
            <div class="tips">当前目录为空,上传你的第一个文件吧</div>
            <div class="op-list">
                <!-- 上传 -->
              <el-upload
                :show-file-list="false"
                :with-credentials="true"
                :multiple="true"
                :http-request="addFile"
                :accept="fileAccept"
              >
                <div class="op-item">
                  <Icon iconName="file" :width="60"></Icon>
                  <div>上传文件</div>
                </div>
              </el-upload>
              <!-- 新建目录 -->
              <div class="op-item" v-if="category == 'all'" @click="newFolder">
                <Icon iconName="folder" :width="60"></Icon>
                <div>新建目录</div>
              </div>
            </div>
          </div>
        </div>

四、 上传优化(列表自动刷新)

(1)上传回调 

Main.vue中

// 添加文件回调
const reload = () => {
    showLoading.value = false;
    // 刷新列表
    loadDataList();
};
defineExpose({ reload });

Framework.vue中

<component @addFile="addFile" ref="routerViewRef" :is="Component"></component>
// 上传文件回调
// 上传文件后的刷新列表(调用Uploader子组件中的函数)
const routerViewRef = ref();
const uploadCallbackHandler = () => {
    nextTick(() => {
        // 它首先等待DOM更新完成(通过nextTick)
        // 然后重新加载一个组件(可能是router-view)
        routerViewRef.value.reload();
        // 并最后调用一个函数来获取空间使用情况。
        getUseSpace();
    });
};

五、文件选择(过滤)

组件:分类文件类型
src/js/CategoryInfo.js

export default {
    "all": {
        accept: "*"
    },
    "video": {
        accept: ".mp4,.avi,.rmvb,.mkv,.mov"
    },
    "music": {
        accept: ".mp3,.wav,.wma,.mp2,.flac,.midi,.ra,.ape,.aac,.cda"
    },
    "image": {
        accept: ".jpeg,.jpg,.png,.gif,.bmp,.dds,.psd,.pdt,.webp,.xmp,.svg,.tiff"
    },
    "doc": {
        accept: ".pdf,.doc,.docx,.xls,.xlsx,.txt"
    },
    "others": {
        accept: "*"
    },
}

Main.vue中上传按钮

:accept="fileAccept"

// 实现文件选择
const fileAccept = computed(() => {
    const categoryItem = CategoryInfo[category.value];
    return categoryItem ? categoryItem.accept : "*";
});

六、搜索功能实现

Main.vue中搜索输入框

 <!-- 搜索输入框 -->
                <div class="search-panel">
                    <el-input
                        clearable
                        placeholder="请输入文件名搜索"
                        v-model="fileNameFuzzy"
                        @keyup.enter="search">
                    <template #suffix>
                        <i class="iconfont icon-search" @click="search"></i>
                    </template>
              </el-input>
                </div>
                <!-- 搜索图标 -->
               <div class="iconfont icon-refresh" @click="loadDataList"></div>


回调 

// 搜索功能
const search = () => {
  showLoading.value = true;
  loadDataList();
};

七、文件移动的目录导航

FolderSelect.vue中

<!-- 目录导航 -->
            <div class="navigation-panel">
                <Navigation
                    ref="navigationRef"
                    @navChange="navChange"
                    :watchPath="false"
                ></Navigation>
            </div>
// 绑定导航栏
const navigationRef = ref();
// 调用Navigation子组件中的navChange,使得参数传递给该组件
const navChange = (data) => {
    const { curFolder } = data;
    currentFolder.value = curFolder;
    filePid.value = curFolder.fileId;
    loadAllFolder();
};
// 选择目录(目录导航)
const selectFolder = (data) => {
    navigationRef.value.openFolder(data);
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙内核源码分析(用户态锁篇) | 如何使用快锁Futex(上)

快锁上下篇 鸿蒙内核实现了Futex&#xff0c;系列篇将用两篇来介绍快锁&#xff0c;主要两个原因: 网上介绍Futex的文章很少&#xff0c;全面深入内核介绍的就更少&#xff0c;所以来一次详细整理和挖透。涉及用户态和内核态打配合&#xff0c;共同作用&#xff0c;既要说用户…

【Linux】文件描述符和重定向

目录 一、回顾C文件 二、系统文件I/O 2.1 系统调用 open 2.2 标志位传参 2.3 系统调用 write 2.4 文件描述符fd 2.5 struct file 2.6 fd的分配规则 2.7 重定向 2.7.1 基本原理&#xff1a; 2.7.2 系统调用 dup2 2.8 标准错误 一、回顾C文件 文件 内容 属性 对…

3分钟,学会一个 Lambda 小知识之【流API】

之前给大家介绍的 Lambda 小知识还记得吗&#xff1f;今天再来给大家介绍&#xff0c; 流API 的相关知识要点。 流API Stream是Java8中处理集合的关键抽象概念&#xff0c;它可以指定你对集合的&#xff0c;可以执行查找、过滤和映射等数据操作。 Stream 使用一种类似用 SQ…

资料如何打印更省钱

在日常工作和学习中&#xff0c;我们经常需要打印各种资料。然而&#xff0c;随着打印成本的不断提高&#xff0c;如何更省钱地打印资料成为了大家关注的焦点。今天&#xff0c;就为大家分享一些资料打印的省钱技巧&#xff0c;并推荐一个省钱又省心的打印平台。 首先&#xff…

冥想的时候怎么专注自己

冥想的时候怎么专注自己&#xff1f;我国传统的打坐养生功法&#xff0c;实际最早可追溯到五千年前的黄帝时代。   每天投资两个半小时的打坐&#xff0c;有上千年之久的功效。因为当你们打坐进入永恒时&#xff0c;时间停止了。这不只是两个半小时&#xff0c;而是百千万亿年…

深入探讨黑盒测试:等价类划分与边界值分析

文章目录 概要黑盒测试等价类划分边界值分析 设计测试用例小结 概要 在软件开发领域&#xff0c;测试是确保产品质量的关键步骤之一。而黑盒测试方法作为其中的一种&#xff0c;通过关注输入与输出之间的关系&#xff0c;而不考虑内部实现的细节&#xff0c;被广泛应用于各种软…

使用git系统来更新FreeBSD ports源码

FreeBSD跟其它系统相比一大特色就是ports系统。 The Ports Collection is a set of Makefiles, patches, and description files. Each set of these files is used to compile and install an individual application on FreeBSD, and is called a port. By default, the Po…

5 款免费好用的精品软件推荐!

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/ 1.系统优化软件 - Wise Care 365 Wise Care 365 -全球最快的系统优化软件&#xff01;精简系统、管理启动项、清理和优化注册表、清理个人隐私…

基于51单片机的冰箱控制系统设计( proteus仿真+程序+设计报告+原理图+讲解视频)

基于51单片机冰箱控制系统设计( proteus仿真程序设计报告原理图讲解视频&#xff09; 基于51单片机冰箱控制系统设计 1. 主要功能&#xff1a;2. 讲解视频&#xff1a;3. 仿真4. 程序代码5. 设计报告6. 原理图7. 设计资料内容清单&&下载链接资料下载链接&#xff1a; …

Debian mariadb 10.11 XXXX message from server: “Too many connections“

问题表现 报错信息&#xff1a;Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Data source rejected establishment of connection, message from server: "Too many connections" 处理步骤 1、尝试能不能通过终端登录&…

从头理解transformer,注意力机制(下)

交叉注意力 交叉注意力里面q和KV生成的数据不一样 自注意力机制就是闷头自学 解码器里面的每一层都会拿着编码器结果进行参考&#xff0c;然后比较相互之间的差异。每做一次注意力计算都需要校准一次 编码器和解码器是可以并行进行训练的 训练过程 好久不见输入到编码器&…

SpringBoot中@Value注入失败

首先&#xff0c;不支持static的 解决&#xff1a;使用setter方法进行属性的赋值,并且setter方法不能有static 生成set/get方法就可以了&#xff0c;然后Value 放在set上

《系统架构设计师教程(第2版)》第10章-软件架构的演化和维护-07-软件架构维护

文章目录 1. 软件架构知识管理1.1 概念1.2 架构知识的获取1.3 作用1.4 架构知识管理的现状 2 软件架构修改管理3 软件架构版本管理4. 示例4.1 背景4.2 数据获取4.3 数据计算4.4 结果分析4.4.1 圈复杂度 (CCN)4.4.2 扇入扇出度 (FFC)4.4.3 模块间耦合度 (CBO)4.4.4 模块的响应 (…

x264 场景切换检测算法分析

x264 编码器场景切换 在 x264 编码器中,场景切换(Scene Cut)检测是一个重要的特性,它用于识别视频中不同场景之间的过渡点。这些过渡点通常是视觉上显著不同的帧,比如从一个镜头切换到另一个镜头。在这些点插入关键帧(I帧)可以提高视频的随机访问性和编码效率。 入口函…

vue 百度地图点击marker修改marker图片,其他marker图片不变。

解决思路&#xff0c;就是直接替换对应marker的图片。获取marker对象判断点击的marker替换成新图片&#xff0c;上一个被点击的就替换成老图片。 marker.name tag;marker.id i; //一定要设置id&#xff0c;我这里是设置的循环key值&#xff0c;要唯一性。map.addOverlay(mark…

SSRF(服务器端请求伪造)的学习以及相关例题(上)

目录 一、SSRF的介绍 二、漏洞产生的原因 三、利用SSRF可以实现的效果&#xff08;攻击方式&#xff09; 四、SSRF的利用 五、SSRF中的函数 file_get_content() 、fsockopen() 、curl_exec() 1.file_get_content()&#xff1a; 2.fsockopen(): 3.curl_exec()&#xff1…

【鸿蒙开发】第二十四章 IPC与RPC进程间通讯服务

1 IPC与RPC通信概述 IPC&#xff08;Inter-Process Communication&#xff09;与RPC&#xff08;Remote Procedure Call&#xff09;用于实现跨进程通信&#xff0c;不同的是前者使用Binder驱动&#xff0c;用于设备内的跨进程通信&#xff0c;后者使用软总线驱动&#xff0c;…

算法设计与分析(超详解!) 第三节 贪婪算法

1.贪心算法基础 1.贪心算法的基本思想 贪心算法是从问题的某一个初始解出发&#xff0c;向给定的目标推进。但它与普通递推求解过程不同的是&#xff0c;其推动的每一步不是依据某一固定的递推式&#xff0c;而是做一个当时看似最佳的贪心选择&#xff0c;不断地将问题实例归…

【选择结构程序设计-谭浩强适配】(适合专升本、考研)

无偿分享学习资料&#xff0c;需要的小伙伴评论区或私信dd。。。 无偿分享学习资料&#xff0c;需要的小伙伴评论区或私信dd。。。 无偿分享学习资料&#xff0c;需要的小伙伴评论区或私信dd。。。 完整资料如下&#xff1a;纯干货、纯干货、纯干货&#xff01;&#xff01;…

uni-app跨端兼容

1.样式兼容 小程序端不支持*选择器&#xff0c;可以使用&#xff08;view,text&#xff09; 页面视口差异(tabar页、普通页) H5端默认开始scoped 例如骨架屏样式出现问题&#xff0c;需要将之前的样式拷贝到骨架屏中 提示&#xff1a;H5端是单页面应用&#xff0c;scoped隔离…