Vue3 + Element-plus + TS —— 动态表格自由编辑

9a69fede8b2044a79dd834e3e48f20b4.png前期回顾   

《 穿越时空的代码、在回首:Evil.js两年后的全新解读 》-CSDN博客

Vue3 + TS + Element-Plus 封装Tree组件 《亲测可用》_ icon-default.png?t=N7T8https://blog.csdn.net/m0_57904695/article/details/131664157?spm=1001.2014.3001.5501  

态表格 自由编辑

 

目录

♻️ 效果图 

 🚩 Vue2 版本

🐗 Vue3 版本


♻️ 效果图 

👉 在线预览

 🚩 Vue2 版本

<template><!-- 可编辑表格V2 --><div id="hello"><!-- 表格 --><p class="tips">单击 右键菜单,单击 左键编辑</p><el-table:data="tableData"height="500px"borderstyle="width: 100%; margin-top: 10px"@cell-click="cellDblclick"@header-contextmenu="(column, event) => rightClick(null, column, event)"@row-contextmenu="rightClick":row-class-name="tableRowClassName"><el-table-columnv-if="columnList.length > 0"type="index":label="'No.'"/><el-table-columnv-for="(col, idx) in columnList":key="col.prop":prop="col.prop":label="col.label":index="idx"/></el-table><div><h3 style="text-align: center">实时数据展示</h3><label>当前目标:</label><p>{{ JSON.stringify(curTarget) }}</p><label>表头:</label><p v-for="col in columnList" :key="col.prop">{{ JSON.stringify(col) }}</p><label>数据:</label><p v-for="(data, idx) in tableData" :key="idx">{{ JSON.stringify(data) }}</p></div><!-- 右键菜单框 --><div v-show="showMenu" id="contextmenu" @mouseleave="showMenu = false"><p style="margin-bottom: 10px">列:</p><el-button size="mini" type="primary" @click="addColumn()">前方插入一列</el-button><el-button size="mini" type="primary" @click="addColumn(true)">后方插入一列</el-button><el-buttontype="primary"size="mini"@click="openColumnOrRowSpringFrame('列')">删除当前列</el-button><el-button size="mini" type="primary" @click="renameCol($event)">更改列名</el-button><div class="line"></div><p style="margin-bottom: 12px">行:</p><el-buttonsize="mini"type="primary"@click="addRow()"v-show="!curTarget.isHead">上方插入一行</el-button><el-buttonsize="mini"type="primary"@click="addRow(true)"v-show="!curTarget.isHead">下方插入一行</el-button><el-buttonsize="mini"type="primary"@click="addRowHead(true)"v-show="curTarget.isHead">下方插入一行</el-button><el-buttontype="primary"size="mini"@click="openColumnOrRowSpringFrame('行')"v-show="!curTarget.isHead">删除当前行</el-button></div><!-- 单元格/表头内容编辑框 --><div v-show="showEditInput" id="editInput"><el-inputv-focusplaceholder="请输入内容"v-model="curTarget.val"clearable@change="updTbCellOrHeader"@blur="showEditInput = false"@keyup="onKeyUp($event)"><template #prepend>{{ curColumn.label || curColumn.prop }}</template></el-input></div></div>
</template><script>
export default {data() {return {columnList: [{ prop: "name", label: "姓名" },{ prop: "age", label: "年龄" },{ prop: "city", label: "城市" },{ prop: "tel", label: "电话" }],tableData: [{ name: "张三", age: 24, city: "广州", tel: "13312345678" },{ name: "李四", age: 25, city: "九江", tel: "18899998888" }],showMenu: false, // 显示右键菜单showEditInput: false, // 显示单元格/表头内容编辑输入框curTarget: {// 当前目标信息rowIdx: null, // 行下标colIdx: null, // 列下标val: null, // 单元格内容/列名isHead: undefined // 当前目标是表头?},countCol: 0 // 新建列计数};},computed: {curColumn() {return this.columnList[this.curTarget.colIdx] || {};}},methods: {// 删除当前列或当前行openColumnOrRowSpringFrame(type) {this.$confirm(`此操作将永久删除该 ${type === "列" ? "列" : "行"}, 是否继续 ?, '提示'`,{confirmButtonText: "确定",cancelButtonText: "取消",type: "warning"}).then(() => {if (type === "列") {this.delColumn();} else if (type === "行") {this.delRow();}this.$message({type: "success",message: "删除成功!"});}).catch(() => {this.$message({type: "info",message: "已取消删除"});});},// 回车键关闭编辑框onKeyUp(e) {if (e.keyCode === 13) {this.showEditInput = false;}},// 单元格双击事件 - 更改单元格数值cellDblclick(row, column, cell, event) {this.showEditInput = false;if (column.index == null) return;this.locateMenuOrEditInput("editInput", 200, event); // 编辑框定位this.showEditInput = true;// 当前目标this.curTarget = {rowIdx: row.row_index,colIdx: column.index,val: row[column.property],isHead: false};},// 单元格/表头右击事件 - 打开菜单rightClick(row, column, event) {// 阻止浏览器自带的右键菜单弹出event.preventDefault(); // window.event.returnValue = falsethis.showMenu = false;if (column.index == null) return;this.locateMenuOrEditInput("contextmenu", 140, event); // 菜单定位this.showMenu = true;// 当前目标this.curTarget = {rowIdx: row ? row.row_index : null, // 目标行下标,表头无 row_indexcolIdx: column.index, // 目标项下标val: row ? row[column.property] : column.label, // 目标值,表头记录名称isHead: !row};},// 去更改列名renameCol($event) {this.showEditInput = false;if (this.curTarget.colIdx === null) return;this.locateMenuOrEditInput("editInput", 200, $event); // 编辑框定位this.showEditInput = true;},// 更改单元格内容/列名updTbCellOrHeader(val) {if (!this.curTarget.isHead)// 更改单元格内容this.tableData[this.curTarget.rowIdx][this.curColumn.prop] = val;else {// 更改列名if (!val) return;this.columnList[this.curTarget.colIdx].label = val;}},// 新增行addRow(later) {this.showMenu = false;const idx = later ? this.curTarget.rowIdx + 1 : this.curTarget.rowIdx;let obj = {};this.columnList.forEach((p) => (obj[p.prop] = ""));this.tableData.splice(idx, 0, obj);},// 表头下新增行addRowHead() {// 关闭菜单this.showMenu = false;// 新增行let obj = {};// 初始化行数据this.columnList.forEach((p) => (obj[p.prop] = ""));// 插入行数据this.tableData.unshift(obj);},// 删除行delRow() {this.showMenu = false;this.curTarget.rowIdx !== null &&this.tableData.splice(this.curTarget.rowIdx, 1);},// 新增列addColumn(later) {this.showMenu = false;const idx = later ? this.curTarget.colIdx + 1 : this.curTarget.colIdx;const colStr = { prop: "col_" + ++this.countCol, label: "" };this.columnList.splice(idx, 0, colStr);this.tableData.forEach((p) => (p[colStr.prop] = ""));},// 删除列delColumn() {this.showMenu = false;this.tableData.forEach((p) => {delete p[this.curColumn.prop];});this.columnList.splice(this.curTarget.colIdx, 1);},// 添加表格行下标tableRowClassName({ row, rowIndex }) {row.row_index = rowIndex;},// 定位菜单/编辑框locateMenuOrEditInput(eleId, eleWidth, event) {let ele = document.getElementById(eleId);ele.style.top = event.clientY - 100 + "px";ele.style.left = event.clientX - 50 + "px";if (window.innerWidth - eleWidth < event.clientX) {ele.style.left = "unset";ele.style.right = 0;}}}
};
</script><style lang="scss" scoped>
#hello {position: relative;height: 100%;width: 100%;
}
.tips {margin-top: 10px;color: #999;
}
#contextmenu {position: absolute;top: 0;left: 0;height: auto;width: 120px;border-radius: 3px;box-shadow: 0 0 10px 10px #e4e7ed;background-color: #fff;border-radius: 6px;padding: 15px 0 10px 15px;button {display: block;margin: 0 0 5px;}
}
.hideContextMenu {position: absolute;top: -4px;right: -5px;
}
#editInput,
#headereditInput {position: absolute;top: 0;left: 0;height: auto;min-width: 200px;max-width: 400px;padding: 0;
}
#editInput .el-input,
#headereditInput .el-input {outline: 0;border: 1px solid #c0f2f9;border-radius: 5px;box-shadow: 0px 0px 10px 0px #c0f2f9;
}
.line {width: 100%;border: 1px solid #e4e7ed;margin: 10px 0;
}
</style>

