antd-tree增删改拖拽操作(五)
一、src/pages/postman/CI.vue文件<template><div ><a-row :gutter="1"><a-col :span="6"><a-card style="width: 100%"><a-input-search v-model="searchValue" style="margin-bottom:
·
Case页面
一、新建文件src/pages/postman/Case.vue文件
<template>
<div>
<a-card class="container">
<div class="tree-container">
<div class="left">
<div class="left-input">
<a-input placeholder="请输入" style="width: 100px;margin-right: 2px"/>
<a-button @click="newlyAdded" class="add">新增</a-button>
</div>
<!-- 树形组织架构树 -->
<a-tree
class="draggable-tree"
draggable
block-node
:defaultExpandAll="false"
:tree-data="treeData"
@dragenter="onDragEnter"
@drop="onDrop"
>
<template slot="custom" slot-scope="item">
<div>
<!-- 名称 -->
<span class="node-title">{{ item.title }} </span>
<span style="margin-left: 20px">
<!-- 新增 -->
<span style="margin-right: 10px;">
<a-icon type="plus-circle" style="color: #0080ff;margin:0 4px" @click="subordinateItem(item)"/>
</span>
<!-- 编辑 -->
<span style="margin-right: 10px;">
<a-icon type="edit" style="color: #0080ff;margin:0 4px" @click="modifyItem(item)"/>
</span>
<!-- 删除 -->
<span style="margin-right: 10px;">
<a-popconfirm title="是否要删除此行?" @confirm="deleteItem(item)">
<a-icon type="delete" style="color: #0080ff;margin:0 4px" />
</a-popconfirm>
</span>
</span>
</div>
</template>
</a-tree>
</div>
<div class="right">
<a-form
:model="formState"
name="horizontal_login"
layout="inline"
autocomplete="off"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<a-form-item
label="用例名称"
name="case_name"
:rules="[{ required: true, message: 'Please input your case_name!' }]"
>
<a-input v-model="formState.casename" />
</a-form-item>
<a-form-item
label="创建人"
name="creator"
:rules="[{ required: true, message: 'Please input your creator!' }]"
>
<a-select v-model="UserType" style="width:150px" allowClear>
<a-select-option :value="item.id" v-for="(item, i) in UserArr" :key="i">
{{ item.name }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
<a-button type="primary" html-type="submit" @click="onSelectData" >查询</a-button>
<a-button :style="{ marginLeft: '8px' }" @click="reset">重置</a-button>
</a-form-item>
</a-form>
<div>
<a-button type="primary" @click="showModal">+用例</a-button>
<a-button type="primary" :disabled="!hasSelected" :loading="loading" @click="showModal">+Task</a-button>
<a-button type="primary" :disabled="!hasSelected" :loading="loading" @click="start">批量删除</a-button>
<a-button type="primary" :disabled="!hasSelected" :loading="loading" @click="start">批量移动用例</a-button>
<span >
<template v-if="hasSelected">
{{ `Selected ${selectedRowKeys.length} items` }}
</template>
</span>
<a-upload
v-model="fileList"
name="file"
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
@change="handleChange"
>
<a-button type="primary" >Xmind导入用例</a-button>
</a-upload>
<a-modal v-model="visible" width="500px" title="添加用例" @ok="handleOk">
<a-form-item label="目录名称" v-bind="project_name">
<a-input v-model="project_name" />
</a-form-item>
</a-modal>
</div>
<!-- 表格开始 -->
<a-table
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
:columns="columns"
:data-source="data"
:rowKey='record=>record.id'
@change="onChange" >
<template slot="operation" slot-scope="record">
<a-tooltip title="详情" >
<a-icon type="eye" style="color: #0080ff;margin:0 4px" @click="onDetail(record)" />
</a-tooltip>
<a-tooltip title="编辑" >
<a-icon style="color: #0080ff;margin:0 4px" type="form" @click="onEdit(record)" />
</a-tooltip>
<a-tooltip title="删除" >
<a-popconfirm
title="Are you sure delete this case?"
ok-text="Yes"
cancel-text="No"
@confirm="confirm"
@cancel="cancel"
>
<a-icon style="color: #0080ff;margin:0 4px" type="delete" @click="onDelete(record)"/>
</a-popconfirm>
</a-tooltip>
</template>
</a-table>
<!-- 表格结束 -->
</div>
</div>
</a-card>
<!-- 弹窗开始 -->
<tree-lower-modules ref="treeLowerModules" @ok="onOk"/>
</div>
</template>
<script>
import { get_case_list } from "@/services/build_history";
import {message} from "ant-design-vue";
import {getDirList, getUserList} from "@/services/postman";
import treeLowerModules from '../../components/treeModules'
const columns = [
{
title : '用例名称',
dataIndex: 'case_name',
resizable: true, //设置 resizable 开启拖动列
width: 20,
},
{
title : '优先级',
dataIndex: 'priority',
resizable: true,
width: 20,
},
{
title: '用例状态',
dataIndex: 'status',
resizable: true,
width: 20,
},
{
title: '创建人',
dataIndex: 'creator',
resizable: true,
width: 20,
},
{
title: '更新时间',
dataIndex: 'update_time',
resizable: true,
width: 50,
//通过指定列的 sorter 函数即可启动排序按钮。sorter: function(rowA, rowB) { ... }, rowA、rowB 为比较的两个行数据
sorter: (a, b) => Date.parse(a.update_time) - Date.parse(b.update_time),
defaultSortOrder: 'descend',
sortDirections: ['ascend', 'descend'], //sortDirections: ['ascend' | 'descend']改变每列可用的排序方式
},
{
title: '操作',
dataIndex: 'operation',
resizable: true,
scopedSlots: { customRender: 'operation' }, //值跟dataIndex对应,支持操作列插槽
width: 50,
},
];
export default {
name: 'OrganizateTree',
components: { treeLowerModules },
data () {
return {
case_list:"http://localhost:7777/auth/case_list",
// 组织树数据
treeData: [],
selectKeys: [],
// 表格数据
data:[],
// 表格行
columns: columns,
//form
formState: {
creator: '',
case_name: '',
},
UserArr:[],
UserType:'',
fileList:[],
form: this.$form.createForm(this, {name: 'case'}),
//table
hasSelected:false,
loading: false,
visible: false,
selectedRowKeys:[],
project_name:"",
}
},
methods: {
onSelectData() {
this.loading = true;
const case_name = this.formState.case_name
const creator = this.UserType
console.log(case_name,creator);
get_case_list(this.case_list, {case_name, creator})
.then((result) => {
this.loading = false;
this.data = result.data.data; //跟后端接口response对齐
})
.catch((err) => {
this.data = err;
});
},
reset() {
this.UserType = '' //清空casename
this.formState = [] //清空创建人
this.data = [] //清空搜索结果
this.hasSelected = false //清空选择条数
},
showModal(){ //点击添加用例后展示弹窗
this.visible = true;
},
handleOk(e){
console.log(e);
this.visible = false;
},
onSelectChange(selectedRowKeys){
this.selectedRowKeys = selectedRowKeys;
if (this.selectedRowKeys.length > 0) {
this.hasSelected = true
}
},
onChange(pagination, filters, sorter) {
console.log('params', pagination, filters, sorter);
},
start(){
this.loading = true;
// ajax request after empty completing
setTimeout(() => {
this.loading = false;
this.selectedRowKeys = [];
}, 1000);
},
handleChange(info) {
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
message.success(`${info.file.name} file uploaded successfully`);
} else if (info.file.status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
},
onFinish(val) {
console.log('endValue', val);
},
onFinishFailed(val) {
console.log('endValue', val);
},
confirm() {},
cancel() {},
// 递归每一项都加 scopedSlots: { title: 'custom' }
handleData (tree) {
for (const item of tree) {
item['scopedSlots'] = { title: 'custom' }
if (item.children && item.children.length) {
this.handleData(item.children)
}
}
},
/**
* 组织树新增按钮
*/
newlyAdded () {
const item = { key: this.treeData[0].key, operation: 1 }
this.$refs.treeLowerModules.add(item)
},
/**
* 添加下級
* @param item
*/
subordinateItem (item) {
item.operation = 2
this.$refs.treeLowerModules.add(item)
},
/**
* 修改
* @param item
*/
modifyItem (item) {
this.$refs.treeLowerModules.edit(item)
},
/**
* 確定按鈕
* @param val
*/
onOk (val) {
// 1:一级新增, 3:编辑, 2:二级新增
if (val.operation === 1) {
this.selectKeys = [val.key]
this.dataDriveAddSame(val.title)
} else if (val.operation === 2) {
this.selectKeys = [val.key]
this.dataDriveAddSub(val.title)
} else if (val.operation === 3) {
this.selectKeys = [val.key]
this.dataDriveModify(val.title)
}
},
/**
* 公共父级修改方法
* @param childs: 组织树数据
* @param findKey 目标key
*/
getTreeDataByKey (childs = [], findKey) {
let finditem = null
for (let i = 0, len = childs.length; i < len; i++) {
const item = childs[i]
if (item.key !== findKey && item.children && item.children.length > 0) {
finditem = this.getTreeDataByKey(item.children, findKey)
}
if (item.key === findKey) {
finditem = item
}
if (finditem != null) {
break
}
}
return finditem
},
/**
* 公共父级方法
* @param childs: 组织树数据
* @param findKey 目标key
*/
getTreeParentChilds (childs = [], findKey) {
let parentChilds = []
for (let i = 0, len = childs.length; i < len; i++) {
const item = childs[i]
if (item.key !== findKey && item.children && item.children.length > 0) {
parentChilds = this.getTreeParentChilds(item.children, findKey)
}
if (item.key === findKey) {
parentChilds = childs
}
if (parentChilds.length > 0) {
break
}
}
return parentChilds
},
/**
* 添加同级
* @param title
*/
dataDriveAddSame (title) {
const parentChilds = this.getTreeParentChilds(
this.treeData,
this.selectKeys[0]
)
// 校验 相同的不能不可以添加
const existence = parentChilds.find(item => { return item.key === title })
if (!existence) {
parentChilds.push({
title: title,
key: new Date().getTime(),
scopedSlots: { title: 'custom' }
})
} else {
this.$message.success('此数据已存在')
return false
}
},
/**
* 添加下级
* @param title
*/
dataDriveAddSub (title) {
const selectItem = this.getTreeDataByKey(this.treeData, this.selectKeys[0])
if (!selectItem.children) {
this.$set(selectItem, 'children', [])
}
// 校验 相同的不能不可以添加
const existence = selectItem.children.find(item => { return item.title === title })
if (!existence) {
selectItem.children.push({
title: title,
key: new Date().getTime(),
scopedSlots: { title: 'custom' }
})
} else {
this.$message.success('此数据已存在')
return false
}
this.$forceUpdate()
},
/**
* 一级修改
* @param title
*/
dataDriveModify (title) {
const selectItem = this.getTreeDataByKey(this.treeData, this.selectKeys[0])
selectItem.title = title
},
/**
* 刪除
* @param item
*/
deleteItem (item) {
this.selectKeys = [item.key]
this.dataDriveDelete()
},
/**
* 删除方法
*/
dataDriveDelete () {
const parentChilds = this.getTreeParentChilds(
this.treeData,
this.selectKeys[0]
)
// 对删除的数据若下面有子级就给与提示不允许删除
parentChilds.map(item => {
console.log(item.children, '000')
return item.children
})
// console.log(noeDel, 'shanchu')
const delIndex = parentChilds.findIndex(
(item) => item.key === this.selectKeys[0]
)
parentChilds.splice(delIndex, 1)
},
/**
* 拖拽
* @param info
*/
onDragEnter (info) {
console.log(info, '12222')
// expandedKeys 需要受控时设置
// this.expandedKeys = info.expandedKeys
},
/**
* 拖拽
* @param info
*/
onDrop (info) {
console.log(info)
const dropKey = info.node.eventKey
const dragKey = info.dragNode.eventKey
const dropPos = info.node.pos.split('-')
const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1])
const loop = (data, key, callback) => {
data.forEach((item, index, arr) => {
if (item.key === key) {
return callback(item, index, arr)
}
if (item.children) {
return loop(item.children, key, callback)
}
})
}
const data = [...this.treeData]
// Find dragObject
let dragObj
loop(data, dragKey, (item, index, arr) => {
arr.splice(index, 1)
dragObj = item
})
if (!info.dropToGap) {
// Drop on the content
loop(data, dropKey, item => {
item.children = item.children || []
// where to insert 示例添加到尾部,可以是随意位置
item.children.push(dragObj)
})
} else if (
(info.node.children || []).length > 0 && // Has children
info.node.expanded && // Is expanded
dropPosition === 1 // On the bottom gap
) {
loop(data, dropKey, item => {
item.children = item.children || []
// where to insert 示例添加到尾部,可以是随意位置
item.children.unshift(dragObj)
})
} else {
let ar
let i
loop(data, dropKey, (item, index, arr) => {
ar = arr
i = index
})
if (dropPosition === -1) {
ar.splice(i, 0, dragObj)
} else {
ar.splice(i + 1, 0, dragObj)
}
}
this.treeData = data
},
},
mounted() {
const dir_id = 1 //TODO 应该传入左侧的dir_id
getUserList().then(res => {
this.UserArr = res.data.data //跟后端接口response对齐
console.log('搜索条件',this.UserArr);
}),
getDirList({dir_id}).then(res => {
this.loading = false;
this.treeData = res.data.data //跟后端接口response对齐
if(this.treeData !==undefined && this.treeData != null && this.treeData.length > 0) {
console.log('getDirList: ', this.treeData);
// 每一项都加 scopedSlots: { title: 'custom' }
this.handleData(this.treeData)
}
})
},
watch: {
// 监听 searchValue 属性的数据变化,只要 searchValue 的值发生变化,这个方法就会被调用
searchValue(value) {
console.log('watch :',value);
}
},
}
</script>
<style scoped lang="less">
.node-title {
padding-right: 15px;
}
.add {
background: #1890ff;
color: #FFFFFF;
}
.tree-container {
display: flex;
flex-direction: row;
.left-input{
display: flex;
flex-direction: row;
margin-bottom: 2px;
}
.right{
margin-left: 0px;
width: 100%;
&-btn{
float: right;
margin-bottom: 10px;
}
}
}
</style>
二、新建文件src/components/treeModules.vue文件
<template>
<a-modal :title="title" v-model="visible" :confirmLoading="confirmLoading" @ok="handleSubmit">
<a-form :form="form">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="名称">
<a-input :disabled="disabled" placeholder="请输入名称" v-decorator="['title', {rules: [{ required: true, message: '请输入名称' }]}]" />
</a-form-item>
</a-form>
</a-modal>
</template>
<script>
import pick from 'lodash/pick'
export default {
address: 'TreeModules',
props: {},
components: {},
data () {
return {
visible: false,
confirmLoading: false,
form: this.$form.createForm(this),
labelCol: {
xs: { span: 24 },
sm: { span: 5 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 }
},
title: '',
mdl: {},
disabled: false,
operation: 1,
record: {}
}
},
beforeCreate () {},
created () {},
computed: {},
methods: {
/**
* 新增彈窗
* @param record
*/
add (record) {
this.operation = record.operation
if (record.operation === 1) {
this.title = '新增'
} else {
this.title = '添加下级'
}
this.form.resetFields()
this.visible = true
this.disabled = false
this.record = record
},
/**
* 編輯彈窗
* @param record
*/
edit (record) {
this.record = record
this.operation = 3
this.disabled = false
this.title = '编辑名称'
this.mdl = Object.assign({}, record)
this.visible = true
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.mdl,
'title'
))
})
},
/**
* 保存
* @param e
*/
handleSubmit (e) {
e.preventDefault()
this.form.validateFields((err, values) => {
if (!err) {
values.operation = this.operation
values.key = this.record.key
this.confirmLoading = true
this.$emit('ok', values)
this.form.resetFields()
this.$message.success('保存成功')
this.visible = false
this.confirmLoading = false
}
})
}
}
}
</script>
<style scoped lang="less">
</style>
三、在src/pages/postman/index.js添加如下内容
export async function get_case_list(url, params) {
return request(url, METHOD.GET, params)
}
export async function getDirList(params) {
return request(CASEDIR, METHOD.GET,params)
}
四、效果
五、待解决问题
1、左右两卡片联动(其实传dir_id给case接口即可)
2、可加tree的查询操作
更多推荐
已为社区贡献1条内容
所有评论(0)