canvas初尝试
最近学习了canvas,就拿它做了这么个小东西,感觉已经爱上canvas了。上代码
/* * @auhor : 开发部-前端组-李鑫超 * @property { tableData : {Array} 表格数据 add v-1.0.0 } * @property { columData : {Array} 表头数据 add v-1.0.0 } * @property { menuList : {Array} 右键菜单 add v-1.0.0 } * @property { myKey : {String} 唯一标识。 add v-1.0.0 } * @property { isRadio : {Boolean} 是否单选,默认false。 add v-1.0.0 } * @property { showpage : {Boolean} 是否显示分页,默认false。 add v-1.0.0 } * @property { currentPage : {Number} 当前页,默认false。 add v-1.0.0 } * @property { pageSize : {Number} 每页显示条目个数。 add v-1.0.0 } * @property { pageTotal : {Number} 总条目数。 add v-1.0.0 } * @property { backMultipleSelection : {Array} 回显数据。 add v-1.0.0 } * @method { makesure : {Function(data)} 表头tree点击事件 add v-1.0.0 } * @method { menuClick : {Function(item)} item 表格右键选项事件 add v-1.0.0 } * @method { currentPageChange : {Function(page)} 当前页改变事件事件 add v-1.0.0 } * @method { handleSelect : {Function(data)} 选中数据时会触发该事件 add v-1.0.0 } * @version 1.0.0 * @edit: 2018/8/15 */ <template> <div class='air-table-wrapper'> <el-table ref="table" :show-header="true" :data="tableData" tooltip-effect="dark" style="width: 100%;" :height="height" :header-row-class-name="headerClassName" id="table" @row-click="rowClick" @selection-change="selectionChange" @select-all="selectAll" @select="selectItem" @row-contextmenu="rowContextmenu" @row-dblclick="rowDblclick" @header-click="headerClick" v-drag> <el-table-column v-if="!isRadio" :type="isType" width="55" align="center"></el-table-column> <el-table-column v-if="isType == 'index'" :index="indexMethod" type="index" :label="'序号'" width="55" align="center"></el-table-column> <!--列表的表头循环--> <el-table-column v-for="(column, index) in columData" :key="index" :label="column.name" :width="column.width"> <template slot-scope="scope"> <!--把所有的数据都变成插槽导出--> <slot :name="column.key" :row="scope.row"></slot> </template> </el-table-column> <template slot=append v-if="showPage"> <div :class="['el-table__append-page']"> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page.sync="page" :page-size="pageSize" layout="total, prev, pager, next" :total="pageTotal"> </el-pagination> </div> </template> </el-table> <air-contex-menu v-if="menuList.length" :position="position" :toggleShow="toggleShow" :menuList="menuList" @menuClick="menuClick"></air-contex-menu> <tree-dialog ref="treeDialog" :pid="treePid" :name="treeName" :id="treeId" :data="treeData" @makesure=makesure placeholder="输入信息,按回车搜索" :title='treeTitle'> </tree-dialog> </div> </template> <script> import airContexMenu from "./contextMenu.js"; import treeDialog from "@/components/newCommon/dialog/treeDialog.vue"; export default { name: "airtable", // 框选的指令 directives: { drag: { // 指令的定义 inserted: function (el, binding, vnode) { var oDiv = el; vnode.context.tableTop = getY(oDiv); vnode.context.tableLeft = getX(oDiv); //方法是确定列表到屏幕的位置 function getX(obj) { var parObj = obj; var left = obj.offsetLeft; while ((parObj = parObj.offsetParent)) { left += parObj.offsetLeft; } return left; } //方法是确定列表到屏幕的位置 function getY(obj) { var parObj = obj; var top = obj.offsetTop; while ((parObj = parObj.offsetParent)) { top += parObj.offsetTop; } return top; } oDiv.onmousedown = function (ev) { // 防止奇葩操作 if ([2, 3, 4, 5].indexOf(ev.buttons) > -1) { if (selDiv) { oDiv.getElementsByClassName("el-table__body")[0].removeChild(selDiv); } return } // 获取当前scrollTop var scrollingTop = vnode.context.targetScroll; if (vnode.context.isRadio) return; //初始化不显示 vnode.context.toggleShow = false; //确保用户在移动鼠标的时候只初始化一次选中 var flag = true; // 获取表格的宽高 let tableWidth = vnode.context.$el.clientWidth; let tableHeight = vnode.context.$el.clientHeight; //用来存储列表 var selList = []; //获得指令下的dom对应的表格 var fileNodes = oDiv.getElementsByTagName("tr"); var countI = 0; //获得鼠标 var evt = window.event || arguments[0]; var startX = evt.x || evt.clientX; var startY = evt.y || evt.clientY; var top, left; //时时获得 top = getY(oDiv); left = getX(oDiv); var selDiv = document.createElement("div"); selDiv.style.cssText = "position:absolute;width:0px;height:0px;font-size:0px;margin:0px;padding:0px;border:1px solid rgba(255,165,22,0.38);background-color:rgba(0,0,0,0.38);z-index:1000;filter:alpha(opacity:60);opacity:0.6;display:none;"; selDiv.id = "selectDiv"; oDiv.getElementsByClassName("el-table__body")[0].appendChild(selDiv); selDiv.style.left = startX + "px"; selDiv.style.top = startY + "px"; var _x = null; var _y = null; vnode.context.clearEventBubble(evt); // 打开开关 vnode.context.mouseflag = true; // 鼠标拖动时画框 document.onmousemove = function (ev) { evt = window.event || arguments[0]; _x = evt.x || evt.clientX; _y = evt.y || evt.clientY; // 为了确定是点击事件还是移动事件 if (Math.abs(_x - startX) < 5 && Math.abs(_y - startY) < 5) { return; } vnode.context.selectItemFlag = false; // 为了确保只有一次的渲染每次框选都把默认选中为空(针对当前页) if (flag) { // 重置选中css for (var i = 0; i < fileNodes.length; i++) { if (fileNodes[i].className.indexOf("el-table__row") != -1) { fileNodes[i].className = "el-table__row"; selList.push(fileNodes[i]); } } // 判断当前页是否有选中的 vnode.context.tableData.forEach((item) => { vnode.context.$refs.table.toggleRowSelection(item, false); vnode.context.multipleSelection.forEach((ele, i) => { if (item[vnode.context.myKey] == ele[vnode.context.myKey]) { vnode.context.$refs.table.toggleRowSelection(ele, false); vnode.context.multipleSelection.splice(ele, 1); i = i - 1; } }) }) flag = false; } // 判断鼠标移动是否超出在当前表格区域 if (_x <= left || _x >= left + tableWidth || _y <= top || _y >= top + tableHeight) { document.onmousemove = null; if (selDiv) { oDiv.getElementsByClassName("el-table__body")[0].removeChild(selDiv); } (selList = null), (_x = null), (_y = null), (selDiv = null), (startX = null), (startY = null), (evt = null); vnode.context.mouseflag = false; vnode.context.$handleSelect(); } if (vnode.context.mouseflag) { if (selDiv.style.display == "none") { selDiv.style.display = ""; } var scrolling = oDiv.getElementsByClassName("is-scrolling-none"); // 两个表格的时候需增加滚屏的高度 // var main_scrolling=document.getElementsByClassName("common_table_height"); // selDiv.style.left = Math.min(_x, startX)-left+scrolling[0].scrollLeft +main_scrolling[0].scrollLeft+ "px"; selDiv.style.left = Math.min(_x, startX) - left + scrolling[0].scrollLeft + "px"; //48是表头的高度 // selDiv.style.top = Math.min(_y, startY)-top - 48 + scrolling[0].scrollTop+main_scrolling[0].scrollTop+ "px"; selDiv.style.top = Math.min(_y, startY) - top - oDiv.getElementsByClassName("is-leaf")[0].offsetHeight + scrollingTop + "px"; selDiv.style.width = Math.abs(_x - startX) + "px"; if (scrolling[0].scrollTop > scrollingTop) { selDiv.style.height = Math.abs(_y - startY) + scrolling[0].scrollTop + "px"; } else { selDiv.style.height = Math.abs(_y - startY) + "px"; } // ---------------- 关键算法确定列表的选中靠的是index--------------------- var _l = selDiv.offsetLeft, _t = selDiv.offsetTop; var _w = selDiv.offsetWidth, _h = selDiv.offsetHeight; for (var i = 0; i < selList.length; i++) { var sl = selList[i].offsetWidth + selList[i].offsetLeft; var st = selList[i].offsetHeight + selList[i].offsetTop; if ( sl > _l && st > _t && selList[i].offsetLeft < _l + _w && selList[i].offsetTop < _t + _h ) { if (selList[i].className.indexOf("seled") == -1) { selList[i].className = selList[i].className + " seled"; vnode.context.$refs.table.toggleRowSelection( vnode.context.tableData[i], true ) vnode.context.multipleSelection.push(vnode.context.tableData[i]) } } else { if (selList[i].className.indexOf("seled") != -1) { selList[i].className = "el-table__row"; vnode.context.$refs.table.toggleRowSelection( vnode.context.tableData[i], false ) let index = vnode.context.multipleSelection.indexOf(vnode.context.tableData[i]) vnode.context.multipleSelection.splice(index, 1) } } } } if (document.onmousemove) { vnode.context.clearEventBubble(evt); } }; //在鼠标抬起后做的重置 oDiv.onmouseup = function () { //把鼠标移动事初始化 document.onmousemove = null; if (selDiv) { oDiv.getElementsByClassName("el-table__body")[0].removeChild(selDiv); } (selList = null), (_x = null), (_y = null), (selDiv = null), (startX = null), (startY = null), (evt = null); vnode.context.mouseflag = false; // 防止重复$handleSelect()事件 if (!vnode.context.selectItemFlag) { vnode.context.$handleSelect(); vnode.context.selectItemFlag = false; } }; }; } } }, components: { airContexMenu, treeDialog }, props: { // 对于列表中唯一的字段myKey默认为id myKey: { type: String, default: "id" }, //列表的数据 tableData: { type: Array, default: () => [] }, //传过来的表头信息 columData: { type: Array, default: () => [] }, //有没有checkbox isType: { type: String, default: "selection" }, //右键菜单 menuList: { type: Array, default: () => [] }, //分页的总页数 pageTotal: { type: Number, default: 0 }, // 每页显示条数 pageSize: { type: Number, default: 20 }, // 当前页 currentPage: { type: Number, default: 1 }, // 当表格需要单选的时候 isRadio: { type: Boolean, default: false }, // table 回显数据 backMultipleSelection: { type: Array, default: () => [] }, // 是否显示分页 showPage: { type: Boolean, default: true }, // 表格高度 height: { type: [Number, String], default: 500 } }, data() { return { //指令中确定的时候是鼠标按下事件 mouseflag: false, //选中的数组 multipleSelection: [], //控制右键菜单弹出显示 dialogVisible: false, //右键鼠标的位置 position: { left: 0, top: 0 }, //控制右键显示隐藏 toggleShow: false, //分页当前的页数 page: this.currentPage, //当前右键点击的列 currentRow: [], //当前滚动的距离, targetScroll: 0, // 是否点击单选按钮 selectItemFlag: false, // 表头tree 数据 treeData: [], treeName: '', treePid: '', treeId: '', treeTitle: "", // 表格top距离 tableTop: null, tableLeft: null }; }, methods: { // 换算index indexMethod(index) { return index + (this.page - 1) * this.pageSize + 1; }, //清除默认事件 clearEventBubble(evt) { if (evt.stopPropagation) evt.stopPropagation(); else evt.cancelBubble = true; if (evt.preventDefault) evt.preventDefault(); else evt.returnValue = false; }, //列表单击选中事件 rowClick(row, event, column) { // 确定当前的row的index var index = 0; this.tableData.forEach((ele, i) => { if (ele[this.myKey] == row[this.myKey]) { index = i + 1; } }); let flag = false; this.toggleShow = false; // 单选 if (this.isRadio) { // 每次点击重置 var dom = this.$refs.table.$el.getElementsByTagName("tr"); this.tableData.forEach(ele => { this.$refs.table.toggleRowSelection(ele, false); }); for (var i = 1; i < dom.length; i++) { dom[i].className = "el-table__row"; } this.$refs.table.toggleRowSelection(row, true); this.$refs.table.$el.getElementsByTagName("tr")[index].className = "el-table__row seled"; this.multipleSelection = Array.of(row) this.$handleSelect(); return } this.selectItemFlag = true; // 如果有就删除 this.multipleSelection.forEach((ele, i) => { if (ele[this.myKey] == row[this.myKey]) { this.$refs.table.toggleRowSelection(row, false); this.$refs.table.$el.getElementsByTagName("tr")[index].className = "el-table__row"; flag = true; this.multipleSelection.splice(i, 1) } }); // 如果没有就push if (!flag) { this.$refs.table.toggleRowSelection(row, true); //后期优化吧 element的方法用不了 只能自己改变类名 this.$refs.table.$el.getElementsByTagName("tr")[index].className = "el-table__row seled"; this.multipleSelection.push(row) } this.$handleSelect(); }, //列表右键点击事件 rowContextmenu(row, event) { //为当前的row赋值 this.currentRow = row; //阻止默认右键点击事件 event.returnValue = false; //获取右键坐标 this.position.left = event.clientX; this.position.top = event.clientY; //菜单出现的flag this.toggleShow = true; //显示弹出窗 this.$emit("contextmenu", row, event); }, //右键菜单弹出事件 menuClick(item) { //右键点击以后隐藏 this.toggleShow = false; this.$emit("rowContextmenu", item, this.currentRow); }, //每页条数变化 ui定死每页20条 handleSizeChange(val) { // console.log(`每页 ${val} 条`); }, // 当前页变化 handleCurrentChange(val) { this.page = val; this.$emit("currentPageChange", this.page); }, //当批量选中结束调用 $handleSelect() { console.log(this.multipleSelection) this.$emit("handleSelect", this.multipleSelection); }, // 选项发生改变事件 ZZ selectionChange(val) { // console.log(val,"mdzz") // this.multipleSelection = val; }, //监听表格的滚动 handleScroll(e) { this.targetScroll = e.target.scrollTop; }, //当单选时触发的事件 selectItem(selection, row) { this.selectItemFlag = true; this.rowClick(row); }, //当表头多选是触发的事件 selectAll(selection) { this.selectItemFlag = true; var dom = this.$refs.table.$el.getElementsByTagName("tr"); if (this.isRadio) { this.tableData.forEach(ele => { this.$refs.table.toggleRowSelection(ele, false); }); for (var i = 1; i < dom.length; i++) { dom[i].className = "el-table__row"; } return; } if (selection.length) { for (var i = 1; i < dom.length; i++) { //为了去掉表头的tr从1开始 dom[i].className = "el-table__row seled"; } this.multipleSelection = [...this.multipleSelection, ...selection]; this.multipleSelection = [...new Set(this.multipleSelection)]; } else { for (var i = 1; i < dom.length; i++) { dom[i].className = "el-table__row"; } this.tableData.forEach((item) => { this.multipleSelection.forEach((ele, i) => { if (item[this.myKey] == ele[this.myKey]) { this.multipleSelection.splice(i, 1) i = i - 1; } }) }) } this.$handleSelect(); }, //双击事件 rowDblclick(row, event) { this.$emit("rowDblclick", row, event); }, // 表头点击事件 headerClick(column, event) { if (event.target.nodeName === 'DIV') this.columData.map((item, index) => { if (item.name == column.label) { if (item.tree && item.tree.data.length) { this.$refs.treeDialog.$el.getElementsByClassName("el-dialog")[0].style.top = this.tableTop + 53 + 'px'; this.$refs.treeDialog.$el.getElementsByClassName("el-dialog")[0].style.left = this.tableLeft + (event.path[1].scrollWidth) * (index) + 55 + 'px'; this.treeData = item.tree.data; this.treePid = item.tree.pid; this.treeId = item.tree.id; this.treeName = item.tree.name; this.treeTitle = item.name; this.$refs.treeDialog.dialogOpen(); } } }) }, clearData() { this.multipleSelection = [] }, // tree确定事件/ makesure(data) { this.$emit('makesure', data); }, initMultipleSelection() { this.$nextTick(() => { //获得tr对应index找到对应的dom 加入选中class var dom = this.$refs.table.$el.getElementsByTagName("tr"); this.$refs.table.clearSelection(); this.tableData.forEach((ele, i) => { dom[i + 1].className = "el-table__row"; }); //为了传过来的值进行回显 if (this.backMultipleSelection.length > 0) { this.backMultipleSelection.forEach(item => { this.tableData.forEach((ele, i) => { if (item[this.myKey] == ele[this.myKey]) { this.$refs.table.toggleRowSelection(ele, true); dom[i + 1].className = "el-table__row seled"; } }); }); } }); } }, watch: { backMultipleSelection: { handler: function (newVal, oldVal) { //为了回显 this.initMultipleSelection(); }, deep: true }, currentPage: { handler(newValue, oldValue) { this.page = newValue; }, immediate: true }, tableData(val) { /* 监听table数据变化时(分页操作),已选中数据做回显 */ this.$nextTick(() => { var dom = this.$refs.table.$el.getElementsByTagName("tr"); this.$refs.table.clearSelection(); this.tableData.forEach((ele, i) => { dom[i + 1].className = "el-table__row"; }); //为了已选中数据进行回显 if (this.multipleSelection.length > 0) { this.multipleSelection.forEach(item => { this.tableData.forEach((ele, i) => { if (item[this.myKey] == ele[this.myKey]) { this.$refs.table.toggleRowSelection(ele, true); dom[i + 1].className = "el-table__row seled"; } }); }); } let obj = this.$refs.table.$el.getElementsByClassName("el-table__body-wrapper")[0]; if (obj.scrollHeight > obj.clientHeight) { this.$refs.table.$el.getElementsByClassName("el-table__append-wrapper")[0].style.cssText = ""; }else{ this.$refs.table.$el.getElementsByClassName("el-table__append-wrapper")[0].style.cssText = "position:absolute;right:0;bottom:0;"; } }) } }, computed: { // 通过滚动距计算阴影 class headerClassName() { if (this.targetScroll == 0) { return "air-table-header__class" } else if (this.targetScroll > 0 && this.targetScroll <= 100) { return "air-table-header__class air-table-header__scroll1" } else if (this.targetScroll > 100 && this.targetScroll <= 200) { return "air-table-header__class air-table-header__scroll2" } else { return "air-table-header__class air-table-header__scroll3" } } }, mounted() { document.onclick = () => { this.toggleShow = false; }; this.initMultipleSelection(); this.$refs.table.$refs.bodyWrapper.addEventListener('scroll', this.handleScroll); }, beforeDestroy() { this.$refs.table.$refs.bodyWrapper.removeEventListener('scroll', this.handleScroll); } }; </script> <style lang="scss"> @import "../../../public/style/mixin.scss"; .air-table-wrapper { @include wh(100%, 100%); %scroll-calss { width: 100%; height: 1px; position: absolute; top: 51px; left: 0; content: ""; z-index: 2; } .seled { background: #f5f5f5 !important; } .no-seled { background: #ffffff !important; } .el-table__body tr { cursor: pointer; box-sizing: border-box; border-top: 1px solid #f5f5f5; border-bottom: 1px solid #f5f5f5; } .air-table-header__class th { padding: 9px 0 !important; box-sizing: border-box; border-bottom: 1px solid #f5f5f5 !important; } .air-table-header__scroll1 { position: relative; &::after { @extend %scroll-calss; box-shadow: 0 2px 1px -1px rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 3px 0 rgba(0, 0, 0, 0.12); } } .air-table-header__scroll2 { position: relative; &::after { @extend %scroll-calss; box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12); } } .air-table-header__scroll3 { position: relative; &::after { @extend %scroll-calss; box-shadow: 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 1px 8px 0 rgba(0, 0, 0, 0.12); } } .el-table__body td { box-sizing: border-box; border-bottom: 1px solid #f5f5f5; } .hover-row { border-top: 1px solid #f5f5f5; border-bottom: 1px solid #f5f5f5; background: #fafafa; } .el-table__append-page { .el-pagination { text-align: right; margin: 48px 0; } } .el-table__append-info { position: absolute; bottom: 0px; width: 100%; .el-pagination { text-align: right; } } .air-table__context--menu { box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.2), 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12); .air-table__context--list { cursor: pointer; height: 48px; @include flexCenter(flex-start, center); .air-table__context--icon { font-size: 14px; margin-left: 20px; color: #757575; } .air-table__context--info { font-size: 14px; margin-left: 20px; color: #212121; } } .air-table__context--list:hover { background: #f5f5f5; } } .el-table th { padding: 9px 0 !important; } .air-treeDialog-wrappers .el-dialog { margin: 0 !important; } } </style>