🐗 Vue3 版本

<template><div id="table-wrap"><!-- 可编辑表格-Vue3 + ElementPlus --><el-table:data="questionChoiceVOlist"stripeborder@cell-click="cellClick"@row-contextmenu="rightClick":row-class-name="tableRowClassName"@header-contextmenu="(column: any, event: MouseEvent) => rightClick(null, column, event)"><el-table-columntype="index"label="序号"align="center":resizable="false"width="70"/><template #empty><el-empty description="暂无数据" /></template><el-table-column:resizable="false"align="center"v-for="(col, idx) in columnList":key="col.prop":prop="col.prop":label="col.label":index="idx"><template #default="{ row }"><divv-if="col.type === 'button'"style="height: 75px; padding-top: 26px; width: 100%"><el-badge type="warning" :value="getRiskLenght(row.riskIds)"><el-button size="small">{{ paramsIdType == 'detail' ? '查看' : '选择' }}</el-button></el-badge></div><el-input-numberv-if="col.type === 'input-number'"v-model.number="row[col.prop]":min="0":max="10":step="0.1":precision="2"/></template></el-table-column></el-table><!-- 右键菜单框 --><div v-show="showMenu" id="contextmenu" @mouseleave="showMenu = false"><p style="margin-bottom: 10px; text-align: left">列:</p><el-button :icon="CaretTop" @click="addColumn(false)"> 前方插入一列 </el-button><el-button :icon="CaretBottom" @click="addColumn(true)"> 后方插入一列 </el-button><el-button :icon="DeleteFilled" @click="openColumnOrRowSpringFrame('列')">删除当前列</el-button><el-button @click="renameCol" :icon="EditPen"> 更改列名 </el-button><div style="color: #ccc">-----------------------</div><p style="margin-bottom: 12px">行:</p><el-button :icon="CaretTop" @click="addRow(false)" v-show="!curTarget.isHead">上方插入一行</el-button><el-button :icon="CaretBottom" @click="addRow(true)" v-show="!curTarget.isHead">下方插入一行</el-button><el-button :icon="DeleteFilled" @click="addRowHead" v-show="curTarget.isHead">下方插入一行</el-button><el-button:icon="DeleteFilled"@click="openColumnOrRowSpringFrame('行')"v-show="!curTarget.isHead">删除当前行</el-button></div><!-- 输入框 --><div v-show="showEditInput" id="editInput"><el-inputref="iptRef"placeholder="请输入内容"v-model="curTarget.val"clearable@change="updTbCellOrHeader"@blur="showEditInput = false"@keyup="onKeyUp($event)"><template #prepend>{{ curColumn.label || curColumn.prop }}</template></el-input></div><!-- 实时数据展示 Start--><!-- 第二个和第三个参数来格式化JSON输出,其中null作为替换函数(这里不进行替换),2表示缩进级别。这样JSON数据会以格式化的形式展示,增加了可读性--><div><h3 style="text-align: center; margin-top: 15px">实时数据展示</h3><label>当前目标:</label><pre><code>{{ JSON.stringify(curTarget, null, 2) }}</code></pre><div style="width: 100%; height: auto"><label>表头:</label><pre><code v-for="col in columnList" :key="col.prop">{{ JSON.stringify(col, null, 2) }}</code></pre></div><div><label>数据:</label><pre><code v-for="(data, idx) in questionChoiceVOlist" :key="idx">{{ JSON.stringify(data, null, 2) }}</code></pre></div></div><!-- 实时数据展示 End--></div>
</template><script setup lang="ts">
import { ref, reactive, computed, toRefs, nextTick } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { DeleteFilled, CaretBottom, CaretTop, EditPen } from '@element-plus/icons-vue';
// Tips: locateMenuOrEditInput 可调整编辑框位置
interface Column {prop: string;label: string;type?: string;
}interface Data {choiceCode: string;choiceContent: string;riskIds: string;itemScore: string | number;[key: string]: unknown;
}interface Target {rowIdx: number | null;colIdx: number | null;val: string | null;isHead: boolean | undefined;
}// 接收addEdit父组件传过来的数据,用于判断是新增、编辑、详情页面
const paramsIdType = 'detail';const state = reactive({columnList: [{ prop: 'choiceCode', label: '选项编码' },{ prop: 'choiceContent', label: '选项内容' },{ prop: 'riskIds', label: '风险点', type: 'button' },{ prop: 'itemScore', label: '选项分值', type: 'input-number' },] as Column[],questionChoiceVOlist: [{choiceCode: 'A',choiceContent: '是',riskIds: '45,47',itemScore: 1,isClickCheckBtn: true,id: 1,},{choiceCode: 'B',choiceContent: '否',riskIds: '46',itemScore: 4,isClickCheckBtn: true,id: 2,},{choiceCode: 'C',choiceContent: '否',riskIds: '',itemScore: 4,isClickCheckBtn: true,id: 3,},] as Data[],showMenu: false, // 显示右键菜单showEditInput: false, // 显示单元格/表头内容编辑输入框curTarget: {// 当前目标信息rowIdx: null, // 行下标colIdx: null, // 列下标val: null, // 单元格内容/列名isHead: undefined, // 当前目标是表头?} as Target,countCol: 0, // 新建列计数
});
const iptRef = ref();const { columnList, questionChoiceVOlist, showMenu, showEditInput, curTarget } = toRefs(state);// 当前列
const curColumn = computed(() => {return curTarget.value.colIdx !== null? columnList.value[curTarget.value.colIdx]: { prop: '', label: '' };
});// 计算风险点数量
const getRiskLenght = computed(() => {return (riskIds: string) => riskIds.split(',').filter(Boolean).length;
});/*** 删除列/行* @method  delColumn* @param {  string }  type - '列' | '行'**/
const openColumnOrRowSpringFrame = (type: string) => {ElMessageBox.confirm(`此操作将永久删除该${type === '列' ? '列' : '行'}, 是否继续 ?, '提示'`, {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning',}).then(() => {if (type === '列') {delColumn();} else if (type === '行') delRow();ElMessage.success('删除成功');}).catch(() => ElMessage.info('已取消删除'));
};// 回车键关闭编辑框
const onKeyUp = (e: KeyboardEvent) => {if (e.key === 'Enter') {showEditInput.value = false;}
};// 控制某字段不能打开弹框
const isPop = (column: { label: string }) => {return column.label === '风险点' || column.label === '选项分值';
};// 左键输入框
const cellClick = (row: { [x: string]: any; row_index: any },column: { index: null; property: string | number; label: string },_cell: any,event: MouseEvent
) => {// 如果是风险点或选项分值,不执行后续代码if (isPop(column)) return;iptRef.value.focus();if (column.index == null) return;locateMenuOrEditInput('editInput', -300, event); // 左键输入框定位 YshowEditInput.value = true;iptRef.value.focus();// 当前目标curTarget.value = {rowIdx: row.row_index,colIdx: column.index,val: row[column.property],isHead: false,};
};// 表头右击事件 - 打开菜单
const rightClick = (row: any, column: any, event: MouseEvent) => {event.preventDefault();if (column.index == null) return;// 如果tableData有数据并且当前目标是表头,那么就返回,不执行后续操作// if (questionChoiceVOlist.value.length > 0 && !row) return;if (isPop(column)) return;showMenu.value = false;locateMenuOrEditInput('contextmenu', -500, event); // 右键输入框showMenu.value = true;curTarget.value = {rowIdx: row ? row.row_index : null,colIdx: column.index,val: row ? row[column.property] : column.label,isHead: !row,};
};// 更改列名
const renameCol = () => {showEditInput.value = false;if (curTarget.value.colIdx === null) return;showEditInput.value = true;nextTick(() => {iptRef.value.focus();});
};// 更改单元格内容/列名
const updTbCellOrHeader = (val: string) => {if (!curTarget.value.isHead) {if (curTarget.value.rowIdx !== null) {(questionChoiceVOlist.value[curTarget.value.rowIdx] as Data)[curColumn.value.prop] =val;}} else {if (!val) return;if (curTarget.value.colIdx !== null) {columnList.value[curTarget.value.colIdx].label = val;}}
};
// 新增行
const addRow = (later: boolean) => {showMenu.value = false;const idx = later ? curTarget.value.rowIdx! + 1 : curTarget.value.rowIdx!;let obj: any = {};columnList.value.forEach((p) => obj[p.prop]);questionChoiceVOlist.value.splice(idx, 0, obj);// 设置新增行数据默认值questionChoiceVOlist.value[idx] = {choiceCode: '',choiceContent: '',riskIds: '',itemScore: 0,id: Math.floor(Math.random() * 100000),};
};// 表头下新增行
const addRowHead = () => {showMenu.value = false;let obj: any = {};columnList.value.forEach((p) => obj[p.prop]);questionChoiceVOlist.value.unshift(obj);questionChoiceVOlist.value[0] = {choiceCode: '',choiceContent: '',riskIds: '',itemScore: 0,id: Math.floor(Math.random() * 100000),};
};
// 删除行
const delRow = () => {showMenu.value = false;curTarget.value.rowIdx !== null &&questionChoiceVOlist.value.splice(curTarget.value.rowIdx!, 1);
};// 新增列
const addColumn = (later: boolean) => {showMenu.value = false;const idx = later ? curTarget.value.colIdx! + 1 : curTarget.value.colIdx!;const colStr = { prop: 'Zk-NewCol - ' + ++state.countCol, label: '' };columnList.value.splice(idx, 0, colStr);questionChoiceVOlist.value.forEach((p) => (p[colStr.prop] = ''));
};// 删除列
const delColumn = () => {showMenu.value = false;questionChoiceVOlist.value.forEach((p) => {delete p[curColumn.value.prop];});columnList.value.splice(curTarget.value.colIdx!, 1);
};// 添加表格行下标
const tableRowClassName = ({ row, rowIndex }: { row: any; rowIndex: number }) => {row.row_index = rowIndex;
};// 定位菜单/编辑框
const locateMenuOrEditInput = (eleId: string, distance: number, event: MouseEvent) => {if (window.innerWidth < 1130 || window.innerWidth < 660)return ElMessage.warning('窗口太小,已经固定菜单位置,或请重新调整窗口大小');const ele = document.getElementById(eleId) as HTMLElement;const x = event.pageX;const y = event.clientY + 200; //右键菜单位置 Ylet left = x + distance + 200; //右键菜单位置 Xlet top;if (eleId == 'editInput') {// 左键top = y + distance;left = x + distance - 120;} else {// 右键top = y + distance + 170;}ele.style.left = `${left}px`;ele.style.top = `${top}px`;
};defineExpose({questionChoiceVOlist,
});
</script><style lang="scss" scoped>
#table-wrap {width: 100%;height: 100%;/* 左键 */#contextmenu {position: absolute;display: flex;flex-direction: column;align-items: center;justify-content: center;z-index: 999999;top: 0;left: 0;height: auto;width: 200px;border-radius: 3px;border: #e2e2e2 1px solid;box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);background-color: #fff;border-radius: 6px;padding: 15px 10px 14px 12px;button {display: block;margin: 0 0 5px;}}/* 右键 */#editInput {position: absolute;top: 0;left: 0;z-index: 999999;}/* 实时数据 */pre {border: 1px solid #cccccc;padding: 10px;overflow: auto;}
}
</style>

7730e2bd39d64179909767e1967da702.jpeg

 _______________________________  期待再见  _______________________________

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

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

相关文章

AtomicInteger原理和CAS与Synchronized(juc编程)

AtomicInteger原理 4.6.1 原理介绍 AtomicInteger的本质&#xff1a;自旋锁 CAS算法 CAS的全成是&#xff1a; Compare And Swap(比较再交换); 是现代CPU广泛支持的一种对内存中的共享数据进行操作的一种特殊指令。CAS可以将read-modify-write转换为原子操作&#xff0c;这…

关于椭圆的方程(有Python画的动图)

关于椭圆的方程&#xff08;有Python画的动图&#xff09; flyfish 几何定义 椭圆是平面上所有到两个固定点&#xff08;焦点&#xff09;的距离之和为常数的点的集合。这两个固定点叫做焦点。 解析几何描述 设椭圆的两个焦点为 F 1 F_1 F1​ 和 F 2 F_2 F2​&#xff…

【高等数学】傅里叶级数

最近刷了会抖音&#xff0c;看到一个非常有趣的现象&#xff1a;傅里叶级数&#xff0c;今天挑了几个视频来供大家学习。 1.傅里叶级数概念 【小崔说数】傅里叶级数专题https://www.bilibili.com/video/BV1Uq4y1q7xk?t117.4 2.傅里叶级数动画 【谜之舒适】12分钟的傅立叶级…

【docker】Dockerfile制作基础镜像 python 底层镜像制作 | 打包所有的requirement依赖

一、Dockerfile思想 我们正常的对一个项目进行打包 docker image 通常是在CI工具编译时进行对依赖的安装&#xff0c;比如golang的go get、python的pip install、node的npm install 等等 好处&#xff1a;我们更新了依赖可以动态的再编译时进行一个对依赖的更新 坏处&#xf…

转速传感器频率信号整形方波输出隔离变送器 地线干扰抑制 200mV~10V/0-12V/0-24V转0-5v/0-12v/0-24v/集电极输出

特点 转速传感器信号直接输入&#xff0c;方波信号输出正弦波、锯齿波信号输入&#xff0c;方波信号输出200mV峰值微弱信号的放大与整形不改变原波形频率&#xff0c;响应速度快电源、信号&#xff1a;输入/输出 3000VDC三隔离辅助电源&#xff1a;5V、12V、15V或24V直流单电源…

在4面体空间内2点结构占比

有一个4面体状空间&#xff0c;由3层甲烷状分子堆积而成&#xff0c;单个甲烷4面体边长10. 内有30个点&#xff0c;在30个点中取2点&#xff0c;有30*29/2435种取法。这里要求两个点的距离必须为6.123 在435个结构中只有40个符合要求 序数 结构 序数 结构 3 1 282 3 7…

如何进行海外网络加速?告别卡顿与访问慢的方法

你是否经常在打开海外网站浏览网页时遇到响应缓慢的问题&#xff1f;或者在进行国际网络会议时&#xff0c;由于网络延迟影响与客户的交流&#xff1f;亦或是由于网络问题&#xff0c;导致OA、ERP、云储存等应用频繁因为数据包丢失而中断下载&#xff1f;如果你经常遇到这些问题…

centos7 离线安装zip和unzip

解压的时候发现不能解压&#xff0c;报-bash: unzip: command not found 1、访问https://www.rpmfind.net/linux/rpm2html/search.php?queryzip&submitSearch…&systemcentos&arch#/ 2、输入zip和centos搜索&#xff0c;选择el7下载 3、输入unzip和centos搜索&am…

显卡nvidia的CUDA和cuDNN的安装

显卡版本&#xff0c;和nvidia下载的 CUDA版本和CUDNN的关系 1. 显卡版本 nvidia-smi 硬件环境&#xff1a;显卡版本 4090 NVIDIA-SMI-555.85 我的驱动是510.85.02&#xff0c;驱动附带cuda12.5 2. nvidia下载的cuda版本 nvcc -V 我下载的是cuda12.5 cuda在安装版本过程…

互联网技术基础-计算机人必看

目录 1.Internet的工作原理 1、Internet是一个分组交换系统 2、路由器是Internet实现互连的“标准件” 3、TCP/IP是Internet的核心协议 4、客户机/服务器的工作模式 2. IP地址 2.1 IP地址分类 2.2特殊IP地址 2.3路由器和IP编制原则 2.4子网的划分 2.5 IPV6 3.域名系…

【因果推断python】46_估计量2

目录 连续型干预变量案例 非线性处理效果 关键思想 连续型干预变量案例 目标转换方法的另一个明显缺点是它仅适用于离散或二元处理。这是你在因果推理文献中经常看到的东西。大多数研究都是针对二元干预案例进行的&#xff0c;但您找不到很多关于连续干预的研究。这让我很困…

Bytebase 对接本地部署的 llama3 开启ChatSQL功能

Bytebase 是为开发人员、测试、DBA和运维工程师构建的数据库 DevOps 领域的&#xff0c;类 GitLab/GitHub 平台。 这篇文章主要关注 Bytebase SQL 编辑器中的 AI 增强功能。使用此功能您可以使用自然语言在 Bytebase SQL 编辑器中查询数据库。同时还能给出针对查询的索引建议&…

RK3568技术笔记十四 Ubuntu创建共享文件夹

单击“虚拟机”&#xff0c;单击“设置”&#xff0c;如图所示&#xff1a; 单击“选项”&#xff0c;选择“总是启用&#xff08;E&#xff09;”&#xff0c;单击“添加”&#xff0c;如图所示&#xff1a; 单击“下一步”&#xff0c;如图所示&#xff1a; 单击“浏览”添加…

Ranger配置图片及json文件预览

文章目录 前言下载apt下载pip下载 配置使用json文件预览方法一 修改scope用cat预览方法二 安装jq预览配置ranger 图片文件预览方法一 使用img2txt预览方法二 使用fim预览配置ranger 总结 前言 本文主要讲解Ranger12如何配置json及图片的预览设置&#xff0c;如下是ranger的介绍…

Spring AI 整合openAI的chatGpt

Spring AI支持ChatGPT&#xff0c;这是OpenAI的AI语言模型。ChatGPT在激发人们对人工智能驱动文本生成的兴趣方面发挥了重要作用。 SpringAi与Spring Boot 的整合详见上一篇文章&#xff1a; Spring AI 介绍以及与 Spring Boot 项目整合 下面分四个部分来分别说明和演示&#…

简单分享github

一、官网 GitHub: Let’s build from here GitHub 二、注册 通过简单的注册步骤&#xff0c;你就可以拥有一个属于自己的GitHub账号。再简单注册完成之后会需要验证你所输入的邮箱才能正常使用你的GitHub。 三、设置自己的库 在注册完成之后&#xff0c;完成一些简单的设置之…

简单且高效的水域物探轨迹坐标转换程序

简单且高效的水域物探轨迹坐标转换程序 前言 水上测线的高精度定位是水域物探的难题&#xff0c;水域磁法、水域地震实施时常采用船舶拖拽传感器进行走航式观测&#xff0c;GPS仪器放在船舶上测量&#xff0c;造成船舶位置与传感器位置存在偏差&#xff0c;后期资料整理需要校…

互联网应用主流框架整合之Spring Boot基本概念

Spring Boot是用来简化Spring应用程序的搭建、开发、测试和部署过程的&#xff0c;该框架使用了特定的方式进行配置&#xff0c;从而使开发人员不再需要定义样板化的配置&#xff0c;SpringBoot致力于快速应用开发(Rapid Application Development)领域的发展&#xff0c;它通过…

RTSP/Onvif安防监控平台EasyNVR抓包命令tcpdump使用不了的解决方法

安防视频监控汇聚EasyNVR智能安防视频监控平台&#xff0c;是基于RTSP/Onvif协议的安防视频平台&#xff0c;可支持将接入的视频流进行全平台、全终端分发&#xff0c;分发的视频流包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等格式。平台可提供的视频能力包括&#xff1a;…

vscode插件开发之 - TestController

TesController概要介绍 TestController 组件是用于实现自定义测试框架和集成测试结果的。它允许开发者定义自己的测试运行器&#xff0c;以支持在VSCode中运行和展示测试。以下是一些使用 TestController 组件的主要场景&#xff1a; 自定义测试框架&#xff1a;如果你正在开发…