关于sortable用法与vuedraggable比较

el/2023/6/3 15:01:11

都挺好,但是element table里用sortabe比较好,原因 https://segmentfault.com/a/1190000019411685?utm_source=tag-newest

然后今天用sortable的时候,一直没效果,不知道咋回事

看了几个地方  一个是要加row-key

而且row-key一定要对应好,别乱写,要写表格数据数组里每个对象的唯一字段,比如id

一个是要用$ref来取表格

<template><div class="steptemp"><div class="temp-table-bottom"><el-table  ref="dragTable"  :data="tableData" class="data_table" style="width: 100%" border row-key="id"><el-table-column  v-for="(item, index) in col":key="`col_${index}`":prop="item.prop":label="item.label"></el-table-column></el-table><div class="temp-count"><p>共{{stempConfig.total}}条记录</p></div></div></div><!--<drag-table :theadConfig="col" :data="tableData" />--></div></template>
<script lang="ts">import { Component, Vue, Prop, Watch } from 'vue-property-decorator';import { ApiWebComponentService } from 'mone-test/services';import DragTable from "mone-test/components/dragTable/index.vue";import Sortable from 'sortablejs'@Component({name: 'StepTemp',components: {
DragTable}})export default class StepTemp extends Vue {/*** props ***/// 通过一个统一props来确定,所以得操作是组件还是脚本@Prop({ type: Object, default: () => { return {}; } }) stepTempInfo;@Prop({ type: String, default: () => { return ''; } }) currentDirectoryName;tableData: Array =[{id: '1',date: '2016-05-01',name: '王小虎1',propVal: 'aaaaaaaaaa',required: true,address: '上海市普陀区金沙江路 101 弄'},{id: '2',date: '2016-05-02',name: '王小虎2',propVal: 'fdggd',required: false,address: '上海市普陀区金沙江路 102 弄'},{id: '3',date: '2016-05-03',name: '王小虎3',propVal: 'cccccccccccc',required: true,address: '上海市普陀区金沙江路 103 弄'},{id: '4',date: '2016-05-04',name: '王小虎4',propVal: 'tttttttttttttt',required: true,address: '上海市普陀区金沙江路 104 弄'}
];stempConfig: Object = {createTime: "yyyy-MM-dd HH:mm:ss",creator: "string",deleted: true,directoryPath: "string",directoryName: "",id: 0,key: "string",moduleDesc: "string",moduleName: "string",moduleTags: "string",rewriter: "string",tags: ["string"],updateTime: "yyyy-MM-dd HH:mm:ss",versionId: 0,webModules: [null]}total = 0formLabelWidth: String = '120px';stepFormVisible: Boolean = false;stepForm: Object = {name: '',eventParam: '',latencyTime: 0,// /1};col = [{label: '日期',prop: 'date',type: 'select',options: [{value: '1', label: '呵呵'},{value: '2', label: '是非得失'},{value: '3', label: '等各方面'},],},{label: '姓名',prop: 'name',type: 'slot'},{label: '属性值',prop: 'propVal',type: 'input',},{label: '必填',prop: 'required',type: 'checkbox',},{label: '地址',prop: 'address'},{label: '操作',prop: 'operate',type: 'slot'},
];dropCol = [{label: '日期',prop: 'date'},{label: '姓名',prop: 'name'},{label: '地址',prop: 'address'}]constructor() {// this.initData()}initData() {}mounted() {this.rowDrop();}// 行拖拽rowDrop () {const _this = thisconst table = _this.$refs.dragTable;const tbody = table.$el.querySelector('.el-table__body-wrapper tbody');Sortable.create(tbody, {onEnd ({ newIndex, oldIndex }) {const currRow = _this.tableData.splice(oldIndex, 1)[0]_this.tableData.splice(newIndex, 0, currRow)}})}}</script>
<style scoped lang="scss"></style>

 

const table = _this.$refs.dragTable; 这句代码最为关键,加上就有效果了

发现封装真的舒服,重复代码不用写了很多,多封装

<template><div class="steptemp"><div class="temp-table-bottom"><el-table ref="dragTable" :data="tableData" class="data_table" style="width: 100%" border row-key="id"><el-table-column v-for="(item, index) in col" :key="`col_${index}`"  header-align="center" align="center" :prop="item.prop" :label="item.label"><template slot-scope="scope"><div v-if="item.type=='sort'" style="text-align: center;"><el-button size="mini" type="parmary" @click="handleStepEdit">拖拽</el-button></div><div v-if="item.type=='execute'" style="text-align: center;"><el-checkbox v-model="scope.row.executed"></el-checkbox></div><div v-if="item.type=='operation'" style="text-align: center;"><el-button size="mini" type="parmary" @click="handleStepEdit">编辑</el-button><el-button size="mini" type="parmary">删除</el-button></div><span v-else>{{ scope.row[item.prop] }}</span></template></el-table-column></el-table><div class="temp-count"><p>共{{stempConfig.total}}条记录</p></div></div></div></div></template>
<script lang="ts">import { Component, Vue, Prop, Watch } from 'vue-property-decorator';import { ApiWebComponentService } from 'mone-test/services';import Sortable from 'sortablejs'@Component({name: 'StepTemp',components: {}})export default class StepTemp extends Vue {/*** props ***/// 通过一个统一props来确定,所以得操作是组件还是脚本@Prop({ type: Object, default: () => { return {}; } }) stepTempInfo;@Prop({ type: String, default: () => { return ''; } }) currentDirectoryName;tableData: Array = [{id: '1',date: '2016-05-01',name: '王小虎1',propVal: 'aaaaaaaaaa',required: true,address: '上海市普陀区金沙江路 101 弄'},{id: '2',date: '2016-05-02',name: '王小虎2',propVal: 'fdggd',required: false,address: '上海市普陀区金沙江路 102 弄'},{id: '3',date: '2016-05-03',name: '王小虎3',propVal: 'cccccccccccc',required: true,address: '上海市普陀区金沙江路 103 弄'},{id: '4',date: '2016-05-04',name: '王小虎4',propVal: 'tttttttttttttt',required: true,address: '上海市普陀区金沙江路 104 弄'}];stempConfig: Object = {createTime: "yyyy-MM-dd HH:mm:ss",creator: "string",deleted: true,directoryPath: "string",directoryName: "",id: 0,key: "string",moduleDesc: "string",moduleName: "string",moduleTags: "string",rewriter: "string",tags: ["string"],updateTime: "yyyy-MM-dd HH:mm:ss",versionId: 0,webModules: [null]}total = 0formLabelWidth: String = '120px';stepFormVisible: Boolean = false;stepForm: Object = {name: '',eventParam: '',latencyTime: 0,// /1};col = [{label: '顺序',type: 'sort'},{label: '序号',prop: 'sort',},{label: '执行',type: 'execute'},{label: '步骤分类',prop: 'type',type: 'prop'},{label: '步骤名称',prop: 'name',type: 'prop'},{label: '等待时间(s)',prop: 'latencyTime',type: 'prop'},{label: '操作',type: 'operation'},];constructor() {// this.initData()}initData() {}mounted() {this.rowDrop();}// 行拖拽rowDrop() {const _this = thisconst table = _this.$refs.dragTable;const tbody = table.$el.querySelector('.el-table__body-wrapper tbody');Sortable.create(tbody, {onEnd({ newIndex, oldIndex }) {const currRow = _this.tableData.splice(oldIndex, 1)[0]_this.tableData.splice(newIndex, 0, currRow)}})}}</script>
<style scoped lang="scss">
</style>

第二个点,如何只让表格中的某一个列具有拖拽功能,其他列没有

可以看看这两篇文章https://www.jianshu.com/p/dcdf1c0b8aab http://www.itxst.com/sortablejs/mbe73qrv.html

<template><div class="steptemp"><!--脚本与组件 步骤调节组件 通过标示区分是调节脚本还是组件--><!--头部toolbars--><div class="toolbars"><ul><!--这里的下面三个调用弹框 传编辑,复制,删除所需要的数据就行了,这个数据,这里单独发请求到了--><li @click="handleEvent(tableEdit)"><i class="el-icon-edit"></i><span>编辑</span></li><li @click="handleEvent(tableCopy)"><i class="el-icon-document-copy"></i><span>复制</span></li><li @click="handleEvent(tableDel)"><i class="el-icon-delete"></i><span>删除</span></li><li><i class="el-icon-upload2"></i><span>导入</span></li></ul><ol><li><el-button size="mini" style="margin-bottom:5px;" type="primary" @click="handleSaveStep">保存</el-button></li><li @click="toStepTemp"><i class="el-icon-back"></i></li></ol></div><!--信息数据--><div class="info"><h2>{{stempConfig.moduleName}}</h2><div class="info-detail"><p>创建人:{{stempConfig.creator}}</p><p>所属分组:{{currentDirectoryName}}</p><p>更新时间:{{stempConfig.updateTime}}</p></div></div><!--表格部分--><div class="temp-table"><div class="temp-table-top"><div class="el-dropdown-link"><el-button type="primary" icon="el-icon-plus" class="btn_new_group" @click="handleStepAdd">添加执行步骤</el-button></div></div><div class="temp-table-bottom"><el-table ref="dragTable" :data="tableData" class="data_table" style="width: 100%" border row-key="stepId"><el-table-column v-for="(item, index) in col" :key="`col_${index}`" header-align="center" align="center" :width="item.formLabelWidth?item.formLabelWidth:'auto'":prop="item.prop" :label="item.label"><template slot-scope="scope"><div class="allowDrag" v-if="item.type=='sort'" style="text-align: center;"><i class="el-icon-sort" style="cursor:pointer;"></i></div><div v-else-if="item.type=='operation'" style="text-align: center;display: flex;justify-content: space-between"><!--增加和删除按钮--><i size="small" class="el-icon-circle-plus-outline fs20" @click="handleStepAdd"></i><i class="el-icon-remove-outline fs20" @click="handleStepDelete(scope.$index)"></i></div><div v-else-if="item.type=='selectEvent'"><el-select style="width:100%" v-model="scope.row.event"><el-option v-for="item1 in eventList" :key="`${item1.value}event`" :label="item1.value" :value="item1.value"><span style="float: left">{{ item1.value }}</span><span style="float: right; color: #8492a6; font-size: 13px">{{ item1.label }}</span></el-option></el-select></div><div v-else-if="item.type=='selectAlert'"><el-select style="width:100%" v-model="scope.row.alertRule"><el-option v-for="item2 in alertRuleList" :key="`${item2.value}alertRule`" :label="item2.value" :value="item2.value"><span style="float: left">{{ item2.value }}</span><span style="float: right; color: #8492a6; font-size: 13px">{{ item2.label }}</span></el-option></el-select></div><div class="event_param" v-else-if="item.type=='triplingParam'"><ul v-show="scope.row.isEventParamShow"><li @mousedown="handleParam('comParam',scope.$index,'triplingParam')">引入公共参数</li><li>引入步骤参数</li><li @mousedown="handleParam('paramconstr',scope.$index,'triplingParam')">参数构造器</li></ul><el-input @input="handleParamInput(scope.$index)" @blur="handleParamBlur(scope.$index)" @focus="handleParamFocus(scope.$index)"v-model="scope.row.eventParam"></el-input></div><div class="event_param" v-else-if="item.type=='singleParam'"><ul v-show="scope.row.isPositionParamShow"><li @mousedown="handleParam('comParam',scope.$index,'singleParam')">引入公共参数</li><!--<li>引入步骤参数</li>--></ul><el-input @input="handleParamInput1(scope.$index)" @blur="handleParamBlur1(scope.$index)" @focus="handleParamFocus1(scope.$index)"v-model="scope.row.locationProperty"></el-input></div><span v-else-if="item.type=='inputNum'"><!--<el-input-number style="width: 100px;" placeholder="请输入等待时间" v-model="scope.row.latencyTime" controls-position="right" :min="0" :max="60"></el-input-number>--><el-input v-model="scope.row[item.prop]" oninput="value=value.replace(/[^\d]/g,'')"></el-input></span><span v-else><el-input ondragstart="return false" v-model="scope.row[item.prop]"></el-input></span></template></el-table-column></el-table></div></div><!--公共参数弹框--><CommonParam v-if="isComParamShow.flag" :isComParamShow="isComParamShow" @commonParamClick="commonParamClick" :treeConfig="{filter: true}":treeReqParams="treeReqParams"></CommonParam><!--参数构造器弹框--><ParamConstructor v-if="isParamConstrShow.flag" @paramConstructorClick="paramConstructorClick" :isParamConstrShow="isParamConstrShow"></ParamConstructor></div></template>
<script lang="ts">import { Component, Vue, Prop, Watch } from 'vue-property-decorator';import { ApiWebComponentService } from 'mone-test/services';import Sortable from 'sortablejs'import CommonParam from 'mone-test/components/commonQuote/commonParam/index.vue'import ParamConstructor from 'mone-test/components/commonQuote/paramConstructor/index.vue'@Component({name: 'StepTempWeb',components: {CommonParam, ParamConstructor}})export default class StepTemp extends Vue {/*** props ***/// 通过一个统一props来确定,所以得操作是组件还是脚本@Prop({ type: Object, default: () => { return {}; } }) stepTempInfo;@Prop({ type: String, default: () => { return ''; } }) currentDirectoryName;@Prop({ type: Object, default: () => { return {}; } }) treeConfig;@Prop({ type: Object, default: () => { return {}; } }) treeReqParams;// get tableDataBySort() {//     const self = this as any//     self.tableData.sort((a, b) => {//         return a.sort - b.sort//     })//     return self.tableData// }// id需要我传时间戳,如果是新增,那么就传个时间戳// 如果是编辑再保存,就不用再传时间戳,就传它请求到底传来的那个id// 也就是通过他本来有没有id来判断是新增还是编辑tableData: object[] = [];stempConfig: object = {createTime: "yyyy-MM-dd HH:mm:ss",creator: "string",deleted: true,directoryPath: "string",directoryName: "",id: 0,key: "string",moduleDesc: "string",moduleName: "string",moduleTags: "string",rewriter: "string",tags: ["string"],updateTime: "yyyy-MM-dd HH:mm:ss",versionId: 0,webModules: [null]}total: number = 0stepStartFormVisible: boolean = false;formLabelWidth: string = '120px'col: object[] = [// {//     label: 'id',//     prop: 'id',//     type: 'prop',//     formLabelWidth: '100px'// },{label: '顺序',type: 'sort',formLabelWidth: '50px'},{label: '步骤名称',prop: 'name',formLabelWidth: '130px'},{label: '事件',prop: 'event',type: 'selectEvent',formLabelWidth: '130px',// selectOption:this.eventList},{label: '定位属性',prop: 'locationProperty',type: 'singleParam',formLabelWidth: '150px'},{label: '参数',prop: 'eventParam',type: 'triplingParam',formLabelWidth: '300px'},{label: '等待时间(s)',prop: 'latencyTime',type: 'inputNum',formLabelWidth: '112px'},{label: '断言规则',prop: 'alertRule',type: 'selectAlert',formLabelWidth: '130px',// selectOption:this.alertRuleList},{label: '预期结果',prop: 'expect',formLabelWidth: '130px'},{label: '设置变量',prop: 'var',formLabelWidth: '130px'},{label: '操作',type: 'operation',formLabelWidth: '60px'},];operation: Number = 1eventList: object[] = [{ positionInsist: true, paramInsist: false, label: "获取html标签文本值", value: "getText" },{ positionInsist: true, paramInsist: false, label: "获取html标签属性", value: "getAttribute" },{ positionInsist: true, paramInsist: false, label: "获取html标签内部html文本", value: "getInnerHTML" },{ positionInsist: true, paramInsist: false, label: "获取html标签内部文本", value: "getInnerText" },{ positionInsist: true, paramInsist: false, label: "清空输入框", value: "clear" },{ positionInsist: true, paramInsist: true, label: "向输入框输入内容", value: "fill" },{ positionInsist: true, paramInsist: false, label: "鼠标单击", value: "click" },{ positionInsist: true, paramInsist: false, label: "鼠标双击", value: "dblclick" },{ positionInsist: true, paramInsist: false, label: "鼠标悬停", value: "hover" },{ positionInsist: true, paramInsist: false, label: "标签元素聚焦", value: "focus" },{ positionInsist: true, paramInsist: false, label: "判断元素是否可用(反义isDisabled)", value: "isEnabled" },{ positionInsist: true, paramInsist: false, label: "判断元素是否禁用", value: "isDisabled" },{ positionInsist: true, paramInsist: false, label: "判断元素是否可编辑", value: "isEditable" },{ positionInsist: true, paramInsist: false, label: "判断元素是否可视化(反义isHidden)", value: "isVisible" },{ positionInsist: true, paramInsist: false, label: "判断元素是否隐藏", value: "isHidden" },{ positionInsist: true, paramInsist: false, label: "单选框或复选框选中", value: "check" },{ positionInsist: true, paramInsist: false, label: "单选框或复选框取消选中", value: "uncheck" },{ positionInsist: true, paramInsist: false, label: "判断单选框或复选框选中状态", value: "isChecked" },{ positionInsist: true, paramInsist: true, label: "模拟键盘输入", value: "type" },{ positionInsist: true, paramInsist: true, label: "模拟键盘按下,通常是快捷键", value: "press" },{ positionInsist: true, paramInsist: true, label: "上传文件,输入是完整文件路径或相对路径", value: "upload" },{ positionInsist: false, paramInsist: true, label: "下载文件,输入下载的指定目录", value: "download" },{ positionInsist: false, paramInsist: true, label: "使用JavaScript", value: "excuteJS" },{ positionInsist: false, paramInsist: true, label: "打开一个浏览器页签", value: "open" },{ positionInsist: false, paramInsist: false, label: "关闭一个浏览器页签", value: "close" },{ positionInsist: false, paramInsist: false, label: "页面重新加载", value: "reload" },{ positionInsist: false, paramInsist: true, label: "获取当前页签标题", value: "getTitle" },{ positionInsist: false, paramInsist: false, label: "对话框点击确定", value: "confirm" },{ positionInsist: false, paramInsist: false, label: "对话框点击取消", value: "dismiss" },{ positionInsist: false, paramInsist: true, label: "对话框输入文本", value: "accept" },{ positionInsist: false, paramInsist: false, label: "页面前进", value: "goForward" },{ positionInsist: false, paramInsist: false, label: "页面回退", value: "goBack" },{ positionInsist: true, paramInsist: true, label: "页面滚动(模拟滚动条滚动)", value: "tap" },{ positionInsist: false, paramInsist: true, label: "截图,输入是完整文件路径或相对路径", value: "screenshot" },{ positionInsist: false, paramInsist: true, label: "录制回放,输入是完整文件路径或相对路径", value: "record" }]alertRuleList: object[] = [{ label: '包含', value: 'contain' },{ label: '等于[=]', value: 'equal' },{ label: '不等于[!=]', value: 'notEqual' }]paramToolShow: boolean = falseexpectToolShow: boolean = falseisComParamShow: object = { flag: false }isParamConstrShow: object = { flag: false }currentParamType: string = "param" //默认事件参数输入框吧isPositionInsist: boolean = true;//步骤是否支持事件isParamInsist: boolean = true;//步骤是否支持参数currentTableIndex: number = 0;currentTableInputType: string = '';constructor() {super()}mounted() {this.rowDrop();}created() {if (this.stepTempInfo && this.stepTempInfo.row && this.stepTempInfo.row.id) {this.getWebModuleSingle()}}// handleParam(inputType, diaType) {//     // 第一个type是区分是把值给哪个输入框,事件参数,还是断言预期参数//     // 第二type,是去分是弹哪个弹框//     // 不管是那种,都要执行这步,这样谈框点确定触发回调改值,才知道要改的是哪个输入框的值//     this.currentParamType = inputType//     if (diaType == 'comParam') {//         this.isComParamShow['flag'] = true//     } else if (diaType == 'paramconstr') {//         this.isParamConstrShow['flag'] = true//     }// }paramConstructorClick(param) {const self = this as anyconsole.log(param, this.currentTableIndex, this.currentTableInputType)//  this.currentTableIndex// this.$set(this.stepPositiontForm, 'eventParam', param)if (this.currentTableInputType == 'singleParam') {self.$set(self.tableData[self.currentTableIndex], 'locationProperty', param)} else if (this.currentTableInputType == 'triplingParam') {self.$set(self.tableData[self.currentTableIndex], 'eventParam', param)}// self.tableData = self.tableData.concat()}commonParamClick(param) {const self = this as anyconsole.log(param, this.currentTableIndex, this.currentTableInputType)// 根据当前index,填充进table里if (this.currentTableInputType == 'singleParam') {self.$set(self.tableData[self.currentTableIndex], 'locationProperty', param)} else if (this.currentTableInputType == 'triplingParam') {self.$set(self.tableData[self.currentTableIndex], 'eventParam', param)}// self.tableData = self.tableData.concat()// this.$set(this.stepPositiontForm, 'eventParam', param)}handleParamBlur(index) {const self = this as any// setTimeout(function () {self.$set(self.tableData[index], 'isEventParamShow', false)self.tableData = self.tableData.concat()// });}handleParamInput(index) {const self = this as anythis.$set(self.tableData[index], 'isEventParamShow', false)}handleParamFocus(index) {const self = this as anyself.$set(self.tableData[index], 'isEventParamShow', true)self.tableData = self.tableData.concat()}handleParamBlur1(index) {const self = this as any// setTimeout(function () {self.$set(self.tableData[index], 'isPositionParamShow', false)self.tableData = self.tableData.concat()// });}handleParamInput1(index) {const self = this as anythis.$set(self.tableData[index], 'isPositionParamShow', false)}handleParamFocus1(index) {const self = this as anyself.$set(self.tableData[index], 'isPositionParamShow', true)self.tableData = self.tableData.concat()}getWebModuleSingle() {const self = this as anyApiWebComponentService.getWebModuleSingle({ id: self.stepTempInfo.row.id }).then(res => {if (res.code == 200) {if (res.data.id) {// 这里做一个判断,如果有至少一条数据,那么tabledata中就不放// 默认的一条空数据,否则默认放一条空数据// 还有一个操作,就是查询的时候要给,每个数据两个属性,用于控制,参数选项是显示还是隐藏,// 对应的,保存的时候,也要删除这两个属性self.total = res.data.totalself.stempConfig = Object.assign({}, res.data)if (res.data.steps && res.data.steps.length != 0) {// self.tableData = res.data.steps.concat()self.tableData = res.data.steps.map(el => {el.isPositionParamShow = falseel.isEventParamShow = falsereturn el}).concat()} else {self.tableData = [{name: '',event: '',locationProperty: '',eventParam: '',latencyTime: '',alertRule: '',expect: '',var: '',isPositionParamShow: false,isEventParamShow: false}]}self.tableData = res.data.steps.concat()}}}).catch(err => {// self.toStepTemp()// self.$emit('toStepTemp', true, self.stepTempInfo.row.directoryPath)})}toStepTemp() {const self = this as anythis.$emit('toStepTemp', true, self.stepTempInfo.row.directoryPath)}handleEvent(funcName) {let obj = { row: {} }obj.row = Object.assign({}, this.stempConfig)this.$parent[funcName](obj)}// 行拖拽rowDrop() {const _this = this as anyconst table = _this.$refs.dragTable;const tbody = table.$el.querySelector('.el-table__body-wrapper tbody');Sortable.create(tbody, {handle: ".allowDrag",onEnd({ newIndex, oldIndex }) {const currRow = _this.tableData.splice(oldIndex, 1)[0]_this.tableData.splice(newIndex, 0, currRow)// 拖拽结束调用排序接口// id对应相应的顺序// const query = { orderMap: {} }// _this.tableData.map((el, index) => {//     query.orderMap[el.id] = index//     return el// })// ApiWebComponentService.setpSort(query).then(res => {//     if (res.code == 200) {//         _this.getWebModuleSingle()//     }// })}})}handleParam(diaType, index, inputType) {// 点击的时候,记录当前的index,用以确定参数放在哪一行this.currentTableIndex = index// 还要记录当前input类型,是三个的还是一个的this.currentTableInputType = inputTypeconsole.log('diaType', diaType)if (diaType == 'paramconstr') {this.isParamConstrShow['flag'] = true} else if (diaType == 'comParam') {this.isComParamShow['flag'] = true}}handleStepAdd() {const self = this as anyself.tableData.push({name: '',event: '',locationProperty: '',eventParam: '',latencyTime: '',alertRule: '',expect: '',var: '',isPositionParamShow: false,isEventParamShow: false})}handleStepDelete(index) {const self = this as anyself.tableData.splice(index, 1)}// 点击保存,保存步骤handleSaveStep() {// 有个校验,如果只有一条全空的数据// 那么也请求接口,但是啥也不传// 点击保存的时候,全为空的数据不传// 传过去的时候,记得吧query里的那两个属性删除const self = this as anylet query = self.tableData.map(el => {delete el.isPositionParamShowdelete el.isEventParamShowreturn el}).filter(el => {let flag = falsefor (const key in el) {// 存在一个不为空的,就可以发送这条数据if (el[key] != '') {flag = truebreak;}}return flag})// .map(el => {//     // 第二个判断是,没有id,加id字段为时间戳,有id的就不用管,//     el.stepId = el.stepId ? el.stepId : new Date().getTime()//     return el// })console.log('query', query);// ApiWebComponentService.saveSteps(query).then(res => {//     console.log('res', res)// })}}</script>
<style scoped lang="scss">.steptemp {flex: 1;overflow: auto;.toolbars {border-bottom: 1px solid #ccc;display: flex;justify-content: space-between;align-items: center;ul {display: flex;li {margin: 0 10px;cursor: pointer;}}ol {display: flex;align-items: center;li {margin: 0 10px;cursor: pointer;}}}.info {border-bottom: 1px solid #ccc;h2 {font-family: '微软雅黑 Bold', '微软雅黑 Regular', '微软雅黑';font-weight: 700;font-style: normal;font-size: 15px;}&-detail {display: flex;p {font-size: 12px;color: #7F7F7F;margin-right: 10px;}}}.temp-table {margin-top: 5px;&-top {padding: 10px 0 10px 10px;border: 1px solid #ccc;.btn_new_group {background: #0EAFC0;border-color: #0EAFC0;padding: 6px;font-size: 12px;border-radius: 0;margin: 0;}.btn_new_group:hover {background: #0adcf3;border-color: #0adcf3;}}&-bottom {.data_table {max-width: 1100px;width: 100%;::v-deep th {background-color: #f2eded;}.event_param {position: relative;ul {position: absolute;left: 50%;top: -2px;transform: translateX(-50%);display: flex;z-index: 1;li {margin-right: 5px;font-weight: 250;font-style: normal;font-size: 11px;text-align: center;width: 80px;padding: 0 4px;height: 20px;line-height: 20px;background-color: rgba(0, 191, 191, 1);border: none;border-radius: 4px;box-shadow: 2px 2px 2px rgba(0, 191, 191, .35);font-family: '微软雅黑 Light', '微软雅黑 Regular', '微软雅黑';color: #FFFFFF;cursor: pointer;}}}}.temp-count {background-color: #F2F2F2;p {margin: 0;height: 45px;line-height: 45px;padding-right: 10px;font-family: 'Arial Normal', 'Arial';font-weight: 400;font-style: normal;font-size: 13px;letter-spacing: normal;color: #333333;text-align: right;}}}}.fs20 {font-size: 20px;cursor: pointer;}}
</style>

如果又有问题,拖拽失效  @cell-mouse-enter.once="rowDrop"可以试试在el-table上写这个

第三点,如何让多个行,其中几行不能拖拽,不能拖动,去掉.allowdrop拖拽类名就行了,但是如何不被其他可拖拽的元素影响呢,可以用onMove做判断,给不能拖拽的行加类名,可以用:row-class-name="tableRowClassName"来给要加的行加 看element文档吧,然后在onMove里判断有没有类名,有就返回,不被其他拖拽影响

其中几行不能拖拽,在onend里判断也可以让这几行不能互相拖拽,但是还会有拖拽效果,能拖动,这样能拖动,结果虽然互相拖不会有影响,但是拖到能拖拽的元素又会换位置

所以需要用去掉.allowdrop拖拽类名,让他不能拖动比较好

代码

<template><div id="mone_test_tmpl_detail" class="tmpl_detail"><template v-if="!isScriptShow&&!isApiEditShow"><div><!--头部--><div class="toolbars"><ul><!--这里的下面三个调用弹框 传编辑,复制,删除所需要的数据就行了,这个数据,这里单独发请求到了--><li @click="toTmplDetail1"><i class="el-icon-back"></i></li><li @click="handleEvent('tableEdit')"><i class="el-icon-edit"></i><span>编辑</span></li><li @click="handleEvent('tableCopy')"><i class="el-icon-document-copy"></i><span>复制</span></li><li @click="handleEvent('tableDel')"><i class="el-icon-delete"></i><span>删除</span></li></ul><div class="enviroment"><div class="enviroment_l"><el-tooltip class="item" effect="dark" content="弹窗显示环境信息" placement="left"><i class="el-icon-view"></i></el-tooltip></div><div class="enviroment_r"><el-select v-model="value" placeholder="未设置环境"><el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option></el-select></div></div></div><!--下面--><div class="tab"><ul class="tab_t"><!--头部tab切换--><li @click="switchTab(1)" :class="{active:currentTabIndex==1}">测试流程</li><li @click="switchTab(2)" :class="{active:currentTabIndex==2}">测试数据</li></ul><div class="tab_b"><!--底部切换的tab--><div v-if="currentTabIndex==1"><!--信息数据--><div class="info"><div class="info_t"><h2>{{stempConfig.templateName}}</h2><div class="info_t_r"><div class="tr_l"><i class="el-icon-odometer" style="margin-right: 5px;"></i><span>执行历史</span></div><div class="tr_r"><el-button size="mini" style="background-color: #0EAFC0;" type="primary" icon="el-icon-odometer">执行测试</el-button></div></div></div><div class="info_detail"><p>分组:{{currentDirectoryName}}</p><p>创建人:{{stempConfig.creator}}</p><p>更新时间:{{stempConfig.updateTime}}</p></div></div><!--表格部分--><div class="temp-table"><div class="temp-table-top"><el-dropdown trigger="click" @command="handleCommand"><div class="el-dropdown-link"><el-button type="primary" icon="el-icon-plus" class="btn_new_group">添加测试步骤</el-button><el-button type="primary" icon="el-icon-caret-bottom" class="btn_new_group"></el-button></div><el-dropdown-menu slot="dropdown"><el-dropdown-item command="a">选择api</el-dropdown-item><el-dropdown-item command="b">新建脚本</el-dropdown-item><el-dropdown-item command="c">新建数据库</el-dropdown-item></el-dropdown-menu></el-dropdown><el-button size="mini" style="margin-left: 20px;padding:6px 5px;" type="danger" :disabled="tableLength" @click="handleCommandBatch">批量删除</el-button></div><div class="temp-table-bottom"><el-table ref="dragTable" :data="tableData" :header-cell-style="{background:'#F8F2F2'}" class="data_table" style="width: 100%"border :row-class-name="tableRowClassName"  row-key="id"><!--@cell-mouse-enter.once="rowDrop"--><el-table-column :width="item.formLabelWidth" show-overflow-tooltip v-for="(item, index) in col" :render-header="(h,obj) => renderTooltip(h, obj)" :key="`col_${index}`" header-align="center"align="center" :prop="item.prop" :label="item.label"><!--:width="item.formLabelWidth?item.formLabelWidth:'auto'"--><template slot-scope="scope"><div :class="scope.row.stepType=='js'?'':'allowDrag'" v-if="item.type=='sort'" style="text-align: center;"><i class="el-icon-sort" style="cursor:pointer;"></i></div><div v-else-if="item.type=='singleCheck'"><el-checkbox v-model="scope.row[item.prop]" @change="handleSingleCheck(scope.row,item.prop)"></el-checkbox></div><div class="tags" v-else-if="item.type=='tag'"><div class="tag_api" v-if="scope.row[item.prop]!='db'&&scope.row[item.prop]!='js'">{{scope.row[item.prop].toUpperCase()}}</div><div class="tag_js" v-if="scope.row[item.prop]=='js'">{{scope.row[item.prop].toUpperCase()}}</div><div class="tag_db" v-if="scope.row[item.prop]=='db'">{{scope.row[item.prop].toUpperCase()}}</div><div class="tag_post" v-if="scope.row[item.prop]!='db'&&scope.row[item.prop]!='js'">{{scope.row.requestType&&scope.row.requestType.toUpperCase()}}</div><div class="tag_db" v-if="scope.row[item.prop]=='db'">{{scope.row.requestType&&scope.row.requestType.toUpperCase()}}</div><!--api, db, js, apiTpl, web--></div><div v-else-if="item.type=='result'"><div v-if="scope.row[item.prop]=='successful'">通过,查看详情</div><div v-else-if="scope.row[item.prop]=='failing'">未通过,查看详情</div><div v-else>尚无测试结果</div></div><div v-else-if="item.type=='operation'"><el-tooltip effect="dark" :content="oBtn.tooltip || oBtn.label" placement="top" v-for="oBtn in btns" :key="oBtn.id"><el-button size="mini" type="text" class="operation_btn"><i @click="triggerIconMethod(oBtn.clickEvent, {index: scope.$index, row: scope.row, currBtnConfig: oBtn })" v-if="oBtn.id == 'execute'":class="scope.row.currentStatus?oBtn.icon:oBtn.icon1"></i><i @click="triggerIconMethod(oBtn.clickEvent, {index: scope.$index, row: scope.row, currBtnConfig: oBtn })" v-else :class="oBtn.icon"></i></el-button></el-tooltip></div><div v-else  ><!--<el-popover placement="top-start"  trigger="hover" :content="scope.row[item.prop]"><p slot="reference" style="overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">{{scope.row[item.prop]}}</p></el-popover>-->{{scope.row[item.prop]}}</div></template></el-table-column></el-table><div class="temp_count"><p>共{{stempConfig.total}}条记录</p></div></div></div></div><div v-else>数据集</div></div></div></div></template><api-choose v-if="isComParamShow.flag" :currentApiIdList="currentApiIdList" :isComParamShow="isComParamShow" @commonParamClick="commonParamClick"></api-choose><create-js-script v-if="isScriptShow" :operateType="operateType" :reqConfig="reqConfig" @saveCallback="saveScriptCb"></create-js-script><!--<create-database-script v-if="isScriptShow" @saveCallback="saveScriptCallback"></create-database-script>--><create v-if="isApiEditShow" :editType="2" :apiEditCheck="apiEditCheck" :reqGet="createReqGet" :reqSave="createReqSave"@saveCallback="saveCreateEditCallback"></create><!--步骤复制谈框--><el-dialog :visible.sync="copyStepVisible"><div slot="title" class="title"><i class="el-icon-circle-plus-outline" style="font-size:17px;"></i>复制步骤</div><el-form :model="copyStepForm" :rules="copyStepFormRules" ref="copyStepRef"><el-form-item label="步骤名称" label-width="120"><el-input style="width:300px;" v-model="copyStepForm.stepName"></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button size="mini" @click="copyStepVisible = false" class="btn1">取 消</el-button><el-button size="mini" type="primary" class="btn2" @click="handleCopyStep">确 定</el-button></div></el-dialog></div>
</template><script lang="ts">declare function require(img: string): string;  // 声明import { Component, Vue, Prop } from 'vue-property-decorator';import { Action, State } from 'vuex-class';import ApiChoose from 'mone-test/components/commonDialog/apiChoose/index.vue'// import CreateDatabaseScript from 'mone-test/components/createDatabaseScript/index.vue'import CreateJsScript from 'mone-test/components/createJsScript/index.vue'import create from 'mone-test/viewModules/apiAutomation/apiManager/src/create/index.vue'import Sortable from 'sortablejs'import { ApiTempService } from 'mone-test/services';@Component({name: 'TmplDetail',components: {ApiChoose,// CreateDatabaseScript,CreateJsScript,create}})export default class TmplDetail extends Vue {@Prop({ type: String, default: () => { return ''; } }) currentDirectoryName;@Prop({ type: Object, default: () => { return {}; } }) stepTempInfo;// 定义变量value: string = ''options: object[] = [{value: '选项1',label: '未设置环境'}, {value: '选项2',label: '测试环境'}, {value: '选项3',label: '预发布环境'}]currentTabIndex: number = 1copyStepForm: object = {stepName: "",}copyStepFormRules: object = {stepName: [{ required: true, message: '请填写步骤名称', trigger: 'change' }],}copyStepVisible: boolean = falsestempConfig: object = {"id": 20,"creator": "test","rewriter": null,"createTime": "2021-04-02 17:35:37","updateTime": null,"directoryPath": null,"templateName": "string22","templateDesc": "string11","templateResult": null,"projectEnvId": null,"versionId": 1,"deleted": false,"steps": [],"total": 0,"pathName": "/api-template/api-template-group1"}col: object[] = [{label: '顺序',type: 'sort',formLabelWidth: '50px'},{label: '执行',type: 'singleCheck',prop: 'executed',// formLabelWidth: '50px'},{label: '锁定',type: 'singleCheck',prop: 'locked',// formLabelWidth: '50px'},{label: '步骤类型',prop: 'stepType',type: 'tag',// formLabelWidth: '130px'},{label: '步骤名称',prop: 'stepName',// formLabelWidth: '130px'},{label: 'URL',prop: 'url',// formLabelWidth: '130px'},{label: '最近测试结果',prop: 'stepResult',type: 'result',// formLabelWidth: '130px'},{label: '操作',type: 'operation',// formLabelWidth: '60px'},];tableData: object[] = []btns: object[] = [{ id: 'execute', label: '', tooltip: '执行', icon: ['el-icon-video-play'], icon1: ['el-icon-video-pause'], status: false, clickEvent: 'executeStep' },{ id: 'edit', label: '', tooltip: '编辑', icon: ['el-icon-edit-outline'], clickEvent: 'editStep' },{ id: 'copy', label: '', tooltip: '复制', icon: ['el-icon-document-copy'], clickEvent: 'copyStep' },{ id: 'delete1', label: '', tooltip: '删除', icon: ['el-icon-delete'], clickEvent: 'deleteStep' }]isComParamShow: object = { flag: false }//api新增页面显示isScriptShow: boolean = false//脚本新增页面显示isApiEditShow: boolean = false//步骤api编辑页面显示createReqGet: object = {};//步骤api编辑页面传值createReqSave: object = {};//步骤api编辑页面传值currentEditApiStepIndex: number = -1//当前api步骤编辑的api的id,这个可以点击编辑的时候拿到apiSteps: object[] = []executeCheck: boolean = false//执行的全选lockedCheck: boolean = false//锁定的全选currentApiIdList: number[] = []//当前api步骤id列表,// 这个id列表要时刻保持更新,意思是每次增删改查,它也要同步更新// 复制的肯定是不算选中的,再说了,这也不是通过id来的// 外面的是步骤id,里面的是api id 不一样的// 所以只能通过那个API名字来// 但是我如何区分复制和非复制的呢// 需不需要区分// 不需要// 为什么呢,因为我操作里面的api根本影响不到复制的哪些,名字都不一样// 其他的脚本啥的同理// 不过最好还是api模板里,我就传模板类型的步骤进去// api脚本里我就传脚本类型的步骤进去// web脚本里我就传js类型的步骤进去// 这个数据我要从当前编辑的步骤身上拿,有就拿,没有就传默认的数据结构进去,不过数组部分还是传空,懂吗// 然后api编辑里面,数组部分,我判断传过来的如果是空// apiEditCheck: object = {//     responseHeader: {//         header: [{//             id: new Date().getTime(),//什么时候需要这个,没有的时候就需要这个,数据传入的时候就check数据是什么就传什么//             // api哪里新增这个hander的时候,每次push的对象都是时间戳id,所以这个不必管//             name: "afasd",//             required: true,//             matchRule: "可选equal,notEqual",//             expect: "asdfasdf"//         }],//         checkRule: "可选check,notCheck"//     },//     responeBody: {//         checkRule: "可选notCheck,code,jsonPath,xpath,text,regex",//         statusCode: 200,//         jsonPath: [{//             value: "asfdasdf",//             matchRule: "可选notContian,contain,equal,notEqual,greaterThan,greatThanOrEqual,lessThan,lessThanOrEqual",//             expect: "asdfaf"//         }],//         xpath: "asfd",//         text: [{//             matchRule: "可选notContian,contain,equal",//             expect: "asdfasd"//         }],//         regex: "asfdasf"//     },//     // 我自己要带过去的数据,//     ownData:{//         failContinue:0,//         sleepTime:0//     }// }apiEditCheck: object = {responseHeader: {header: [],checkRule: ""},responeBody: {checkRule: "",statusCode: 200,jsonPath: [],xpath: "",text: [],regex: ""},// 我自己要带过去的数据,ownData: {failContinue: 0,sleepTime: 0}}tooltip1: any = require('mone-test/assets/img/tooltip1.png');tooltip2: any = require('mone-test/assets/img/tooltip2.png');operateType: number = 1; // 操作类型: 1-新增js脚本 , 2-编辑js脚本reqConfig: object = {getReq: {query: {// id: 27},},saveReq: {data: {type: 'template',apiId: this.stepTempInfo.row.id,// id: 27 // 新建脚本不需要传脚本id, 编辑脚本时需要传脚本id},}};// 实时步骤列白长度,以设置批量删除按钮的是否可点击get tableLength() {const self = this as anyif (self.tableData.length == 0) {return true} else {return false}}constructor() {super()}created() {this.initData();if (this.stepTempInfo && this.stepTempInfo.row && this.stepTempInfo.row.id) {this.getApiTemp()// this.getApiTempStep()}}mounted() {const self = this as anythis.rowDrop();}// mounted(){}// methods// 初始化数据initData() {}// 头部三个操作触发父组件函数handleEvent(funcName) {let obj = { row: {} }obj.row = Object.assign({}, this.stempConfig)this.$parent[funcName](obj)}handleSaveStep() {}// 触发步骤操作icon的回调triggerIconMethod(funName, data) {const self = this as anyself[funName](data)}// 执行步骤executeStep(data) {const self = this as anyself.tableData[data.index].currentStatus = !self.tableData[data.index].currentStatus}// 编辑步骤editStep(data) {const self = this as any// 这里要根据类型,判断不同的步骤,他们编辑是不同的// console.log()// 根据type和stepType来if (data.row.type == 'template' && data.row.stepType == 'api') {// this.currentEditApiStepIndex = data.row.index// 首先这些发过去,上部分数据this.createReqGet = {url: `/test/api/management/${data.row.index}`};this.createReqSave = {url: '/test/api/management',data: {ids: [data.row.index]}};// 然后这部分要从步骤身身上取到check里的字段,没有就默认全传空,传空的结构过去self.getApiTempStep(data.row.id, res => {console.log('res', res)if (res.check) {self.apiEditCheck=Object.assign({},res.check)}this.isApiEditShow = true})} else if (data.row.type == 'template' && data.row.stepType == 'js') {console.log('data', data)self.reqConfig.getReq.query.id = data.row.idself.reqConfig.saveReq.data.id = data.row.idconsole.log('self.reqConfig', self.reqConfig)this.operateType = 2this.isScriptShow = true//         reqConfig: object = {//     getReq: {//         query: {//             // id: 27//         },//     },//     saveReq: {//         data: {//             type: 'template',//             apiId: this.stepTempInfo.row.id,//             id: data.id // 新建脚本不需要传脚本id, 编辑脚本时需要传脚本id//         },//     }// };}}// 复制步骤谈框出现copyStep(data) {const self = this as anythis.copyStepVisible = true;this.$set(self.copyStepForm, 'stepName', data.row.stepName)self.getApiTempStep(data.row.id, res => {self.copyStepForm = Object.assign({}, data.row, { input: res.request })})}// 确定复制步骤handleCopyStep() {const self = this as anyself.apiSteps = []let query = { apiSteps: [] }let obj = {versionId: 1,//版本idtype: 'template',//请求类型,template和sciprt,api模板中是template,其他地方都是scriptstepType: 'api',//步骤类型,api,apiTml,db,js,web五种,根据选择来sleepTime: self.copyStepForm.sleepTime,//休眠时间failContinue: Number(self.copyStepForm.failContinue),//失败后是否继续执行,传数字input: self.copyStepForm.input,//api传input,check里的下部分stepName: self.copyStepForm.stepName,//步骤名称index: self.copyStepForm.index,//api,API模板,web组件,那js和数据库需要传id吗,按理说也要啊。apiId: self.copyStepForm.apiId,//当前在表格中的idrequestType: self.copyStepForm.requestType,//请求类型,这个api才有url: self.copyStepForm.url,//url有就传,这个api类型才传copy: 1//是否是复制,不是就不传,是就传1}self.apiSteps.push(obj)query.apiSteps = self.apiSteps.concat()ApiTempService.addApiTempStep(query).then(res => {if (res.code == 200) {// 新增之后,刷新详情this.copyStepVisible = falseself.getApiTemp()}})}// 删除步骤发请求handleDeleteStep(ids) {const self = this as any// 发个请求删除,然后更新this.$confirm('此操作将永久删除该步骤, 是否继续?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {ApiTempService.delApiTempStep({ ids }).then(res => {if (res.code == 200) {self.$message({message: '删除成功',type: 'success'});self.getApiTemp()} else {self.$message({message: '删除失败',type: 'fail'});}})}).catch(() => {this.$message({type: 'info',message: '已取消删除'});});}// 单个删除步骤deleteStep(data) {const self = this as anylet arr = [data.row.id]self.handleDeleteStep(arr)}// 返回表格页面toTmplDetail1() {const self = this as any// 返回的时候要保存// self.handleSaveStep('back')this.$emit('toTmplDetail1')}// 测试流程和测试数据切换switchTab(num) {this.currentTabIndex = num}// api选择谈框回调commonParamClick(param) {const self = this as any// 这里是api的回调,但实际上,有三个谈框,// 同一个接口,每个传的参数都有点区别,不过这里是api的// 在这里,批量删除与批量增加console.log('param', param)if (param.addList.length != 0) {self.apiSteps = []let query = { apiSteps: [] }param.addList.map(el => {let obj = {versionId: 1,type: 'template',stepType: 'api',sleepTime: 0,failContinue: 0,input: JSON.parse(el.request),stepName: el.apiName,index: el.id,apiId: self.stepTempInfo.row.id,requestType: el.method.toLowerCase(),url: el.host + el.path}self.apiSteps.push(obj)})query.apiSteps = self.apiSteps.concat()ApiTempService.addApiTempStep(query).then(res => {if (res.code == 200) {// 新增之后,刷新详情self.getApiTemp()}})}if (param.deleteList.length != 0) {// 根据api id找到步骤id列表let ids: any = []self.tableData.map(el => {param.deleteList.map(item => {if (el.index == item) {if (!ids.includes(el.id)) {ids.push(el.id)}}})})if (ids.length > 0) {ApiTempService.delApiTempStep({ ids }).then(res => {if (res.code == 200) {self.$message({message: '删除成功',type: 'success'});self.getApiTemp()} else {self.$message({message: '删除失败',type: 'fail'});}})}}}// 脚本页面回调saveScriptCb(data) {this.isScriptShow = falsethis.getApiTemp()// self.apiSteps = []// let jsData= data.data.apiSteps[0]// if(jsData.length==0)return//     let query:any = { apiSteps: [] }//         let obj = {//             versionId: 1,//             type: 'template',//             stepType: 'js',//             sleepTime: jsData.sleepTime?Number(jsData.sleepTime):0,//             failContinue: Number(jsData.failContinue),//             // input: JSON.parse(el.request),//             stepName: jsData.stepName,//             // index: el.id,//             apiId: jsData.apiId,//             // requestType: el.method.toLowerCase(),//             input:jsData.input,//             output:jsData.output//         }//         // self.apiSteps.push(obj)//         query.apiSteps.push(obj)//     // query.apiSteps = self.apiSteps.concat()//     ApiTempService.addApiTempStep(query).then(res => {//         if (res.code == 200) {//             // 新增之后,刷新详情//             self.getApiTemp()//         }//     })}// api步骤编辑的回调 saveCreateEditCallback(res) {// 首先console.log('res{data,apiEditCheck}', res)this.isApiEditShow = false// self.apiSteps = []//     let query = { apiSteps: [] }//     param.addList.map(el => {//         let obj = {//             versionId: 1,//             type: 'template',//             stepType: 'api',//             sleepTime: 1000,//             failContinue: 0,//             input: el.request,//             stepName: el.apiName,//             index: el.id,//             apiId: self.stepTempInfo.row.id,//             requestType: el.method.toLowerCase(),//             url: el.host + el.path,//             check:{}//         }//         self.apiSteps.push(obj)//     })//     query.apiSteps = self.apiSteps.concat()//     ApiTempService.addApiTempStep(query).then(res => {//         if (res.code == 200) {//             // 新增之后,刷新详情//             self.getApiTemp()//         }//     })}// 步骤谈框选择,api,脚本,等handleCommand(command) {const self = this as any// 新增步骤// 启动和元素定位都是同一个接口,不过是传不同的东西,意思是两个都可以新增步骤// this.operation = 1//新增都要显示这个确定,不同的谈框中,没影响if (command == 'a') {self.isComParamShow.flag = true}else if (command == 'b') {// 如果是脚本类型,那么要重置一下参数self.reqConfig.getReq.query.id = ''self.reqConfig.saveReq.data.id = ''// console.log('self.reqConfig', self.reqConfig)this.operateType = 1this.isScriptShow = true}}// 批量删除步骤handleCommandBatch() {const self = this as anylet arr = self.tableData.map(el => el.id)// let arr = [data.row.id]self.handleDeleteStep(arr)}// 获取api模板基本信息,包含粗略步骤列表getApiTemp() {const self = this as anyApiTempService.getApiTemp(self.stepTempInfo.row.id).then(res => {if (res.code == 200) {self.stempConfig = Object.assign({}, res.data)self.tableData = res.data.steps.concat()// 每次查询,currentApiIdList也跟着跟新self.currentApiIdList = self.tableData.filter(el => el.stepType == 'api' && el.type == 'template' && el.copy != 1).map(el => el.index)// 如果步骤的executed全都为true,那么let exeFlag1 = self.tableData.every(el => el.executed)if (self.tableData.length == 0) {self.executeCheck = false} else {if (exeFlag1) {self.executeCheck = true} else {self.executeCheck = false}}let exeFlag2 = self.tableData.every(el => el.locked)if (self.tableData.length == 0) {self.lockedCheck = false} else {if (exeFlag2) {self.lockedCheck = true} else {self.lockedCheck = false}}}}).catch(err => {// self.toTmplDetail1()})}// 获取API模板步骤列表详情getApiTempStep(id, callback) {const self = this as anyApiTempService.getApiTempStep('', { id }).then(res => {if (res.code == 200) {// self.total = res.data.total// self.tableData = Object.assign({}, res.data)callback(res.data)}}).catch(err => {// self.toTmplDetail1()})}// 执行与锁定checkbox单选handleSingleCheck(data, prop) {const self = this as anylet query: any = {}query.apiSteps = []if (prop == 'executed') {let exeFlag1 = self.tableData.every(el => el.executed)if (self.tableData.length == 0) {self.executeCheck = false} else {if (exeFlag1) {self.executeCheck = true} else {self.executeCheck = false}}query.apiSteps = [{ id: data.id, executed: data.executed }]} else if (prop == 'locked') {let exeFlag2 = self.tableData.every(el => el.locked)if (self.tableData.length == 0) {self.lockedCheck = false} else {if (exeFlag2) {self.lockedCheck = true} else {self.lockedCheck = false}}query.apiSteps = [{ id: data.id, locked: data.locked }]}ApiTempService.isExeOrLock(query).then(res => {if (res.code == 200) {self.getApiTemp()}})}// 渲染表头renderTooltip(h, { column, $index }) {const self = this as any;if (column.label == '执行' || column.label == '锁定') {return h('div', { style: 'display: flex;align-items:center;justify-content:space-evenly;' }, [h('el-checkbox', {props: {value: column.label == '执行' ? self.executeCheck : self.lockedCheck,},on: {change: function (e) {let query: any = {}query.apiSteps = []if (column.label == '执行') {self.executeCheck = !self.executeCheckself.tableData.map(el => {self.$set(el, 'executed', self.executeCheck)query.apiSteps.push({ id: el.id, executed: el.executed })})} else if (column.label == '锁定') {self.lockedCheck = !self.lockedCheckself.tableData.map(el => {self.$set(el, 'locked', self.lockedCheck)query.apiSteps.push({ id: el.id, locked: el.locked })})}ApiTempService.isExeOrLock(query).then(res => {if (res.code == 200) {self.getApiTemp()}})}}}),h('div', {domProps: {innerHTML: column.label}}),h('el-tooltip',  //  标签的名称{props: {content: (function () {if (column.label == '执行') {return '仅会执行勾选的步骤'} else {return '勾选锁定的步骤一定会按顺序执行,即使其他步骤出现异常'}})(),placement: 'top-end',// effect: "light",  //  默认为黑色主题},},[h('div', {style: {position: 'relative'}}, [h('img', {attrs: {src: self.tooltip1},}),h('img', {attrs: {src: self.tooltip2},style: {position: 'absolute',left: '6px',top: '6px',height: '8px',width: '3px'}}),])])]);}return h('div', { style: 'display: flex;justify-content:center;' }, [h('div', {domProps: {innerHTML: column.label}}),]);}// 表格行添加 classtableRowClassName({row, rowIndex}){const self = this as any;if(row.stepType=='js'){return 'drag_table_disabled_drag_row';}return '';}// 行拖拽rowDrop() {const _this = this as anyconst table = _this.$refs.dragTable;const tbody = table.$el.querySelector('.el-table__body-wrapper tbody');Sortable.create(tbody, {handle: ".allowDrag",onEnd({ newIndex, oldIndex }) {// 如果是模板类型,就不许交换if(newIndex==oldIndex)returnif(_this.tableData[newIndex].stepType=='js') returnconst currRow = _this.tableData.splice(oldIndex, 1)[0]_this.tableData.splice(newIndex, 0, currRow)// 拖拽结束调用排序接口// id对应相应的顺序const query = { orderMap: {} }_this.tableData.map((el, index) => {query.orderMap[el.id] = indexreturn el})ApiTempService.tempSort(query).then(res => {if (res.code == 200) {_this.getApiTemp()}})},// 拖拽移动的时候onMove: function (/**Event*/evt, /**Event*/originalEvent) {if(evt.related.className.indexOf('drag_table_disabled_drag_row') !== -1){return false;}console.log('evt',evt)},})}}</script><style scoped lang="scss">#mone_test_tmpl_detail {/*height: 100%;*/flex: 1;overflow: auto;.toolbars {display: flex;justify-content: space-between;align-items: center;ul {display: flex;li {margin: 0 10px;cursor: pointer;}}.enviroment {display: flex;overflow: hidden;&_l {width: 50px;border: 1px solid #d7d7d7;display: flex;justify-content: center;align-items: center;}&_r {::v-deep .el-input__inner {border-radius: 0;border-left: 0;border-color: #d7d7d7;}}}}.tab {&_t {display: flex;border-bottom: 1px solid #d7d7d7;padding-left: 20px;margin-top: 20px;li {width: 66px;display: inline-block;float: left;height: 30px;/*border: 1px solid #d7d7d7;*//*border-bottom: 1px solid transparent;*/margin-bottom: -1px;background: #fff;text-align: center;margin-left: 12px;padding-bottom: 10px;line-height: 30px;}li:hover {border: 1px solid #d7d7d7;border-bottom: 1px solid transparent;cursor: pointer;}li.active {border: 1px solid #d7d7d7;border-bottom: 1px solid transparent;}}&_b {.info {border-bottom: 1px solid #ccc;&_tag {display: flex;li {padding: 2px;height: 22px;background-color: rgba(236, 245, 255, 1);border-radius: 2px;font-size: 12px;margin-right: 5px;}}&_t {display: flex;justify-content: space-between;align-items: center;h2 {font-family: '微软雅黑 Bold', '微软雅黑 Regular', '微软雅黑';font-weight: 700;font-style: normal;font-size: 15px;}.info_t_r {display: flex;align-items: center;.tr_l {margin-right: 10px;}.tr_r {}}}&_detail {display: flex;p {font-size: 12px;color: #7F7F7F;margin-right: 10px;}}}.temp-table {margin-top: 5px;&-top {padding: 10px 0 10px 10px;border: 1px solid #ccc;.btn_new_group {background: #0EAFC0;border-color: #0EAFC0;padding: 6px;font-size: 12px;border-radius: 0;margin: 0;}.btn_new_group:hover {background: #0adcf3;border-color: #0adcf3;}}&-bottom {.data_table {width: 100%;}.temp_count {background-color: #F2F2F2;p {margin: 0;height: 45px;line-height: 45px;padding-right: 10px;font-family: 'Arial Normal', 'Arial';font-weight: 400;font-style: normal;font-size: 13px;letter-spacing: normal;color: #333333;text-align: let;}}.tags {display: flex;justify-content: space-between;.tag_api {color: #348FE4;border: 1px solid #348FE4;padding: 0 10px;font-size: 12px;}.tag_post {background-color: #0eafc0;padding: 0 10px;color: #fff;font-size: 12px;}.tag_js {border: 1px solid #BB741A;padding: 0 10px;color: #BB741A;font-size: 12px;}.tag_db {border: 1px solid #333333;padding: 0 10px;color: #333333;font-size: 12px;}}}}}}}::v-deep .el-dropdown-menu__item {padding: 8px 16px 8px 16px;font-family: 'Microsoft YaHei Regular', 'Microsoft YaHei';font-weight: 400;font-style: normal;font-size: 12px;color: rgba(85, 85, 85, 0.647058823529412);border-bottom: 1px dashed #ccc;}::v-deep .el-dialog {width: 440px;.dialog-footer {display: flex;justify-content: flex-end;margin: 16px 5px 16px 0;.btn1,.btn2 {box-shadow: 5px 5px 5px rgba(0, 0, 0, .35);border-radius: 5px;font-family: '微软雅黑';}.btn1 {color: #AAA;background-color: #f2f2f2;}.btn2 {background-color: #0EAFC0;}}.el-dialog__header {background: #FCFCFC;border-bottom: 1px solid #d7d7d7;padding: 0;padding: 10px;}.el-dialog__body {padding: 0!important;padding-left: 70px!important;padding-top: 20px!important;}}.title {font-size: 15px;color: #333;font-weight: bold;}
</style>

 

http://www.ngui.cc/el/3419224.html

相关文章

es6的一些点--持续更新

1.https://segmentfault.com/a/1190000020182715?utm_sourcetag-newest 讲了 ?和?? 这里我先说下,?最开始的作用是三目运算符,es6,又可以 obj.row?.id ,这样就算obj是空对象,没有row属性,也不会有cannot read ... undefined的错误,直接写,如果obj是空对象,没有row属性,…

一些问题,专门用来提问的博客

1.想问问vue2.0创建项目的时候,这个地方怎么切换,完全切不动,只能默认第一个 解决了 在vscode打开命令行就行了 2.作用域插槽&#xff0c;我之前自己试的demo&#xff0c;好像弄不出来带名字的作用域插槽&#xff0c;只能弄没名字的 今天看到一个 datatable组件里这么封装el…

computed中不能写异步逻辑也就是不能发请求,如何解决

其实不好解决,哈哈 不过仔细想想有以下几种解决方案 1.computed中的数据只要变化,computed值就会动态计算,所以你只要在交互之处,比如input,点击事件等操作中,发请求改得到结果赋值给相应的影响computed的data值,就可以让computed变化了, 2.可以用vuex,state,mutation,actio…

关于vue中,使用element或者其他情况发生的,更改了数据,但是视图不更新,几种处理方法

1.this.$set() 2.this.$forceUpdate() 3.如果前两种没用&#xff0c;那就用最后的方法&#xff0c;让数据地址改变一下&#xff0c;拿数组来说&#xff0c;可以通过&#xff0c;this.arr this.arr.concat(),这样数组地址更新了&#xff0c; 看看这篇博客 https://blog.csdn…

关于element的一些问题和解决--持续更新

1.问题描述&#xff0c;就是我一个下拉项串行并行切换的时候&#xff0c;同时也会切换form的rules&#xff0c;而切换之后&#xff0c;会自动触发一次validate的校验&#xff0c;导致页面没提交&#xff0c;只是切换 下拉&#xff0c;却出现了校验一次的情况&#xff0c;rules…

子元素盖住父元素边框

代码 .tab {&_t {display: flex;border-bottom: 1px solid #d7d7d7;padding-left: 20px;li {width: 66px;display: inline-block;float: left;height: 30px;border: 1px solid #d7d7d7;border-bottom: 1px solid transparent;margin-bottom: -1px;background: #fff;margin-…

一个数组删除数组内另外一个数组存在的元素

let choosedArr data.concat()let dataIdList data.map(el>el.id)let noChoosedArr self.tableData.filter(function (item) {return dataIdList.indexOf(item.id) < 0;})console.log(choosedArr, dataIdList,noChoosedArr) 可以看看https://blog.csdn.net/onlylele/a…

数组的处理方法,感觉需要总结一下了,最近用到的特别多,各种数组之间的处理--持续更新吧

为了案例的综合性,尽量采用对象数组的方式,以及对象数组混合普通数组 1数组去重 // 数组去重,去重指的是重复的只留下一个,而不是全删了 // 这里以数组对象与普通对象为例子 // 并且是以单个数组去重为例子 // 多个数组去重没有必要赘述 // 因为多个数组去重,也不过是数组conc…

一些点的记录2---持续更新

1.vue中scss的安装 cnpm i node-sass4.14.1 -D cnpm i sass-loader7.3.1 -D 在webpack.base.conf.js中的module的rules 加上 { test: /\.scss$/, loader: sass-loader!style-loader!css-loader, }, 2.如果echarts运行的时候由于v-if的切换造成重新渲染, <div v-if&quo…

computed能作为组件传值传递吗

点击后 可以 你想想,你父组件有个computed被一些数据影响着值,然后你computed传给了子组件 那子组件是不是也动态变了,是不是很爽 还有vuex的getters其实就是计算属性 看看这篇博客 https://www.jb51.net/article/159727.htm 使用场景,打个比方 若是对数据进行处理输出&a…