Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev
This commit is contained in:
commit
582ebddd21
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 = () => {
|
||||||
|
|
|
@ -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') {
|
||||||
|
|
|
@ -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: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -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('');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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>
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
Loading…
Reference in New Issue