Merge pull request #67 from jetlinks/dev-hub

fix: 修复运维管理部分bug
This commit is contained in:
胡彪 2023-03-22 20:43:20 +08:00 committed by GitHub
commit a6a8bbefc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 344 additions and 162 deletions

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@ -16,6 +16,9 @@
moment(slotProps.requestTime).format('YYYY-MM-DD HH:mm:ss')
}}
</template>
<template #description="slotProps">
{{ slotProps.action }}
</template>
<template #responseTime="slotProps">
<j-tag color="purple">
{{ slotProps.responseTime - slotProps.requestTime }} ms
@ -59,12 +62,7 @@
</template>
</j-pro-table>
</div>
<j-modal
:width="1100"
v-model:visible="visible"
title="详情"
@ok="handleOk"
>
<j-modal :width="1100" v-model:visible="visible" title="详情">
<j-descriptions :data="descriptionsData" title="" bordered :column="2">
<j-descriptions-item label="URL">
{{ descriptionsData?.url }}
@ -112,6 +110,9 @@
/>
</j-descriptions-item>
</j-descriptions>
<template #footer>
<j-button type="primary" @click="handleOk">关闭</j-button>
</template>
</j-modal>
</template>
<script lang="ts" setup name="AccessLog">
@ -145,6 +146,16 @@ const columns = [
},
ellipsis: true,
},
{
title: '说明',
dataIndex: 'description',
key: 'description',
scopedSlots: true,
search: {
type: 'string',
},
ellipsis: true,
},
{
title: '请求方法',
dataIndex: 'httpMethod',
@ -198,9 +209,9 @@ const columns = [
title: '请求用户',
dataIndex: 'username',
key: 'username',
search: {
type: 'string',
},
// search: {
// type: 'string',
// },
width: 150,
scopedSlots: true,
},

View File

@ -66,12 +66,7 @@
</template>
</j-pro-table>
</div>
<j-modal
:width="1100"
v-model:visible="visible"
title="详情"
@ok="handleOk"
>
<j-modal :width="1100" v-model:visible="visible" title="详情">
<div>
<span class="mr-10">[{{ descriptionsData?.threadName }}]</span>
<span class="mr-10">{{
@ -102,6 +97,9 @@
placeholder="暂无数据"
:auto-size="{ minRows: 24, maxRows: 28 }"
/>
<template #footer>
<j-button type="primary" @click="handleOk">关闭</j-button>
</template>
</j-modal>
</template>
<script lang="ts" setup name="SystemLog">

View File

@ -237,6 +237,8 @@ const emit = defineEmits(['change']);
const id = props.data.id;
const VersionOrder = props.data.versionOrder;
const VersionSign = props.data.sign;
const VersionUrl = props.data.url;
const formData: any = ref({
name: '',
@ -254,12 +256,14 @@ const extraValue: any = ref({});
const validatorSign = async (_: Record<string, any>, value: string) => {
const { signMethod, url } = formData.value;
if (value && !!signMethod && !!url && !extraValue.value) {
return extraValue.value[signMethod] !== value
? Promise.reject('签名不一致,请检查文件是否上传正确')
: Promise.resolve();
} else {
if (id && VersionSign === value && VersionUrl === url) {
return Promise.resolve();
} else {
if (value && !!signMethod && !!url && !!extraValue.value) {
return extraValue.value[signMethod] !== value
? Promise.reject('签名不一致,请检查文件是否上传正确')
: Promise.resolve();
}
}
};
const validatorVersionOrder = async (_: Record<string, any>, value: string) => {
@ -270,7 +274,9 @@ const validatorVersionOrder = async (_: Record<string, any>, value: string) => {
if (value && !!signMethod && productId) {
const res = await validateVersion(productId, value);
if (res.status === 200) {
return Promise.reject(res.result ? '版本序号已存在' : '');
return res.result
? Promise.reject('版本序号已存在')
: Promise.resolve();
}
}
}

View File

@ -280,7 +280,7 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
if (!data) {
return [];
}
const Actions = [
const Actions: any = [
{
key: 'view',
text: '查看',
@ -292,7 +292,11 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
handlEye(data.errorReason);
},
},
{
];
const { state, mode } = data;
if (mode.value === 'push' && state.value === 'failed') {
Actions.push({
key: 'update',
text: '重试',
tooltip: {
@ -305,8 +309,9 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
handlTry(data.id);
},
},
},
];
});
}
return Actions;
};

View File

@ -208,10 +208,4 @@ watch(
);
</script>
<style lang="less" scoped>
.form {
.form-submit {
background-color: @primary-color !important;
}
}
</style>
<style lang="less" scoped></style>

View File

@ -143,7 +143,7 @@ const columns = [
key: 'createTime',
dataIndex: 'createTime',
search: {
type: 'time',
type: 'date',
},
width: 200,
scopedSlots: true,
@ -197,6 +197,9 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
{
key: 'delete',
text: '删除',
tooltip: {
title: '删除',
},
popConfirm: {
title: '确认删除?',
okText: ' 确定',

View File

@ -3,7 +3,7 @@
hoverable
:class="[
'card-render',
'container',
`access-${data.type || 'network'}`,
checked === data.id ? 'checked' : '',
]"
@click="checkedChange(data.id)"
@ -106,8 +106,16 @@ const checkedChange = (id: string) => {
}
}
}
.container {
background: url('/public/images/access-icon.png') no-repeat;
.access-media {
background: url('/public/images/access-media.png') no-repeat;
background-position: bottom right;
}
.access-network {
background: url('/public/images/access-network.png') no-repeat;
background-position: bottom right;
}
.access-protocol {
background: url('/public/images/access-protocol.png') no-repeat;
background-position: bottom right;
}
</style>

View File

@ -20,7 +20,11 @@
message: '请输入名称',
trigger: 'blur',
},
{ max: 64, message: '最多可输入64个字符' },
{
max: 64,
message: '最多可输入64个字符',
trigger: 'blur',
},
]"
>
<j-input
@ -119,7 +123,7 @@ const onFinish = async (values: any) => {
};
onMounted(() => {
if (id === ':id') {
if (id !== ':id') {
formState.value = {
name: props.data.name,
description: props.data?.description || '',

View File

@ -1,7 +1,7 @@
<template>
<div>
<j-steps class="steps-steps" :current="stepCurrent">
<j-step v-for="item in steps" :key="item" :title="item" />
<j-step disabled v-for="item in steps" :key="item" :title="item" />
</j-steps>
<div class="steps-content">
<div class="steps-box" v-if="current === 0">
@ -51,6 +51,7 @@
max: 64,
message:
'最多可输入64个字符',
trigger: 'blur',
},
]"
>
@ -75,6 +76,7 @@
max: 64,
message:
'最多可输入64个字符',
trigger: 'blur',
},
]"
>
@ -184,7 +186,11 @@
</PermissionButton>
</div>
<j-scrollbar height="480">
<j-row :gutter="[24, 24]" v-if="procotolList.length > 0">
<j-row
:gutter="[24, 24]"
style="width: 100%"
v-if="procotolList.length > 0"
>
<j-col
:span="8"
v-for="item in procotolList"
@ -193,12 +199,16 @@
<AccessCard
@checkedChange="procotolChange"
:checked="procotolCurrent"
:data="item"
:data="{ ...item, type: 'protocol' }"
>
</AccessCard>
</j-col>
</j-row>
<j-empty v-else description="暂无数据" />
<j-empty
style="margin-top: 10%"
v-else
description="暂无数据"
/>
</j-scrollbar>
</div>
</div>
@ -222,7 +232,11 @@
message: '请输入名称',
trigger: 'blur',
},
{ max: 64, message: '最多可输入64个字符' },
{
max: 64,
message: '最多可输入64个字符',
trigger: 'blur',
},
]"
>
<j-input

View File

@ -1,7 +1,7 @@
<template>
<div>
<j-steps class="steps-steps" :current="stepCurrent">
<j-step v-for="item in steps" :key="item" :title="item" />
<j-step disabled v-for="item in steps" :key="item" :title="item" />
</j-steps>
<div class="steps-content">
<div class="steps-box" v-if="current === 0">
@ -63,6 +63,7 @@
max: 64,
message:
'最多可输入64个字符',
trigger: 'blur',
},
]"
>
@ -86,13 +87,14 @@
max: 64,
message:
'最多可输入64个字符',
trigger: 'blur',
},
]"
>
<template #label>
通知Token
<j-tooltip
title="接收OneNet推送的Token地址"
title="自定义token,可用于验证请求是否来自OneNet"
>
<AIcon
type="QuestionCircleOutlined"
@ -116,6 +118,7 @@
max: 64,
message:
'最多可输入64个字符',
trigger: 'blur',
},
]"
>
@ -266,7 +269,11 @@
</PermissionButton>
</div>
<j-scrollbar height="480">
<j-row :gutter="[24, 24]" v-if="procotolList.length > 0">
<j-row
:gutter="[24, 24]"
style="width: 100%"
v-if="procotolList.length > 0"
>
<j-col
:span="8"
v-for="item in procotolList"
@ -275,12 +282,16 @@
<AccessCard
@checkedChange="procotolChange"
:checked="procotolCurrent"
:data="item"
:data="{ ...item, type: 'protocol' }"
>
</AccessCard>
</j-col>
</j-row>
<j-empty v-else description="暂无数据" />
<j-empty
style="margin-top: 10%"
v-else
description="暂无数据"
/>
</j-scrollbar>
</div>
</div>
@ -305,7 +316,11 @@
message: '请输入名称',
trigger: 'blur',
},
{ max: 64, message: '最多可输入64个字符' },
{
max: 64,
message: '最多可输入64个字符',
trigger: 'blur',
},
]"
>
<j-input

