Vue实现JSON字符串格式化编辑器组件

相信很多同学都用过网上的在线JSON格式化工具来将杂乱的JSON数据转换成易于我们阅读和编辑的格式。那么,你有没有想过自己动手实现一个这样的工具呢?今天,我将介绍如何使用Vue.js来构建一个简单的JSON格式化工具。

功能简述

  • 支持格式化JSON字符串
  • 支持去除字符串中的空格
  • 支持全屏操作
  • 实时展示格式化状态
  • 控制台展示成功和失败的详情,支持错误定位
  • 编辑器精准计算字符串的行号

效果图展示

默认

全屏

功能介绍

按钮

其他

1、自动补全

输入”(“、”{“、”[“将会自动补全另一半

2、自动删除

删除括号时也会自动删除另一半

3、括号匹配

点击括号会高亮另一半括号,方便定位

4、支持ctrl+z撤销和ctrl+y重做功能

5、编辑器根据字符串的换行计算行号并展示

代码

vue文件
<!--JsonEditor.vue-->
<template><div ref="center" id="editor_body" :style="{ height: editorHeight, width: editorWidth }"><div style="height: 80%"><div class="tool_slider"><div style="display: flex; align-items: center"><imgsrc="@/assets/icons/format.svg"class="icon_hover"@click="prettyFormat(viewJsonStr)"title="格式化"/><div style="height: 18px; border: 1px solid #858585; margin: 0 3px"></div><imgsrc="@/assets/icons/clearLine.svg"class="icon_hover"@click="viewJsonStr = viewJsonStr.replace(/\s+/g, '')"title="去除空格"/><divstyle="display: flex;align-items: center;border-left: 2px solid #858585;height: 18px;margin: 0 3px;padding: 0 3px;"><imgsrc="@/assets/icons/full.svg"v-if="!isFullScreen"class="icon_hover"@click="fullScreen"title="全屏"/><imgsrc="@/assets/icons/closeFull.svg"v-elseclass="icon_hover"@click="fullScreen"title="退出"/></div></div><div style="display: flex; align-items: center"><imgsrc="@/assets/icons/success.svg"title="格式正确"v-if="isPass"style="height: 20px; width: 20px"/><imgsrc="@/assets/icons/error.svg"title="格式错误"v-elsestyle="height: 17px; width: 17px"/></div></div><div class="edit-container"><textareawrap="off"cols="1"id="leftNum"disabledonscroll="document.getElementById('rightNum').scrollTop = this.scrollTop;"></textarea><textarearef="myTextarea"id="rightNum":key="isFullScreen"style="width: 100%"placeholder="请输入JSON字符串"onscroll="document.getElementById('leftNum').scrollTop = this.scrollTop;":value="viewJsonStr"@click="handleClick"@input="handleTextareaInput1"/></div></div><div id="console">{{ jsonObj }}</div></div>
</template><script lang="ts" setup>import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';import { cloneDeep } from 'lodash-es';import {handleBackspace,handleClick,handleClickEnter,handleTabKey,handleTextareaInput,setAutoKey,} from '@/components/JsonEditor';const emit = defineEmits(['update:value']);const props = defineProps({value: {type: String,default: '',},width: {type: String,default: '1000px',},height: {type: String,default: '400px',},});const viewJsonStr: any = ref(props.value);const editorWidth: any = ref(JSON.parse(JSON.stringify(props.width)));const editorHeight: any = ref(JSON.parse(JSON.stringify(props.height)));// 自动补全function handleTextareaInput1(event) {handleTextareaInput(viewJsonStr, event);}const isPass = ref(true);watch(() => viewJsonStr.value,(newValue) => {calculateNum(newValue);emit('update:value', newValue);},);const num = ref('');function calculateNum(value) {let lineBbj: any = document.getElementById('leftNum');num.value = '';let str = value;if (str === null || str === undefined) {str = '';}str = str.replace(/\r/gi, '');str = str.split('\n');let n = str.length;if (n.toString().length > 3) {lineBbj.style.width = n.toString().length * 10 + 'px';} else {lineBbj.style.width = '30px';}for (let i = 1; i <= n; i++) {if (document.all) {num.value += i + '\r\n'; //判断浏览器是否是IE} else {num.value += i + '\n';}}lineBbj.value = num.value;}// 预览对象const jsonObj = computed(() => {const str = cloneDeep(viewJsonStr.value);// 如果输入的全是数字,JSON.parse(str)不会报错,需要手动处理一下const onlyNumber = /^\d+$/.test(str);const dom = document.getElementById('console');function setColor(color) {if (dom) {dom.style.color = color;}}if (str) {try {if (onlyNumber) {setColor('red');isPass.value = false;return getCurrentTime() + str + ' is not valid JSON';}setColor('black');isPass.value = true;if (JSON.parse(str)) {setColor('green');return `${getCurrentTime()}校验通过`;}} catch (e: any) {isPass.value = false;setColor('red');if (e.message?.match(/position\s+(\d+)/)) {const location = e.message?.match(/position\s+(\d+)/)[1];const str1 = str.substring(0, location).trim();const str2 = str1.split('\n');const message = e.message.substring(0, e.message.indexOf('position'));// 如果当前行或者前一行有'['if (str2[str2.length - 1]?.includes('[')) {const { line, column } = getLineAndColumn(str1, str1.length - 1);return `${message} at line ${line},column ${column}`;}const { line, column } = getLineAndColumn(str, location);return `${getCurrentTime()}${message} at line ${line},column ${column}`;} else {return getCurrentTime() + str + ' is not valid JSON';}}} else {return null;}});// 获取当前时间function getCurrentTime() {let now = new Date(); // 获取当前日期和时间let hours = now.getHours(); // 获取小时let minutes: string | number = now.getMinutes(); // 获取分钟let seconds: string | number = now.getSeconds(); // 获取秒let period = hours >= 12 ? '下午' : '上午'; // 判断是上午还是下午// 将小时转换为12小时制hours = hours % 12 || 12;// 格式化分钟和秒,确保它们是两位数minutes = minutes < 10 ? '0' + minutes : minutes;seconds = seconds < 10 ? '0' + seconds : seconds;// 构造最终的时间字符串let currentTime = period + hours + ':' + minutes + ':' + seconds;return '【' + currentTime + '】 ';}//计算错误信息所在行列function getLineAndColumn(str, index) {let line = 1;let column = 1;for (let i = 0; i < index; i++) {if (str[i] === '\n') {line++;column = 1;} else {column++;}}return { line, column };}//json格式美化function prettyFormat(str) {try {// 设置缩进为2个空格str = JSON.stringify(JSON.parse(str), null, 4);str = str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');viewJsonStr.value = str.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,function (match) {return match;},);} catch (e) {console.log('异常信息:' + e);}}const center = ref();const isFullScreen = ref(false);// 添加或删除全屏属性function fullScreen() {if (center.value) {if (center.value.className.includes('fullScreen')) {editorHeight.value = JSON.parse(JSON.stringify(props.height));editorWidth.value = JSON.parse(JSON.stringify(props.width));center.value.className = center.value.className.replace(' fullScreen', '');isFullScreen.value = false;} else {editorHeight.value = '100vh';editorWidth.value = '100vw';center.value.className += ' fullScreen';isFullScreen.value = true;}}}const myTextarea: any = ref(null);function handleKeyDown(event) {if (myTextarea.value) {if (event.key === 'Backspace') {handleBackspace(viewJsonStr, event);} else if (event.key === 'Enter') {handleClickEnter(viewJsonStr, event);} else if (event.key === 'Tab') {handleTabKey(event);} else if (event.key === 'Escape') {if (isFullScreen.value) {fullScreen();}}}}// 符号自动补全以及选中文本后输入符号自动包裹function getMouseCheck(event) {setAutoKey(viewJsonStr, event);}onMounted(() => {window.addEventListener('keydown', handleKeyDown);document.addEventListener('keydown', getMouseCheck);calculateNum(props.value);});onBeforeUnmount(() => {window.removeEventListener('keydown', handleKeyDown);document.removeEventListener('keydown', getMouseCheck);});
</script><style scoped lang="less">#editor_body {border: 1px solid #d9d9d9;border-radius: 4px;padding: 5px;box-sizing: border-box;}.tool_slider {padding-left: 5px;padding-right: 5px;display: flex;width: 100%;box-sizing: border-box;justify-content: space-between;align-items: center;height: 25px;border: 1px solid #d9d9d9;border-bottom: 0;}.icon_hover {height: 20px;width: 20px;cursor: pointer;&:hover {color: #5c82ff;}}#leftNum {overflow: hidden;padding: 3px 2px;height: 100%;width: 30px;line-height: 22px;font-size: 13px;color: rgba(0, 0, 0, 0.25);font-weight: bold;resize: none;text-align: center;outline: none;border: 0;background: #f5f7fa;box-sizing: border-box;border-right: 1px solid;font-family: 微软雅黑;}#rightNum {white-space: nowrap;height: 100%;padding: 3px;line-height: 22px;box-sizing: border-box;resize: none;border: 0;font-family: 微软雅黑;&::-webkit-scrollbar {width: 5px;height: 5px;background-color: #efeae6;}&:focus-visible {outline: none;}&:hover {border: 0;}&:focus {border: 0;}}.leftBox {height: 100%;text-align: left;}.edit-container {height: calc(100% - 25px);width: 100%;box-sizing: border-box;border: 1px solid #d9d9d9;display: flex;}.fullScreen {position: fixed;z-index: 9999;left: 0;top: 0;right: 0;bottom: 0;background-color: #f5f7fa;}#console {padding: 12px;box-sizing: border-box;height: calc(20% - 5px);margin-top: 5px;width: 100%;background-color: white;border: 1px solid #d9d9d9;overflow: auto;font-family: 微软雅黑;text-align: left;}
</style>
配置文件
/*index.ts*/
import { nextTick } from 'vue';// 获取文本框的值
export const handleTextareaInput = (viewJsonStr, event) => {const textarea = event.target;const newValue = textarea.value;viewJsonStr.value = newValue;
};// 设置自动补全
export const setAutoKey = (viewJsonStr, event) => {const textarea: any = document.getElementById('rightNum');if (event.key === "'" || event.key === '"') {event.preventDefault(); // 阻止默认行为const selectedText = textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);const newText = `${event.key}` + selectedText + `${event.key}`;const cursorPosition = textarea.selectionStart + 1;textarea.value =textarea.value.substring(0, textarea.selectionStart) +newText +textarea.value.substring(textarea.selectionEnd);textarea.setSelectionRange(cursorPosition, cursorPosition);} else if (event.key === '(') {event.preventDefault(); // 阻止默认行为const selectedText = textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);const newText = '(' + selectedText + ')';const cursorPosition = textarea.selectionStart + 1;textarea.value =textarea.value.substring(0, textarea.selectionStart) +newText +textarea.value.substring(textarea.selectionEnd);textarea.setSelectionRange(cursorPosition, cursorPosition);} else if (event.key === '[') {event.preventDefault(); // 阻止默认行为const selectedText = textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);const newText = '[' + selectedText + ']';const cursorPosition = textarea.selectionStart + 1;textarea.value =textarea.value.substring(0, textarea.selectionStart) +newText +textarea.value.substring(textarea.selectionEnd);textarea.setSelectionRange(cursorPosition, cursorPosition);} else if (event.key === '{') {event.preventDefault(); // 阻止默认行为const selectedText = textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);const newText = '{' + selectedText + '}';const cursorPosition = textarea.selectionStart + 1;textarea.value =textarea.value.substring(0, textarea.selectionStart) +newText +textarea.value.substring(textarea.selectionEnd);textarea.setSelectionRange(cursorPosition, cursorPosition);}viewJsonStr.value = textarea.value;
};
/*------------------------------------------------括号高亮------------------------------------------------------------*/
const findOpeningBracketIndex = (text, startIndex, char) => {const openingBrackets = {']': '[','}': '{',')': '(',};let count = 0;for (let i = startIndex; i >= 0; i--) {if (text.charAt(i) === char) {count++;} else if (text.charAt(i) === openingBrackets[char]) {count--;if (count === 0) {return i;}}}return -1;
};const findClosingBracketIndex = (text, startIndex, char) => {const closingBrackets = {'[': ']','{': '}','(': ')',};let count = 0;for (let i = startIndex; i < text.length; i++) {if (text.charAt(i) === char) {count++;} else if (text.charAt(i) === closingBrackets[char]) {count--;if (count === 0) {return i;}}}return -1;
};
const isBracket = (char) => {return ['[', ']', '{', '}', '(', ')'].includes(char);
};
// 点击括号寻找对应另一半
export const handleClick = (event) => {const textarea: any = document.getElementById('rightNum');const { selectionStart, selectionEnd, value } = textarea;const clickedChar = value.charAt(selectionStart);if (isBracket(clickedChar)) {const openingBracketIndex = findOpeningBracketIndex(value, selectionStart, clickedChar);const closingBracketIndex = findClosingBracketIndex(value, selectionStart, clickedChar);if (openingBracketIndex !== -1) {textarea.setSelectionRange(openingBracketIndex, openingBracketIndex + 1);} else if (closingBracketIndex !== -1) {textarea.setSelectionRange(closingBracketIndex, closingBracketIndex + 1);}}
};
/*键盘事件*/
export function handleClickEnter(viewJsonStr, event) {if (event.key == 'Enter') {const textarea = event.target;const cursorPosition: any = textarea.selectionStart; // 获取光标位置const value = textarea.value;if ((value[cursorPosition - 1] === '{' && value[cursorPosition] == '}') ||(value[cursorPosition - 1] === '[' && value[cursorPosition] == ']')) {textarea.value = value.slice(0, cursorPosition) + '\n' + value.slice(cursorPosition);textarea.setSelectionRange(cursorPosition, cursorPosition);viewJsonStr.value = textarea.value;// 将光标移动到插入的空格后面setTimeout(() => {handleTabKey(syntheticEvent);}, 30);}}
}
// 新建tab按键对象
const syntheticEvent = new KeyboardEvent('keydown', {key: 'Tab',
});
// 按下tab键时的操作
export const handleTabKey = (event) => {const textarea: any = document.getElementById('rightNum');const { selectionStart, selectionEnd } = textarea;const tabSpaces = '    '; // 4 spacesevent.preventDefault();// 在当前光标位置插入4个空格textarea.value =textarea.value.substring(0, selectionStart) +tabSpaces +textarea.value.substring(selectionEnd);// 将光标向右移动4个空格textarea.selectionStart = selectionStart + tabSpaces.length;textarea.selectionEnd = selectionStart + tabSpaces.length;
};// 按下Backspace按键时
export function handleBackspace(viewJsonStr, event) {const textarea = event.target;const cursorPosition = textarea.selectionStart;const textBeforeCursor = viewJsonStr.value.slice(0, cursorPosition);const textAfterCursor = viewJsonStr.value.slice(cursorPosition);if ((textBeforeCursor.endsWith('"') && textAfterCursor.startsWith('"')) ||(textBeforeCursor.endsWith("'") && textAfterCursor.startsWith("'")) ||(textBeforeCursor.endsWith('[') && textAfterCursor.startsWith(']')) ||(textBeforeCursor.endsWith('{') && textAfterCursor.startsWith('}')) ||(textBeforeCursor.endsWith('(') && textAfterCursor.startsWith(')'))) {event.preventDefault(); // 阻止默认的删除行为viewJsonStr.value = textBeforeCursor.slice(0, -1) + textAfterCursor.slice(1);nextTick(() => {textarea.selectionStart = cursorPosition - 1;textarea.selectionEnd = cursorPosition - 1;}).then((r) => {});}
}
调用方式
 <JsonEditor  v-model:value="testStr" />const testStr = ref('123');

总结

这个JSON编辑器不仅能够让你方便地格式化JSON字符串,还能帮你去掉不必要的空格。而且,它的全屏功能让编辑更加顺畅。最酷的是,它还能实时告诉你格式化的进度,如果遇到问题了,控制台会详细告诉你哪里出错了,这样你就能快速找到问题并解决它。编辑器还能精确地计算行号,这对于查找问题也是很有帮助的。而且,它还有自动补全、自动删除和括号匹配这些贴心的功能,让你的编辑工作变得更加轻松。如果你不小心做错了,也不用担心,因为它支持撤销和重做。希望它能帮助到大家,让我们的工作更加愉快!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用spring boot实现异常的统一返回

在这个前后端分离的时代&#xff0c;一个 统一的数据格式非常重要。本次我们实现用spring boot实现一下返回给前端数据的统一格式&#xff0c;不再出现服务器500的错误。 新建一个spring boot项目&#xff0c;并导入knife4j的依赖。 写一个controller控制器&#xff0c;用来是…

57.网游逆向分析与插件开发-游戏增加自动化助手接口-接管游戏的自动药水设定功能

内容来源于&#xff1a;易道云信息技术研究院VIP课 码云地址&#xff08;master分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&#xff1a;51307d6bf69f2f3c645c70d09f841f5e32da79b9 代码下载地址&#xff0c;在 SRO_EX 目录下&…

计算机网络——基础知识汇总(八)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;V…

基于电商场景的高并发RocketMQ实战-促销活动推送千万级用户解决方案【多线程+分片推送实现高性能推送】

&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308; 欢迎关注公众号&#xff08;通过文章导读关注&#xff09;&#xff0c;发送【资料】可领取 深入理解 Redis 系列文章结合电商场景讲解 Redis 使用场景、中间件系列…

如何使用Docker将.Net6项目部署到Linux服务器(三)

目录 四 安装nginx 4.1 官网下载nginx 4.2 下载解压安装nginx 4.3 进行configure 4.4 执行make 4.5 查看nginx是否安装成功 4.6 nginx的一些常用命令 4.6.1 启动nginx 4.6.2 通过命令查看nginx是否启动成功 4.6.3 关闭Nginx 4.6.5 重启Nginx 4.6.6 杀掉所有Nginx进…

浏览器---善用的一些调试技巧

https://www.cnblogs.com/dasusu/p/17932742.html

c# 捕获全部线程的异常 试验

1.概要 捕获全部线程的异常 试验&#xff0c;最终结果task的异常没有找到捕获方法 2.代码 2.1.试验1 2.1.1 试验结果 2.2 代码 2.2.1主程序代码 using NLog; using System; using System.Threading; using System.Windows.Forms;namespace 异常监控 {static class Program…

C++day4作业

定义一个Person类&#xff0c;私有成员int age&#xff0c;string &name&#xff0c;定义一个Stu类&#xff0c;包含私有成员double *score&#xff0c;写出两个类的构造函数、析构函数、拷贝构造和拷贝赋值函数&#xff0c;完成对Person的运算符重载(算术运算符、条件运算…

IRQ Handler 的使用——以USART串口接收中断分别在标准库与HAL库版本下的举例

前言&#xff1a; 1.中断系统及EXTI外部中断知识点见我的博文&#xff1a; 9.中断系统、EXTI外部中断_eirq-CSDN博客文章浏览阅读301次&#xff0c;点赞7次&#xff0c;收藏6次。EXTI&#xff08;Extern Interrupt&#xff09;外部中断EXTI可以监测指定GPIO口的电平信号&…

Redis(认识NoSQL,认识redis,安装redis,redis桌面客户端,redis常见命令,redis的Java客户端)

文章目录 Redis快速入门1.初识Redis1.1.认识NoSQL1.1.1.结构化与非结构化1.1.2.关联和非关联1.1.3.查询方式1.1.4.事务1.1.5.总结 1.2.认识Redis1.3.安装Redis1.3.1.依赖库1.3.2.上传安装包并解压1.3.3.启动1.3.4.默认启动1.3.5.指定配置启动1.3.6.开机自启 1.4.Redis桌面客户端…

病情聊天机器人,利用Neo4j图数据库和Elasticsearch全文搜索引擎相结合

项目设计目的&#xff1a; 本项目旨在开发一个病情聊天机器人&#xff0c;利用Neo4j图数据库和Elasticsearch全文搜索引擎相结合&#xff0c;实现对病情相关数据的存储、查询和自动回答。通过与用户的交互&#xff0c;机器人可以根据用户提供的症状描述&#xff0c;给出初步的可…

Linux---进程控制

一、进程创建 fork函数 在Linux中fork函数是非常重要的函数&#xff0c;它从已存在进程中创建一个新进程&#xff0c;原进程为父进程 fork函数的功能&#xff1a; 分配新的内存和内核数据结构给子进程将父进程部分数据结构内容拷贝至子进程添加子进程到系统的进程列表中fork返…

听GPT 讲Rust源代码--library/portable-simd

File: rust/library/portable-simd/crates/core_simd/examples/spectral_norm.rs spectral_norm.rs是一个示例程序&#xff0c;它展示了如何使用Portable SIMD库中的SIMD&#xff08;Single Instruction Multiple Data&#xff09;功能来实现频谱规范化算法。该示例程序是Rust源…

如何使用SeaFile搭建本地私有云盘并结合cpolar实现远程访问

文章目录 1. 前言2. SeaFile云盘设置2.1 SeaFile的安装环境设置2.2 SeaFile下载安装2.3 SeaFile的配置 3. cpolar内网穿透3.1 Cpolar下载安装3.2 Cpolar的注册3.3 Cpolar云端设置3.4 Cpolar本地设置 4.公网访问测试5.结语 1. 前言 现在我们身边的只能设备越来越多&#xff0c;…

从零开始:使用 BIND 构建和管理您的 DNS 服务器

1 前言 在这篇文章中&#xff0c;我将详细介绍如何使用 BIND&#xff08;Berkeley Internet Name Domain&#xff09;软件包中的 named 程序来配置和管理一个基本的 DNS 服务器。 从安装 BIND 开始&#xff0c;到设置 DNS 区域文件&#xff0c;再到运行和测试您的服务器&#x…

typescript 中 infer 用法

infer 介绍 infer 一般在 extends 子语句中,infer 会引入一个待推断的类型变量 (如 infer R) R可以是任意单词字母 这个推断的类型变量可以在有条件类型的 true 分支中被引用 允许出现多个同类型变量的 infer。 基本示例 type ParamType<T> T extends (arg: infer…

kubeadm创建k8s集群

kubeadm来快速的搭建一个k8s集群&#xff1a; 二进制搭建适合大集群&#xff0c;50台以上。 kubeadm更适合中下企业的业务集群。 部署框架 master192.168.10.10dockerkubelet kubeadm kubectl flannelnode1192.168.10.20dockerkubelet kubeadm kubectl flannelnode2192.168.1…

新火种AI|福布斯Top50,估值高达50亿,这家AI法律公司令人震惊

2023年3月&#xff0c;OpeAI以雷霆之势推出了ChatGPT&#xff0c;为AI产业带来了颠覆性的进展&#xff0c;让所有人为之震惊。其中有一项对于ChatGPT的测试还引起了了不小的轰动&#xff0c;当时美国伊利诺伊理工大学芝加哥肯特法学院称&#xff0c;GPT-4通过了美国律师资格考试…

嵌入式视频播放器(mplayer)

1.文件准备&#xff1a; MPlayer-1.0rc2.tar.bz2 libmad-0.15.1b.tar.gz 直接Git到本地 git clone https://gitee.com/zxz_FINE/mplayer_tarball.git 2.文件夹准备&#xff1a; src存放解压后的源码文件&#xff0c;target_Mplayer存放编译安装的目标文件 mkdir src targe…

将本地工作空间robot_ws上传到gitee仓库

git config --global user.name "geniusChinaHN" git config --global user.email "12705243geniuschinahnuser.noreply.gitee.com" cd ~/robot_ws #git init#创建原始仓库时候用 git add . git commit -m "上传文件内容描述" #git remote add r…