vue+exceljs前端下载、导出xlsx文件
首先安装插件
npm install exceljs file-saver
第一种 简单导出
//页面引入
import ExcelJS from 'exceljs';
import {saveAs} from 'file-saver';
export default {methods: {
/** 导出操作 */async handleExportFun() {let that = this// 获取当前年月日 用户下载xlsx的文件名称设置const now = new Date();const year = now.getFullYear();const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从0开始,需要加1,并确保两位数const day = String(now.getDate()).padStart(2, '0'); // 确保两位数const hours = String(now.getHours()).padStart(2, '0'); // 确保两位数const minutes = String(now.getMinutes()).padStart(2, '0'); // 确保两位数var currentTime = `${year}${month}${day}${hours}${minutes}`;//创建const workbook = new ExcelJS.Workbook();const worksheet = workbook.addWorksheet('Sheet1');// 假设 exportData 的第一行是列头const headers = this.exportData[0]; // 确保这里是获取的第一行数据作为列头// 填充表头数据 因为我的数据是不固定的二维数组worksheet.columns = headers.map((headerText, index) => ({header: headerText,key: index.toString(),width: 22,}));// 设置表头行高const headerRow = worksheet.getRow(1);headerRow.height = 35; // 设置行高为35(可以根据需要调整)//申明样式let style = {alignment: {vertical: "middle",horizontal: "center",wrapText: true,},fill: {type: "pattern",pattern: "solid",fgColor: {argb: "EEEEEE"},},border: {top: {style: "thin",color: { rgb: 'E3F2D9' } // 设置左边框样式和颜色}, // 设置上边框样式为细线bottom: {style: "thin",color: { rgb: 'E3F2D9' } // 设置左边框样式和颜色}, // 设置下边框样式为细线left: {style: "thin",color: { rgb: 'E3F2D9' } // 设置左边框样式和颜色}, // 设置左边框样式为细线right: {style: "thin",color: { rgb: 'E3F2D9' } // 设置左边框样式和颜色}, // 设置右边框样式为细线},font: {bold: true, // 设置字体加粗name: 'Arial', // 设置字体名称(可选)size: 12 // 设置字体大小(可选)}};// 填充工作表的其他数据,并设置行高for (let rowIndex = 1; rowIndex < this.exportData.length; rowIndex++) {const row = this.exportData[rowIndex];const excelRow = worksheet.getRow(rowIndex + 1);excelRow.height = 30; // 设置其他行的行高为25(可以根据需要调整)row.forEach((cellValue, cellIndex) => {const cell = excelRow.getCell(cellIndex + 1);cell.value = cellValue;});}//给每行设置样式worksheet.eachRow((row, rowNumber) => {if (rowNumber > 0) {row.height = 35;row.alignment = style.alignment;// 你还可以根据单元格的内容或列号执行不同的操作row.eachCell((cell, colNumber) => {if (rowNumber === 1&&colNumber<=that.exportData[0].length) {//对第一列的单元格执行特殊操作cell.font=style.fontcell.fill=style.fill}cell.border = style.border;});}});try {const buffer = await workbook.xlsx.writeBuffer();const blob = new Blob([buffer], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});saveAs(blob, currentTime + '_设备数据.xlsx'); // 导出的文件名可以根据需要动态设置} catch (err) {console.error('导出Excel时出错:', err);}}}}
第二种复杂的表单导出
如图
数据格式
this.summaryStatistics={"type": null,"xuhao": null,"user": "汇总","timeJson": [{\"bxPrice\":0.00,\"fyPrice\":0.00,\"fyzb\":0.00,\"htPrice\":0.00,\"swPrice\":0.00,\"time\":\"2023年\"},{\"bxPrice\":129584000.80,\"fyPrice\":129592366.80,\"fyzb\":738.96,\"htPrice\":17537026.00,\"swPrice\":8366.00,\"time\":\"2024年\"}],"htXj": 17537026,"htZb": null,"fyXj": 129592366.8,"fyZbXj": 738.96}this.times = this.summaryStatistics.timeJsonthis.userStatistics = [{"type": null,"xuhao": 1,"user": "admin","timeJson": "[{\"bxPrice\":0.00,\"fyPrice\":0.00,\"fyzb\":0.00,\"htPrice\":0.00,\"swPrice\":0.00,\"time\":\"2023年\"},{\"bxPrice\":0.00,\"fyPrice\":0.00,\"fyzb\":0.00,\"htPrice\":274000.00,\"swPrice\":0.00,\"time\":\"2024年\"}]","htXj": 274000,"htZb": 1.56,"fyXj": 0,"fyZbXj": 0},{"type": null,"xuhao": 2,"user": "肖永梅","timeJson": "[{\"bxPrice\":0.00,\"fyPrice\":0.00,\"fyzb\":0.00,\"htPrice\":0.00,\"swPrice\":0.00,\"time\":\"2023年\"},{\"bxPrice\":0.00,\"fyPrice\":0.00,\"fyzb\":0.00,\"htPrice\":0.00,\"swPrice\":0.00,\"time\":\"2024年\"}]","htXj": 0,"htZb": 0,"fyXj": 0,"fyZbXj": 0}]//将数据添在第一行this.userStatistics.unshift(this.summaryStatistics);this.userStatistics.forEach((item) => {if (item.timeJson != null) {JSON.parse(item.timeJson).forEach((child, index) => {item[`htPrice${index}`] = child.htPrice;item[`fyPrice${index}`] = child.fyPrice;item[`fyzb${index}`] = child.fyzb;});}});
数据准备好后就是导出方法了(handleExport)
/** 导出按钮操作 */handleExport() {const loading = this.$loading({lock: true,text: "导出excel中...(若长时间无响应请刷新页面)",spinner: "el-icon-loading",background: "rgba(0, 0, 0, 0.7)",});const Exceljs = require("exceljs"); // 引入exceljsconst workbook = new Exceljs.Workbook(); // 创建工作簿const workSheet = workbook.addWorksheet("sheet1"); // 创建工作表(sheet1)workSheet.getRow(1).height = 60;workSheet.getRow(2).height = 36;workSheet.getRow(3).height = 28;workSheet.getRow(4).height = 28;let style = {alignment: {vertical: "middle",horizontal: "center",wrapText: true,},fill: {type: "pattern",pattern: "solid",fgColor: { argb: "E3F2D9" },},border: {top: { style: "thin" }, // 设置上边框样式为细线bottom: { style: "thin" }, // 设置下边框样式为细线left: { style: "thin" }, // 设置左边框样式为细线right: { style: "thin" }, // 设置右边框样式为细线},};// 获取动态年份条数let years = this.times.length;workSheet.mergeCells(`A1:${this.numberToMatchLetter(3 * years + 6)}1`); //将A1到xx的单元格合并const cell_A1 = workSheet.getCell("A1"); // 获取A1单元格cell_A1.value = "碧朗科技销售员销售额及费用统计表";cell_A1.font = { size: 18, bold: true, name: "宋体" }; // 设置字体大小为16,加粗,仿宋cell_A1.alignment = style.alignment;workSheet.mergeCells(`${this.numberToMatchLetter(3 * years + 7)}1:${this.numberToMatchLetter(3 * years + 10)}1`); //将xx1到xx1的单元格合并const cell_P1 = workSheet.getCell(`${this.numberToMatchLetter(3 * years + 7)}1`); // 获取xx1单元格cell_P1.value = "注:第4行的年份根据筛选条件动态变化";cell_P1.font = {size: 11,name: "Times New Roman",color: { argb: "FF0000" },};cell_P1.alignment = style.alignment;workSheet.mergeCells("A2:E2"); //将A2到E2的单元格合并const cell_A2 = workSheet.getCell("A2"); // 获取A2单元格cell_A2.value = `年份:${this.queryParams.startYear ? this.queryParams.startYear : ""}-${this.queryParams.endYear ? this.queryParams.endYear : ""} 月份:${this.queryParams.startMonth ? this.queryParams.startMonth : ""}-${this.queryParams.endMonth ? this.queryParams.endMonth : ""}`;cell_A2.font = { size: 8, bold: true, name: "宋体" };cell_A2.alignment = {vertical: "middle",wrapText: true,};workSheet.mergeCells("A3:A4"); //将A3到A4的单元格合并const cell_A3 = workSheet.getCell("A3"); // 获取A3单元格cell_A3.value = "序号";cell_A3.style = style;cell_A3.font = { size: 11, bold: true, name: "宋体" };// 遍历合并A3到A4的单元格的区域,为其余部分设置边框for (let row = 3; row <= 4; row++) {for (let col = 1; col <= 1; col++) {const cell = workSheet.getCell(row, col);cell.border = cell_A3.border;}}workSheet.mergeCells("B3:B4"); //将B3到B4的单元格合并const cell_B3 = workSheet.getCell("B3"); // 获取B3单元格cell_B3.value = "销售员";cell_B3.style = style;cell_B3.font = { size: 11, bold: true, name: "宋体" };// 遍历合并B3到B4的单元格的区域,为其余部分设置边框for (let row = 3; row <= 4; row++) {for (let col = 2; col <= 2; col++) {const cell = workSheet.getCell(row, col);cell.border = cell_B3.border;}}workSheet.mergeCells(`C3:${this.numberToMatchLetter(years + 4)}3`);const cell_diy1 = workSheet.getCell("C3");cell_diy1.value = "销售额";cell_diy1.style = style;cell_diy1.font = { size: 11, bold: true, name: "宋体" };// 遍历合并的单元格的区域,为其余部分设置边框for (let row = 3; row <= 3; row++) {for (let col = 3; col <= years + 4; col++) {const cell = workSheet.getCell(row, col);cell.border = cell_diy1.border;}}workSheet.mergeCells(`${this.numberToMatchLetter(years + 5)}3:${this.numberToMatchLetter(2 * years + 5)}3`);const cell_diy2 = workSheet.getCell(`${this.numberToMatchLetter(years + 5)}3`);// 创建富文本内容const richText = [{text: "费用",font: {name: "宋体",size: 11,bold: true,},},{text: "(报销的费用+备案商务费)",font: {name: "宋体",size: 9,bold: true,color: { argb: "FF0000" },},},];cell_diy2.value = { richText: richText };cell_diy2.style = style;// 遍历合并的单元格的区域,为其余部分设置边框for (let row = 3; row <= 3; row++) {for (let col = years + 5; col <= 2 * years + 5; col++) {const cell = workSheet.getCell(row, col);cell.border = cell_diy2.border;}}workSheet.mergeCells(`${this.numberToMatchLetter(2 * years + 6)}3:${this.numberToMatchLetter(3 * years + 6)}3`);const cell_diy3 = workSheet.getCell(`${this.numberToMatchLetter(2 * years + 6)}3`);cell_diy3.value = "费用占比(%)";cell_diy3.style = style;cell_diy3.font = { size: 11, bold: true, name: "宋体" };// 遍历合并的单元格的区域,为其余部分设置边框for (let row = 3; row <= 3; row++) {for (let col = 2 * years + 6; col <= 3 * years + 6; col++) {const cell = workSheet.getCell(row, col);cell.border = cell_diy3.border;}}const cell_XJ1 = workSheet.getCell(`${this.numberToMatchLetter(3 + years)}4`); // 获取单元格cell_XJ1.value = "小计";cell_XJ1.style = style;cell_XJ1.font = { size: 11, bold: true, name: "宋体" };const cell_ZB1 = workSheet.getCell(`${this.numberToMatchLetter(4 + years)}4`); // 获取单元格cell_ZB1.value = "占比(%)";cell_ZB1.style = style;cell_ZB1.font = { size: 11, bold: true, name: "宋体" };const cell_XJ2 = workSheet.getCell(`${this.numberToMatchLetter(5 + 2 * years)}4`); // 获取单元格cell_XJ2.value = "小计";cell_XJ2.style = style;cell_XJ2.font = { size: 11, bold: true, name: "宋体" };const cell_XJ3 = workSheet.getCell(`${this.numberToMatchLetter(6 + 3 * years)}4`); // 获取单元格cell_XJ3.value = "小计";cell_XJ3.style = style;cell_XJ3.font = { size: 11, bold: true, name: "宋体" };// 销售额动态年份填充for (let i = 1; i <= years; i++) {workSheet.getCell(`${this.numberToMatchLetter(2 + i)}4`).value =this.times[i - 1].time;workSheet.getCell(`${this.numberToMatchLetter(2 + i)}4`).style = style;workSheet.getCell(`${this.numberToMatchLetter(2 + i)}4`).font = {size: 11,bold: true,name: "宋体",};workSheet.getCell(`${this.numberToMatchLetter(years + 4 + i)}4`).value =this.times[i - 1].time;workSheet.getCell(`${this.numberToMatchLetter(years + 4 + i)}4`).style =style;workSheet.getCell(`${this.numberToMatchLetter(years + 4 + i)}4`).font ={size: 11,bold: true,name: "宋体",};workSheet.getCell(`${this.numberToMatchLetter(2 * years + 5 + i)}4`).value = this.times[i - 1].time;workSheet.getCell(`${this.numberToMatchLetter(2 * years + 5 + i)}4`).style = style;workSheet.getCell(`${this.numberToMatchLetter(2 * years + 5 + i)}4`).font = {size: 11,bold: true,name: "宋体",};}let exportTableProp = [{ key: "xuhao" },{ key: "user" },{ key: "htXj", width: 18 },{ key: "htZb", width: 18 },{ key: "fyXj", width: 18 },{ key: "fyZbXj", width: 18 },];if (this.times.length > 0) {this.times.forEach((item, index) => {exportTableProp.splice(index + 2, 0, {key: `htPrice${index}`,width: 18,});});this.times.forEach((item, index) => {exportTableProp.splice(index + years + 4, 0, {key: `fyPrice${index}`,width: 18,});});this.times.forEach((item, index) => {exportTableProp.splice(index + 2 * years + 5, 0, {key: `fyzb${index}`,width: 18,});});}workSheet.columns = exportTableProp; // 工作表添加表头workSheet.addRows(this.userStatistics); // 往工作表插入数据workSheet.eachRow((row, rowNumber) => {if (rowNumber > 4) {row.height = 20;row.alignment = style.alignment;row.eachCell({start: 1,end: 3 * years + 6,},(cell, colNumber) => {cell.border = style.border;});}});// 下载工作簿workbook.xlsx.writeBuffer().then((buffer) => {saveAs(new Blob([buffer], { type: "application/octet-stream" }),"销售员销售额及费用表格导出.xlsx");});loading.close();}
相关文章:

vue+exceljs前端下载、导出xlsx文件
首先安装插件 npm install exceljs file-saver第一种 简单导出 //页面引入 import ExcelJS from exceljs; import {saveAs} from file-saver; export default {methods: { /** 导出操作 */async handleExportFun() {let that this// 获取当前年月日 用户下载xlsx的文件名称设…...

算法定制LiteAIServer摄像机实时接入分析平台烟火检测算法的主要功能
在现代社会,随着人工智能技术的飞速发展,智能监控系统在公共安全领域的应用日益广泛。其中,烟火检测作为预防火灾的重要手段,其准确性和实时性对于减少火灾损失、保障人民生命财产安全具有重要意义。而算法定制LiteAIServer烟火检…...

用 Python 从零开始创建神经网络(二)
用 Python 从零开始创建神经网络(二) 引言1. Tensors, Arrays and Vectors:2. Dot Product and Vector Additiona. Dot Product (点积)b. Vector Addition (向量加法) 3. A Single Neuron with …...

嘉吉连续第七年亮相进博会
以“新质绿动,共赢未来”为主题,嘉吉连续第七年亮相进博会舞台。嘉吉带来了超过120款产品与解决方案,展示嘉吉在农业、食品、金融和工业等领域以客户为中心的创新成果。这些产品融合了嘉吉在相关领域的前瞻性思考,以及对本土市场的…...

设计模式之单列模式(7种单例模式案例,Effective Java 作者推荐枚举单例模式)
前言 在设计模式中按照不同的处理方式共包含三大类;创建型模式、结构型模式和行为模式,其中创建型模式目前已经介绍了其中的四个;工厂方法模式、抽象工厂模式、生成器模式和原型模式,除此之外还有最后一个单例模式。 单列模式介绍…...
多个服务器共享同一个Redis Cluster集群,并且可以使用Redisson分布式锁
Redisson 是一个高级的 Redis 客户端,它支持多种分布式 Java 对象和服务。其中之一就是分布式锁(RLock),它可以跨多个应用实例在多个服务器上使用同一个 Redis 集群,为这些实例提供锁服务。 当你在不同服务器上运行的…...
100种算法【Python版】第59篇——滤波算法之扩展卡尔曼滤波
本文目录 1 算法步骤2 算法示例2.1 示例描述2.2 python代码3 算法应用:机器人位姿估计扩展卡尔曼滤波(EKF)是一种处理非线性系统的状态估计算法。它通过线性化非线性系统来实现类似于线性卡尔曼滤波的效果。 1 算法步骤 (1)初始化 初始状态: x ^ 0 ∣ 0 \hat{x}_{0|0}...

制造业数字化转型的强大赋能平台:盘古信息IMS OS工软技术底座
在制造业数字化转型的浪潮中,技术底座的选择与实施至关重要。它不仅决定了企业数字化转型的深度与广度,还影响着企业的生产效率、成本控制和市场竞争力。盘古信息IMS OS作为一款强大的工软技术底座,凭借其高度模块化、可配置的设计理念&#…...

域名+服务器+Nginx+宝塔使用SSL证书配置HTTPS
前言 在我的前面文章里,有写过一篇文章 linux服务器宝塔从头部署别人可访问的网站 在这篇文章,有教学怎么使用宝塔和买的服务器的公网IP,以及教怎么打包vue和springboot去部署不用域名的网站让别人访问 那么,这篇文章将在这个…...

UnityAssetsBundle字体优化解决方案
Unity开发某个项目,打包后的apk包体已经高达1.25G了,这是非常离谱的。为了不影响用户体验,需要将apk包体缩小。因为项目本身不包含很多模型以及其他大型资源,排除法将AB包删除,发现app本身就100多M。 由此可以锁定是AB…...

Go的环境搭建以及GoLand安装教程
目录 一、开发环境Golang安装 二、配置环境变量 三、GoLand安装 四、Go 语言的 Hello World 一、开发环境Golang安装 官方网址: The Go Programming Language 1. 首先进入官网,点击Download,选择版本并进行下载: 2. …...

git clone,用https还是ssh
前言 在使用Git去克隆项目时,会遇到https和ssh等形式,这两种又有何种区别呢,本文将重点讨论在具体使用中的问题。 注:第一次使用Git 时,需要先设置全局用户名和邮箱,否则后续使用命令时会报错,也是提醒先添…...
量化交易系统开发-实时行情自动化交易-Okex行情交易数据
19年创业做过一年的量化交易但没有成功,作为交易系统的开发人员积累了一些经验,最近想重新研究交易系统,一边整理一边写出来一些思考供大家参考,也希望跟做量化的朋友有更多的交流和合作。 接下来聊聊基于Okex交易所API获取行情数…...

【重装系统后重新配置2】pycharm 终端无法激活conda环境
pycharm 终端无法激活 conda 环境,但是 Windows本地终端是可以激活的 原因是pycharm 默认的终端是 Windows PowerShell 解决方法有两个: 一、在设置里,修改为cmd 二、下面直接选择...

【LeetCode每日一题】——802.找到最终的安全状态
文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时空频度】九【代码实现】十【提交结果】 一【题目类别】 图 二【题目难度】 中等 三【题目编号】 802.找到最终的安全状态 四【题目描述】 有一个有…...

kafka安装部署--详细教程
2.1 安装部署 每次进入 linux 都会自动进入 base 环境,如何关闭 base conda deactivate 手动关闭 conda config --set auto_activate_base false 关闭自动进入 2.1.1 集群规划 bigdata01 bigdata02 bigdata03 zk zk zk kafka kafka kafka 2.1.2 集群部…...

CMD 查询python 出现 No pyvenv.cfg file 很奇怪 2024/11/9
CMD 查询python 出现 No pyvenv.cfg file 很奇怪 查询得到我有很多个...........版本 删除这个变量就不会 因为 没有安装软件 跳转到 Windows 商店 再把主要使用的python路径置顶 现在运行cmd查询 对比之前的图片 可以发现 这一条商店的没有了! 完整测试效果,问题解决了!...

learnopencv系列二:U2-Net/IS-Net图像分割(背景减除)算法、使用背景减除实现视频转ppt应用
文章目录 一、视频转幻灯片应用1.1 什么是背景减除?1.1.1 背景减除简介1.1.2 bgslibrary 1.2 OpenCV背景减除技术1.3 差异哈希1.3.1 图像哈希技术1.3.2 dHash算法1.3.3 图像哈希的速度和准确性测试 1.4 视频转幻灯片应用的工作流程1.5 项目代码1.5.1 环境准备1.5.2 …...

linux命令详解,文件系统权限相关
文件系统权限相关 linux系统中一切都是文件 查看权限 Is -la /etc/passwd更改文件所有者 chown root file修改文件权限 sudo chmod urwx,grw,o-r file sudo chmod ux,gtw,o-r file chmod 400 <file>一、Linux系统中一切都是文件 在linux系统中,几乎所有的…...
2024-11-5 学习人工智能的Day22 openCV(4)
face_recognition 介绍 face_recognition 是一个非常流行的 Python 库,专门用于人脸识别任务。它基于 dlib 库和 HOG(Histogram of Oriented Gradients)特征以及深度学习模型,提供了简单易用的接口来进行人脸检测、面部特征点定位…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...

剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...