在Electron中实现实时下载进度显示的完整指南

news2025/3/19 5:53:47

在开发Electron应用时,提供良好的用户体验至关重要,尤其是在下载大文件时。用户需要知道下载进度、预计完成时间以及当前下载速度。本文将详细介绍如何在Electron应用中实现实时下载进度显示功能,从主进程到渲染进程的完整流程。
技术栈是electron+vue3作为示例,其它的技术栈同样可以使用

系统架构概述

实现下载进度显示功能需要以下三个主要组件协同工作:

  1. 主进程(Main Process):负责实际的文件下载和进度跟踪
  2. 预加载脚本(Preload Script):安全地暴露主进程的功能和事件给渲染进程
  3. 渲染进程(Renderer Process):负责显示下载进度界面和用户交互

下面是整个系统的工作流程:

主进程(main.js) ─┐
                 │ IPC通信
预加载脚本(preload.js) ─┐
                       │ 暴露API
渲染进程(App.vue + DownloadModal.vue)

实现步骤

1. 主进程中实现下载和进度跟踪

首先,在main.js中实现下载处理器,并添加进度跟踪逻辑:

// client/electron/main.js
const { app, BrowserWindow, ipcMain } = require('electron');
const fs = require('fs');
const path = require('path');
const http = require('http');
const https = require('https');

// 处理下载请求
ipcMain.handle('download-update', async (event, options) => {
  try {
    const { url, filename, version } = options;
    if (!url || !filename) {
      return { success: false, error: '下载地址或文件名无效' };
    }
    
    // 准备下载路径,要下载到哪个目录下,userHomeDir系统的默认主目录
    const userHomeDir = os.homedir();
    const downloadDir = path.join(userHomeDir, '要下载到的目录名');
    
    // 确保目录存在
    if (!fs.existsSync(downloadDir)) {
      fs.mkdirSync(downloadDir, { recursive: true });
    }
    
    // 确定下载文件路径
    let filePath;
    if (process.platform === "win32") {
      filePath = path.join(downloadDir, '文件名.exe');
    } else {
      filePath = path.join(downloadDir, '文件名');
    }
    
    // 开始下载文件
    const result = await downloadFileWithProgress(event, url, filePath);
    return result;
  } catch (error) {
    console.error('下载失败:', error);
    return { success: false, error: error.message };
  }
});

// 实现带进度的下载函数
async function downloadFileWithProgress(event, url, filePath) {
  return new Promise((resolve, reject) => {
    // 根据URL选择协议
    const requester = url.startsWith('https') ? https : http;
    
    console.log('文件将下载到:', filePath);
    
    const request = requester.get(url, (response) => {
      // 处理重定向
      if (response.statusCode === 301 || response.statusCode === 302) {
        const redirectUrl = response.headers.location;
        console.log('下载重定向到:', redirectUrl);
        return resolve(downloadFileWithProgress(event, redirectUrl, filePath));
      }
      
      // 获取文件大小
      const totalSize = parseInt(response.headers['content-length'], 10);
      let downloadedSize = 0;
      let lastProgressTime = Date.now();
      let lastDownloadedSize = 0;
      
      // 创建文件写入流
      const file = fs.createWriteStream(filePath);
      
      // 监听数据接收事件,更新进度
      response.on('data', (chunk) => {
        downloadedSize += chunk.length;
        const percent = totalSize ? Math.round((downloadedSize / totalSize) * 100) : 0;
        
        // 计算下载速度 (每秒更新一次)
        const now = Date.now();
        const elapsedTime = now - lastProgressTime;
        
        if (elapsedTime >= 1000 || percent === 100) {
          const bytesPerSecond = Math.round((downloadedSize - lastDownloadedSize) / (elapsedTime / 1000));
          
          // 将下载大小格式化为可读的字符串
          const formattedDownloaded = formatBytes(downloadedSize);
          const formattedTotal = formatBytes(totalSize);
          const formattedSpeed = formatBytes(bytesPerSecond) + '/s';
          
          // 更新最后进度时间和大小
          lastProgressTime = now;
          lastDownloadedSize = downloadedSize;
          
          // 发送进度给渲染进程
          event.sender.send('download-progress', {
            percent: percent,
            downloaded: downloadedSize,
            total: totalSize,
            formattedDownloaded: formattedDownloaded,
            formattedTotal: formattedTotal,
            speed: formattedSpeed
          });
        }
      });
      
      // 将响应导入文件
      response.pipe(file);
      
      // 监听文件写入完成事件
      file.on('finish', () => {
        file.close();
        console.log('文件下载完成:', filePath);
        resolve({ success: true, filePath });
      });
      
      // 监听错误
      file.on('error', (err) => {
        fs.unlink(filePath, () => {});
        console.error('文件写入错误:', err);
        reject(err);
      });
    });
    
    // 处理请求错误
    request.on('error', (err) => {
      console.error('下载失败:', err);
      fs.unlink(filePath, () => {}); // 删除可能部分下载的文件
      reject(err);
    });
  });
}

// 辅助函数:格式化字节大小
function formatBytes(bytes, decimals = 2) {
  if (bytes === 0) return '0 Bytes';
  
  const k = 1024;
  const dm = decimals < 0 ? 0 : decimals;
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
  
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  
  return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

主进程实现了以下关键功能:

  1. 通过downloadFileWithProgress函数下载文件,同时跟踪进度
  2. 计算下载百分比、下载速度和格式化的文件大小
  3. 使用event.sender.send()方法向渲染进程发送实时进度更新
  4. 处理重定向、错误和完成事件
  5. 包含辅助函数formatBytes将字节大小转换为可读格式

2. 预加载脚本中暴露下载功能和事件

preload.js中,我们需要安全地暴露下载功能和进度事件给渲染进程:

// client/electron/preload.js
const { contextBridge, ipcRenderer } = require('electron');

// 安全地暴露主进程功能给渲染进程
contextBridge.exposeInMainWorld('electron', {
  // 下载文件API
  downloadUpdate: (options) => {
    return ipcRenderer.invoke('download-update', options);
  },
  
  // 下载进度事件监听器
  onDownloadProgress: (callback) => {
    // 移除可能存在的旧监听器
    ipcRenderer.removeAllListeners('download-progress');
    
    // 添加新的监听器
    ipcRenderer.on('download-progress', (event, progressData) => {
      callback(progressData);
    });
    
    // 返回清理函数
    return () => {
      ipcRenderer.removeAllListeners('download-progress');
    };
  }
});

预加载脚本完成了两个关键任务:

  1. 暴露downloadUpdate方法,使渲染进程能够调用主进程的下载功能
  2. 暴露onDownloadProgress事件监听器,使渲染进程能够接收下载进度更新
  3. 提供清理函数,确保不会留下多余的事件监听器

3. 渲染进程中接收和处理下载进度

在App.vue中,我们需要设置状态变量和事件监听器来处理下载进度:

// client/src/App.vue
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import DownloadModal from '@/components/DownloadModal.vue';

// 下载状态
const downloadState = ref({
  visible: false,
  fileName: '',
  url: '',
  version: '',
  percentage: 0,
  downloadedSize: '0 KB',
  totalSize: '0 MB',
  speed: '0 KB/s'
});

// 下载对话框引用
const downloadModalRef = ref(null);

// 清理函数引用
let cleanupProgressListener = null;

// 组件挂载时设置进度监听器
onMounted(() => {
  if (window.electron && window.electron.onDownloadProgress) {
    cleanupProgressListener = window.electron.onDownloadProgress((progressData) => {
      // 更新下载状态
      downloadState.value.percentage = progressData.percent;
      downloadState.value.downloadedSize = progressData.formattedDownloaded;
      downloadState.value.totalSize = progressData.formattedTotal;
      downloadState.value.speed = progressData.speed;
      
      console.log(`下载进度: ${progressData.percent}%, 速度: ${progressData.speed}`);
    });
  }
});

// 组件卸载时清理监听器
onUnmounted(() => {
  if (cleanupProgressListener) {
    cleanupProgressListener();
  }
});

// 确认更新开始下载
const confirmUpdate = async () => {
  // 隐藏确认对话框
  updateConfirm.value.visible = false;
  
  // 重置下载状态
  downloadState.value = {
    visible: true,
    fileName: process.platform === 'win32' ? 'secmate.exe' : 'secmate',
    url: updateConfirm.value.url,
    version: updateConfirm.value.version,
    percentage: 0,
    downloadedSize: '0 KB',
    totalSize: '计算中...',
    speed: '0 KB/s'
  };
  
  // 显示下载对话框
  if (downloadModalRef.value) {
    downloadModalRef.value.startDownload();
  }
  
  try {
    if (!window.electron || !window.electron.downloadUpdate) {
      throw new Error('下载功能不可用');
    }
    
    // 开始下载
    const result = await window.electron.downloadUpdate({
      url: updateConfirm.value.url,
      filename: downloadState.value.fileName,
      version: updateConfirm.value.version
    });
    
    console.log('下载结果:', result);
    
    if (result.success) {
      // 下载成功,完成下载动画
      if (downloadModalRef.value) {
        downloadModalRef.value.completeDownload();
      }
    } else {
      // 下载失败
      showMessage('下载失败: ' + (result.error || '未知错误'), 'error');
      downloadState.value.visible = false;
    }
  } catch (error) {
    console.error('下载过程出错:', error);
    showMessage('下载过程出错: ' + error.message, 'error');
    downloadState.value.visible = false;
  }
};

// 处理下载完成
const handleDownloadComplete = async () => {
  console.log('下载已完成');
  
  // 添加版本信息到成功消息
  const versionText = downloadState.value.version ? ` (版本 ${downloadState.value.version})` : '';
  showMessage(`更新文件已下载完成${versionText}`, 'success');
  
  // 关闭下载状态
  downloadState.value.visible = false;
  
  // 启动后端服务或其他后续操作...
};
</script>

<template>
  <!-- 下载进度弹窗 -->
  <DownloadModal
    :visible="downloadState.visible"
    :fileName="downloadState.fileName"
    :progress="downloadState.percentage"
    :total="downloadState.totalSize"
    :downloadState="downloadState"
    ref="downloadModalRef"
    @complete="handleDownloadComplete"
  />
  
  <!-- 其他组件... -->
</template>

App.vue中的关键实现包括:

  1. 创建downloadState响应式对象,存储下载状态信息
  2. 使用onMountedonUnmounted生命周期钩子管理进度事件监听器
  3. confirmUpdate函数中开始下载流程并重置下载状态
  4. 将下载状态传递给DownloadModal组件显示进度信息
  5. 通过handleDownloadComplete处理下载完成后的逻辑

4. 创建下载进度显示组件

最后,创建DownloadModal.vue组件来显示下载进度:

<!-- client/src/components/DownloadModal.vue -->
<template>
  <div class="download-modal" v-show="visible">
    <div class="download-dialog">
      <div class="download-header">
        <img width="24px" height="24px" src="@/assets/zhuce.png" alt="下载" class="download-emoji">
        <h3>正在下载…</h3>
      </div>
      
      <div class="progress-container">
        <!-- 进度条 -->
        <div class="progress-bar">
          <div class="progress-fill" :style="{ width: `${progress}%` }"></div>
        </div>
        
        <!-- 进度信息 -->
        <div class="progress-stats">
          <div class="progress-text">{{ progress }}%</div>
          <div class="progress-size">{{ downloadState.downloadedSize }}/{{ downloadState.totalSize }}</div>
        </div>
        
        <!-- 下载速度 -->
        <div class="download-speed" v-if="downloadState.speed">
          {{ downloadState.speed }}
        </div>
      </div>
      
      <div class="download-message">{{ message }}</div>
    </div>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue';

const props = defineProps({
  visible: Boolean,
  progress: {
    type: Number,
    default: 0
  },
  fileName: String,
  downloadState: {
    type: Object,
    default: () => ({
      downloadedSize: '0 KB',
      totalSize: '0 MB',
      speed: '0 KB/s'
    })
  }
});

const emit = defineEmits(['complete']);

// 状态变量
const message = ref('');
let progressInterval = null;

// 监听进度变化,更新提示消息
watch(() => props.progress, (newProgress) => {
  if (newProgress < 30) {
    message.value = '正在下载更新包...';
  } else if (newProgress < 60) {
    message.value = '正在下载更新包...';
  } else if (newProgress < 90) {
    message.value = '下载中,请稍候...';
  } else {
    message.value = '即将完成下载...';
  }
});

// 开始下载动画
function startDownload() {
  // 清除可能存在的旧计时器
  if (progressInterval) clearInterval(progressInterval);
  
  // 设置初始消息
  message.value = '正在连接下载服务器...';
}

// 完成下载
function completeDownload() {
  clearInterval(progressInterval);
  message.value = '下载完成!';
  
  // 延迟关闭
  setTimeout(() => {
    emit('complete');
  }, 1000);
}

// 暴露方法给父组件
defineExpose({
  startDownload,
  completeDownload
});
</script>

<style scoped>
.download-modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 10000000;
}

