做网站有名的公司有哪些,网站在建设中模板下载,wordpress 添加自定义字段,涂料网站源码前期回顾
《 穿越时空的代码、在回首#xff1a;Evil.js两年后的全新解读 》-CSDN博客
Vue3 TS Element-Plus 封装Tree组件 《亲测可用》_ https://blog.csdn.net/m0_57904695/article/details/131664157?spm1001.2014.3001.5501
态表格 自由编辑 目录
♻️ 效果图…前期回顾
《 穿越时空的代码、在回首Evil.js两年后的全新解读 》-CSDN博客
Vue3 TS Element-Plus 封装Tree组件 《亲测可用》_ https://blog.csdn.net/m0_57904695/article/details/131664157?spm1001.2014.3001.5501
态表格 自由编辑 目录
♻️ 效果图 Vue2 版本 Vue3 版本 ♻️ 效果图 在线预览 Vue2 版本
template!-- 可编辑表格V2 --div idhello!-- 表格 --p classtips单击 右键菜单单击 左键编辑/pel-table:datatableDataheight500pxborderstylewidth: 100%; margin-top: 10pxcell-clickcellDblclickheader-contextmenu(column, event) rightClick(null, column, event)row-contextmenurightClick:row-class-nametableRowClassNameel-table-columnv-ifcolumnList.length 0typeindex:labelNo./el-table-columnv-for(col, idx) in columnList:keycol.prop:propcol.prop:labelcol.label:indexidx//el-tabledivh3 styletext-align: center实时数据展示/h3label当前目标/labelp{{ JSON.stringify(curTarget) }}/plabel表头/labelp v-forcol in columnList :keycol.prop{{ JSON.stringify(col) }}/plabel数据/labelp v-for(data, idx) in tableData :keyidx{{ JSON.stringify(data) }}/p/div!-- 右键菜单框 --div v-showshowMenu idcontextmenu mouseleaveshowMenu falsep stylemargin-bottom: 10px列/pel-button sizemini typeprimary clickaddColumn()前方插入一列/el-buttonel-button sizemini typeprimary clickaddColumn(true)后方插入一列/el-buttonel-buttontypeprimarysizeminiclickopenColumnOrRowSpringFrame(列)删除当前列/el-buttonel-button sizemini typeprimary clickrenameCol($event)更改列名/el-buttondiv classline/divp stylemargin-bottom: 12px行/pel-buttonsizeminitypeprimaryclickaddRow()v-show!curTarget.isHead上方插入一行/el-buttonel-buttonsizeminitypeprimaryclickaddRow(true)v-show!curTarget.isHead下方插入一行/el-buttonel-buttonsizeminitypeprimaryclickaddRowHead(true)v-showcurTarget.isHead下方插入一行/el-buttonel-buttontypeprimarysizeminiclickopenColumnOrRowSpringFrame(行)v-show!curTarget.isHead删除当前行/el-button/div!-- 单元格/表头内容编辑框 --div v-showshowEditInput ideditInputel-inputv-focusplaceholder请输入内容v-modelcurTarget.valclearablechangeupdTbCellOrHeaderblurshowEditInput falsekeyuponKeyUp($event)template #prepend{{ curColumn.label || curColumn.prop }}/template/el-input/div/div
/templatescript
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;}}}
};
/scriptstyle langscss 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 版本
templatediv idtable-wrap!-- 可编辑表格-Vue3 ElementPlus --el-table:dataquestionChoiceVOliststripebordercell-clickcellClickrow-contextmenurightClick:row-class-nametableRowClassNameheader-contextmenu(column: any, event: MouseEvent) rightClick(null, column, event)el-table-columntypeindexlabel序号aligncenter:resizablefalsewidth70/template #emptyel-empty description暂无数据 //templateel-table-column:resizablefalsealigncenterv-for(col, idx) in columnList:keycol.prop:propcol.prop:labelcol.label:indexidxtemplate #default{ row }divv-ifcol.type buttonstyleheight: 75px; padding-top: 26px; width: 100%el-badge typewarning :valuegetRiskLenght(row.riskIds)el-button sizesmall{{ paramsIdType detail ? 查看 : 选择 }}/el-button/el-badge/divel-input-numberv-ifcol.type input-numberv-model.numberrow[col.prop]:min0:max10:step0.1:precision2//template/el-table-column/el-table!-- 右键菜单框 --div v-showshowMenu idcontextmenu mouseleaveshowMenu falsep stylemargin-bottom: 10px; text-align: left列/pel-button :iconCaretTop clickaddColumn(false) 前方插入一列 /el-buttonel-button :iconCaretBottom clickaddColumn(true) 后方插入一列 /el-buttonel-button :iconDeleteFilled clickopenColumnOrRowSpringFrame(列)删除当前列/el-buttonel-button clickrenameCol :iconEditPen 更改列名 /el-buttondiv stylecolor: #ccc-----------------------/divp stylemargin-bottom: 12px行/pel-button :iconCaretTop clickaddRow(false) v-show!curTarget.isHead上方插入一行/el-buttonel-button :iconCaretBottom clickaddRow(true) v-show!curTarget.isHead下方插入一行/el-buttonel-button :iconDeleteFilled clickaddRowHead v-showcurTarget.isHead下方插入一行/el-buttonel-button:iconDeleteFilledclickopenColumnOrRowSpringFrame(行)v-show!curTarget.isHead删除当前行/el-button/div!-- 输入框 --div v-showshowEditInput ideditInputel-inputrefiptRefplaceholder请输入内容v-modelcurTarget.valclearablechangeupdTbCellOrHeaderblurshowEditInput falsekeyuponKeyUp($event)template #prepend{{ curColumn.label || curColumn.prop }}/template/el-input/div!-- 实时数据展示 Start--!-- 第二个和第三个参数来格式化JSON输出其中null作为替换函数这里不进行替换2表示缩进级别。这样JSON数据会以格式化的形式展示增加了可读性--divh3 styletext-align: center; margin-top: 15px实时数据展示/h3label当前目标/labelprecode{{ JSON.stringify(curTarget, null, 2) }}/code/prediv stylewidth: 100%; height: autolabel表头/labelprecode v-forcol in columnList :keycol.prop{{ JSON.stringify(col, null, 2) }}/code/pre/divdivlabel数据/labelprecode v-for(data, idx) in questionChoiceVOlist :keyidx{{ JSON.stringify(data, null, 2) }}/code/pre/div/div!-- 实时数据展示 End--/div
/templatescript setup langts
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,
});
/scriptstyle langscss 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_______________________________ 期待再见 _______________________________