649 lines
20 KiB
Vue
649 lines
20 KiB
Vue
<script setup lang="ts">
|
||
import { ref, watch } from 'vue';
|
||
import type { TableColumnType } from 'ant-design-vue';
|
||
import {
|
||
EnvironmentOutlined,
|
||
SearchOutlined,
|
||
PlusOutlined,
|
||
EditOutlined,
|
||
DeleteOutlined
|
||
} from '@ant-design/icons-vue';
|
||
import { getDashboardSiteList, addSite, deleteSite, getSiteConfig, updateSite, getMeshConfig, updateMeshConfig, getRoamingConfig, updateRoamingConfig } from '@/service/api/auth';
|
||
import { useI18n } from 'vue-i18n';
|
||
import { Form, Modal, Divider, Checkbox, Select, Input, Button } from 'ant-design-vue';
|
||
import { message } from 'ant-design-vue';
|
||
import { regionOptions, timeZoneOptions } from '@/constants/site-options';
|
||
const { t } = useI18n();
|
||
|
||
defineOptions({
|
||
name: 'CardData'
|
||
});
|
||
|
||
// 表单实例
|
||
const formRef = ref();
|
||
const useForm = Form.useForm;
|
||
|
||
// 弹窗控制
|
||
const showAddDialog = ref(false);
|
||
|
||
// 表单数据
|
||
const formData = ref({
|
||
name: '',
|
||
region: '',
|
||
timeZone: '',
|
||
scenario: '',
|
||
deviceAccountSetting: {
|
||
username: '',
|
||
password: ''
|
||
}
|
||
});
|
||
|
||
// 表单验证规则
|
||
const { validate, validateInfos } = useForm(formData, {
|
||
name: [
|
||
{ required: true, message: t('page.carddata.nameRequired') },
|
||
{
|
||
pattern: /^[^ \+\-\@\=]$|^[^ \+\-\@\=].{0,62}[^ ]$/,
|
||
message: t('page.carddata.nameInvalid')
|
||
}
|
||
],
|
||
region: [{ required: true, message: t('page.carddata.regionRequired') }],
|
||
timeZone: [{ required: true, message: t('page.carddata.timeZoneRequired') }],
|
||
scenario: [{ required: true, message: t('page.carddata.scenarioRequired') }],
|
||
'deviceAccountSetting.username': [
|
||
{ required: true, message: t('page.carddata.usernameRequired') },
|
||
{
|
||
pattern: /^[\x21-\x7E]{1,64}$/,
|
||
message: t('page.carddata.usernameInvalid')
|
||
}
|
||
],
|
||
'deviceAccountSetting.password': [
|
||
{ required: true, message: t('page.carddata.passwordRequired') },
|
||
{
|
||
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\!\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\@\[\\\]\^\_\`\{\|\}\~])(?!.*[\00-\040\042\077\0177]).{8,64}$/,
|
||
message: t('page.carddata.passwordInvalid')
|
||
}
|
||
]
|
||
});
|
||
|
||
// 使用场景选项
|
||
const scenarioOptions = [
|
||
{ label: t('page.carddata.office'), value: 'office' },
|
||
{ label: t('page.carddata.hotel'), value: 'hotel' },
|
||
{ label: t('page.carddata.education'), value: 'education' },
|
||
{ label: t('page.carddata.retail'), value: 'retail' },
|
||
{ label: t('page.carddata.other'), value: 'other' }
|
||
];
|
||
|
||
// 处理添加站点
|
||
const handleAddSite = async () => {
|
||
try {
|
||
await validate();
|
||
const response = await addSite(formData.value);
|
||
if (response) {
|
||
message.success(t('page.carddata.addSuccess'));
|
||
showAddDialog.value = false;
|
||
fetchSiteList(); // 刷新列表
|
||
}
|
||
} catch (error) {
|
||
console.error('Add site failed:', error);
|
||
message.error(t('page.carddata.addFailed'));
|
||
}
|
||
};
|
||
|
||
// 打开添加对话框
|
||
const openAddDialog = () => {
|
||
formData.value = {
|
||
name: '',
|
||
region: '',
|
||
timeZone: '',
|
||
scenario: '',
|
||
deviceAccountSetting: {
|
||
username: '',
|
||
password: ''
|
||
}
|
||
};
|
||
showAddDialog.value = true;
|
||
};
|
||
|
||
// 搜索和分页状态
|
||
const searchValue = ref('');
|
||
const currentPage = ref(1);
|
||
const pageSize = ref(10);
|
||
const loading = ref(false);
|
||
const total = ref(0);
|
||
|
||
const siteData = ref<Api.DashboardSite[]>([]);
|
||
|
||
const fetchSiteList = async () => {
|
||
try {
|
||
loading.value = true;
|
||
const { data } = await getDashboardSiteList({
|
||
pageNum: currentPage.value,
|
||
pageSize: pageSize.value,
|
||
searchKey: searchValue.value
|
||
});
|
||
console.log('API Response:', data);
|
||
if (data) {
|
||
siteData.value = data.rows || [];
|
||
total.value = data.total || 0;
|
||
console.log('Processed Site Data:', siteData.value);
|
||
console.log('Total:', total.value);
|
||
} else {
|
||
siteData.value = [];
|
||
total.value = 0;
|
||
}
|
||
}catch (error){
|
||
console.error('Failed to fetch site list:', error);
|
||
siteData.value = [];
|
||
total.value = 0;
|
||
}finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
// 修改监听方式
|
||
|
||
watch([currentPage, pageSize, searchValue], () => {
|
||
fetchSiteList();
|
||
}, { immediate: true });
|
||
|
||
|
||
|
||
// 表格列定义
|
||
const columns: TableColumnType<Api.DashboardSite>[] = [
|
||
{
|
||
title: t('page.carddata.sitename'),
|
||
key: 'name',
|
||
dataIndex: 'name'
|
||
},
|
||
{
|
||
title: t('page.carddata.country'),
|
||
key: 'region',
|
||
dataIndex: 'region'
|
||
},
|
||
{
|
||
title: t('page.carddata.alert'),
|
||
key: 'alerts',
|
||
width: 100
|
||
},
|
||
// {
|
||
// title: t('page.carddata.gateway'),
|
||
// key: 'gateway',
|
||
// width: 100
|
||
// },
|
||
// {
|
||
// title: t('page.carddata.switches'),
|
||
// key: 'switches',
|
||
// width: 100
|
||
// },
|
||
// {
|
||
// title: 'OLTS',
|
||
// key: 'olts',
|
||
// width: 100
|
||
// },
|
||
{
|
||
title: 'EAPS',
|
||
key: 'eaps',
|
||
width: 100
|
||
},
|
||
{
|
||
title: t('page.carddata.clients'),
|
||
key: 'clients',
|
||
width: 150
|
||
},
|
||
{
|
||
title: 'ACTION',
|
||
key: 'action',
|
||
width: 100,
|
||
fixed: 'right'
|
||
}
|
||
];
|
||
// 按钮操作处理函数
|
||
|
||
//
|
||
// const handleCopy = (record: Api.DashboardSite) => {
|
||
// console.log('Copy:', record);
|
||
// };
|
||
//
|
||
const handleDelete = (record: Api.DashboardSite) => {
|
||
Modal.confirm({
|
||
title: t('page.carddata.deleteConfirmTitle'),
|
||
content: t('page.carddata.deleteConfirmContent', { name: record.name }),
|
||
okText: t('page.carddata.confirm'),
|
||
cancelText: t('page.carddata.cancel'),
|
||
okType: 'danger',
|
||
async onOk() {
|
||
try {
|
||
await deleteSite(record.siteId);
|
||
message.success(t('page.carddata.deleteSuccess'));
|
||
fetchSiteList(); // 刷新列表
|
||
} catch (error) {
|
||
}
|
||
}
|
||
});
|
||
};
|
||
//
|
||
// const handleHome = (record: Api.DashboardSite) => {
|
||
// console.log('Home:', record);
|
||
// };
|
||
|
||
// 分页处理函数
|
||
const handlePageChange = (page: number, pageSize: number) => {
|
||
currentPage.value = page;
|
||
if (pageSize !== undefined) {
|
||
handlePageSizeChange(pageSize);
|
||
}
|
||
};
|
||
|
||
const handlePageSizeChange = (size: number) => {
|
||
pageSize.value = size;
|
||
currentPage.value = 1;
|
||
};
|
||
|
||
// 编辑弹窗控制
|
||
const showEditDialog = ref(false);
|
||
|
||
// 编辑表单数据
|
||
const editFormData = ref({
|
||
name: '',
|
||
region: '',
|
||
timeZone: '',
|
||
scenario: '',
|
||
meshEnable: false,
|
||
autoFailoverEnable: false,
|
||
defGatewayEnable: true,
|
||
gateway: '',
|
||
fastRoamingEnable: false,
|
||
nonStickRoamingEnable: false,
|
||
aiRoamingEnable: false
|
||
});
|
||
|
||
// 当前编辑的站点ID
|
||
const currentEditSiteId = ref('');
|
||
|
||
// 编辑表单验证规则
|
||
const { validate: validateEdit, validateInfos: validateEditInfos } = useForm(editFormData, {
|
||
name: [
|
||
{ required: true, message: t('page.carddata.nameRequired') },
|
||
{
|
||
pattern: /^[^ \+\-\@\=]$|^[^ \+\-\@\=].{0,62}[^ ]$/,
|
||
message: t('page.carddata.nameInvalid')
|
||
}
|
||
],
|
||
region: [{ required: true, message: t('page.carddata.regionRequired') }],
|
||
timeZone: [{ required: true, message: t('page.carddata.timeZoneRequired') }],
|
||
scenario: [{ required: true, message: t('page.carddata.scenarioRequired') }]
|
||
});
|
||
|
||
// IP分段输入
|
||
const gatewayIpParts = ref(['', '', '', '']);
|
||
|
||
// 拆分IP到4段
|
||
function splitGatewayIp(ip: string) {
|
||
if (!ip) return ['', '', '', ''];
|
||
const parts = ip.split('.');
|
||
return [parts[0] || '', parts[1] || '', parts[2] || '', parts[3] || ''];
|
||
}
|
||
// 拼接4段为IP
|
||
function joinGatewayIp(parts: string[]) {
|
||
return parts.map(p => p.trim()).join('.');
|
||
}
|
||
|
||
// 弹窗打开时同步分段
|
||
watch(() => editFormData.value.gateway, (val) => {
|
||
gatewayIpParts.value = splitGatewayIp(val);
|
||
}, { immediate: true });
|
||
// 分段输入时同步到gateway
|
||
watch(gatewayIpParts, (val) => {
|
||
editFormData.value.gateway = joinGatewayIp(val);
|
||
}, { deep: true });
|
||
|
||
// 处理编辑按钮点击
|
||
const handleEdit = async (record: Api.DashboardSite) => {
|
||
try {
|
||
currentEditSiteId.value = record.siteId;
|
||
const [siteRes, meshRes, roamingRes] = await Promise.all([
|
||
getSiteConfig(record.siteId),
|
||
getMeshConfig(record.siteId),
|
||
getRoamingConfig(record.siteId)
|
||
]);
|
||
if (siteRes.data) {
|
||
editFormData.value = {
|
||
name: siteRes.data.name,
|
||
region: siteRes.data.region,
|
||
timeZone: siteRes.data.timeZone,
|
||
scenario: siteRes.data.scenario,
|
||
meshEnable: meshRes.data?.mesh?.meshEnable ?? false,
|
||
autoFailoverEnable: meshRes.data?.mesh?.autoFailoverEnable ?? false,
|
||
defGatewayEnable: meshRes.data?.mesh?.defGatewayEnable ?? true,
|
||
gateway: meshRes.data?.mesh?.gateway ?? '',
|
||
fastRoamingEnable: roamingRes.data?.roaming?.fastRoamingEnable ?? false,
|
||
nonStickRoamingEnable: roamingRes.data?.roaming?.nonStickRoamingEnable ?? false,
|
||
aiRoamingEnable: roamingRes.data?.roaming?.aiRoamingEnable ?? false
|
||
};
|
||
gatewayIpParts.value = splitGatewayIp(editFormData.value.gateway);
|
||
showEditDialog.value = true;
|
||
}
|
||
} catch (error) {
|
||
console.error('Get site config failed:', error);
|
||
message.error(t('page.carddata.getConfigFailed'));
|
||
}
|
||
};
|
||
|
||
// 处理更新站点
|
||
const handleUpdateSite = async () => {
|
||
try {
|
||
await validateEdit();
|
||
// 先保存基础配置
|
||
await updateSite(currentEditSiteId.value, {
|
||
name: editFormData.value.name,
|
||
region: editFormData.value.region,
|
||
timeZone: editFormData.value.timeZone,
|
||
scenario: editFormData.value.scenario
|
||
});
|
||
// mesh 配置为嵌套结构,meshEnable为true时才携带defGatewayEnable和gateway
|
||
const meshData: any = { mesh: { meshEnable: editFormData.value.meshEnable } };
|
||
if (editFormData.value.meshEnable) {
|
||
meshData.mesh.autoFailoverEnable = editFormData.value.autoFailoverEnable;
|
||
meshData.mesh.defGatewayEnable = editFormData.value.defGatewayEnable;
|
||
if (editFormData.value.defGatewayEnable === false) {
|
||
meshData.mesh.gateway = editFormData.value.gateway;
|
||
}
|
||
}
|
||
// roaming 配置为嵌套结构
|
||
const roamingData: any = { roaming: { fastRoamingEnable: editFormData.value.fastRoamingEnable } };
|
||
if (editFormData.value.fastRoamingEnable) {
|
||
roamingData.roaming.nonStickRoamingEnable = editFormData.value.nonStickRoamingEnable;
|
||
roamingData.roaming.aiRoamingEnable = editFormData.value.aiRoamingEnable;
|
||
}
|
||
await Promise.all([
|
||
updateMeshConfig(currentEditSiteId.value, meshData),
|
||
updateRoamingConfig(currentEditSiteId.value, roamingData)
|
||
]);
|
||
message.success(t('page.carddata.updateSuccess'));
|
||
showEditDialog.value = false;
|
||
fetchSiteList(); // 刷新列表
|
||
} catch (error) {
|
||
console.error('Update site failed:', error);
|
||
message.error(t('page.carddata.updateFailed'));
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<template>
|
||
<ACard :bordered="false" size="small" class="card-wrapper">
|
||
<div class="flex justify-between items-center mb-16px">
|
||
<div class="flex items-center gap-8px">
|
||
<span class="text-16px font-medium">{{ t('page.carddata.sitelist') }}</span>
|
||
</div>
|
||
<div class="flex items-center gap-16px">
|
||
<AInput
|
||
v-model:value="searchValue"
|
||
:placeholder="t('page.carddata.search')"
|
||
class="w-240px"
|
||
allow-clear
|
||
>
|
||
<template #prefix>
|
||
<search-outlined />
|
||
</template>
|
||
</AInput>
|
||
<AButton type="primary" @click="openAddDialog">
|
||
<template #icon>
|
||
<plus-outlined />
|
||
</template>
|
||
{{ t('page.carddata.addsite') }}
|
||
</AButton>
|
||
</div>
|
||
</div>
|
||
|
||
<ATable
|
||
:columns="columns"
|
||
:data-source="siteData"
|
||
:pagination="{
|
||
current: currentPage,
|
||
pageSize: pageSize,
|
||
total: total,
|
||
showSizeChanger: true,
|
||
showQuickJumper: true,
|
||
showTotal: (total: number) => `${t('page.carddata.total')} ${total} `,
|
||
onChange: handlePageChange,
|
||
onShowSizeChange: handlePageSizeChange
|
||
}"
|
||
row-key="siteId"
|
||
:scroll="{ x: 1200 }"
|
||
:loading="loading"
|
||
>
|
||
<!-- 添加一个调试信息 -->
|
||
<template #headerCell="{ column }">
|
||
<span>{{ column.title }}</span>
|
||
</template>
|
||
<template #bodyCell="{ column, record }">
|
||
<!-- 添加调试信息 -->
|
||
{{ console.log('Rendering cell:', column.key, record) }}
|
||
<template v-if="column.key === 'name'">
|
||
<div class="flex items-center gap-8px">
|
||
<environment-outlined class="text-16px" />
|
||
<span>{{ record.name }}</span>
|
||
</div>
|
||
</template>
|
||
<template v-else-if="column.key === 'region'">
|
||
<span>{{ record.region }}</span>
|
||
</template>
|
||
<template v-else-if="column.key === 'alerts'">
|
||
<span>0</span>
|
||
</template>
|
||
<template v-else-if="column.key === 'gateway'">
|
||
<span>{{ record.connectedGatewayNum }}/{{ record.disconnectedGatewayNum }}</span>
|
||
</template>
|
||
<template v-else-if="column.key === 'switches'">
|
||
<span>{{ record.connectedSwitchNum }}/{{ record.disconnectedSwitchNum }}</span>
|
||
</template>
|
||
<template v-else-if="column.key === 'olts'">
|
||
<span>0/0</span>
|
||
</template>
|
||
<template v-else-if="column.key === 'eaps'">
|
||
<span>{{ record.connectedApNum }}/{{ record.disconnectedApNum }}/{{ record.isolatedApNum }}</span>
|
||
</template>
|
||
<template v-else-if="column.key === 'clients'">
|
||
<span>{{ record.wiredClientNum }}/{{ record.wirelessClientNum }}/{{ record.guestNum }}</span>
|
||
</template>
|
||
<template v-else-if="column.key === 'action'">
|
||
<div class="flex items-center gap-8px">
|
||
<edit-outlined class="cursor-pointer text-primary" @click="handleEdit(record)" />
|
||
<!-- <copy-outlined class="cursor-pointer text-primary" @click="handleCopy(record)" />-->
|
||
<delete-outlined class="cursor-pointer text-red-500" @click="handleDelete(record)" />
|
||
<!-- <home-outlined class="cursor-pointer text-primary" @click="handleHome(record)" />-->
|
||
</div>
|
||
</template>
|
||
</template>
|
||
</ATable>
|
||
|
||
<!-- 添加站点对话框 -->
|
||
<AModal
|
||
v-model:visible="showAddDialog"
|
||
:title="t('page.carddata.addsite')"
|
||
@ok="handleAddSite"
|
||
@cancel="showAddDialog = false"
|
||
:maskClosable="false"
|
||
>
|
||
<AForm
|
||
ref="formRef"
|
||
:model="formData"
|
||
:label-col="{ span: 6 }"
|
||
:wrapper-col="{ span: 18 }"
|
||
>
|
||
<AFormItem
|
||
name="name"
|
||
:label="t('page.carddata.sitename')"
|
||
v-bind="validateInfos.name"
|
||
>
|
||
<AInput v-model:value="formData.name" />
|
||
</AFormItem>
|
||
|
||
<AFormItem
|
||
name="region"
|
||
:label="t('page.carddata.region')"
|
||
v-bind="validateInfos.region"
|
||
>
|
||
<ASelect
|
||
v-model:value="formData.region"
|
||
:options="regionOptions"
|
||
show-search
|
||
:filter-option="(input, option) =>
|
||
option?.label?.toLowerCase().includes(input.toLowerCase())"
|
||
/>
|
||
</AFormItem>
|
||
|
||
<AFormItem
|
||
name="timeZone"
|
||
:label="t('page.carddata.timezone')"
|
||
v-bind="validateInfos.timeZone"
|
||
>
|
||
<ASelect
|
||
v-model:value="formData.timeZone"
|
||
:options="timeZoneOptions"
|
||
show-search
|
||
:filter-option="(input, option) =>
|
||
option?.label?.toLowerCase().includes(input.toLowerCase())"
|
||
/>
|
||
</AFormItem>
|
||
|
||
<AFormItem
|
||
name="scenario"
|
||
:label="t('page.carddata.scenario')"
|
||
v-bind="validateInfos.scenario"
|
||
>
|
||
<ASelect
|
||
v-model:value="formData.scenario"
|
||
:options="scenarioOptions"
|
||
/>
|
||
</AFormItem>
|
||
|
||
<AFormItem
|
||
name="deviceAccountSetting.username"
|
||
:label="t('page.carddata.username')"
|
||
v-bind="validateInfos['deviceAccountSetting.username']"
|
||
>
|
||
<AInput v-model:value="formData.deviceAccountSetting.username" />
|
||
</AFormItem>
|
||
|
||
<AFormItem
|
||
name="deviceAccountSetting.password"
|
||
:label="t('page.carddata.password')"
|
||
v-bind="validateInfos['deviceAccountSetting.password']"
|
||
>
|
||
<AInputPassword v-model:value="formData.deviceAccountSetting.password" />
|
||
</AFormItem>
|
||
</AForm>
|
||
</AModal>
|
||
|
||
<!-- 编辑站点对话框 -->
|
||
<AModal
|
||
v-model:visible="showEditDialog"
|
||
:title="t('page.carddata.editsite')"
|
||
@ok="handleUpdateSite"
|
||
@cancel="showEditDialog = false"
|
||
:maskClosable="false"
|
||
:width="700"
|
||
>
|
||
<AForm
|
||
:model="editFormData"
|
||
:label-col="{ span: 6 }"
|
||
:wrapper-col="{ span: 18 }"
|
||
>
|
||
<AFormItem
|
||
name="name"
|
||
:label="t('page.carddata.sitename')"
|
||
v-bind="validateEditInfos.name"
|
||
>
|
||
<AInput v-model:value="editFormData.name" />
|
||
</AFormItem>
|
||
|
||
<AFormItem
|
||
name="region"
|
||
:label="t('page.carddata.region')"
|
||
v-bind="validateEditInfos.region"
|
||
>
|
||
<ASelect
|
||
v-model:value="editFormData.region"
|
||
:options="regionOptions"
|
||
show-search
|
||
:filter-option="(input, option) =>
|
||
option?.label?.toLowerCase().includes(input.toLowerCase())"
|
||
/>
|
||
</AFormItem>
|
||
|
||
<AFormItem
|
||
name="timeZone"
|
||
:label="t('page.carddata.timezone')"
|
||
v-bind="validateEditInfos.timeZone"
|
||
>
|
||
<ASelect
|
||
v-model:value="editFormData.timeZone"
|
||
:options="timeZoneOptions"
|
||
show-search
|
||
:filter-option="(input, option) =>
|
||
option?.label?.toLowerCase().includes(input.toLowerCase())"
|
||
/>
|
||
</AFormItem>
|
||
|
||
<AFormItem
|
||
name="scenario"
|
||
:label="t('page.carddata.scenario')"
|
||
v-bind="validateEditInfos.scenario"
|
||
>
|
||
<ASelect
|
||
v-model:value="editFormData.scenario"
|
||
:options="scenarioOptions"
|
||
/>
|
||
</AFormItem>
|
||
|
||
<a-divider />
|
||
<div style="margin-bottom: 8px; font-weight: bold;">组网配置</div>
|
||
<AFormItem>
|
||
<a-checkbox v-model:checked="editFormData.meshEnable">Mesh</a-checkbox>
|
||
</AFormItem>
|
||
<AFormItem v-if="editFormData.meshEnable" style="margin-left: 24px;">
|
||
<ASelect v-model:value="editFormData.defGatewayEnable" style="width: 200px;">
|
||
<ASelectOption :value="true">Auto(Recommended)</ASelectOption>
|
||
<ASelectOption :value="false">Custom</ASelectOption>
|
||
</ASelect>
|
||
<template v-if="editFormData.defGatewayEnable === false">
|
||
<div style="display: inline-flex; align-items: center; margin-left: 12px;">
|
||
<AInput v-for="(part, idx) in gatewayIpParts" :key="idx" v-model:value="gatewayIpParts[idx]" maxlength="3" style="width: 48px; text-align: center; margin-right: 4px;" />
|
||
<span v-if="idx < 3" v-for="idx in 3" :key="'dot'+idx">.</span>
|
||
</div>
|
||
</template>
|
||
</AFormItem>
|
||
<AFormItem v-if="editFormData.meshEnable" style="margin-left: 24px;">
|
||
<a-checkbox v-model:checked="editFormData.autoFailoverEnable">Auto Failover</a-checkbox>
|
||
</AFormItem>
|
||
<AFormItem>
|
||
<a-checkbox v-model:checked="editFormData.fastRoamingEnable">Fast Roaming</a-checkbox>
|
||
</AFormItem>
|
||
<AFormItem v-if="editFormData.fastRoamingEnable" style="margin-left: 24px;">
|
||
<a-checkbox v-model:checked="editFormData.nonStickRoamingEnable">Non-Stick Roaming</a-checkbox>
|
||
</AFormItem>
|
||
<AFormItem v-if="editFormData.fastRoamingEnable" style="margin-left: 24px;">
|
||
<a-checkbox v-model:checked="editFormData.aiRoamingEnable">AI Roaming</a-checkbox>
|
||
</AFormItem>
|
||
</AForm>
|
||
</AModal>
|
||
</ACard>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.card-wrapper {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.cursor-pointer {
|
||
cursor: pointer;
|
||
}
|
||
|
||
.w-240px {
|
||
width: 240px;
|
||
}
|
||
</style>
|