.download-dialog {
  width: 380px;
  background-color: white;
  border-radius: 14px;
  padding: 30px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
}

.download-header {
  display: flex;
  align-items: center;
  margin-bottom: 20px;
}

h3 {
  margin: 0;
  font-size: 18px;
  color: #333;
}

.progress-container {
  margin-bottom: 16px;
}

.progress-bar {
  height: 8px;
  background-color: #f0f0f0;
  border-radius: 4px;
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  background: linear-gradient(90deg, #7af2ff, #477cff);
  width: 0;
  border-radius: 4px;
  transition: width 0.3s ease;
}

.progress-stats {
  display: flex;
  justify-content: space-between;
  margin-top: 8px;
}

.progress-text, .progress-size {
  font-size: 14px;
  color: #666;
}

.download-speed {
  text-align: right;
  margin-top: 4px;
  font-size: 13px;
  color: #888;
}

.download-message {
  text-align: center;
  font-size: 14px;
  color: #555;
  min-height: 20px;
  margin-top: 10px;
}
</style>

DownloadModal组件的核心功能包括:

  1. 接收并显示下载进度、文件大小和下载速度
  2. 提供动态进度条,显示当前下载百分比
  3. 根据进度显示相应的提示消息
  4. 提供startDownloadcompleteDownload方法供父组件调用

关键技术点解析

1. 实时进度计算和格式化

在主进程中,我们不仅计算下载百分比,还计算下载速度并格式化文件大小:

// 计算下载速度
const now = Date.now();
const elapsedTime = now - lastProgressTime;

if (elapsedTime >= 1000 || percent === 100) {
  const bytesPerSecond = Math.round((downloadedSize - lastDownloadedSize) / (elapsedTime / 1000));
  
  // 格式化大小为可读字符串
  const formattedDownloaded = formatBytes(downloadedSize);
  const formattedTotal = formatBytes(totalSize);
  const formattedSpeed = formatBytes(bytesPerSecond) + '/s';
  
  // 更新最后进度时间和大小
  lastProgressTime = now;
  lastDownloadedSize = downloadedSize;
  
  // 发送进度数据...
}

2. 安全的IPC通信

通过预加载脚本,我们安全地桥接了主进程和渲染进程的通信:

// 在预加载脚本中暴露事件监听器
onDownloadProgress: (callback) => {
  ipcRenderer.removeAllListeners('download-progress');
  ipcRenderer.on('download-progress', (event, progressData) => {
    callback(progressData);
  });
  
  return () => {
    ipcRenderer.removeAllListeners('download-progress');
  };
}

3. 响应式UI更新

在Vue组件中,我们使用响应式对象和计算属性来确保UI与下载状态同步:

// 通过 props 将下载状态传递给组件
<DownloadModal
  :visible="downloadState.visible"
  :progress="downloadState.percentage"
  :downloadState="downloadState"
  ref="downloadModalRef"
  @complete="handleDownloadComplete"
/>

// 在组件内部监听进度变化
watch(() => props.progress, (newProgress) => {
  if (newProgress < 30) {
    message.value = '正在下载更新包...';
  } else if (newProgress < 60) {
    message.value = '正在下载更新包...';
  } else if (newProgress < 90) {
    message.value = '下载中,请稍候...';
  } else {
    message.value = '即将完成下载...';
  }
});

最佳实践与优化建议

  1. 节流进度更新:对于较大的文件,每个数据块都发送进度更新会导致性能问题。我们使用时间间隔(每秒更新一次)来节流进度更新。

  2. 格式化显示大小:使用formatBytes函数将字节数转换为人类可读的格式,如KB、MB和GB。

  3. 提供下载速度:显示当前下载速度,帮助用户估计剩余时间。

  4. 正确清理资源:在组件卸载时清理事件监听器,避免内存泄漏。

  5. 显示不同阶段的消息:根据下载进度显示不同的提示消息,增强用户体验。

  6. 处理下载错误:捕获并显示下载过程中的错误,提供有意义的错误信息。

  7. 保留下载历史:可以考虑添加下载历史记录功能,允许用户查看和管理历史下载。

应用场景

这种实时下载进度显示功能可以应用于多种场景:

  1. 应用自动更新:显示新版本下载进度
  2. 大文件下载:下载大型资源文件,如视频、音乐或文档
  3. 插件安装:下载和安装第三方插件或扩展
  4. 批量下载:同时下载多个文件并显示总体进度
  5. 数据导入/导出:在数据迁移过程中显示进度

总结

在Electron应用中实现实时下载进度显示是提升用户体验的重要一环。通过主进程跟踪下载进度、预加载脚本安全地暴露IPC通信,以及渲染进程中的响应式UI更新,我们可以创建一个流畅、信息丰富的下载体验。

这种架构不仅保证了安全性,还提供了良好的性能和用户体验。通过显示下载百分比、文件大小和下载速度,用户可以清楚地了解下载状态,减少等待过程中的焦虑感。

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

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

相关文章

MacBook部署达梦V8手记

背景 使用Java SpringBootDM开发Web应用&#xff0c;框架有License&#xff0c;OSX加载dll失败&#xff0c;安装了Windows 11&#xff0c;只有一个C盘&#xff0c;达梦安装后因为C盘权限问题&#xff0c;创建数据库失败&#xff0c;遂采用Docker容器方式部署。 下载介质 官网在…

外贸 B2B 平台没落?多语言批发系统正在崛起

近年来&#xff0c;全球外贸行业正在发生快速变化&#xff0c;传统的 B2B 平台正面临越来越多的挑战&#xff0c;尤其是在面对新兴的多语言批发系统时。这种变化不仅影响了供应商和买家之间的交易方式&#xff0c;也正在推动外贸行业的数字化升级和转型。今天&#xff0c;让我们…

[spring] Spring JPA - Hibernate 多表联查 1

[spring] Spring JPA - Hibernate 多表联查 之前在 [spring] spring jpa - hibernate 名词解释&配置 和 [spring] spring jpa - hibernate CRUD 简单的学习了一下怎么使用 Hibernate 实现 CRUD 操作&#xff0c;不过涉及到的部分都是逻辑上比较简单的实现——只在一张表上…

鸿蒙Next开发实战教程—电影app

最近忙忙活活写了不少教程&#xff0c;但是总感觉千篇一律&#xff0c;没什么意思&#xff0c;大家如果有感兴趣的项目可以私信给幽蓝君写一写。 今天分享一个电影App。 这个项目也比较简单&#xff0c;主要是一些简单页面的开发和本地视频的播放以及横竖屏切换。 页面搭建以…

停车场停车位数据集,标注停车位上是否有车,平均正确识别率99.5%,支持yolov5-11, coco json,darknet,xml格式标注

停车场停车位数据集&#xff0c;标注停车位上是否有车&#xff0c;平均正确识别率98.0&#xff05;&#xff0c;支持yolov5-11&#xff0c; coco json&#xff0c;darknet&#xff0c;xml格式标注 数据集-识别停车场所有车辆的数据集 数据集分割 一共184张图片 训练组 89&am…

ssm框架之mybatis框架讲解

1&#xff0c;Mybatis 1.1 Mybatis概述 1.1.1 Mybatis概念 MyBatis 是一款优秀的持久层框架&#xff0c;用于简化 JDBC 开发 MyBatis 本是 Apache 的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code&#xff0c;并且改名为MyBatis 。2…

CEF 多进程模式时,注入函数,获得交互信息

CEF 控制台添加一函数,枚举 注册的供前端使用的CPP交互函数有哪些-CSDN博客 上篇文章,是在模拟环境,单进程中设置的,这篇文章,将其改到正常多进程环境中设置。 对应于工程中的 CEF_RENDER项目 一、多进程模式中,改写 修改步骤 1、注入函数 client_app_render.cpp 在…

Androidstudio出现警告warning:意外的元素

这些警告信息通常与 Android SDK 或系统镜像的配置文件有关&#xff0c;可能是由于 SDK 工具或系统镜像的版本不兼容或配置文件格式发生了变化。以下是解决这些警告的步骤&#xff1a; 1. 更新 Android SDK 工具 确保你使用的是最新版本的 Android SDK 工具&#xff1a; 打开…

深入了解Linux —— git三板斧

版本控制器git 为了我们方便管理不同版本的文件&#xff0c;就有了版本控制器&#xff1b; 所谓的版本控制器&#xff0c;就是能够了解到一个文件的历史记录&#xff08;修改记录&#xff09;&#xff1b;简单来说就是记录每一次的改动和版本迭代的一个管理系统&#xff0c;同…

【软件系统架构】单体架构

一、引言 在软件开发的漫长历程中&#xff0c;架构的选择一直是至关重要的决策。单体架构作为一种经典的架构模式&#xff0c;曾经在许多项目中发挥着不可替代的作用。虽然如今微服务等架构逐渐流行&#xff0c;但理解单体架构对于深入掌握软件架构体系仍然有着重要意义。 二、…

【求助】【建议放弃】【谷粒商城版】Kubernetes

本文作者&#xff1a; slience_me 文章目录 Kubernetes【谷粒商城版】【建议放弃】1. docker安装2. kubernetes安装前3. kubeadm,kubelet,kubectl3.1 简介kubeadmkubeletkubectl常用指令 3.2 安装3.3 kubeadm初始化3.4 加入从节点(工作节点)3.5 安装Pod网络插件&#xff08;CNI…

C# Unity 唐老狮 No.10 模拟面试题

本文章不作任何商业用途 仅作学习与交流 安利唐老狮与其他老师合作的网站,内有大量免费资源和优质付费资源,我入门就是看唐老师的课程 打好坚实的基础非常非常重要: Unity课程 - 游习堂 - 唐老狮创立的游戏开发在线学习平台 - Powered By EduSoho C# 1. 内存中&#xff0c;堆和…

第十五届蓝桥杯2024JavaB组省赛试题A:报数游戏

简单的找规律题目。题目给得数列&#xff0c;第奇数项是20的倍数&#xff0c;第偶数项时24的倍数。题目要求第n 202420242024 项是多少。这一项是偶数&#xff0c;所以答案一定是24的倍数&#xff0c;并且偶数项的个数和奇数项的个数各占一半&#xff0c;所以最终的答案ans( n…

Matlab 汽车二自由度转弯模型

1、内容简介 Matlab 187-汽车二自由度转弯模型 可以交流、咨询、答疑 2、内容说明 略 摘 要 本文前一部分提出了侧偏角和横摆角速度作为参数。描述了车辆运动的运动状态&#xff0c;其中文中使用的参考模型是二自由度汽车模型。汽车速度被认为是建立基于H.B.Pacejka的轮胎模…

学c++的人可以几天速通python?

学了俩天啊&#xff0c;文章写纸上了 还是蛮有趣的

Rocky Linux 9.x 基于 kubeadm部署k8s 1.32

一、部署说明 1、主机操作系统说明 序号操作系统及版本备注1Rocky Linux release 9下载链接&#xff1a;https://mirrors.163.com/rocky/9.5/isos/x86_64/Rocky-9.5-x86_64-minimal.iso 2、主机硬件配置说明 作用IP地址操作系统配置关键组件k8s-master01192.168.234.51Rocky…

解决git init 命令不显示.git

首先在自己的项目代码右击 打开git bash here 输入git init 之后自己的项目没有.git文件&#xff0c;有可能是因为.git文件隐藏了&#xff0c;下面是解决办法

利用AI让数据可视化

1. 从问卷星上下载一份答题结果。 序号用户ID提交答卷时间所用时间来源来源详情来自IP总分1、《中华人民共和国电子商务法》正式实施的时间是&#xff08;&#xff09;。2、&#xff08;&#xff09;可以判断企业在行业中所处的地位。3、&#xff08;&#xff09;是指店铺内有…

解决qt中自定插件加载失败,不显示问题。

这个问题断断续续搞了一天多&#xff0c;主要是版本不匹配问题。 我们先来看下 Based on Qt 6.6.0 → 说明 Qt Creator 本身 是基于 Qt 6.6.0 框架构建的。MSVC 2019, 64-bit → 说明 Qt Creator 是使用 Microsoft Visual C 2019 编译器&#xff08;64 位&#xff09; 编译的。…

智慧社区3.0

项目介绍&#xff1a; 此项目旨在推动成都市探索**超大城市社区发展治理新路**&#xff0c;由三个实验室负责三大内容 1、**研发社区阵地空间管理模块**&#xff1a;AI算法实现态势感知&#xff08;如通过社区图片和视频、文本&#xff0c;对环境 空间质量、绿视率、安全感分…