Transfer
https://www.bookstack.cn/read/vue.ant.design-v1.3.8/e5baab041f53bdf6.md#%E5%B8%A6%E6%90%9C%E7%B4%A2%E6%A1%86
一、transfer组件
targetKeys使用v-model的话,无法实现穿梭
<template>
<a-transfer
:dataSource="mockData"
showSearch
:filterOption="filterOption"
:targetKeys="targetKeys"
@change="handleChange"
@search="handleSearch"
:render="item=>item.title"
>
</a-transfer>
</template>
<script>
export default {
data () {
return {
mockData: [],
targetKeys: [],
}
},
mounted() {
this.getMock()
},
methods: {
getMock() {
const targetKeys = [];
const mockData = [];
for (let i = 0; i < 20; i++) {
const data = {
key: i.toString(),
title: `content${i + 1}`,
description: `description of content${i + 1}`,
chosen: Math.random() * 2 > 1,
};
if (data.chosen) {
targetKeys.push(data.key);
}
mockData.push(data);
}
this.mockData = mockData
this.targetKeys = targetKeys
},
filterOption(inputValue, option) {
return option.description.indexOf(inputValue) > -1;
},
handleChange(targetKeys, direction, moveKeys) {
console.log(targetKeys, direction, moveKeys);
this.targetKeys = targetKeys
},
handleSearch (dir, value) {
console.log('search:', dir, value);
},
},
}
</script>
二、完整代码
<template>
<div>
<a-card :bordered="false">
<div style="display: flex; flex-wrap: wrap">
<head-info title="我的待办" content="8个任务" :bordered="true"/>
<head-info title="本周任务平均处理时间" content="32分钟" :bordered="true"/>
<head-info title="本周完成任务数" content="24个"/>
</div>
</a-card>
<br />
<a-form
:model="formState"
name="horizontal_login"
layout="inline"
autocomplete="off"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<a-form-item
label="任务id"
name="task_id"
:rules="[{ required: true, message: 'Please input your task_id!' }]"
>
<a-input v-model="formState.task_id" />
</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 :style="{ marginLeft: '8px' }" @click="onSearchData">查询</a-button>
<a-button :style="{ marginLeft: '8px' }" @click="reset">重置</a-button>
<a-button :style="{ marginLeft: '8px' }" @click="showModal">+任务</a-button>
<a-popconfirm
title="task执行后不可修改,你确定要执行吗?"
ok-text="确定"
cancel-text="取消"
@confirm="confirm"
@cancel="cancel"
>
<a-button :style="{ marginLeft: '8px' }" :disabled="!hasSelected" @click="start">批量执行</a-button>
<a-button :style="{ marginLeft: '8px' }" :disabled="!hasSelected" @click="start">关联需求</a-button>
</a-popconfirm>
</a-form-item>
<a-modal v-model="visible" width="500px" title="添加用例" @ok="handleOk">
<p>Some contents...</p>
</a-modal>
</a-form>
<a-table :columns="columns" :data-source="data" class="components-table-demo-nested" @change="onChanged" rowKey="id"
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }">
<template slot="operation" slot-scope="record">
<a-tooltip title="详情" >
<a-icon style="color: #0080ff;margin:0 4px" type="eye" @click="onDetail(record)" />
</a-tooltip>
<a-drawer :title="myTitle" :visible="isEnable" :mask=false @close="onClose" width="520" :bodyStyle="{ marginBottom: '30px' }">
<a-form :model="contents" :rules="rules" layout="vertical">
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="任务ID" name="id">
<a-input v-model="contents.id" :disabled="disabled" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="任务名称" name="task_name">
<a-input v-model="contents.task_name" style="width: 100%" :disabled="disabled" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="关联需求" name="demand">
<a-input v-model="contents.demand" style="width: 100%" :disabled="disabled" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="状态" name="status">
<a-input v-model="contents.status" style="width: 100%" :disabled="disabled" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="创建人" name="creator">
<a-input v-model="contents.creator" style="width: 100%" :disabled="disabled" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="创建时间" name="createdAt">
<a-input v-model="contents.createdAt" style="width: 100%" :disabled="disabled" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="更新时间" name="updatedAt">
<a-input v-model="contents.updatedAt" style="width: 100%" :disabled="disabled" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="关联case数" name="bind_case">
<a-transfer
:dataSource="mockData"
showSearch
v-if="flag"
:filterOption="filterOption"
:targetKeys="targetKeys"
@change="handleChange"
@search="handleSearch"
:render="item=>item.title"
/>
<a-input v-model="contents.bind_case" v-if="!flag" style="width: 100%" :disabled="disabled" />
</a-form-item>
</a-col>
</a-row>
</a-form>
<template #extra>
<a-button style="margin-right: 8px" @click="onClose">Cancel</a-button>
<a-button type="primary" @click="onClose">Submit</a-button>
</template>
</a-drawer>
<a-tooltip title="编辑" >
<a-icon style="color: #0080ff;margin:0 4px" type="form" @click="onEdit(record)" />
</a-tooltip>
<!-- 运行 -->
<a-tooltip title="运行">
<a-icon type="play-circle" style="color: #0080ff;margin:0 4px" @click="onRun(record)"/>
<a-modal v-model="isshow" title="Task运行进度:" @ok="handleOk">
<a-form
:model="env"
name="basic"
autocomplete="off"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<a-form-item
label="运行环境"
name="env"
:rules="[{ required: true, message: 'Please input env!' }]"
>
<a-input v-model="env" />
</a-form-item>
</a-form>
</a-modal>
</a-tooltip>
<a-tooltip title="删除" >
<a-popconfirm
title="Are you sure delete this task?"
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>
<template #expandedRowRender>
<a-table :columns="innerColumns" :data-source="innerData" :pagination="false">
<template slot="operation" slot-scope="record">
<a-tooltip title="详情" >
<a-icon style="color: #0080ff;margin:0 4px" type="eye" @click="onDetail(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>
</template>
</a-table>
</div>
</template>
<script>
import HeadInfo from "@/components/tool/HeadInfo";
import { get_task_detail, get_case_list, get_task_list} from "@/services/build_history";
import {getUserList} from "@/services/postman";
import {message} from "ant-design-vue";
const columns = [
{ title: '任务ID', dataIndex: 'id', key: 'id' },
{ title: '任务名称', dataIndex: 'task_name', key: 'task_name' },
{ title: '关联case数', dataIndex: 'bind_case', key: 'bind_case',
sorter: (a, b) => a.bind_case - b.bind_case,
defaultSortOrder: 'descend',
sortDirections: ['ascend', 'descend'] },
{ title: '关联需求', dataIndex: 'demand', key: 'demand'},
{ title: '状态', dataIndex: 'status', key: 'status' },
{ title: '创建人', dataIndex: 'creator', key: 'creator' },
{ title: '创建时间', dataIndex: 'createdAt', key: 'createdAt',
sorter: (a, b) => Date.parse(a.createdAt) - Date.parse(b.createdAt),
defaultSortOrder: 'descend',
sortDirections: ['ascend', 'descend'] },
{ title: '更新时间', dataIndex: 'updatedAt', key: 'updatedAt',
sorter: (a, b) => Date.parse(a.updatedAt) - Date.parse(b.updatedAt),
defaultSortOrder: 'descend',
sortDirections: ['ascend', 'descend'] },
{ title: '操作', key: 'operation',
scopedSlots: { customRender: 'operation' }, //值跟dataIndex对应,支持操作列插槽
width: 120,
},
];
const rules = {
id: [{ required: true, message: 'Please enter id' }],
task_name: [{ required: true, message: 'please enter task_name' }],
bind_case: [{ required: true, message: 'Please select bind_case' }],
demand: [{ required: true, message: 'Please choose the demand' }],
status: [{ required: true, message: 'Please choose the status' }],
creator: [{ required: true, message: 'Please choose the creator', type: 'object' }],
createdAt: [{ required: true, message: 'Please enter createdAt' }],
updatedAt: [{ required: true, message: 'Please enter updatedAt' }],
};
const innerColumns = [
{
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,
},
{
title: '操作',
dataIndex: 'operation',
resizable: true,
scopedSlots: { customRender: 'operation' }, //值跟dataIndex对应,支持操作列插槽
width: 50,
},
];
export default {
name: "Task",
data() {
return {
case_list: "http://localhost:7777/auth/case_list",
task_list:"http://localhost:7777/auth/task_list",
task_detail:"http://localhost:7777/auth/task_detail",
data:[],
columns,
innerColumns,
innerData:[],
formState: {
creator: '',
task_id: '',
},
form: this.$form.createForm(this, {name: 'task'}),
UserArr:[],
UserType:'',
visible:false,
hasSelected:false,
selectedRowKeys:[],
contents:"",
isEnable:false,
myTitle:"Task详情页",
disabled:false,
flag:false,
rules: rules,
//Transfer
mockData: [],
targetKeys: [],
isshow:false,
}
},
components: {HeadInfo},
methods: {
onSearchData() {
this.loading = true;
const task_id = this.formState.task_id
const creator = this.UserType
console.log(task_id,creator);
get_task_list(this.task_list, {task_id, creator})
.then((result) => {
this.loading = false;
this.data = result.data.data; //跟后端接口response对齐
})
.catch((err) => {
this.data = err;
});
},
onSelectChange(selectedRowKeys){
this.selectedRowKeys = selectedRowKeys;
if (this.selectedRowKeys.length > 0) {
this.hasSelected = true
}
},
onChanged(pagination, filters, sorter) {
console.log('params', pagination, filters, sorter);
},
reset() {
this.UserType = '' //清空casename
this.formState = [] //清空创建人
this.data = [] //清空搜索结果
},
start(){
this.loading = true;
// ajax request after empty completing
setTimeout(() => {
this.loading = false;
this.selectedRowKeys = [];
}, 1000);
},
cancel(e) {
console.log(e);
message.error('Click on No');
},
confirm(e) {
console.log(e);
message.error('Click on Yes');
},
onFinish(val) {
console.log('endValue', val);
},
onFinishFailed(val) {
console.log('endValue', val);
},
showModal(){ //点击添加用例后展示弹窗
this.visible = true;
},
handleOk(e){
console.log(e);
this.visible = false;
},
onEdit(record) {
this.isEnable = true //控制展示 Drawer抽屉
this.disabled = false
this.flag = true //控制编辑页 展示Transfer 穿梭框
this.myTitle = "更新Task"
console.log('task edit:', this.flag);
get_task_detail(this.task_detail, {record})
.then((result) => {
this.loading = false;
this.contents = result.data.data; //跟后端接口response对齐
console.log('onedit ', this.contents);
})
.catch((err) => {
this.contents = err;
});
},
onDelete(e) {
console.log(e);
},
onRun(record) {
this.isEnable = false //控制不展示 Drawer抽屉
this.flag = false //控制不展示 Transfer穿梭框
this.isshow= true
this.myTitle = "Task运行"
console.log('task run:', record); //TODO 调用后端运行接口
},
onDetail(record) {
this.isEnable = true //控制展示 Drawer抽屉
this.disabled = true //控制详情页输入框 不可编辑
this.flag = false //控制详情页 不展示Transfer穿梭框
this.myTitle = "Task详情页"
console.log('task detai:', this.flag);
get_task_detail(this.task_detail, {record})
.then((result) => {
this.loading = false;
this.contents = result.data.data; //跟后端接口response对齐
console.log('ondetail ', this.contents);
})
.catch((err) => {
this.contents = err;
});
},
onClose () {
this.isEnable = false;
},
filterOption (inputValue, option) {
return option.description.indexOf(inputValue) > -1;
},
handleChange (keys, direction, moveKeys) {
console.log(keys, direction, moveKeys);
this.targetKeys = keys
},
handleSearch (dir, value) {
console.log('search:', dir, value);
},
getMock () {
const keys = [];
const mData = [];
for (let i = 0; i < 20; i++) {
const data = {
key: i.toString(),
title: `content${i + 1}`,
description: `description of content${i + 1}`,
chosen: Math.random() * 2 > 1,
};
if (data.chosen) {
keys.push(data.key);
}
mData.push(data);
}
this.mockData = mData;
this.targetKeys = keys;
},
},
mounted() {
this.getMock() //TODO 改成后端获取真实数据
const task_id = 1 //TODO 任务id
getUserList().then(res => {
this.UserArr = res.data.data //跟后端接口response对齐
console.log('搜索条件',this.UserArr);
}),
get_case_list(this.case_list,{task_id}).then(res => {
this.loading = false;
this.innerData = res.data.data //跟后端接口response对齐
if( this.innerData.length > 0) {
console.log('res:--- ', this.innerData);
}
})
},
}
</script>
<style scoped>
</style>
三、效果
详情页:
编辑页:
运行: