Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev

This commit is contained in:
JiangQiming 2023-03-24 18:48:10 +08:00
commit 582ebddd21
11 changed files with 601 additions and 334 deletions

View File

@ -4,7 +4,7 @@ export const queryCollector = (data: any) =>
server.post(`/data-collect/collector/_query/no-paging?paging=false`, data); server.post(`/data-collect/collector/_query/no-paging?paging=false`, data);
export const queryChannelNoPaging = () => export const queryChannelNoPaging = () =>
server.post(`/data-collect/channel/_query/no-paging?paging=false`, {}); server.post(`/data-collect/channel/_query/no-paging`, { paging: false });
export const save = (data: any) => server.post(`/data-collect/collector`, data); export const save = (data: any) => server.post(`/data-collect/collector`, data);

View File

@ -57,8 +57,18 @@
v-model:value="formData.pointKey" v-model:value="formData.pointKey"
:min="0" :min="0"
:max="255" :max="255"
:precision="0"
/> />
</j-form-item> </j-form-item>
<p
style="color: #616161"
v-if="formData.configuration.function && formData.pointKey"
>
PLC地址:{{
InitAddress[formData.configuration.function] +
formData.pointKey
}}
</p>
<j-form-item <j-form-item
label="寄存器数量" label="寄存器数量"
:name="['configuration', 'parameter', 'quantity']" :name="['configuration', 'parameter', 'quantity']"
@ -70,6 +80,7 @@
v-model:value="formData.configuration.parameter.quantity" v-model:value="formData.configuration.parameter.quantity"
:min="1" :min="1"
:max="255" :max="255"
:precision="0"
@blur="changeQuantity" @blur="changeQuantity"
/> />
</j-form-item> </j-form-item>
@ -258,6 +269,12 @@ const collectorId = props.data.collectorId;
const provider = props.data.provider; const provider = props.data.provider;
const oldPointKey = props.data.pointKey; const oldPointKey = props.data.pointKey;
const InitAddress = {
Coils: 1,
HoldingRegisters: 40001,
InputRegisters: 30001,
};
const formData = ref({ const formData = ref({
name: '', name: '',
configuration: { configuration: {
@ -371,9 +388,20 @@ const getProviderList = async () => {
value: item.id, value: item.id,
label: item.name, label: item.name,
})); }));
setProviderList(formData.value.configuration.function);
}; };
getProviderList(); getProviderList();
const setProviderList = (value: string | undefined) => {
providerList.value =
value === 'HoldingRegisters'
? providerListAll.value
: providerListAll.value.filter(
(item: any) => item.value !== 'bool',
);
};
watch( watch(
() => formData.value.configuration.parameter.quantity, () => formData.value.configuration.parameter.quantity,
(value) => { (value) => {
@ -384,14 +412,9 @@ watch(
watch( watch(
() => formData.value.configuration.function, () => formData.value.configuration.function,
(value) => { (value) => {
providerList.value = setProviderList(value);
value === 'HoldingRegisters'
? providerListAll.value
: providerListAll.value.filter(
(item: any) => item.value !== 'bool',
);
}, },
{ deep: true }, { immediate: true, deep: true },
); );
watch( watch(
() => props.data, () => props.data,

View File

@ -77,7 +77,7 @@
v-model:value="formData.circuitBreaker.type" v-model:value="formData.circuitBreaker.type"
:options="[ :options="[
{ label: '降频', value: 'LowerFrequency' }, { label: '降频', value: 'LowerFrequency' },
{ label: '断', value: 'Break' }, { label: '', value: 'Break' },
{ label: '忽略', value: 'Ignore' }, { label: '忽略', value: 'Ignore' },
]" ]"
@change="changeCardSelectType" @change="changeCardSelectType"
@ -203,7 +203,7 @@ const getTypeTooltip = (value: string) =>
value === 'LowerFrequency' value === 'LowerFrequency'
? '连续20次异常降低连接频率至原有频率的1/10重试间隔不超过1分钟故障处理后自动恢复至设定连接频率' ? '连续20次异常降低连接频率至原有频率的1/10重试间隔不超过1分钟故障处理后自动恢复至设定连接频率'
: value === 'Break' : value === 'Break'
? '连续10分钟异常停止采集数据进入断状态,设备重新启用后恢复采集状态' ? '连续10分钟异常停止采集数据进入状态,设备重新启用后恢复采集状态'
: '忽略异常保持原采集频率超时时间为5s'; : '忽略异常保持原采集频率超时时间为5s';
const handleCancel = () => { const handleCancel = () => {

View File

@ -70,6 +70,7 @@ import { EventLevel, ExpandsTypeList } from '@/views/device/data';
import { useMetadataStore } from '@/store/metadata'; import { useMetadataStore } from '@/store/metadata';
import { validateJson } from './validator'; import { validateJson } from './validator';
import { Rule } from 'ant-design-vue/es/form'; import { Rule } from 'ant-design-vue/es/form';
import { debounce } from 'lodash';
const props = defineProps({ const props = defineProps({
type: { type: {
@ -98,7 +99,7 @@ if (props.modelType === 'events' || props.modelType === 'tags') {
const productStore = useProductStore() const productStore = useProductStore()
const config = ref<Record<any, any>[]>([]) const config = ref<Record<any, any>[]>([])
const asyncOtherConfig = async () => { const asyncOtherConfig = debounce(async () => {
if (props.type !== 'product') return if (props.type !== 'product') return
const { valueType, id } = props.value const { valueType, id } = props.value
const { type } = valueType || {} const { type } = valueType || {}
@ -115,7 +116,7 @@ const asyncOtherConfig = async () => {
if (resp.status === 200) { if (resp.status === 200) {
config.value = resp.result config.value = resp.result
} }
} }, 500)
onMounted(() => { onMounted(() => {
if (props.modelType === 'properties') { if (props.modelType === 'properties') {

View File

@ -113,8 +113,8 @@ export const Validator = {
regOnlyNumber: new RegExp(/^\d+$/), regOnlyNumber: new RegExp(/^\d+$/),
}; };
const validateAddress = (_rule: any, value: string): Promise<any> => const validateAddress = (_rule: any, value: string): Promise<any> => {
new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
if ( if (
Validator.regIpv4.test(value) || Validator.regIpv4.test(value) ||
Validator.regIPv6.test(value) || Validator.regIPv6.test(value) ||
@ -122,9 +122,10 @@ const validateAddress = (_rule: any, value: string): Promise<any> =>
) { ) {
return resolve(''); return resolve('');
} else { } else {
return reject('请输入正确的IP地址或者域名'); return value ? reject('请输入正确的IP地址或者域名') : resolve('');
} }
}); });
};
export const Rules = { export const Rules = {
name: [ name: [
@ -176,11 +177,6 @@ export const Rules = {
validator: validateAddress, validator: validateAddress,
message: '请输入正确的IP地址或者域名', message: '请输入正确的IP地址或者域名',
}, },
// {
// pattern:
// Validator.regIpv4 || Validator.regIPv6 || Validator.regDomain,
// message: '请输入正确格式的域名或ip',
// },
], ],
publicPort: [ publicPort: [
{ {

View File

@ -302,7 +302,7 @@ const validateAddress = (_rule: any, value: string): Promise<any> =>
) { ) {
return resolve(''); return resolve('');
} else { } else {
return reject('请输入正确的IP地址或者域名'); return value ? reject('请输入正确的IP地址或者域名') : resolve('');
} }
}); });

View File

@ -15,10 +15,12 @@
:treeData="builtInList" :treeData="builtInList"
placeholder="请选择参数" placeholder="请选择参数"
style="width: calc(100% - 120px)" style="width: calc(100% - 120px)"
:fieldNames="{ label: 'name', value: 'id' }"
@change="(val) => itemOnChange(undefined, val)"
> >
<template #title="{ name, description }"> <template #title="{ fullName, description }">
<j-space> <j-space>
{{ name }} {{ fullName }}
<span style="color: grey; margin-left: 5px">{{ description }}</span> <span style="color: grey; margin-left: 5px">{{ description }}</span>
</j-space> </j-space>
</template> </template>
@ -95,14 +97,15 @@ const sourceChange = (val: any) => {
emit('update:value', { emit('update:value', {
...props.value, ...props.value,
source: val, source: val,
value: undefined, value: undefined
}); });
}; };
const itemOnChange = (val: any) => { const itemOnChange = (val: any, _upperKey?: string) => {
emit('update:value', { emit('update:value', {
...props.value, ...props.value,
value: val, value: val,
upperKey: _upperKey
}); });
}; };

View File

@ -122,7 +122,8 @@ const table = reactive({
routeParams.id && routeParams.id &&
getMenuInfo_api(routeParams.id).then((resp: any) => { getMenuInfo_api(routeParams.id).then((resp: any) => {
menuInfo.value = resp.result; menuInfo.value = resp.result;
table.tableData = resp.result.buttons as tableDataItem[]; table.tableData =
(resp.result?.buttons as tableDataItem[]) || [];
}); });
}, },
clickDel: (row: tableDataItem) => { clickDel: (row: tableDataItem) => {

View File

@ -0,0 +1,388 @@
<template>
<div class="setting-container">
<h5 class="top">
<AIcon type="ExclamationCircleOutlined" />
<span style="padding-left: 12px"
>基于系统源代码中的菜单数据配置系统菜单</span
>
</h5>
<div class="transfer">
<!-- 左侧树 -->
<div class="basic-tree left">
<div class="title">
<div class="title-label">
<span>源菜单</span>
<j-tooltip>
<template #title
>根据系统代码自动读取的菜单数据</template
>
<AIcon type="QuestionCircleOutlined" />
</j-tooltip>
</div>
<div class="title-func">
<j-button
type="primary"
@click="dialogShow = true"
ghost
>一键拷贝</j-button
>
</div>
</div>
<div class="content">
<j-input
v-model:value="transfer.data.leftSearchValue"
style="margin-bottom: 8px"
placeholder="请输入菜单名称"
@change="transfer.changeTreeLeft"
>
<template #prefix>
<AIcon
type="SearchOutlined"
style="color: #b3b3b3"
/>
</template>
</j-input>
<j-tree
v-if="transfer.data.leftTreeData.length !== 0"
:tree-data="transfer.data.leftTreeData"
draggable
defaultExpandAll
:height="550"
>
<template #title="row">
<div>{{ row.name }}</div>
</template>
</j-tree>
</div>
</div>
<div class="center">
<j-button>请拖动至右侧</j-button>
</div>
<!-- 右侧树 -->
<div class="basic-tree right">
<div class="title">
<div class="title-label">
<span>系统菜单</span>
<j-tooltip>
<template #title
>菜单管理页面配置的菜单数据</template
>
<AIcon type="QuestionCircleOutlined" />
</j-tooltip>
</div>
</div>
<div class="content">
<j-input
v-model:value="transfer.data.rightSearchValue"
style="margin-bottom: 8px"
placeholder="请输入菜单名称"
@change="transfer.changeTreeRight"
>
<template #prefix>
<AIcon
type="SearchOutlined"
style="color: #b3b3b3"
/>
</template>
</j-input>
<j-tree
v-if="transfer.data.rightTreeData.length !== 0"
draggable
defaultExpandAll
:tree-data="transfer.data.rightTreeData"
@drop="transfer.onRightDrop"
:height="550"
>
<template #title="row">
<div
style="
display: flex;
justify-content: space-between;
"
>
<span>{{ row.name }}</span>
<j-popconfirm
title="确认删除?"
ok-text="确定"
cancel-text="取消"
@confirm="transfer.removeItem(row)"
placement="topRight"
>
<j-tooltip>
<template #title>删除</template>
<j-button
style="
padding: 0;
margin-right: 12px;
"
type="link"
>
<AIcon type="CloseOutlined" />
</j-button>
</j-tooltip>
</j-popconfirm>
</div>
</template>
</j-tree>
</div>
</div>
</div>
<j-button type="primary" style="margin-top: 24px">保存</j-button>
<div class="dialogs">
<j-modal
v-model:visible="dialogShow"
title="一键拷贝"
@ok="transfer.copy"
cancelText="取消"
okText="确认"
>
<p>源数据将会覆盖当前的系统菜单数据确定要一键拷贝吗</p>
</j-modal>
</div>
</div>
</template>
<script setup lang="ts" name="MenuSetting">
import { getMenuTree_api } from '@/api/system/menu';
import { getSystemPermission as getSystemPermission_api } from '@/api/initHome';
import { filterMenu, getKeys, loop } from './utils';
import BaseTreeData from './baseMenu';
import type {
AntTreeNodeDropEvent,
TreeDataItem,
TreeProps,
} from 'ant-design-vue/es/tree';
import { treeFilter } from '@/utils/tree';
import { cloneDeep } from 'lodash';
const transfer = {
data: reactive({
//
leftSearchValue: '',
leftTreeData: [] as any[],
//
rightSearchValue: '',
rightTreeData: [] as any[],
}),
leftSourceData: [] as any[],
rightSourceData: [] as any[],
init: () => {
//
const sourceMenu = getSystemPermission_api().then((resp: any) => {
const newTree = filterMenu(
resp.result.map((item: any) => JSON.parse(item).id),
BaseTreeData,
);
transfer.leftSourceData = [...newTree];
transfer.data.leftTreeData = newTree;
});
const params = {
paging: false,
terms: [
{
terms: [
{
column: 'owner',
termType: 'eq',
value: 'iot',
},
{
column: 'owner',
termType: 'isnull',
value: '1',
type: 'or',
},
],
},
],
};
//
const systemMenu = getMenuTree_api(params).then((resp: any) => {
transfer.data.rightTreeData = resp.result;
transfer.rightSourceData = [...resp.result];
});
//
Promise.all([sourceMenu, systemMenu]).then(() => transfer.updateTree());
},
copy: () => {
transfer.data.rightTreeData = [...toRaw(transfer.data.leftTreeData)];
dialogShow.value = false;
},
removeItem: (row: any) => {
loop(
transfer.data.rightTreeData,
row.id,
(item: TreeDataItem, index: number, arr: TreeProps['treeData']) => {
arr?.splice(index, 1);
},
);
transfer.updateTree();
},
onRightDrop: (info: AntTreeNodeDropEvent) => {
const dropKey = info.node.id;
const dragKey = info.dragNode.id;
const dropPos = (info.node.pos && info.node.pos.split('-')) || [];
const dropPosition =
info.dropPosition - Number(dropPos[dropPos.length - 1]);
const data = [...transfer.data.rightTreeData];
let dragObj: TreeDataItem = { key: '' };
loop(
data,
dragKey,
(item: TreeDataItem, index: number, arr: TreeProps['treeData']) => {
arr?.splice(index, 1);
dragObj = item;
},
);
if (!info.dropToGap) {
// Drop on the content
loop(data, dropKey, (item: TreeDataItem) => {
item.children = item.children || [];
/// where to insert
item.children.unshift(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: TreeDataItem) => {
item.children = item.children || [];
// where to insert
item.children.unshift(dragObj);
});
} else {
let ar: TreeProps['treeData'] = [];
let i = 0;
loop(
data,
dropKey,
(
_item: TreeDataItem,
index: number,
arr: TreeProps['treeData'],
) => {
ar = arr;
i = index;
},
);
if (dropPosition === -1) {
ar.splice(i, 0, dragObj);
} else {
ar.splice(i + 1, 0, dragObj);
}
}
transfer.data.rightTreeData = data;
},
updateTree: () => {
console.log(getKeys(transfer.data.rightTreeData));
},
changeTreeLeft: (val: any) => {
const value = val.target.value;
if (value) {
transfer.data.leftTreeData = treeFilter(
transfer.leftSourceData,
value,
'name',
);
} else {
transfer.data.leftTreeData = cloneDeep(transfer.rightSourceData);
}
},
changeTreeRight: (val: any) => {
const value = val.target.value;
if (value) {
transfer.data.rightTreeData = treeFilter(
transfer.rightSourceData,
value,
'name',
);
} else {
transfer.data.rightTreeData = cloneDeep(transfer.rightSourceData);
}
},
};
transfer.init();
const dialogShow = ref<boolean>(false);
</script>
<style lang="less" scoped>
.setting-container {
padding: 24px;
margin: 24px;
background-color: #fff;
.top {
font-size: inherit;
margin-bottom: 24px;
padding: 10px 24px;
color: rgba(0, 0, 0, 0.55);
background-color: #f6f6f6;
}
.transfer {
display: flex;
justify-content: space-between;
.basic-tree {
flex: 1;
.title {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
padding: 14px 16px;
font-weight: 400;
font-size: 16px;
background-color: #f3f4f4;
.title-label {
span {
padding-right: 12px;
}
}
}
.content {
padding: 12px;
border: 1px solid #e0e0e0;
border-radius: 4px;
.ant-input-affix-wrapper {
width: 75%;
}
:deep(.ant-tree) {
height: 550px;
overflow: auto;
.ant-tree-list-holder-inner {
> .ant-tree-treenode {
width: 100%;
}
.ant-tree-node-content-wrapper {
width: 100%;
}
}
}
}
}
.center {
flex-basis: 120px;
display: flex;
align-items: center;
margin: 0 24px;
}
}
}
</style>

View File

@ -1,350 +1,202 @@
<template> <template>
<div class="setting-container"> <page-container>
<h5 class="top"> <j-card>
<AIcon type="ExclamationCircleOutlined" /> <div class="top">
<span style="padding-left: 12px" <AIcon style="padding: 12px" type="ExclamationCircleOutlined" />
>基于系统源代码中的菜单数据配置系统菜单</span <span>基于系统源代码中的菜单数据配置系统菜单</span>
> </div>
</h5> <div class="content">
<div class="transfer">
<!-- 左侧树 -->
<div class="basic-tree left">
<div class="title"> <div class="title">
<div class="title-label"> 系统菜单
<span>源菜单</span> <j-tooltip>
<j-tooltip> <template #title
<template #title >根据系统代码自动读取的菜单数据</template
>根据系统代码自动读取的菜单数据</template
>
<AIcon type="QuestionCircleOutlined" />
</j-tooltip>
</div>
<div class="title-func">
<j-button
type="primary"
@click="dialogShow = true"
ghost
>一键拷贝</j-button
> >
</div> <AIcon type="QuestionCircleOutlined" />
</j-tooltip>
</div> </div>
<div class="content"> <div class="tree">
<j-input <j-input
v-model:value="transfer.data.leftSearchValue" v-model="filterText"
style="margin-bottom: 8px" placeholder="请输入"
placeholder="请输入菜单名称" @change="change"
> style="margin-bottom: 12px"
<template #prefix> />
<AIcon
type="SearchOutlined"
style="color: #b3b3b3"
/>
</template>
</j-input>
<j-tree <j-tree
autoExpandParent show-line
:tree-data="transfer.data.leftTreeData" defaultExpandAll
multiple
draggable draggable
:tree-data="treeData"
:height="500"
> >
<template #title="row"> <template #title="row">
<div>{{ row.name }}</div> <div class="tree-content">
</template> <div class="tree-content-title">
</j-tree> <AIcon type="HolderOutlined" />
</div> <div style="margin-left: 8px">
</div> {{ row.name }}
</div>
<div class="center"> </div>
<j-button>请拖动至右侧</j-button> <div class="tree-content-action">
</div> <span @click="(e) => e.stopPropagation()">
<!-- 右侧树 --> <PermissionButton
<div class="basic-tree right"> type="text"
<div class="title"> :tooltip="{
<div class="title-label"> title: '删除',
<span>系统菜单</span> }"
<j-tooltip> hasPermission="DataCollect/Collector:delete"
<template #title :popConfirm="{
>菜单管理页面配置的菜单数据</template title: `确定删除?`,
> onConfirm: () =>
<AIcon type="QuestionCircleOutlined" /> handlDelete(row.id),
</j-tooltip> }"
</div>
</div>
<div class="content">
<j-input
v-model:value="transfer.data.rightSearchValue"
style="margin-bottom: 8px"
placeholder="请输入菜单名称"
>
<template #prefix>
<AIcon
type="SearchOutlined"
style="color: #b3b3b3"
/>
</template>
</j-input>
<j-tree
draggable
autoExpandParent
:tree-data="transfer.data.rightTreeData"
@drop="transfer.onRightDrop"
>
<template #title="row">
<div
style="
display: flex;
justify-content: space-between;
"
>
<span>{{ row.name }}</span>
<j-popconfirm
title="确认删除?"
ok-text="确定"
cancel-text="取消"
@confirm="transfer.removeItem(row)"
>
<j-tooltip>
<template #title>删除</template>
<j-button
style="padding: 0"
type="link"
> >
<AIcon type="CloseOutlined" /> <AIcon type="CloseOutlined" />
</j-button> </PermissionButton>
</j-tooltip> </span>
</j-popconfirm> </div>
</div> </div>
</template> </template>
</j-tree> </j-tree>
</div> </div>
<j-button type="primary">保存</j-button>
</div> </div>
</div> </j-card>
</page-container>
<j-button type="primary" style="margin-top: 24px">保存</j-button>
<div class="dialogs">
<j-modal
v-model:visible="dialogShow"
title="一键拷贝"
@ok="transfer.copy"
cancelText="取消"
okText="确认"
>
<p>源数据将会覆盖当前的系统菜单数据确定要一键拷贝吗</p>
</j-modal>
</div>
</div>
</template> </template>
<script setup lang="ts" name="MenuSetting"> <script setup lang="ts" name="MenuSetting">
import { getMenuTree_api } from '@/api/system/menu'; import { getMenuTree_api } from '@/api/system/menu';
import { getSystemPermission as getSystemPermission_api } from '@/api/initHome'; import { getSystemPermission as getSystemPermission_api } from '@/api/initHome';
import { filterMenu, getKeys, loop } from './utils'; import { filterMenu, getKeys, loop } from './utils';
import BaseTreeData from './baseMenu'; import BaseMenu from './baseMenu';
import type { import type {
AntTreeNodeDropEvent, AntTreeNodeDropEvent,
TreeDataItem, TreeDataItem,
TreeProps, TreeProps,
} from 'ant-design-vue/es/tree'; } from 'ant-design-vue/es/tree';
import { treeFilter } from '@/utils/tree';
import { cloneDeep } from 'lodash';
const transfer = { const treeData = ref<any>();
data: reactive({ const filterText = ref('');
// treeData.value = [...BaseMenu];
leftSearchValue: '', let systemMenu: any = reactive([]);
leftTreeData: [] as any[], const baseMenu = cloneDeep(BaseMenu);
// const BaseMenuMap = new Map();
rightSearchValue: '', BaseMenu.forEach((item) => {
rightTreeData: [] as any[], BaseMenuMap.set(item.code, item);
}), });
leftSourceData: [] as any[],
rightSourceData: [] as any[],
init: () => { console.log(11, BaseMenuMap);
//
const sourceMenu = getSystemPermission_api().then((resp: any) => { const params = {
const newTree = filterMenu( paging: false,
resp.result.map((item: any) => JSON.parse(item).id), terms: [
BaseTreeData, {
);
transfer.leftSourceData = [...newTree];
transfer.data.leftTreeData = newTree;
});
const params = {
paging: false,
terms: [ terms: [
{ {
terms: [ column: 'owner',
{ termType: 'eq',
column: 'owner', value: 'iot',
termType: 'eq', },
value: 'iot', {
}, column: 'owner',
{ termType: 'isnull',
column: 'owner', value: '1',
termType: 'isnull', type: 'or',
value: '1',
type: 'or',
},
],
}, },
], ],
}; },
// ],
const systemMenu = getMenuTree_api(params).then((resp: any) => {
transfer.data.rightTreeData = resp.result;
transfer.rightSourceData = [...resp.result];
});
//
Promise.all([sourceMenu, systemMenu]).then(() => transfer.updateTree());
},
copy: () => {
transfer.data.rightTreeData = [...toRaw(transfer.data.leftTreeData)];
dialogShow.value = false;
},
removeItem: (row: any) => {
loop(
transfer.data.rightTreeData,
row.id,
(item: TreeDataItem, index: number, arr: TreeProps['treeData']) => {
arr?.splice(index, 1);
},
);
transfer.updateTree();
},
onRightDrop: (info: AntTreeNodeDropEvent) => {
const dropKey = info.node.id;
const dragKey = info.dragNode.id;
const dropPos = (info.node.pos && info.node.pos.split('-')) || [];
const dropPosition =
info.dropPosition - Number(dropPos[dropPos.length - 1]);
const data = [...transfer.data.rightTreeData];
let dragObj: TreeDataItem = { key: '' };
loop(
data,
dragKey,
(item: TreeDataItem, index: number, arr: TreeProps['treeData']) => {
arr?.splice(index, 1);
dragObj = item;
},
);
if (!info.dropToGap) {
// Drop on the content
loop(data, dropKey, (item: TreeDataItem) => {
item.children = item.children || [];
/// where to insert
item.children.unshift(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: TreeDataItem) => {
item.children = item.children || [];
// where to insert
item.children.unshift(dragObj);
});
} else {
let ar: TreeProps['treeData'] = [];
let i = 0;
loop(
data,
dropKey,
(
_item: TreeDataItem,
index: number,
arr: TreeProps['treeData'],
) => {
ar = arr;
i = index;
},
);
if (dropPosition === -1) {
ar.splice(i, 0, dragObj);
} else {
ar.splice(i + 1, 0, dragObj);
}
}
transfer.data.rightTreeData = data;
},
updateTree: () => {
console.log(getKeys(transfer.data.rightTreeData));
},
}; };
transfer.init();
const dialogShow = ref<boolean>(false); const change = (val: any) => {
treeData.value = treeFilter(baseMenu, val.target.value, 'name');
};
const handlDelete = (value) => {
console.log(22, value);
};
onMounted(() => {
getMenuTree_api(params).then((resp: any) => {
if (resp.status == 200) {
systemMenu = resp.result;
console.log(2, systemMenu);
}
// transfer.data.rightTreeData = resp.result;
});
});
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.setting-container { .top {
padding: 24px; background: #f6f6f6;
margin: 24px; height: 40px;
background-color: #fff; font-style: normal;
font-weight: 400;
.top { font-size: 14px;
font-size: inherit; line-height: 20px;
margin-bottom: 24px; color: rgba(0, 0, 0, 0.55);
padding: 10px 24px; }
color: rgba(0, 0, 0, 0.55); .content {
background-color: #f6f6f6; width: 50%;
margin-top: 24px;
.title {
font-style: normal;
font-weight: 800;
font-size: 16px;
height: 48px;
padding: 12px;
background: #f3f4f4;
color: rgba(0, 0, 0, 0.8);
} }
:deep(.ant-tree) {
.transfer { .ant-tree-switcher {
display: flex;
justify-content: space-between;
.basic-tree {
flex: 1;
.title {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
padding: 14px 16px;
font-weight: 400;
font-size: 16px;
background-color: #f3f4f4;
.title-label {
span {
padding-right: 12px;
}
}
}
.content {
padding: 12px;
border: 1px solid #e0e0e0;
border-radius: 4px;
.ant-input-affix-wrapper {
width: 75%;
}
:deep(.ant-tree) {
height: 500px;
overflow: auto;
.ant-tree-list-holder-inner {
> .ant-tree-treenode {
width: 100%;
}
.ant-tree-node-content-wrapper {
width: 100%;
}
}
}
}
}
.center {
flex-basis: 120px;
display: flex; display: flex;
align-items: center; align-items: center;
margin: 0 24px; justify-content: center;
}
.ant-tree-switcher-noop {
visibility: hidden;
}
.ant-tree-treenode {
width: 100%;
}
.ant-tree-node-content-wrapper {
width: 100%;
}
}
.tree {
height: 580px;
margin: 16px 0;
padding: 12px;
background: #ffffff;
border: 1px solid #e0e0e0;
border-radius: 4px;
overflow: hidden;
width: 100%;
&-content {
display: flex;
justify-content: space-between;
width: 100%;
&-title {
flex: 1;
font-weight: 800;
font-size: 12px;
line-height: 17px;
display: flex;
align-items: center;
color: #333333;
}
&-action {
// width: 20px;
}
} }
} }
} }

View File

@ -95,8 +95,8 @@ const confirm = () => {
loading.value = true; loading.value = true;
formRef.value && formRef.value &&
formRef.value.validate().then(() => { formRef.value.validate().then(() => {
const buttons = toRaw(props.menuInfo.buttons); const buttons = toRaw(props.menuInfo?.buttons);
const button = buttons.find((item) => item.id === form.data.id); const button = buttons?.find((item) => item.id === form.data.id);
if (button) { if (button) {
Object.entries(form.data).forEach(([key, value]) => { Object.entries(form.data).forEach(([key, value]) => {
button[key] = value; button[key] = value;
@ -112,7 +112,10 @@ const confirm = () => {
emits('confirm'); emits('confirm');
emits('update:visible', false); emits('update:visible', false);
}) })
.finally(() => (loading.value = false)); .finally(() => (loading.value = false))
.catch(() => {
loading.value = false;
});
}); });
}; };
const initForm = { const initForm = {