都挺好,但是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 = 0
formLabelWidth: 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 = this
const 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 = 0
formLabelWidth: 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 = this
const 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 = 0
stepStartFormVisible: 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 = 1
eventList: 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 = false
expectToolShow: boolean = false
isComParamShow: 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 any
console.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 any
console.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 any
this.$set(self.tableData[index], 'isEventParamShow', false)
}
handleParamFocus(index) {
const self = this as any
self.$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 any
this.$set(self.tableData[index], 'isPositionParamShow', false)
}
handleParamFocus1(index) {
const self = this as any
self.$set(self.tableData[index], 'isPositionParamShow', true)
self.tableData = self.tableData.concat()
}
getWebModuleSingle() {
const self = this as any
ApiWebComponentService.getWebModuleSingle({ id: self.stepTempInfo.row.id }).then(res => {
if (res.code == 200) {
if (res.data.id) {
// 这里做一个判断,如果有至少一条数据,那么tabledata中就不放
// 默认的一条空数据,否则默认放一条空数据
// 还有一个操作,就是查询的时候要给,每个数据两个属性,用于控制,参数选项是显示还是隐藏,
// 对应的,保存的时候,也要删除这两个属性
self.total = res.data.total
self.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 = false
el.isEventParamShow = false
return 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 any
this.$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 any
const 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 = inputType
console.log('diaType', diaType)
if (diaType == 'paramconstr') {
this.isParamConstrShow['flag'] = true
} else if (diaType == 'comParam') {
this.isComParamShow['flag'] = true
}
}
handleStepAdd() {
const self = this as any
self.tableData.push({
name: '',
event: '',
locationProperty: '',
eventParam: '',
latencyTime: '',
alertRule: '',
expect: '',
var: '',
isPositionParamShow: false,
isEventParamShow: false
})
}
handleStepDelete(index) {
const self = this as any
self.tableData.splice(index, 1)
}
// 点击保存,保存步骤
handleSaveStep() {
// 有个校验,如果只有一条全空的数据
// 那么也请求接口,但是啥也不传
// 点击保存的时候,全为空的数据不传
// 传过去的时候,记得吧query里的那两个属性删除
const self = this as any
let query = self.tableData.map(el => {
delete el.isPositionParamShow
delete el.isEventParamShow
return el
})
.filter(el => {
let flag = false
for (const key in el) {
// 存在一个不为空的,就可以发送这条数据
if (el[key] != '') {
flag = true
break;
}
}
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 = 1
copyStepForm: object = {
stepName: "",
}
copyStepFormRules: object = {
stepName: [{ required: true, message: '请填写步骤名称', trigger: 'change' }],
}
copyStepVisible: boolean = false
stempConfig: 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 any
if (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 any
this.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 any
self[funName](data)
}
// 执行步骤
executeStep(data) {
const self = this as any
self.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.id
self.reqConfig.saveReq.data.id = data.row.id
console.log('self.reqConfig', self.reqConfig)
this.operateType = 2
this.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 any
this.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 any
self.apiSteps = []
let query = { apiSteps: [] }
let obj = {
versionId: 1,//版本id
type: 'template',//请求类型,template和sciprt,api模板中是template,其他地方都是script
stepType: '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,//当前在表格中的id
requestType: 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 = false
self.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 any
let 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 = false
this.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 = 1
this.isScriptShow = true
}
}
// 批量删除步骤
handleCommandBatch() {
const self = this as any
let arr = self.tableData.map(el => el.id)
// let arr = [data.row.id]
self.handleDeleteStep(arr)
}
// 获取api模板基本信息,包含粗略步骤列表
getApiTemp() {
const self = this as any
ApiTempService.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 any
ApiTempService.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 any
let 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.executeCheck
self.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.lockedCheck
self.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
}
}),
]);
}
// 表格行添加 class
tableRowClassName({row, rowIndex}){
const self = this as any;
if(row.stepType=='js'){
return 'drag_table_disabled_drag_row';
}
return '';
}
// 行拖拽
rowDrop() {
const _this = this as any
const 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)return
if(_this.tableData[newIndex].stepType=='js') return
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
})
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>