View File

@ -5,7 +5,7 @@
class="steps-steps"
:current="stepCurrent"
>
<j-step v-for="item in steps" :key="item" :title="item" />
<j-step disabled v-for="item in steps" :key="item" :title="item" />
</j-steps>
<div v-if="channel !== 'edge-child-device'" class="steps-content">
<div class="steps-box" v-if="current === 0">
@ -30,7 +30,11 @@
</PermissionButton>
</div>
<j-scrollbar height="480">
<j-row :gutter="[24, 24]" v-if="networkList.length > 0">
<j-row
:gutter="[24, 24]"
style="width: 100%"
v-if="networkList.length > 0"
>
<j-col
:span="8"
v-for="item in networkList"
@ -91,7 +95,11 @@
</AccessCard>
</j-col>
</j-row>
<j-empty v-else description="暂无数据" />
<j-empty
style="margin-top: 10%"
v-else
description="暂无数据"
/>
</j-scrollbar>
</div>
</div>
@ -119,7 +127,11 @@
message: '请输入名称',
trigger: 'blur',
},
{ max: 64, message: '最多可输入64个字符' },
{
max: 64,
message: '最多可输入64个字符',
trigger: 'blur',
},
]"
>
<j-input

View File

@ -1,7 +1,7 @@
<template>
<div style="margin-top: 10px">
<j-steps :current="stepCurrent">
<j-step v-for="item in steps" :key="item" :title="item" />
<j-step disabled v-for="item in steps" :key="item" :title="item" />
</j-steps>
<div class="steps-content">
<div class="steps-box" v-if="current === 0">
@ -29,7 +29,8 @@
},
{
max: 64,
message: '最大可输入64个字符',
message: '最多可输入64个字符',
trigger: 'blur',
},
]"
>
@ -50,7 +51,8 @@
},
{
max: 64,
message: '最大可输入64个字符',
message: '最多可输入64个字符',
trigger: 'blur',
},
]"
>
@ -202,7 +204,11 @@
<j-collapse v-model:activeKey="activeKey">
<j-collapse-panel
:key="cluster.id"
:header="`#${index + 1}.节点`"
:header="
cluster.clusterNodeId
? cluster.clusterNodeId
: `#${index + 1}.配置信息`
"
>
<template #extra>
<span
@ -504,7 +510,7 @@ interface Form2 {
}
interface FormState {
domain: string | undefined;
sipId: string | undefined;
sipId: string | number | undefined;
shareCluster: boolean;
hostPort: {
port: string | undefined;
@ -576,7 +582,7 @@ const removeCluster = (item: Form2) => {
};
const addCluster = () => {
const id = Date.now();
const id: any = Date.now();
dynamicValidateForm.cluster.push({
clusterNodeId: undefined,
port: undefined,
@ -613,7 +619,11 @@ const { resetFields, validate, validateInfos } = useForm(
reactive({
name: [
{ required: true, message: '请输入名称', trigger: 'blur' },
{ max: 64, message: '最多可输入64个字符' },
{
max: 64,
message: '最多可输入64个字符',
trigger: 'blur',
},
],
description: [{ max: 200, message: '最多可输入200个字符' }],
}),
@ -628,7 +638,7 @@ const saveData = () => {
transport: 'SIP',
channel: 'gb28181',
};
params.configuration.sipId = Number(params.configuration?.sipId);
const resp =
id === ':id' ? await save(params) : await update({ ...params, id });
if (resp.status === 200) {
@ -645,20 +655,32 @@ const next = async () => {
data1.hostPort.port = port;
}
if (!data1?.shareCluster) {
let data2 = await formRef2.value?.validate();
if (data2 && data2?.cluster) {
data2.cluster.forEach((i: any) => {
i.enabled = true;
i.port = JSON.parse(i.port).port;
await formRef2.value
?.validate()
.then((data2) => {
if (data2 && data2?.cluster) {
data2.cluster.forEach((i: any) => {
i.enabled = true;
i.port = JSON.parse(i.port).port;
});
data1 = {
...data1,
...data2,
};
}
current.value = current.value + 1;
params.configuration = data1;
})
.catch((err) => {
err.errorFields.forEach((item: any) => {
const activeId: any =
dynamicValidateForm.cluster[item.name[1]].id;
if (!activeKey.value.includes(activeId)) {
activeKey.value.push(activeId); //
}
});
});
data1 = {
...data1,
...data2,
};
}
}
current.value = current.value + 1;
params.configuration = data1;
};
const prev = () => {
current.value = current.value - 1;
@ -698,11 +720,25 @@ onMounted(() => {
});
if (id !== ':id') {
formState.value = props.data.configuration;
formData.value = {
name: props.data.name,
description: props.data?.description || '',
};
const { configuration, name, description = '' } = props.data;
formData.value = { name, description };
if (configuration?.shareCluster) {
formState.value = {
...formState.value,
...props.data.configuration,
};
} else {
formState.value = {
...formState.value,
...props.data.configuration,
};
dynamicValidateForm.cluster = configuration.cluster;
if (dynamicValidateForm.cluster.length === 1) {
activeKey.value = ['1'];
dynamicValidateForm.cluster[0].id = 1;
}
}
}
});

View File

@ -21,7 +21,11 @@
message: '请输入名称',
trigger: 'blur',
},
{ max: 64, message: '最多可输入64个字符' },
{
max: 64,
message: '最多可输入64个字符',
trigger: 'blur',
},
]"
>
<j-input

View File

@ -1,7 +1,7 @@
<template>
<div>
<j-steps :current="stepCurrent">
<j-step v-for="item in steps" :key="item" :title="item" />
<j-step disabled v-for="item in steps" :key="item" :title="item" />
</j-steps>
<div class="steps-content">
<div class="steps-box" v-if="current === 0">
@ -26,7 +26,11 @@
</PermissionButton>
</div>
<j-scrollbar height="480">
<j-row :gutter="[24, 24]" v-if="networkList.length > 0">
<j-row
:gutter="[24, 24]"
style="width: 100%"
v-if="networkList.length > 0"
>
<j-col
:span="8"
v-for="item in networkList"
@ -40,6 +44,7 @@
description: item.description
? item.description
: descriptionList[provider.id],
type: 'network',
}"
>
<template #other>
@ -87,7 +92,11 @@
</AccessCard>
</j-col>
</j-row>
<j-empty v-else description="暂无数据" />
<j-empty
style="margin-top: 10%"
v-else
description="暂无数据"
/>
</j-scrollbar>
</div>
<div class="steps-box" v-else-if="current === 1">
@ -112,21 +121,29 @@
</PermissionButton>
</div>
<j-scrollbar height="480">
<j-row :gutter="[24, 24]" v-if="procotolList.length > 0">
<j-row
:gutter="[24, 24]"
style="width: 100%"
v-if="procotolList.length > 0"
>
<j-col
:span="8"
v-for="item in procotolList"
:key="item?.id"
>
<access-card
<AccessCard
@checkedChange="procotolChange"
:checked="procotolCurrent"
:data="item"
:data="{ ...item, type: 'protocol' }"
>
</access-card>
</AccessCard>
</j-col>
</j-row>
<j-empty v-else description="暂无数据" />
<j-empty
style="margin-top: 10%"
v-else
description="暂无数据"
/>
</j-scrollbar>
</div>
<div class="steps-box" v-else>
@ -366,7 +383,11 @@ const { resetFields, validate, validateInfos } = useForm(
reactive({
name: [
{ required: true, message: '请输入名称', trigger: 'blur' },
{ max: 64, message: '最多可输入64个字符' },
{
max: 64,
message: '最多可输入64个字符',
trigger: 'blur',
},
],
description: [{ max: 200, message: '最多可输入200个字符' }],
}),

View File

@ -186,6 +186,7 @@ const tableRef = ref<Record<string, any>>({});
const params = ref<Record<string, any>>({});
let providersList = ref<Record<string, any>>([]);
const providersOptions = ref<Record<string, any>>([]);
const statusMap = new Map();
statusMap.set('enabled', 'success');
@ -207,13 +208,7 @@ const columns = [
key: 'provider',
search: {
type: 'select',
options: async () => {
const res: any = await getProviders();
return (res?.result || [])?.map((item: any) => ({
lable: item.name,
value: item.id,
}));
},
options: providersOptions,
},
},
{
@ -315,6 +310,10 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
const getProvidersList = async () => {
const res: any = await getProviders();
providersList.value = res.result;
providersOptions.value = (res?.result || [])?.map((item: any) => ({
label: item.name,
value: item.id,
}));
};
getProvidersList();

View File

@ -54,12 +54,17 @@ const loading = ref(false);
const handleChange = (info: UploadChangeParam) => {
loading.value = true;
if (info.file.status === 'done') {
onlyMessage('上传成功!', 'success');
const result = info.file.response?.result;
keystoreBase64.value = result;
const reg = new RegExp(/\.pem$/i);
if (reg.test(info.file.name)) {
keystoreBase64.value = result;
emit('change', result);
emit('update:modelValue', result);
onlyMessage('上传成功!', 'success');
} else {
onlyMessage('请上传.pem格式的文件', 'error');
}
loading.value = false;
emit('change', result);
emit('update:modelValue', result);
}
};
const textChange = (val: any) => {

View File

@ -9,7 +9,7 @@
:model="formData"
name="basic"
:label-col="{ span: 8 }"
:wrapper-col="{ span: 16 }"
:wrapper-col="{ span: 24 }"
autocomplete="off"
>
<j-form-item
@ -86,7 +86,7 @@
<h1>2. 配置说明</h1>
<h2>1证书文件</h2>
<div>
您可以使用文本编辑工具打开PEM或者CRT格式的证书文件复制其中的内容并粘贴到该文本框或者单击该文本框下的上传并选择存储在本地计算机的证书文件将文件内容上传到文本框
您可以使用文本编辑工具打开PEM格式的证书文件复制其中的内容并粘贴到该文本框或者单击该文本框下的上传并选择存储在本地计算机的证书文件将文件内容上传到文本框
</div>
<h2>2证书私钥</h2>
<div>
@ -208,31 +208,4 @@ detail(id);
}
}
}
.doc {
height: 100%;
padding: 0 24px;
overflow-y: auto;
color: rgba(#000, 0.8);
font-size: 14px;
background-color: #fff;
h1 {
margin: 16px 0;
color: rgba(#000, 0.85);
font-weight: bold;
font-size: 14px;
&:first-child {
margin-top: 0;
}
}
h2 {
margin: 6px 10px;
color: rgba(0, 0, 0, 0.8);
font-weight: 400;
font-size: 14px;
}
}
</style>

View File

@ -76,14 +76,17 @@ const columns = [
width: 200,
ellipsis: true,
search: {
type: 'select',
options: [
{
label: '证书标准',
value: 'common',
},
],
type: 'string',
},
// search: {
// type: 'select',
// options: [
// {
// label: '',
// value: 'common',
// },
// ],
// },
scopedSlots: true,
},
{
@ -118,17 +121,17 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
return [];
}
return [
{
key: 'view',
text: '查看',
tooltip: {
title: '查看',
},
icon: 'EyeOutlined',
onClick: async () => {
handlEye(data.id);
},
},
// {
// key: 'view',
// text: '',
// tooltip: {
// title: '',
// },
// icon: 'EyeOutlined',
// onClick: async () => {
// handlEye(data.id);
// },
// },
{
key: 'update',
text: '编辑',
@ -143,6 +146,9 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
{
key: 'delete',
text: '删除',
tooltip: {
title: '删除',
},
popConfirm: {
title: '确认删除?',
okText: ' 确定',

View File

@ -91,17 +91,41 @@
>
<j-collapse
v-model:activeKey="activeKey"
class="collapse"
:class="[
!formData.shareCluster
? 'collapse'
: 'collapse-panel',
]"
:ghost="formData.shareCluster"
collapsible="header"
>
<j-collapse-panel
:key="cluster.id"
:show-arrow="!formData.shareCluster"
>
<!-- <j-collapse-panel
:key="cluster.id"
:header="
cluster.serverId
? cluster.serverId
: `#${index + 1}.配置信息`
: !formData.shareCluster
? `#${index + 1}.配置信息`
: ''
"
collapsible="header"
>
:show-arrow="!formData.shareCluster"
> -->
<template #header v-if="!shareCluster">
<div class="collapse-header">
{{
cluster.serverId
? cluster.serverId
: !formData.shareCluster
? `#${index + 1}.配置信息`
: ''
}}
</div>
</template>
<template #extra v-if="!shareCluster">
<j-popconfirm
@confirm.prevent="
@ -1112,6 +1136,10 @@ const filterConfigByType = (data: any, type: string) => {
});
};
const changeheader = (value: string) => {
console.log(22, value);
};
const getPortOptions = (portOptions: object, index = 0) => {
if (!portOptions) return;
const type = formData.value.type;
@ -1358,7 +1386,7 @@ watch(
top: -10px;
left: 10px;
right: 10px;
width: calc(100%-10px);
width: calc(100% - 10px);
height: 100%;
background-color: #f4f4f4;
content: ' ';
@ -1369,7 +1397,20 @@ watch(
.collapse {
margin-bottom: 20px;
background: #f4f4f4;
:deep(.ant-collapse-header-text) {
flex: 1;
}
}
.collapse-panel {
margin-bottom: 20px;
border: #d9d9d9 1px solid;
background: #f4f4f4;
border-radius: 2px;
:deep(.ant-collapse-header) {
padding: 0;
}
}
.delete-btn {
display: inline-block;
color: #e50012;

View File

@ -20,8 +20,8 @@ export const Configuration = {
script: '',
size: '',
length: '4',
offset: '0',
little: 'false',
offset: undefined,
little: undefined,
},
};
@ -103,11 +103,12 @@ export const isVisible = (
) => VisibleData[LastName].includes(dependencies);
export const Validator = {
regIp: new RegExp(
/((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}/,
regIpv4: new RegExp(
/^((([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))\.){3}(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))$/,
),
regIPv6: new RegExp(/^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/),
regDomain: new RegExp(
/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/,
/^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i,
),
regOnlyNumber: new RegExp(/^\d+$/),
};
@ -159,7 +160,8 @@ export const Rules = {
message: '请输入公网地址',
},
{
pattern: Validator.regIp || Validator.regDomain,
pattern:
Validator.regIpv4 || Validator.regIPv6 || Validator.regDomain,
message: '请输入正确格式的域名或ip',
},
],
@ -179,7 +181,9 @@ export const Rules = {
message: '请输入远程地址',
},
{
pattern: Validator.regIp || Validator.regDomain,
pattern:
Validator.regIpv4 || Validator.regIPv6 || Validator.regDomain,
message: '请输入正确格式的域名或ip',
},
],

View File

@ -76,7 +76,10 @@
message: '请输入API Host',
},
{
pattern: regDomain,
pattern:
Validator.regIpv4 ||
Validator.regIPv6 ||
Validator.regDomain,
message: '请输入正确的IP地址或者域名',
},
]"
@ -130,7 +133,10 @@
message: '请输入RTP IP',
},
{
pattern: regDomain,
pattern:
Validator.regIpv4 ||
Validator.regIPv6 ||
Validator.regDomain,
message: '请输入正确的IP地址或者域名',
},
]"
@ -191,7 +197,15 @@
style="width: 100%"
:min="1"
:max="
formData.configuration.dynamicRtpPortRange1
Number(
formData.configuration
.dynamicRtpPortRange1,
) < 65535
? Number(
formData.configuration
.dynamicRtpPortRange1,
)
: 65535
"
:precision="0"
placeholder="起始端口"
@ -279,8 +293,17 @@ const formRef = ref<FormInstance>();
const loading = ref(false);
const options = ref([]);
const checked = ref(false);
const regDomain =
/[j-zA-Z0-9][-j-zA-Z0-9]{0,62}(\.[j-zA-Z0-9][-j-zA-Z0-9]{0,62})+\.?/;
const Validator = {
regIpv4: new RegExp(
/^((([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))\.){3}(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))$/,
),
regIPv6: new RegExp(/^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/),
regDomain: new RegExp(
/^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i,
),
regOnlyNumber: new RegExp(/^\d+$/),
};
const formData = ref<FormDataType>({
name: '',