feat: 完成鸽达达后台安全与审计改造
This commit is contained in:
@@ -0,0 +1,568 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header"><n-card :bordered="false" title="安全策略" /></div>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<BasicForm ref="searchFormRef" @register="register" @submit="reloadAll" @reset="reloadAll" />
|
||||
<n-tabs v-model:value="activeTab" type="line" animated>
|
||||
<n-tab-pane name="policy" tab="策略版本">
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:request="loadTable"
|
||||
:row-key="(r) => r.id"
|
||||
:actionColumn="actionColumn"
|
||||
:scroll-x="policyScrollX"
|
||||
:resizeHeightOffset="-10000"
|
||||
>
|
||||
<template #tableTitle
|
||||
><n-space
|
||||
><n-button
|
||||
type="primary"
|
||||
:disabled="!hasPermission(['/imSecurityPolicy/save'])"
|
||||
@click="openPolicy()"
|
||||
>新建草稿</n-button
|
||||
><n-button
|
||||
:disabled="!hasPermission(['/imSecurityPolicy/restoreDefault'])"
|
||||
@click="restoreDefault"
|
||||
>恢复默认</n-button
|
||||
></n-space
|
||||
></template
|
||||
>
|
||||
</BasicTable>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="ip" tab="IP黑白名单">
|
||||
<BasicTable
|
||||
ref="ipTableRef"
|
||||
:columns="ipColumns"
|
||||
:request="loadIPTable"
|
||||
:row-key="(r) => r.id"
|
||||
:actionColumn="ipActionColumn"
|
||||
:scroll-x="ipScrollX"
|
||||
:resizeHeightOffset="-10000"
|
||||
>
|
||||
<template #tableTitle
|
||||
><n-button
|
||||
type="primary"
|
||||
:disabled="!hasPermission(['/imSecurityPolicy/ipRuleEdit'])"
|
||||
@click="openIPRule()"
|
||||
>新增IP规则</n-button
|
||||
></template
|
||||
>
|
||||
</BasicTable>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
|
||||
<n-modal
|
||||
v-model:show="policyVisible"
|
||||
preset="dialog"
|
||||
:show-icon="false"
|
||||
:title="policyForm.id ? '编辑安全策略草稿' : '新建安全策略草稿'"
|
||||
:style="{ width: dialogWidth }"
|
||||
>
|
||||
<n-form
|
||||
ref="policyFormRef"
|
||||
:model="policyForm"
|
||||
:rules="policyRules"
|
||||
:disabled="policyReadOnly"
|
||||
label-placement="left"
|
||||
label-width="132"
|
||||
>
|
||||
<n-tabs type="segment" animated>
|
||||
<n-tab-pane name="friend" tab="好友控制">
|
||||
<n-form-item label="策略名称" path="policyName"
|
||||
><n-input v-model:value="policyForm.policyName"
|
||||
/></n-form-item>
|
||||
<n-form-item label="允许添加好友"
|
||||
><n-switch
|
||||
v-model:value="policyForm.globalFriendEnabled"
|
||||
:checked-value="1"
|
||||
:unchecked-value="0"
|
||||
@update:value="onGlobalFriendChange"
|
||||
/></n-form-item>
|
||||
<template v-if="!policyForm.globalFriendEnabled">
|
||||
<n-form-item label="隐藏个人资料"
|
||||
><n-switch v-model:value="policyForm.friendPolicy.hide_profile" disabled
|
||||
/></n-form-item>
|
||||
<n-form-item label="禁止二维码添加"
|
||||
><n-switch v-model:value="policyForm.friendPolicy.block_qrcode" disabled
|
||||
/></n-form-item>
|
||||
<n-form-item label="禁止群成员入口"
|
||||
><n-switch
|
||||
v-model:value="policyForm.friendPolicy.block_group_member_entry"
|
||||
disabled
|
||||
/></n-form-item>
|
||||
<n-form-item label="禁用加好友入口">
|
||||
<n-checkbox-group v-model:value="policyForm.friendPolicy.blocked_entries" disabled>
|
||||
<n-space>
|
||||
<n-checkbox
|
||||
v-for="option in friendEntryOptions"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
:label="option.label"
|
||||
/>
|
||||
</n-space>
|
||||
</n-checkbox-group>
|
||||
</n-form-item>
|
||||
<n-form-item label="移动端提示"
|
||||
><n-input v-model:value="policyForm.friendPolicy.message"
|
||||
/></n-form-item>
|
||||
</template>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="curfew" tab="消息宵禁">
|
||||
<n-form-item label="启用消息宵禁"
|
||||
><n-switch
|
||||
v-model:value="policyForm.messageCurfewEnabled"
|
||||
:checked-value="1"
|
||||
:unchecked-value="0"
|
||||
/></n-form-item>
|
||||
<n-space vertical size="large">
|
||||
<n-card
|
||||
v-for="(rule, index) in policyForm.curfewRules"
|
||||
:key="rule.ruleCode"
|
||||
size="small"
|
||||
:title="`规则 ${index + 1}`"
|
||||
>
|
||||
<template v-if="!policyReadOnly" #header-extra
|
||||
><n-button quaternary type="error" @click="removeCurfew(index)"
|
||||
>删除</n-button
|
||||
></template
|
||||
>
|
||||
<n-grid :cols="2" :x-gap="16">
|
||||
<n-form-item-gi label="规则编码"
|
||||
><n-input v-model:value="rule.ruleCode" /></n-form-item-gi
|
||||
><n-form-item-gi label="规则名称"
|
||||
><n-input v-model:value="rule.ruleName"
|
||||
/></n-form-item-gi>
|
||||
<n-form-item-gi label="日期类型"
|
||||
><n-select
|
||||
v-model:value="rule.dateType"
|
||||
:options="dateOptions" /></n-form-item-gi
|
||||
><n-form-item-gi label="管控范围"
|
||||
><n-select v-model:value="rule.scope" :options="scopeOptions"
|
||||
/></n-form-item-gi>
|
||||
<n-form-item-gi label="开始时间"
|
||||
><n-time-picker
|
||||
v-model:formatted-value="rule.startTime"
|
||||
format="HH:mm"
|
||||
value-format="HH:mm" /></n-form-item-gi
|
||||
><n-form-item-gi label="结束时间"
|
||||
><n-time-picker
|
||||
v-model:formatted-value="rule.endTime"
|
||||
format="HH:mm"
|
||||
value-format="HH:mm"
|
||||
/></n-form-item-gi>
|
||||
<n-form-item-gi label="时区"
|
||||
><n-input v-model:value="rule.timezone" /></n-form-item-gi
|
||||
><n-form-item-gi label="状态"
|
||||
><n-select v-model:value="rule.status" :options="statusOptions"
|
||||
/></n-form-item-gi>
|
||||
</n-grid>
|
||||
<n-form-item label="提示文案"><n-input v-model:value="rule.message" /></n-form-item>
|
||||
<n-form-item label="豁免用户ID"
|
||||
><n-input v-model:value="rule.whitelistUserText" placeholder="多个ID用逗号分隔"
|
||||
/></n-form-item>
|
||||
</n-card>
|
||||
<n-button v-if="!policyReadOnly" dashed block @click="addCurfew"
|
||||
>添加宵禁规则</n-button
|
||||
>
|
||||
</n-space>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="advanced" tab="其他策略">
|
||||
<n-form-item label="密码策略JSON"
|
||||
><n-input
|
||||
v-model:value="policyForm.passwordPolicy"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 3, maxRows: 8 }"
|
||||
/></n-form-item>
|
||||
<n-form-item label="登录限频JSON"
|
||||
><n-input
|
||||
v-model:value="policyForm.loginRateLimit"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 3, maxRows: 8 }"
|
||||
/></n-form-item>
|
||||
<n-form-item label="实名策略JSON"
|
||||
><n-input
|
||||
v-model:value="policyForm.realnamePolicy"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 3, maxRows: 8 }"
|
||||
/></n-form-item>
|
||||
<n-form-item label="备注"
|
||||
><n-input v-model:value="policyForm.remark" type="textarea"
|
||||
/></n-form-item>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-form>
|
||||
<template #action
|
||||
><n-space
|
||||
><n-button @click="policyVisible = false">取消</n-button
|
||||
><n-button v-if="!policyReadOnly" type="primary" :loading="submitting" @click="savePolicy"
|
||||
>保存草稿</n-button
|
||||
></n-space
|
||||
></template
|
||||
>
|
||||
</n-modal>
|
||||
|
||||
<n-modal
|
||||
v-model:show="ipVisible"
|
||||
preset="dialog"
|
||||
:show-icon="false"
|
||||
:title="ipForm.id ? '编辑IP规则' : '新增IP规则'"
|
||||
:style="{ width: '620px' }"
|
||||
><n-form
|
||||
ref="ipFormRef"
|
||||
:model="ipForm"
|
||||
:rules="ipRules"
|
||||
label-placement="left"
|
||||
label-width="100"
|
||||
><n-grid :cols="2" :x-gap="16"
|
||||
><n-form-item-gi label="规则类型"
|
||||
><n-select v-model:value="ipForm.ruleType" :options="ipTypeOptions" /></n-form-item-gi
|
||||
><n-form-item-gi label="IP/CIDR" path="ipCidr"
|
||||
><n-input v-model:value="ipForm.ipCidr" /></n-form-item-gi
|
||||
><n-form-item-gi label="动作"
|
||||
><n-select v-model:value="ipForm.action" :options="actionOptions" /></n-form-item-gi
|
||||
><n-form-item-gi label="风险等级"
|
||||
><n-select v-model:value="ipForm.riskLevel" :options="riskOptions" /></n-form-item-gi
|
||||
><n-form-item-gi label="状态"
|
||||
><n-select
|
||||
v-model:value="ipForm.status"
|
||||
:options="statusOptions" /></n-form-item-gi></n-grid
|
||||
><n-form-item label="生效场景"
|
||||
><n-select
|
||||
v-model:value="ipForm.scenes"
|
||||
multiple
|
||||
:options="ipSceneOptions" /></n-form-item
|
||||
><n-form-item label="备注"
|
||||
><n-input v-model:value="ipForm.remark" type="textarea" /></n-form-item></n-form
|
||||
><template #action
|
||||
><n-space
|
||||
><n-button @click="ipVisible = false">取消</n-button
|
||||
><n-button type="primary" :loading="submitting" @click="saveIPRule"
|
||||
>保存</n-button
|
||||
></n-space
|
||||
></template
|
||||
></n-modal
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, h, onMounted, reactive, ref } from 'vue';
|
||||
import { useDialog, useMessage } from 'naive-ui';
|
||||
import { BasicForm, useForm } from '@/components/Form';
|
||||
import { BasicTable, TableAction } from '@/components/Table';
|
||||
import { useDictStore } from '@/store/modules/dict';
|
||||
import { usePermission } from '@/hooks/web/usePermission';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { adaModalWidth, adaTableScrollX } from '@/utils/hotgo';
|
||||
import {
|
||||
IPRuleDelete,
|
||||
IPRuleEdit,
|
||||
IPRuleList,
|
||||
List,
|
||||
Publish,
|
||||
RestoreDefault,
|
||||
Save,
|
||||
View,
|
||||
} from '@/api/imSecurityPolicy';
|
||||
import { columns, ipColumns, loadOptions, schemas } from './model';
|
||||
import { loadPolicyTenantOptions, parseJSON } from '@/views/imPolicyShared';
|
||||
const dict = useDictStore(),
|
||||
message = useMessage(),
|
||||
dialog = useDialog(),
|
||||
userStore = useUserStore();
|
||||
const { hasPermission } = usePermission();
|
||||
const searchFormRef = ref<any>({}),
|
||||
tableRef = ref(),
|
||||
ipTableRef = ref(),
|
||||
policyFormRef = ref(),
|
||||
ipFormRef = ref();
|
||||
const activeTab = ref('policy'),
|
||||
policyVisible = ref(false),
|
||||
ipVisible = ref(false),
|
||||
submitting = ref(false);
|
||||
const dialogWidth = computed(() => adaModalWidth(900));
|
||||
const tenantId = () => Number(searchFormRef.value?.formModel?.tenantId || 0);
|
||||
const dateOptions = computed(() => dict.getOptions('ImCurfewDateTypeOptions')),
|
||||
scopeOptions = computed(() => dict.getOptions('ImCurfewScopeOptions')),
|
||||
statusOptions = computed(() => dict.getOptions('sys_normal_disable')),
|
||||
actionOptions = computed(() => dict.getOptions('ImSecurityActionOptions')),
|
||||
friendEntryOptions = computed(() => dict.getOptions('ImFriendEntryOptions')),
|
||||
riskOptions = computed(() => dict.getOptions('ImRiskLevelOptions'));
|
||||
const ipTypeOptions = [
|
||||
{ label: '白名单', value: 'whitelist' },
|
||||
{ label: '黑名单', value: 'blacklist' },
|
||||
],
|
||||
ipSceneOptions = [
|
||||
{ label: '登录', value: 'login' },
|
||||
{ label: '注册', value: 'register' },
|
||||
{ label: '发消息', value: 'message' },
|
||||
{ label: '上传文件', value: 'upload' },
|
||||
];
|
||||
const newCurfew = (i = 1) => ({
|
||||
ruleCode: `work_off_${i}`,
|
||||
ruleName: '下班时段',
|
||||
dateType: 'weekday',
|
||||
customDates: [],
|
||||
weekdays: [1, 2, 3, 4, 5],
|
||||
timezone: 'Asia/Shanghai',
|
||||
startTime: '18:00',
|
||||
endTime: '09:00',
|
||||
scope: 'private_message',
|
||||
whitelistUserIds: [],
|
||||
whitelistGroupIds: [],
|
||||
whitelistUserText: '',
|
||||
message: '当前时段暂不能发送消息',
|
||||
status: 1,
|
||||
sort: i * 10,
|
||||
});
|
||||
const allFriendEntries = [
|
||||
'search',
|
||||
'profile_card',
|
||||
'group_member',
|
||||
'qrcode',
|
||||
'invite_auto_add',
|
||||
'recommendation',
|
||||
];
|
||||
const defaultFriendDisabledPolicy = () => ({
|
||||
hide_profile: true,
|
||||
block_qrcode: true,
|
||||
block_group_member_entry: true,
|
||||
blocked_entries: [...allFriendEntries],
|
||||
message: '当前时段暂不允许添加好友',
|
||||
});
|
||||
const emptyPolicy = () => ({
|
||||
id: 0,
|
||||
tenantId: tenantId(),
|
||||
policyName: '安全策略',
|
||||
passwordPolicy: '{}',
|
||||
loginRateLimit: '{}',
|
||||
sensitiveWordPolicy: '{}',
|
||||
fileReviewPolicy: '{}',
|
||||
realnamePolicy: '{}',
|
||||
riskChallengePolicy: '{}',
|
||||
globalFriendEnabled: 1,
|
||||
friendPolicy: defaultFriendDisabledPolicy(),
|
||||
messageCurfewEnabled: 0,
|
||||
remark: '',
|
||||
curfewRules: [] as any[],
|
||||
});
|
||||
const policyForm = ref<any>(emptyPolicy());
|
||||
const policyReadOnly = computed(
|
||||
() => policyForm.value.id > 0 && policyForm.value.publishStatus !== 'draft'
|
||||
);
|
||||
const policyRules = { policyName: { required: true, message: '请输入策略名称' } };
|
||||
const emptyIP = () => ({
|
||||
id: 0,
|
||||
tenantId: tenantId(),
|
||||
ruleType: 'blacklist',
|
||||
ipCidr: '',
|
||||
action: 'block',
|
||||
scenes: ['login'],
|
||||
riskLevel: 'high',
|
||||
status: 1,
|
||||
remark: '',
|
||||
});
|
||||
const ipForm = ref<any>(emptyIP());
|
||||
const ipRules = { ipCidr: { required: true, message: '请输入IP或CIDR' } };
|
||||
const tenantSchema = schemas.value.find((v) => v.field === 'tenantId');
|
||||
if (tenantSchema) tenantSchema.ifShow = () => userStore.isCompanyDept;
|
||||
const [register] = useForm({ gridProps: { cols: '1 s:1 m:2 l:3' }, labelWidth: 90, schemas });
|
||||
const loadTable = (res) => List({ ...searchFormRef.value?.formModel, ...res }),
|
||||
loadIPTable = (res) => IPRuleList({ tenantId: tenantId(), ...res });
|
||||
const reloadAll = () => {
|
||||
tableRef.value?.reload();
|
||||
ipTableRef.value?.reload();
|
||||
};
|
||||
const actionColumn = reactive({
|
||||
width: 220,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
render: (row) =>
|
||||
h(TableAction as any, {
|
||||
style: 'button',
|
||||
actions: [
|
||||
{
|
||||
label: row.publishStatus === 'draft' ? '编辑' : '查看',
|
||||
auth: ['/imSecurityPolicy/view'],
|
||||
onClick: () => openPolicy(row),
|
||||
},
|
||||
{
|
||||
label: '发布',
|
||||
type: 'warning',
|
||||
auth: ['/imSecurityPolicy/publish'],
|
||||
ifShow: () => row.publishStatus === 'draft',
|
||||
onClick: () => publishPolicy(row),
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
const ipActionColumn = reactive({
|
||||
width: 160,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
render: (row) =>
|
||||
h(TableAction as any, {
|
||||
style: 'button',
|
||||
actions: [
|
||||
{ label: '编辑', auth: ['/imSecurityPolicy/ipRuleEdit'], onClick: () => openIPRule(row) },
|
||||
{
|
||||
label: '删除',
|
||||
type: 'error',
|
||||
auth: ['/imSecurityPolicy/ipRuleDelete'],
|
||||
onClick: () => removeIP(row),
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
const policyScrollX = computed(() => adaTableScrollX(columns, actionColumn.width)),
|
||||
ipScrollX = computed(() => adaTableScrollX(ipColumns, ipActionColumn.width));
|
||||
async function openPolicy(row?: any) {
|
||||
if (!row) {
|
||||
policyForm.value = emptyPolicy();
|
||||
policyVisible.value = true;
|
||||
return;
|
||||
}
|
||||
const data = await View({ id: row.id });
|
||||
policyForm.value = {
|
||||
...data,
|
||||
friendPolicy: {
|
||||
...defaultFriendDisabledPolicy(),
|
||||
...parseJSON(data.friendDisabledPolicy, {}),
|
||||
},
|
||||
passwordPolicy: data.passwordPolicy || '{}',
|
||||
loginRateLimit: data.loginRateLimit || '{}',
|
||||
sensitiveWordPolicy: data.sensitiveWordPolicy || '{}',
|
||||
fileReviewPolicy: data.fileReviewPolicy || '{}',
|
||||
realnamePolicy: data.realnamePolicy || '{}',
|
||||
riskChallengePolicy: data.riskChallengePolicy || '{}',
|
||||
curfewRules: (data.curfewRules || []).map((v) => ({
|
||||
...v,
|
||||
customDates: parseJSON(v.customDates, []),
|
||||
weekdays: parseJSON(v.weekdays, []),
|
||||
whitelistUserIds: parseJSON(v.whitelistUserIds, []),
|
||||
whitelistGroupIds: parseJSON(v.whitelistGroupIds, []),
|
||||
whitelistUserText: parseJSON(v.whitelistUserIds, []).join(','),
|
||||
})),
|
||||
};
|
||||
policyVisible.value = true;
|
||||
}
|
||||
function addCurfew() {
|
||||
policyForm.value.curfewRules.push(newCurfew(policyForm.value.curfewRules.length + 1));
|
||||
}
|
||||
function onGlobalFriendChange(enabled: number) {
|
||||
if (enabled === 0) {
|
||||
policyForm.value.friendPolicy = {
|
||||
...policyForm.value.friendPolicy,
|
||||
hide_profile: true,
|
||||
block_qrcode: true,
|
||||
block_group_member_entry: true,
|
||||
blocked_entries: [...allFriendEntries],
|
||||
};
|
||||
}
|
||||
}
|
||||
function removeCurfew(i) {
|
||||
policyForm.value.curfewRules.splice(i, 1);
|
||||
}
|
||||
function jsonObject(text, label) {
|
||||
try {
|
||||
return JSON.parse(text || '{}');
|
||||
} catch {
|
||||
throw new Error(`${label}不是合法JSON`);
|
||||
}
|
||||
}
|
||||
async function savePolicy() {
|
||||
submitting.value = true;
|
||||
try {
|
||||
await policyFormRef.value?.validate();
|
||||
const f = policyForm.value;
|
||||
await Save({
|
||||
...f,
|
||||
passwordPolicy: jsonObject(f.passwordPolicy, '密码策略'),
|
||||
loginRateLimit: jsonObject(f.loginRateLimit, '登录限频'),
|
||||
sensitiveWordPolicy: jsonObject(f.sensitiveWordPolicy, '敏感词策略'),
|
||||
fileReviewPolicy: jsonObject(f.fileReviewPolicy, '文件审核策略'),
|
||||
realnamePolicy: jsonObject(f.realnamePolicy, '实名策略'),
|
||||
riskChallengePolicy: jsonObject(f.riskChallengePolicy, '风控策略'),
|
||||
friendDisabledPolicy: f.friendPolicy,
|
||||
curfewRules: f.curfewRules.map((v) => ({
|
||||
...v,
|
||||
whitelistUserIds: v.whitelistUserText
|
||||
.split(',')
|
||||
.map((x) => Number(x.trim()))
|
||||
.filter(Boolean),
|
||||
})),
|
||||
});
|
||||
message.success('草稿已保存');
|
||||
policyVisible.value = false;
|
||||
reloadAll();
|
||||
} catch (e: any) {
|
||||
message.error(e.message || '保存失败');
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
function publishPolicy(row) {
|
||||
dialog.warning({
|
||||
title: '发布安全策略',
|
||||
content: `发布版本 ${row.policyVersion} 后将由IM后端执行,确认发布?`,
|
||||
positiveText: '发布',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
await Publish({ id: row.id, reason: '管理员发布' });
|
||||
message.success('发布成功');
|
||||
reloadAll();
|
||||
},
|
||||
});
|
||||
}
|
||||
function restoreDefault() {
|
||||
dialog.warning({
|
||||
title: '恢复默认策略',
|
||||
content: '将根据全局默认创建一个新草稿,不会直接发布。',
|
||||
positiveText: '创建草稿',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
await RestoreDefault({ tenantId: tenantId(), reason: '管理员恢复默认' });
|
||||
message.success('默认策略草稿已创建');
|
||||
reloadAll();
|
||||
},
|
||||
});
|
||||
}
|
||||
function openIPRule(row?: any) {
|
||||
ipForm.value = row ? { ...row, scenes: parseJSON(row.sceneJson, []) } : emptyIP();
|
||||
ipVisible.value = true;
|
||||
}
|
||||
async function saveIPRule() {
|
||||
submitting.value = true;
|
||||
try {
|
||||
await ipFormRef.value?.validate();
|
||||
await IPRuleEdit({ ...ipForm.value, sceneJson: ipForm.value.scenes });
|
||||
message.success('保存成功');
|
||||
ipVisible.value = false;
|
||||
reloadAll();
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
function removeIP(row) {
|
||||
dialog.error({
|
||||
title: '删除IP规则',
|
||||
content: `确认删除 ${row.ipCidr}?`,
|
||||
positiveText: '删除',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
await IPRuleDelete({ id: row.id });
|
||||
message.success('删除成功');
|
||||
reloadAll();
|
||||
},
|
||||
});
|
||||
}
|
||||
onMounted(async () => {
|
||||
loadOptions();
|
||||
if (userStore.isCompanyDept) await loadPolicyTenantOptions(true);
|
||||
reloadAll();
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user