前端验证 / verify (push) Failing after 15m3s
- 统一页面标题、圆角、字体和响应式断点,调整暗色主题及图表配色 - 新增表格列标签插件,在窄屏下将表格转换为卡片布局,优化实例、订单、工单等列表展示 - 支持移动端点击订单行进入详情,并完善订单凭据的打印布局 - 在财务中心展示本月及累计消费,补充充值关闭时的提示 - 优化工单详情布局,将附件上传入口整合至富文本编辑器工具栏 - 调整实例操作按钮、SSH 密钥操作及安全菜单图标 - 为多个页面显式导入 definePageMeta,完善页面元数据声明
2411 lines
95 KiB
Vue
2411 lines
95 KiB
Vue
<script>
|
||
/**
|
||
* 安全组:组列表、出入站规则与实例关联共用页面;主列表、规则、已关联和可关联主机分别用请求代次隔离,批量关联保留失败目标供重试。
|
||
*/
|
||
import { definePageMeta } from "#imports";
|
||
import "~/assets/styles/PageInfoNotice._16xRFrB.css";
|
||
import "~/assets/styles/groups.Buw8nwl1.css";
|
||
definePageMeta({
|
||
key: (e) => e.fullPath,
|
||
alias: [`/security/groups/group`],
|
||
});
|
||
import { toDisplayString } from "vue";
|
||
import { renderList } from "vue";
|
||
import { onMounted } from "vue";
|
||
import { useId } from "vue";
|
||
import { normalizeClass } from "vue";
|
||
import { onBeforeUnmount } from "vue";
|
||
import { watch } from "vue";
|
||
import { createBlock } from "vue";
|
||
import { ref } from "vue";
|
||
import { withCtx } from "vue";
|
||
import { createCommentVNode } from "vue";
|
||
import { computed } from "vue";
|
||
import { reactive } from "vue";
|
||
import { isRef } from "vue";
|
||
import { createElementBlock } from "vue";
|
||
import { Fragment } from "vue";
|
||
import { createElementVNode } from "vue";
|
||
import { createTextVNode } from "vue";
|
||
import { unref } from "vue";
|
||
import { defineComponent } from "vue";
|
||
import { createVNode } from "vue";
|
||
import { openBlock } from "vue";
|
||
import { withKeys } from "vue";
|
||
import { useRoute } from "#app";
|
||
import { useApi } from "~/lib/auth.js";
|
||
import dialog from "primevue/dialog";
|
||
import button from "primevue/button";
|
||
import { t as _plugin_vue_export_helper_t } from "~/lib/_plugin-vue_export-helper.js";
|
||
import message from "primevue/message";
|
||
import inputtext from "primevue/inputtext";
|
||
import { n as product_name_n } from "~/lib/product-name.js";
|
||
import { r as dict_r } from "~/lib/dict.js";
|
||
import datatable from "primevue/datatable";
|
||
import paginator from "primevue/paginator";
|
||
import select from "primevue/select";
|
||
import checkbox from "primevue/checkbox";
|
||
import column from "primevue/column";
|
||
import ListEmpty from "~/components/ListEmpty.vue";
|
||
import textarea from "primevue/textarea";
|
||
import tag from "primevue/tag";
|
||
import PageInfoNotice from "~/components/PageInfoNotice.vue";
|
||
import multiselect from "primevue/multiselect";
|
||
import { n as complete_list_n } from "~/lib/complete-list.js";
|
||
import { t as complete_list_t } from "~/lib/complete-list.js";
|
||
import { t as composables_t } from "~/lib/composables-2.js";
|
||
var Te = {
|
||
class: `list-page`,
|
||
},
|
||
Ee = {
|
||
key: 0,
|
||
class: `list-head`,
|
||
},
|
||
De = {
|
||
class: `list-head-actions`,
|
||
},
|
||
Oe = {
|
||
key: 2,
|
||
class: `list-toolbar`,
|
||
},
|
||
ke = {
|
||
key: 3,
|
||
class: `list-table`,
|
||
},
|
||
Ae = [`onClick`],
|
||
je = {
|
||
class: `row-actions`,
|
||
},
|
||
Me = {
|
||
key: 4,
|
||
class: `list-foot`,
|
||
},
|
||
Ne = {
|
||
class: `form`,
|
||
},
|
||
Pe = {
|
||
class: `field`,
|
||
},
|
||
Fe = [`for`],
|
||
Ie = {
|
||
class: `field`,
|
||
},
|
||
Le = [`for`],
|
||
Re = {
|
||
key: 5,
|
||
class: `group-detail`,
|
||
},
|
||
ze = {
|
||
class: `group-detail-head`,
|
||
},
|
||
Be = {
|
||
class: `group-meta`,
|
||
},
|
||
Ve = {
|
||
class: `row-actions`,
|
||
},
|
||
He = {
|
||
class: `rules`,
|
||
},
|
||
Ue = {
|
||
class: `dlg-tabs`,
|
||
},
|
||
We = {
|
||
class: `rule-toolbar`,
|
||
},
|
||
Ge = {
|
||
class: `mono`,
|
||
},
|
||
Ke = {
|
||
class: `rule-add`,
|
||
},
|
||
qe = {
|
||
class: `rule-form`,
|
||
},
|
||
Je = {
|
||
class: `rule-form batch-relate-form`,
|
||
},
|
||
Ye = {
|
||
class: `rule-form`,
|
||
},
|
||
Xe = {
|
||
class: `mono`,
|
||
},
|
||
Ze = {
|
||
class: `rule-form`,
|
||
},
|
||
Qe = {
|
||
class: `batch-dlg`,
|
||
},
|
||
$e = {
|
||
class: `quick-grid`,
|
||
},
|
||
et = {
|
||
class: `advanced-rules`,
|
||
},
|
||
b = _plugin_vue_export_helper_t(
|
||
defineComponent({
|
||
__name: `index`,
|
||
setup(re) {
|
||
let y = useApi(),
|
||
b = !1,
|
||
x = 0,
|
||
tt = 0,
|
||
nt = 0,
|
||
rt = 0;
|
||
onBeforeUnmount(() => {
|
||
((b = !0), x++, tt++, nt++, rt++);
|
||
});
|
||
let it = useId(),
|
||
at = useRoute();
|
||
composables_t({
|
||
alias: `/security/groups/group`,
|
||
key: (e) => e.fullPath,
|
||
});
|
||
let ot = ref([]),
|
||
S = ref(0),
|
||
st = ref(!0),
|
||
C = ref(1),
|
||
w = ref(10),
|
||
T = ref(``),
|
||
E = ref(``),
|
||
ct = ref(``),
|
||
lt = computed(() => ot.value),
|
||
D = ref(`in`),
|
||
O = ref(!1),
|
||
ut = [
|
||
{
|
||
name: `FTP`,
|
||
port: `20-21`,
|
||
},
|
||
{
|
||
name: `SSH`,
|
||
port: `22`,
|
||
},
|
||
{
|
||
name: `Telnet`,
|
||
port: `23`,
|
||
},
|
||
{
|
||
name: `TFTP`,
|
||
port: `69`,
|
||
},
|
||
{
|
||
name: `RDP`,
|
||
port: `3389`,
|
||
},
|
||
{
|
||
name: `VNC`,
|
||
port: `5900-5902`,
|
||
},
|
||
{
|
||
name: `Linux`,
|
||
port: `1024-65535`,
|
||
},
|
||
{
|
||
name: `HTTPS`,
|
||
port: `443`,
|
||
},
|
||
{
|
||
name: `SMTP`,
|
||
port: `25`,
|
||
},
|
||
{
|
||
name: `POP3`,
|
||
port: `110`,
|
||
},
|
||
{
|
||
name: `IMAP`,
|
||
port: `143`,
|
||
},
|
||
{
|
||
name: `HTTP`,
|
||
port: `80`,
|
||
},
|
||
{
|
||
name: `SQL Server`,
|
||
port: `1433`,
|
||
},
|
||
{
|
||
name: `Oracle`,
|
||
port: `1521`,
|
||
},
|
||
{
|
||
name: `MySQL`,
|
||
port: `3306`,
|
||
},
|
||
{
|
||
name: `PostgreSQL`,
|
||
port: `5432`,
|
||
},
|
||
{
|
||
name: `Redis`,
|
||
port: `6379`,
|
||
},
|
||
{
|
||
name: `MongoDB`,
|
||
port: `27017`,
|
||
},
|
||
],
|
||
k = ref([]);
|
||
// 常用端口快捷选择仅生成待确认的文本规则;默认 IP 为全网范围,生成并不立即下发。
|
||
function dt() {
|
||
$.value = ut
|
||
.filter((e) => k.value.includes(e.port))
|
||
.map(
|
||
(e) =>
|
||
`${D.value} ${e.port === `69` ? `udp` : `tcp`} ${e.port} 0.0.0.0/0 ${e.name}`,
|
||
).join(`
|
||
`);
|
||
}
|
||
// 打开新增规则弹窗,方向取当前入站/出站标签。
|
||
function ft() {
|
||
((R.value = null), (L.direction = D.value), (O.value = !0));
|
||
}
|
||
// 把已选规则复制到草稿后编辑,避免未保存时改动列表。
|
||
function pt(e) {
|
||
((R.value = e),
|
||
Object.assign(L, {
|
||
description: e.description || ``,
|
||
direction: e.direction || D.value,
|
||
protocol: e.protocol || `tcp`,
|
||
port: e.port || ``,
|
||
ip: e.ip || `0.0.0.0/0`,
|
||
}),
|
||
(T.value = ``),
|
||
(O.value = !0));
|
||
}
|
||
let A = ref(!1),
|
||
mt = ref(!1),
|
||
j = ref(null),
|
||
M = reactive({
|
||
name: ``,
|
||
description: ``,
|
||
}),
|
||
N = ref(!1),
|
||
P = ref(null),
|
||
ht = ref([]),
|
||
gt = ref(0),
|
||
F = ref(1),
|
||
_t = ref(20),
|
||
I = ref(``),
|
||
vt = ref(!1),
|
||
yt = ref(!1),
|
||
L = reactive({
|
||
description: ``,
|
||
direction: `in`,
|
||
protocol: `tcp`,
|
||
port: ``,
|
||
ip: `0.0.0.0/0`,
|
||
}),
|
||
R = ref(null),
|
||
bt = computed(() => ht.value.filter((e) => e.direction === D.value));
|
||
// 按关键词分页查询安全组,组件离开或新请求发起后废弃旧响应。
|
||
async function z() {
|
||
let e = ++x;
|
||
((st.value = !0), (T.value = ``));
|
||
try {
|
||
let t = await y.get(`/security_group`, {
|
||
page: C.value,
|
||
limit: w.value,
|
||
keywords: ct.value,
|
||
});
|
||
if (b || e !== x) return;
|
||
((ot.value = t.list), (S.value = t.count));
|
||
} catch (t) {
|
||
if (b || e !== x) return;
|
||
((T.value = t instanceof Error ? t.message : `加载安全组失败`),
|
||
(ot.value = []),
|
||
(S.value = 0));
|
||
} finally {
|
||
e === x && !b && (st.value = !1);
|
||
}
|
||
}
|
||
// 新建组前清空上一次编辑目标和名称描述。
|
||
function xt() {
|
||
((j.value = null),
|
||
(M.name = ``),
|
||
(M.description = ``),
|
||
(T.value = ``),
|
||
(A.value = !0));
|
||
}
|
||
// 编辑组时回填现有名称描述,保留原对象用于更新 ID。
|
||
function St(e) {
|
||
((j.value = e),
|
||
(M.name = e.name),
|
||
(M.description = e.description || ``),
|
||
(T.value = ``),
|
||
(A.value = !0));
|
||
}
|
||
// 名称必填且同一时间只提交一次;新增/更新后重新读取组列表。
|
||
async function Ct() {
|
||
if (!mt.value) {
|
||
if (((T.value = ``), !M.name.trim())) {
|
||
T.value = `请输入安全组名称`;
|
||
return;
|
||
}
|
||
mt.value = !0;
|
||
try {
|
||
(j.value
|
||
? await y.put(`/security_group/${j.value.id}`, {
|
||
id: j.value.id,
|
||
...M,
|
||
})
|
||
: await y.post(`/security_group`, {
|
||
...M,
|
||
}),
|
||
(A.value = !1),
|
||
await z());
|
||
} catch (e) {
|
||
T.value = e instanceof Error ? e.message : `保存失败`;
|
||
} finally {
|
||
mt.value = !1;
|
||
}
|
||
}
|
||
}
|
||
// 删除组后刷新列表,失败保留页面错误。
|
||
async function wt(e) {
|
||
try {
|
||
(await y.del(`/security_group/${e.id}`), await z());
|
||
} catch (e) {
|
||
T.value = e instanceof Error ? e.message : `删除失败`;
|
||
}
|
||
}
|
||
// 进入组详情时重置规则页码、草稿与当前标签,再加载该组规则。
|
||
async function Tt(e) {
|
||
((P.value = e),
|
||
(N.value = !0),
|
||
(V.value = `rule`),
|
||
(F.value = 1),
|
||
(I.value = ``),
|
||
(T.value = ``),
|
||
Object.assign(L, {
|
||
description: ``,
|
||
direction: `in`,
|
||
protocol: `tcp`,
|
||
port: ``,
|
||
ip: `0.0.0.0/0`,
|
||
}),
|
||
await B());
|
||
}
|
||
// 规则响应必须匹配组 ID 与请求代次,避免切组后展示上一组规则。
|
||
async function B() {
|
||
if (!P.value) return;
|
||
let e = ++tt,
|
||
t = P.value.id,
|
||
n = () => !b && e === tt && t === P.value?.id;
|
||
((vt.value = !0), (T.value = ``));
|
||
try {
|
||
let e = await y.get(`/security_group/${t}/rule`, {
|
||
page: F.value,
|
||
limit: _t.value,
|
||
keywords: I.value.trim(),
|
||
direction: D.value,
|
||
});
|
||
if (!n()) return;
|
||
((ht.value = e.list ?? []),
|
||
(gt.value = e.count ?? ht.value.length));
|
||
} catch (e) {
|
||
if (!n()) return;
|
||
((T.value = e instanceof Error ? e.message : `加载规则失败`),
|
||
(ht.value = []),
|
||
(gt.value = 0));
|
||
} finally {
|
||
n() && (vt.value = !1);
|
||
}
|
||
}
|
||
let V = ref(`rule`),
|
||
Et = ref([]),
|
||
H = ref(!1),
|
||
Dt = ref([]),
|
||
U = ref(null),
|
||
W = ref([]),
|
||
G = ref(!1),
|
||
Ot = ref(!1),
|
||
K = ref(``),
|
||
q = ref(``),
|
||
J = ref(``),
|
||
Y = ref(!1),
|
||
kt = ref(``),
|
||
At = computed(() =>
|
||
Et.value.filter((e) =>
|
||
`${e.name} ${e.ip || ``}`
|
||
.toLowerCase()
|
||
.includes(kt.value.trim().toLowerCase()),
|
||
),
|
||
),
|
||
X = computed(
|
||
() =>
|
||
H.value ||
|
||
Y.value ||
|
||
!!q.value ||
|
||
!!J.value ||
|
||
G.value ||
|
||
Ot.value,
|
||
);
|
||
// 读取当前组全部关联主机用于搜索与去重,而不是只拿关联列表第一页。
|
||
async function Z() {
|
||
if (!P.value) return;
|
||
let e = P.value.id,
|
||
t = ++nt,
|
||
n = () => !b && t === nt && e === P.value?.id;
|
||
((H.value = !0), (q.value = ``));
|
||
try {
|
||
let t = await complete_list_n(
|
||
(t, n) =>
|
||
y.get(`/security_group/${e}/host`, {
|
||
page: t,
|
||
limit: n,
|
||
}),
|
||
n,
|
||
);
|
||
n() && (Et.value = t);
|
||
} catch (e) {
|
||
if (!n()) return;
|
||
((Et.value = []),
|
||
(q.value = e instanceof Error ? e.message : `关联实例加载失败`));
|
||
} finally {
|
||
n() && (H.value = !1);
|
||
}
|
||
}
|
||
// 分页收集可用主机,失败时清空旧候选,避免沿用已失效的关联资格。
|
||
async function jt() {
|
||
let e = ++rt,
|
||
t = () => !b && e === rt;
|
||
((Y.value = !0), (J.value = ``));
|
||
try {
|
||
let e = await complete_list_n(
|
||
(e, t) =>
|
||
y.get(`/host`, {
|
||
page: e,
|
||
limit: t,
|
||
}),
|
||
t,
|
||
);
|
||
t() && (Dt.value = e);
|
||
} catch (e) {
|
||
if (!t()) return;
|
||
((Dt.value = []),
|
||
(J.value = e instanceof Error ? e.message : `可用实例加载失败`));
|
||
} finally {
|
||
t() && (Y.value = !1);
|
||
}
|
||
}
|
||
let Mt = [
|
||
`idcsmart_cloud`,
|
||
`common_cloud`,
|
||
`mf_cloud`,
|
||
`mf_cloud_mysql`,
|
||
`pve_cloud`,
|
||
`kky_ecs`,
|
||
],
|
||
Nt = computed(() =>
|
||
Dt.value
|
||
.filter((e) => Mt.includes(e.module ?? ``))
|
||
.filter((e) => !Et.value.some((t) => t.id === e.id))
|
||
.map((e) => ({
|
||
label: `${product_name_n(e.product_name)} ${e.name}`.trim(),
|
||
value: e.id,
|
||
})),
|
||
);
|
||
// 批量提交前核对所有选择仍可关联;解析部分成功结果后保留失败 ID,刷新已关联列表及组计数。
|
||
async function Pt() {
|
||
if (!P.value || !W.value.length || X.value) return;
|
||
let e = P.value.id,
|
||
t = [...new Set(W.value)];
|
||
if (t.some((e) => !Nt.value.some((t) => t.value === e))) {
|
||
T.value = `请选择当前可关联的实例`;
|
||
return;
|
||
}
|
||
((Ot.value = !0), (T.value = ``), (K.value = ``));
|
||
try {
|
||
let n = await y.post(`/security_group/${e}/host`, {
|
||
host_id: t,
|
||
});
|
||
if (b || P.value?.id !== e) return;
|
||
let r = complete_list_t(n, t);
|
||
if (
|
||
((W.value = r.failed),
|
||
await Promise.all([Z(), z()]),
|
||
b || P.value?.id !== e)
|
||
)
|
||
return;
|
||
((T.value = r.details.join(`;`)),
|
||
r.succeeded.length &&
|
||
(K.value = `已关联 ${r.succeeded.length} 台实例`),
|
||
K.value && setTimeout(() => (K.value = ``), 4e3));
|
||
} catch (t) {
|
||
if (b || P.value?.id !== e) return;
|
||
T.value = t instanceof Error ? t.message : `批量关联失败`;
|
||
} finally {
|
||
Ot.value = !1;
|
||
}
|
||
}
|
||
// 单主机关联仅接受当前候选项,提交后刷新关联及组列表。
|
||
async function Ft() {
|
||
if (
|
||
P.value &&
|
||
U.value &&
|
||
!X.value &&
|
||
Nt.value.some((e) => e.value === U.value)
|
||
) {
|
||
((G.value = !0), (T.value = ``));
|
||
try {
|
||
(await y.post(`/security_group/${P.value.id}/host/${U.value}`),
|
||
(U.value = null),
|
||
await Z(),
|
||
await z());
|
||
} catch (e) {
|
||
T.value = e instanceof Error ? e.message : `关联失败`;
|
||
} finally {
|
||
G.value = !1;
|
||
}
|
||
}
|
||
}
|
||
// 解除实例与组的关联后刷新状态,不在本地假定服务端已删除关系。
|
||
async function It(e) {
|
||
if (P.value && !X.value) {
|
||
((G.value = !0), (T.value = ``));
|
||
try {
|
||
(await y.del(`/security_group/${P.value.id}/host/${e.id}`),
|
||
await Z(),
|
||
await z());
|
||
} catch (e) {
|
||
T.value = e instanceof Error ? e.message : `取消关联失败`;
|
||
} finally {
|
||
G.value = !1;
|
||
}
|
||
}
|
||
}
|
||
let Q = ref(!1),
|
||
$ = ref(``),
|
||
Lt = ref(!1);
|
||
// 先把自由文本收敛为方向、协议的封闭枚举;任一行无效就拒绝整批,不能猜成入站规则。
|
||
function Rt(e) {
|
||
let t = [];
|
||
for (let n of e.split(`
|
||
`)) {
|
||
let e = n.trim();
|
||
if (!e) continue;
|
||
let r = e.split(/\s+/);
|
||
if (r.length < 4) throw Error(`格式不正确:${e}`);
|
||
let [ee, i, te = ``, a = ``, ...o] = r;
|
||
let direction = ee.toLowerCase(),
|
||
protocol = i.toLowerCase();
|
||
if (!["in", "out"].includes(direction))
|
||
throw Error(`规则方向必须为 in 或 out:${ee}`);
|
||
if (!["tcp", "udp", "icmp", "all"].includes(protocol))
|
||
throw Error(`规则协议必须为 tcp、udp、icmp 或 all:${i}`);
|
||
t.push({
|
||
direction,
|
||
protocol,
|
||
port: te,
|
||
ip: a,
|
||
description: o.join(` `) || void 0,
|
||
});
|
||
}
|
||
return t;
|
||
}
|
||
// 批量规则先完整解析再提交,至少需要一条;成功后清空文本并刷新规则和组计数。
|
||
async function zt() {
|
||
if (!P.value || Lt.value) return;
|
||
T.value = ``;
|
||
let e;
|
||
try {
|
||
e = Rt($.value);
|
||
} catch (e) {
|
||
T.value = e instanceof Error ? e.message : `格式不正确`;
|
||
return;
|
||
}
|
||
if (!e.length) {
|
||
T.value = `请输入至少一条规则`;
|
||
return;
|
||
}
|
||
Lt.value = !0;
|
||
try {
|
||
let t = await y.post(`/security_group/${P.value.id}/rule/batch`, {
|
||
rule: e,
|
||
});
|
||
((Q.value = !1),
|
||
($.value = ``),
|
||
await B(),
|
||
await z(),
|
||
(T.value = ``),
|
||
t?.success_num !== void 0 &&
|
||
((Bt.value = `成功添加 ${t.success_num} 条规则`),
|
||
setTimeout(() => (Bt.value = ``), 4e3)));
|
||
} catch (e) {
|
||
T.value = e instanceof Error ? e.message : `批量添加失败`;
|
||
} finally {
|
||
Lt.value = !1;
|
||
}
|
||
}
|
||
let Bt = ref(``);
|
||
watch(V, (e) => {
|
||
e === `host` && (Z(), jt());
|
||
});
|
||
// 单条规则按是否有编辑目标选择 PUT/POST;端口必填,成功后重置编辑器并刷新。
|
||
async function Vt() {
|
||
if (!yt.value) {
|
||
if (((T.value = ``), !P.value)) {
|
||
T.value = `请先选择安全组`;
|
||
return;
|
||
}
|
||
if (!L.port.trim()) {
|
||
T.value = `请输入端口(如 22 或 100-65535)`;
|
||
return;
|
||
}
|
||
yt.value = !0;
|
||
try {
|
||
(R.value
|
||
? await y.put(`/security_group/rule/${R.value.id}`, {
|
||
...L,
|
||
})
|
||
: await y.post(`/security_group/${P.value.id}/rule`, {
|
||
...L,
|
||
}),
|
||
Object.assign(L, {
|
||
description: ``,
|
||
direction: `in`,
|
||
protocol: `tcp`,
|
||
port: ``,
|
||
ip: `0.0.0.0/0`,
|
||
}),
|
||
(R.value = null),
|
||
(O.value = !1),
|
||
await B(),
|
||
await z());
|
||
} catch (e) {
|
||
T.value = e instanceof Error ? e.message : `添加规则失败`;
|
||
} finally {
|
||
yt.value = !1;
|
||
}
|
||
}
|
||
}
|
||
// 删除规则后同步规则列表与安全组统计。
|
||
async function Ht(e) {
|
||
try {
|
||
(await y.del(`/security_group/rule/${e.id}`), await B(), await z());
|
||
} catch (e) {
|
||
T.value = e instanceof Error ? e.message : `删除规则失败`;
|
||
}
|
||
}
|
||
// 把已知协议枚举转换为可读名称,未知协议保留大写标识。
|
||
function Ut(e) {
|
||
return (
|
||
{
|
||
tcp: `TCP`,
|
||
udp: `UDP`,
|
||
icmp: `ICMP`,
|
||
all: `全部`,
|
||
rdp: `RDP`,
|
||
ssh: `SSH`,
|
||
http: `HTTP`,
|
||
https: `HTTPS`,
|
||
}[e] || e.toUpperCase()
|
||
);
|
||
}
|
||
function Wt(e) {
|
||
((C.value = e.page + 1), (w.value = e.rows), z());
|
||
}
|
||
// 确认搜索词后回到第一页查询组列表。
|
||
function Gt() {
|
||
((ct.value = E.value.trim()), (C.value = 1), z());
|
||
}
|
||
// 清空搜索草稿及已应用词,并恢复第一页。
|
||
function Kt() {
|
||
((E.value = ``), (ct.value = ``), (C.value = 1), z());
|
||
}
|
||
// 切换规则方向时恢复规则第一页,再按服务端方向筛选读取。
|
||
function qt(e) {
|
||
((V.value = `rule`), (D.value = e), (F.value = 1), B());
|
||
}
|
||
function Jt(e) {
|
||
((F.value = e.page + 1), (_t.value = e.rows), B());
|
||
}
|
||
return (
|
||
onMounted(async () => {
|
||
await z();
|
||
let e = Number(at.query.id);
|
||
at.path === `/security/groups/group` &&
|
||
Number.isInteger(e) &&
|
||
e > 0 &&
|
||
(await Tt(
|
||
ot.value.find((t) => t.id === e) ?? {
|
||
id: e,
|
||
name: `安全组 #${e}`,
|
||
},
|
||
));
|
||
}),
|
||
// 组列表、规则详情、主机关联和批量规则各自使用独立草稿与提交锁,避免一个弹窗改变另一操作目标。
|
||
(n, r) => {
|
||
let i = button,
|
||
te = PageInfoNotice,
|
||
o = message,
|
||
l = inputtext,
|
||
u = column,
|
||
re = ListEmpty,
|
||
v = datatable,
|
||
ae = paginator,
|
||
y = textarea,
|
||
ue = dialog,
|
||
Se = tag,
|
||
Ce = select,
|
||
we = multiselect,
|
||
b = checkbox;
|
||
return (
|
||
openBlock(),
|
||
createElementBlock(`div`, Te, [
|
||
unref(N)
|
||
? createCommentVNode(``, !0)
|
||
: (openBlock(),
|
||
createElementBlock(`div`, Ee, [
|
||
(r[30] ||= createElementVNode(`h2`, null, `安全组`, -1)),
|
||
createElementVNode(`div`, De, [
|
||
createVNode(i, {
|
||
label: `创建安全组`,
|
||
icon: `pi pi-plus`,
|
||
onClick: xt,
|
||
}),
|
||
]),
|
||
])),
|
||
createVNode(
|
||
te,
|
||
{
|
||
class: `security-group-notice`,
|
||
"aria-label": `安全组使用提示`,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
...(r[31] ||= [
|
||
createTextVNode(
|
||
` 服务器默认放行全部端口。如需限制端口或控制访问,建议优先配置服务器操作系统内的防火墙。平台安全组配置较为复杂,部分场景可能存在兼容性限制。 `,
|
||
-1,
|
||
),
|
||
]),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
unref(T) && !unref(A) && !unref(N)
|
||
? (openBlock(),
|
||
createBlock(
|
||
o,
|
||
{
|
||
key: 1,
|
||
severity: `error`,
|
||
closable: !1,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(toDisplayString(unref(T)), 1),
|
||
]),
|
||
_: 1,
|
||
},
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
unref(N)
|
||
? createCommentVNode(``, !0)
|
||
: (openBlock(),
|
||
createElementBlock(`div`, Oe, [
|
||
createVNode(
|
||
l,
|
||
{
|
||
modelValue: unref(E),
|
||
"onUpdate:modelValue": (r[0] ||= (e) =>
|
||
isRef(E) ? (E.value = e) : null),
|
||
class: `list-search`,
|
||
"aria-label": `搜索安全组`,
|
||
placeholder: `输入关键字`,
|
||
onKeyup: withKeys(Gt, [`enter`]),
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`],
|
||
),
|
||
createVNode(i, {
|
||
label: `搜索`,
|
||
icon: `pi pi-search`,
|
||
severity: `secondary`,
|
||
outlined: ``,
|
||
onClick: Gt,
|
||
}),
|
||
createVNode(i, {
|
||
label: `清除筛选`,
|
||
icon: `pi pi-filter-slash`,
|
||
severity: `secondary`,
|
||
outlined: ``,
|
||
onClick: Kt,
|
||
}),
|
||
])),
|
||
unref(N)
|
||
? createCommentVNode(``, !0)
|
||
: (openBlock(),
|
||
createElementBlock(`div`, ke, [
|
||
createVNode(
|
||
v,
|
||
{
|
||
value: unref(lt),
|
||
loading: unref(st),
|
||
lazy: ``,
|
||
rows: unref(w),
|
||
totalRecords: unref(S),
|
||
first: (unref(C) - 1) * unref(w),
|
||
dataKey: `id`,
|
||
},
|
||
{
|
||
empty: withCtx(() => [
|
||
createVNode(re, {
|
||
icon: `pi pi-shield`,
|
||
title: `还没有安全组`,
|
||
description: `创建安全组后可在开通云服务器时选用`,
|
||
}),
|
||
]),
|
||
default: withCtx(() => [
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `ID`,
|
||
style: {
|
||
width: `100px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createTextVNode(
|
||
`#` + toDisplayString(t.id),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `名称`,
|
||
style: {
|
||
width: `200px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createElementVNode(
|
||
`button`,
|
||
{
|
||
type: `button`,
|
||
class: `group-name`,
|
||
onClick: (e) => Tt(t),
|
||
},
|
||
toDisplayString(t.name),
|
||
9,
|
||
Ae,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `描述`,
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createTextVNode(
|
||
toDisplayString(t.description || `--`),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `规则数`,
|
||
style: {
|
||
width: `90px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createTextVNode(
|
||
toDisplayString(t.rule_num ?? 0),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `关联实例`,
|
||
style: {
|
||
width: `100px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createTextVNode(
|
||
toDisplayString(t.host_num ?? 0),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `创建时间`,
|
||
style: {
|
||
width: `170px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createTextVNode(
|
||
toDisplayString(
|
||
t.create_time
|
||
? (`formatTime` in n
|
||
? n.formatTime
|
||
: unref(dict_r))(t.create_time)
|
||
: `--`,
|
||
),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `操作`,
|
||
style: {
|
||
width: `220px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: e }) => [
|
||
createElementVNode(`div`, je, [
|
||
createVNode(
|
||
i,
|
||
{
|
||
label: `规则`,
|
||
size: `small`,
|
||
severity: `secondary`,
|
||
outlined: ``,
|
||
onClick: (t) => Tt(e),
|
||
},
|
||
null,
|
||
8,
|
||
[`onClick`],
|
||
),
|
||
createVNode(
|
||
i,
|
||
{
|
||
label: `编辑`,
|
||
size: `small`,
|
||
severity: `secondary`,
|
||
text: ``,
|
||
onClick: (t) => St(e),
|
||
},
|
||
null,
|
||
8,
|
||
[`onClick`],
|
||
),
|
||
createVNode(
|
||
i,
|
||
{
|
||
icon: `pi pi-trash`,
|
||
"aria-label": `删除安全组`,
|
||
size: `small`,
|
||
severity: `danger`,
|
||
text: ``,
|
||
onClick: (t) => wt(e),
|
||
},
|
||
null,
|
||
8,
|
||
[`onClick`],
|
||
),
|
||
]),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
8,
|
||
[`value`, `loading`, `rows`, `totalRecords`, `first`],
|
||
),
|
||
])),
|
||
!unref(N) && unref(S) > 0
|
||
? (openBlock(),
|
||
createElementBlock(`div`, Me, [
|
||
(r[32] ||= createElementVNode(
|
||
`div`,
|
||
{
|
||
class: `list-bulk`,
|
||
},
|
||
null,
|
||
-1,
|
||
)),
|
||
createVNode(
|
||
ae,
|
||
{
|
||
rows: unref(w),
|
||
totalRecords: unref(S),
|
||
first: (unref(C) - 1) * unref(w),
|
||
rowsPerPageOptions: [10, 20, 50],
|
||
onPage: Wt,
|
||
},
|
||
null,
|
||
8,
|
||
[`rows`, `totalRecords`, `first`],
|
||
),
|
||
]))
|
||
: createCommentVNode(``, !0),
|
||
createVNode(
|
||
ue,
|
||
{
|
||
visible: unref(A),
|
||
"onUpdate:visible": (r[4] ||= (e) =>
|
||
isRef(A) ? (A.value = e) : null),
|
||
header: unref(j) ? `编辑安全组` : `创建安全组`,
|
||
modal: ``,
|
||
draggable: !1,
|
||
style: {
|
||
width: `338px`,
|
||
},
|
||
},
|
||
{
|
||
footer: withCtx(() => [
|
||
createVNode(i, {
|
||
label: `取消`,
|
||
severity: `secondary`,
|
||
text: ``,
|
||
onClick: (r[3] ||= (e) => (A.value = !1)),
|
||
}),
|
||
createVNode(
|
||
i,
|
||
{
|
||
label: `保存`,
|
||
loading: unref(mt),
|
||
onClick: Ct,
|
||
},
|
||
null,
|
||
8,
|
||
[`loading`],
|
||
),
|
||
]),
|
||
default: withCtx(() => [
|
||
createElementVNode(`div`, Ne, [
|
||
createElementVNode(`div`, Pe, [
|
||
createElementVNode(
|
||
`label`,
|
||
{
|
||
for: `${unref(it)}-name`,
|
||
},
|
||
[
|
||
...(r[33] ||= [
|
||
createTextVNode(`名称 `, -1),
|
||
createElementVNode(
|
||
`span`,
|
||
{
|
||
class: `req`,
|
||
},
|
||
`*`,
|
||
-1,
|
||
),
|
||
]),
|
||
],
|
||
8,
|
||
Fe,
|
||
),
|
||
createVNode(
|
||
l,
|
||
{
|
||
id: `${unref(it)}-name`,
|
||
"aria-required": `true`,
|
||
modelValue: unref(M).name,
|
||
"onUpdate:modelValue": (r[1] ||= (e) =>
|
||
(unref(M).name = e)),
|
||
placeholder: `安全组名称`,
|
||
fluid: ``,
|
||
},
|
||
null,
|
||
8,
|
||
[`id`, `modelValue`],
|
||
),
|
||
]),
|
||
createElementVNode(`div`, Ie, [
|
||
createElementVNode(
|
||
`label`,
|
||
{
|
||
for: `${unref(it)}-description`,
|
||
},
|
||
`描述`,
|
||
8,
|
||
Le,
|
||
),
|
||
createVNode(
|
||
y,
|
||
{
|
||
id: `${unref(it)}-description`,
|
||
modelValue: unref(M).description,
|
||
"onUpdate:modelValue": (r[2] ||= (e) =>
|
||
(unref(M).description = e)),
|
||
placeholder: `输入描述`,
|
||
rows: `3`,
|
||
fluid: ``,
|
||
},
|
||
null,
|
||
8,
|
||
[`id`, `modelValue`],
|
||
),
|
||
]),
|
||
unref(T)
|
||
? (openBlock(),
|
||
createBlock(
|
||
o,
|
||
{
|
||
key: 0,
|
||
severity: `error`,
|
||
closable: !1,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(toDisplayString(unref(T)), 1),
|
||
]),
|
||
_: 1,
|
||
},
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
]),
|
||
]),
|
||
_: 1,
|
||
},
|
||
8,
|
||
[`visible`, `header`],
|
||
),
|
||
unref(N)
|
||
? (openBlock(),
|
||
createElementBlock(`section`, Re, [
|
||
createElementVNode(
|
||
`button`,
|
||
{
|
||
type: `button`,
|
||
class: `detail-back`,
|
||
onClick: (r[5] ||= (e) => (N.value = !1)),
|
||
},
|
||
[
|
||
...(r[34] ||= [
|
||
createElementVNode(
|
||
`i`,
|
||
{
|
||
class: `pi pi-arrow-left`,
|
||
},
|
||
null,
|
||
-1,
|
||
),
|
||
createTextVNode(` 返回安全组`, -1),
|
||
]),
|
||
],
|
||
),
|
||
createElementVNode(`div`, ze, [
|
||
createElementVNode(`div`, null, [
|
||
createElementVNode(`h2`, null, [
|
||
createTextVNode(
|
||
toDisplayString(unref(P)?.name || `安全组`) + ` `,
|
||
1,
|
||
),
|
||
createElementVNode(
|
||
`small`,
|
||
null,
|
||
`#` + toDisplayString(unref(P)?.id),
|
||
1,
|
||
),
|
||
]),
|
||
createElementVNode(`div`, Be, [
|
||
createElementVNode(`span`, null, [
|
||
(r[35] ||= createTextVNode(`创建时间`, -1)),
|
||
(r[36] ||= createElementVNode(
|
||
`br`,
|
||
null,
|
||
null,
|
||
-1,
|
||
)),
|
||
createElementVNode(
|
||
`b`,
|
||
null,
|
||
toDisplayString(
|
||
unref(P)?.create_time
|
||
? (`formatTime` in n
|
||
? n.formatTime
|
||
: unref(dict_r))(unref(P).create_time)
|
||
: `—`,
|
||
),
|
||
1,
|
||
),
|
||
]),
|
||
createElementVNode(`span`, null, [
|
||
(r[37] ||= createTextVNode(`描述`, -1)),
|
||
(r[38] ||= createElementVNode(
|
||
`br`,
|
||
null,
|
||
null,
|
||
-1,
|
||
)),
|
||
createElementVNode(
|
||
`b`,
|
||
null,
|
||
toDisplayString(unref(P)?.description || `—`),
|
||
1,
|
||
),
|
||
]),
|
||
]),
|
||
]),
|
||
createElementVNode(`div`, Ve, [
|
||
createVNode(i, {
|
||
label: `编辑安全组`,
|
||
icon: `pi pi-pencil`,
|
||
onClick: (r[6] ||= (e) => unref(P) && St(unref(P))),
|
||
}),
|
||
createVNode(i, {
|
||
label: `删除安全组`,
|
||
icon: `pi pi-trash`,
|
||
severity: `danger`,
|
||
onClick: (r[7] ||= (e) => unref(P) && wt(unref(P))),
|
||
}),
|
||
]),
|
||
]),
|
||
createElementVNode(`div`, He, [
|
||
createElementVNode(`div`, Ue, [
|
||
createElementVNode(
|
||
`button`,
|
||
{
|
||
type: `button`,
|
||
class: normalizeClass({
|
||
active:
|
||
unref(V) === `rule` && unref(D) === `in`,
|
||
}),
|
||
onClick: (r[8] ||= (e) => qt(`in`)),
|
||
},
|
||
`入方向`,
|
||
2,
|
||
),
|
||
createElementVNode(
|
||
`button`,
|
||
{
|
||
type: `button`,
|
||
class: normalizeClass({
|
||
active:
|
||
unref(V) === `rule` && unref(D) === `out`,
|
||
}),
|
||
onClick: (r[9] ||= (e) => qt(`out`)),
|
||
},
|
||
`出方向`,
|
||
2,
|
||
),
|
||
createElementVNode(
|
||
`button`,
|
||
{
|
||
type: `button`,
|
||
class: normalizeClass({
|
||
active: unref(V) === `host`,
|
||
}),
|
||
onClick: (r[10] ||= (e) => (V.value = `host`)),
|
||
},
|
||
`关联实例`,
|
||
2,
|
||
),
|
||
]),
|
||
unref(V) === `rule`
|
||
? (openBlock(),
|
||
createElementBlock(
|
||
Fragment,
|
||
{
|
||
key: 0,
|
||
},
|
||
[
|
||
createElementVNode(`div`, We, [
|
||
createVNode(
|
||
l,
|
||
{
|
||
modelValue: unref(I),
|
||
"onUpdate:modelValue": (r[11] ||= (e) =>
|
||
isRef(I) ? (I.value = e) : null),
|
||
placeholder: `搜索规则`,
|
||
"aria-label": `搜索安全组规则`,
|
||
onKeyup: (r[12] ||= withKeys(
|
||
(e) => {
|
||
((F.value = 1), B());
|
||
},
|
||
[`enter`],
|
||
)),
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`],
|
||
),
|
||
createVNode(i, {
|
||
label: `搜索`,
|
||
icon: `pi pi-search`,
|
||
severity: `secondary`,
|
||
outlined: ``,
|
||
onClick: (r[13] ||= (e) => {
|
||
((F.value = 1), B());
|
||
}),
|
||
}),
|
||
createVNode(i, {
|
||
label: `快速添加`,
|
||
icon: `pi pi-plus`,
|
||
onClick: (r[14] ||= (e) => (Q.value = !0)),
|
||
}),
|
||
createVNode(i, {
|
||
label: `手动添加`,
|
||
icon: `pi pi-plus`,
|
||
severity: `secondary`,
|
||
text: ``,
|
||
onClick: ft,
|
||
}),
|
||
]),
|
||
unref(T)
|
||
? (openBlock(),
|
||
createBlock(
|
||
o,
|
||
{
|
||
key: 0,
|
||
severity: `error`,
|
||
closable: !1,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(
|
||
toDisplayString(unref(T)),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
unref(Bt)
|
||
? (openBlock(),
|
||
createBlock(
|
||
o,
|
||
{
|
||
key: 1,
|
||
severity: `success`,
|
||
closable: !1,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(
|
||
toDisplayString(unref(Bt)),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
createVNode(
|
||
v,
|
||
{
|
||
value: unref(bt),
|
||
loading: unref(vt),
|
||
dataKey: `id`,
|
||
scrollable: ``,
|
||
},
|
||
{
|
||
empty: withCtx(() => [
|
||
...(r[39] ||= [
|
||
createElementVNode(
|
||
`div`,
|
||
{
|
||
class: `empty-rule`,
|
||
},
|
||
`暂无规则`,
|
||
-1,
|
||
),
|
||
]),
|
||
]),
|
||
default: withCtx(() => [
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `方向`,
|
||
style: {
|
||
width: `80px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: e }) => [
|
||
createVNode(
|
||
Se,
|
||
{
|
||
value:
|
||
e.direction === `in`
|
||
? `入站`
|
||
: `出站`,
|
||
severity:
|
||
e.direction === `in`
|
||
? `info`
|
||
: `secondary`,
|
||
},
|
||
null,
|
||
8,
|
||
[`value`, `severity`],
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `授权策略`,
|
||
style: {
|
||
width: `110px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(() => [
|
||
createVNode(Se, {
|
||
value: `允许`,
|
||
severity: `success`,
|
||
}),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `协议`,
|
||
style: {
|
||
width: `90px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createTextVNode(
|
||
toDisplayString(Ut(t.protocol)),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `端口`,
|
||
style: {
|
||
width: `130px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createTextVNode(
|
||
toDisplayString(t.port),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `来源/目标 IP`,
|
||
style: {
|
||
width: `160px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createElementVNode(
|
||
`span`,
|
||
Ge,
|
||
toDisplayString(t.ip),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `描述`,
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createTextVNode(
|
||
toDisplayString(
|
||
t.description || `--`,
|
||
),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `操作`,
|
||
style: {
|
||
width: `80px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: e }) => [
|
||
createVNode(
|
||
i,
|
||
{
|
||
icon: `pi pi-pencil`,
|
||
"aria-label": `编辑规则`,
|
||
size: `small`,
|
||
severity: `secondary`,
|
||
text: ``,
|
||
onClick: (t) => pt(e),
|
||
},
|
||
null,
|
||
8,
|
||
[`onClick`],
|
||
),
|
||
createVNode(
|
||
i,
|
||
{
|
||
icon: `pi pi-trash`,
|
||
"aria-label": `删除规则`,
|
||
size: `small`,
|
||
severity: `danger`,
|
||
text: ``,
|
||
onClick: (t) => Ht(e),
|
||
},
|
||
null,
|
||
8,
|
||
[`onClick`],
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
8,
|
||
[`value`, `loading`],
|
||
),
|
||
unref(gt) > 0
|
||
? (openBlock(),
|
||
createBlock(
|
||
ae,
|
||
{
|
||
key: 2,
|
||
rows: unref(_t),
|
||
totalRecords: unref(gt),
|
||
first: (unref(F) - 1) * unref(_t),
|
||
rowsPerPageOptions: [20, 50, 100],
|
||
onPage: Jt,
|
||
},
|
||
null,
|
||
8,
|
||
[`rows`, `totalRecords`, `first`],
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
],
|
||
64,
|
||
))
|
||
: (openBlock(),
|
||
createElementBlock(
|
||
Fragment,
|
||
{
|
||
key: 1,
|
||
},
|
||
[
|
||
unref(q)
|
||
? (openBlock(),
|
||
createBlock(
|
||
o,
|
||
{
|
||
key: 0,
|
||
severity: `error`,
|
||
closable: !1,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(
|
||
toDisplayString(unref(q)) + ` `,
|
||
1,
|
||
),
|
||
createVNode(i, {
|
||
label: `重试关联实例`,
|
||
text: ``,
|
||
onClick: Z,
|
||
}),
|
||
]),
|
||
_: 1,
|
||
},
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
unref(J)
|
||
? (openBlock(),
|
||
createBlock(
|
||
o,
|
||
{
|
||
key: 1,
|
||
severity: `error`,
|
||
closable: !1,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(
|
||
toDisplayString(unref(J)) + ` `,
|
||
1,
|
||
),
|
||
createVNode(i, {
|
||
label: `重试可用实例`,
|
||
text: ``,
|
||
onClick: jt,
|
||
}),
|
||
]),
|
||
_: 1,
|
||
},
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
createElementVNode(`div`, Ke, [
|
||
createElementVNode(`div`, qe, [
|
||
createVNode(
|
||
Ce,
|
||
{
|
||
modelValue: unref(U),
|
||
"onUpdate:modelValue": (r[15] ||= (
|
||
e,
|
||
) => (isRef(U) ? (U.value = e) : null)),
|
||
options: unref(Nt),
|
||
optionLabel: `label`,
|
||
optionValue: `value`,
|
||
filter: ``,
|
||
placeholder: `选择要关联的实例`,
|
||
class: `grow`,
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`, `options`],
|
||
),
|
||
createVNode(
|
||
i,
|
||
{
|
||
label: `关联`,
|
||
size: `small`,
|
||
loading: unref(G),
|
||
disabled: !unref(U) || unref(X),
|
||
onClick: Ft,
|
||
},
|
||
null,
|
||
8,
|
||
[`loading`, `disabled`],
|
||
),
|
||
]),
|
||
createElementVNode(`div`, Je, [
|
||
createVNode(
|
||
we,
|
||
{
|
||
modelValue: unref(W),
|
||
"onUpdate:modelValue": (r[16] ||= (
|
||
e,
|
||
) => (isRef(W) ? (W.value = e) : null)),
|
||
options: unref(Nt),
|
||
optionLabel: `label`,
|
||
optionValue: `value`,
|
||
filter: ``,
|
||
display: `chip`,
|
||
maxSelectedLabels: 2,
|
||
placeholder: `批量选择多个实例`,
|
||
class: `grow`,
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`, `options`],
|
||
),
|
||
createVNode(
|
||
i,
|
||
{
|
||
label: `批量关联`,
|
||
icon: `pi pi-link`,
|
||
size: `small`,
|
||
loading: unref(Ot),
|
||
disabled: !unref(W).length || unref(X),
|
||
onClick: Pt,
|
||
},
|
||
null,
|
||
8,
|
||
[`loading`, `disabled`],
|
||
),
|
||
]),
|
||
unref(T)
|
||
? (openBlock(),
|
||
createBlock(
|
||
o,
|
||
{
|
||
key: 0,
|
||
severity: `error`,
|
||
closable: !1,
|
||
class: `rule-error`,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(
|
||
toDisplayString(unref(T)),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
unref(K)
|
||
? (openBlock(),
|
||
createBlock(
|
||
o,
|
||
{
|
||
key: 1,
|
||
severity: `success`,
|
||
closable: !1,
|
||
class: `rule-error`,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(
|
||
toDisplayString(unref(K)),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
(r[40] ||= createElementVNode(
|
||
`p`,
|
||
{
|
||
class: `sg-note`,
|
||
},
|
||
` 仅部分云服务器实例支持加入安全组。不受支持的实例,可在其详情页的「网络与安全」中查看其自带的安全策略。 `,
|
||
-1,
|
||
)),
|
||
]),
|
||
createElementVNode(`div`, Ye, [
|
||
createVNode(
|
||
l,
|
||
{
|
||
modelValue: unref(kt),
|
||
"onUpdate:modelValue": (r[17] ||= (e) =>
|
||
isRef(kt) ? (kt.value = e) : null),
|
||
placeholder: `搜索关联实例名称或 IP`,
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`],
|
||
),
|
||
createVNode(
|
||
i,
|
||
{
|
||
label: `刷新实例`,
|
||
loading: unref(H) || unref(Y),
|
||
text: ``,
|
||
onClick: (r[18] ||= (e) => {
|
||
(Z(), jt());
|
||
}),
|
||
},
|
||
null,
|
||
8,
|
||
[`loading`],
|
||
),
|
||
]),
|
||
createVNode(
|
||
v,
|
||
{
|
||
value: unref(At),
|
||
loading: unref(H),
|
||
dataKey: `id`,
|
||
scrollable: ``,
|
||
paginator: ``,
|
||
rows: 20,
|
||
"rows-per-page-options": [20, 50, 100],
|
||
},
|
||
{
|
||
empty: withCtx(() => [
|
||
...(r[41] ||= [
|
||
createElementVNode(
|
||
`div`,
|
||
{
|
||
class: `empty-rule`,
|
||
},
|
||
`尚未关联实例`,
|
||
-1,
|
||
),
|
||
]),
|
||
]),
|
||
default: withCtx(() => [
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `实例名称`,
|
||
style: {
|
||
"min-width": `200px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createTextVNode(
|
||
toDisplayString(t.name),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `IP`,
|
||
style: {
|
||
width: `170px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createElementVNode(
|
||
`span`,
|
||
Xe,
|
||
toDisplayString(t.ip || `--`),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `操作`,
|
||
style: {
|
||
width: `110px`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: e }) => [
|
||
createVNode(
|
||
i,
|
||
{
|
||
label: `取消关联`,
|
||
size: `small`,
|
||
severity: `danger`,
|
||
text: ``,
|
||
onClick: (t) => It(e),
|
||
},
|
||
null,
|
||
8,
|
||
[`onClick`],
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
8,
|
||
[`value`, `loading`],
|
||
),
|
||
],
|
||
64,
|
||
)),
|
||
]),
|
||
]))
|
||
: createCommentVNode(``, !0),
|
||
createVNode(
|
||
ue,
|
||
{
|
||
visible: unref(O),
|
||
"onUpdate:visible": (r[25] ||= (e) =>
|
||
isRef(O) ? (O.value = e) : null),
|
||
header: unref(R) ? `编辑规则` : `手动添加`,
|
||
modal: ``,
|
||
draggable: !1,
|
||
style: {
|
||
width: `720px`,
|
||
},
|
||
},
|
||
{
|
||
footer: withCtx(() => [
|
||
createVNode(i, {
|
||
label: `取消`,
|
||
severity: `secondary`,
|
||
text: ``,
|
||
onClick: (r[24] ||= (e) => {
|
||
((O.value = !1), (R.value = null));
|
||
}),
|
||
}),
|
||
createVNode(
|
||
i,
|
||
{
|
||
label: unref(R) ? `保存修改` : `确认`,
|
||
icon: `pi pi-check`,
|
||
loading: unref(yt),
|
||
onClick: Vt,
|
||
},
|
||
null,
|
||
8,
|
||
[`label`, `loading`],
|
||
),
|
||
]),
|
||
default: withCtx(() => [
|
||
createVNode(
|
||
o,
|
||
{
|
||
severity: `info`,
|
||
closable: !1,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
...(r[42] ||= [
|
||
createTextVNode(
|
||
`请仅开放需要的端口,并将授权对象限制为可信的 IP 地址。`,
|
||
-1,
|
||
),
|
||
]),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createElementVNode(`div`, Ze, [
|
||
(r[43] ||= createElementVNode(
|
||
`label`,
|
||
null,
|
||
`方向`,
|
||
-1,
|
||
)),
|
||
createVNode(
|
||
Ce,
|
||
{
|
||
"aria-label": `规则方向`,
|
||
modelValue: unref(L).direction,
|
||
"onUpdate:modelValue": (r[19] ||= (e) =>
|
||
(unref(L).direction = e)),
|
||
options: [
|
||
{
|
||
label: `入站`,
|
||
value: `in`,
|
||
},
|
||
{
|
||
label: `出站`,
|
||
value: `out`,
|
||
},
|
||
],
|
||
optionLabel: `label`,
|
||
optionValue: `value`,
|
||
class: `w-90`,
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`],
|
||
),
|
||
(r[44] ||= createElementVNode(
|
||
`label`,
|
||
null,
|
||
`协议`,
|
||
-1,
|
||
)),
|
||
createVNode(
|
||
Ce,
|
||
{
|
||
"aria-label": `规则协议`,
|
||
modelValue: unref(L).protocol,
|
||
"onUpdate:modelValue": (r[20] ||= (e) =>
|
||
(unref(L).protocol = e)),
|
||
options: [
|
||
{
|
||
label: `TCP`,
|
||
value: `tcp`,
|
||
},
|
||
{
|
||
label: `UDP`,
|
||
value: `udp`,
|
||
},
|
||
{
|
||
label: `ICMP`,
|
||
value: `icmp`,
|
||
},
|
||
{
|
||
label: `全部`,
|
||
value: `all`,
|
||
},
|
||
],
|
||
optionLabel: `label`,
|
||
optionValue: `value`,
|
||
class: `w-100`,
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`],
|
||
),
|
||
(r[45] ||= createElementVNode(
|
||
`label`,
|
||
null,
|
||
`端口范围`,
|
||
-1,
|
||
)),
|
||
createVNode(
|
||
l,
|
||
{
|
||
"aria-label": `端口范围`,
|
||
modelValue: unref(L).port,
|
||
"onUpdate:modelValue": (r[21] ||= (e) =>
|
||
(unref(L).port = e)),
|
||
placeholder: `端口 22 / 100-65535`,
|
||
class: `w-port`,
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`],
|
||
),
|
||
(r[46] ||= createElementVNode(
|
||
`label`,
|
||
null,
|
||
`授权对象`,
|
||
-1,
|
||
)),
|
||
createVNode(
|
||
l,
|
||
{
|
||
"aria-label": `授权对象`,
|
||
modelValue: unref(L).ip,
|
||
"onUpdate:modelValue": (r[22] ||= (e) =>
|
||
(unref(L).ip = e)),
|
||
placeholder: `来源 IP`,
|
||
class: `w-160`,
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`],
|
||
),
|
||
(r[47] ||= createElementVNode(
|
||
`label`,
|
||
null,
|
||
`描述`,
|
||
-1,
|
||
)),
|
||
createVNode(
|
||
l,
|
||
{
|
||
"aria-label": `规则描述`,
|
||
modelValue: unref(L).description,
|
||
"onUpdate:modelValue": (r[23] ||= (e) =>
|
||
(unref(L).description = e)),
|
||
placeholder: `描述(选填)`,
|
||
class: `grow`,
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`],
|
||
),
|
||
]),
|
||
unref(T)
|
||
? (openBlock(),
|
||
createBlock(
|
||
o,
|
||
{
|
||
key: 0,
|
||
severity: `error`,
|
||
closable: !1,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(toDisplayString(unref(T)), 1),
|
||
]),
|
||
_: 1,
|
||
},
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
]),
|
||
_: 1,
|
||
},
|
||
8,
|
||
[`visible`, `header`],
|
||
),
|
||
createVNode(
|
||
ue,
|
||
{
|
||
visible: unref(Q),
|
||
"onUpdate:visible": (r[29] ||= (e) =>
|
||
isRef(Q) ? (Q.value = e) : null),
|
||
header: `快速添加`,
|
||
modal: ``,
|
||
draggable: !1,
|
||
style: {
|
||
width: `960px`,
|
||
},
|
||
},
|
||
{
|
||
footer: withCtx(() => [
|
||
createVNode(i, {
|
||
label: `取消`,
|
||
severity: `secondary`,
|
||
text: ``,
|
||
onClick: (r[28] ||= (e) => (Q.value = !1)),
|
||
}),
|
||
createVNode(
|
||
i,
|
||
{
|
||
label: `添加`,
|
||
loading: unref(Lt),
|
||
onClick: zt,
|
||
},
|
||
null,
|
||
8,
|
||
[`loading`],
|
||
),
|
||
]),
|
||
default: withCtx(() => [
|
||
createElementVNode(`div`, Qe, [
|
||
createVNode(
|
||
o,
|
||
{
|
||
severity: `info`,
|
||
closable: !1,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
...(r[48] ||= [
|
||
createTextVNode(
|
||
`请选择需要开放的服务。请勿添加重复规则,公网服务建议限制授权 IP 范围。`,
|
||
-1,
|
||
),
|
||
]),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createElementVNode(`div`, $e, [
|
||
(openBlock(),
|
||
createElementBlock(
|
||
Fragment,
|
||
null,
|
||
renderList(ut, (t) =>
|
||
createElementVNode(
|
||
`label`,
|
||
{
|
||
key: t.port,
|
||
},
|
||
[
|
||
createVNode(
|
||
b,
|
||
{
|
||
modelValue: unref(k),
|
||
"onUpdate:modelValue": (r[26] ||= (e) =>
|
||
isRef(k) ? (k.value = e) : null),
|
||
value: t.port,
|
||
"input-id": `quick-port-${t.port}`,
|
||
onChange: dt,
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`, `value`, `input-id`],
|
||
),
|
||
createElementVNode(
|
||
`span`,
|
||
null,
|
||
toDisplayString(t.name) +
|
||
` (` +
|
||
toDisplayString(t.port) +
|
||
`)`,
|
||
1,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
64,
|
||
)),
|
||
]),
|
||
createElementVNode(`details`, et, [
|
||
(r[49] ||= createElementVNode(
|
||
`summary`,
|
||
null,
|
||
`高级规则编辑`,
|
||
-1,
|
||
)),
|
||
(r[50] ||= createElementVNode(
|
||
`p`,
|
||
{
|
||
class: `batch-hint`,
|
||
},
|
||
[
|
||
createTextVNode(` 每行一条规则,用空格分隔:`),
|
||
createElementVNode(
|
||
`code`,
|
||
null,
|
||
`方向 协议 端口 IP 描述`,
|
||
),
|
||
createElementVNode(`br`),
|
||
createTextVNode(` 方向 `),
|
||
createElementVNode(`code`, null, `in`),
|
||
createTextVNode(` 入站 / `),
|
||
createElementVNode(`code`, null, `out`),
|
||
createTextVNode(` 出站;协议支持 `),
|
||
createElementVNode(
|
||
`code`,
|
||
null,
|
||
`tcp udp icmp all`,
|
||
),
|
||
createTextVNode(` 等。 `),
|
||
],
|
||
-1,
|
||
)),
|
||
createVNode(
|
||
y,
|
||
{
|
||
modelValue: unref($),
|
||
"onUpdate:modelValue": (r[27] ||= (e) =>
|
||
isRef($) ? ($.value = e) : null),
|
||
rows: `8`,
|
||
placeholder: `in tcp 22 0.0.0.0/0 SSH
|
||
in tcp 80-443 0.0.0.0/0 Web`,
|
||
autoResize: ``,
|
||
fluid: ``,
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`],
|
||
),
|
||
]),
|
||
unref(T)
|
||
? (openBlock(),
|
||
createBlock(
|
||
o,
|
||
{
|
||
key: 0,
|
||
severity: `error`,
|
||
closable: !1,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(toDisplayString(unref(T)), 1),
|
||
]),
|
||
_: 1,
|
||
},
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
createVNode(
|
||
v,
|
||
{
|
||
value: ut.filter((e) => unref(k).includes(e.port)),
|
||
},
|
||
{
|
||
empty: withCtx(() => [
|
||
...(r[53] ||= [createTextVNode(`暂无数据`, -1)]),
|
||
]),
|
||
default: withCtx(() => [
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `方向`,
|
||
},
|
||
{
|
||
body: withCtx(() => [
|
||
createTextVNode(
|
||
toDisplayString(
|
||
unref(D) === `in` ? `入方向` : `出方向`,
|
||
),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `授权策略`,
|
||
},
|
||
{
|
||
body: withCtx(() => [
|
||
...(r[51] ||= [
|
||
createTextVNode(`允许`, -1),
|
||
]),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `协议`,
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createTextVNode(
|
||
toDisplayString(
|
||
t.port === `69` ? `UDP` : `TCP`,
|
||
),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(u, {
|
||
field: `port`,
|
||
header: `端口范围`,
|
||
}),
|
||
createVNode(
|
||
u,
|
||
{
|
||
header: `授权对象`,
|
||
},
|
||
{
|
||
body: withCtx(() => [
|
||
...(r[52] ||= [
|
||
createTextVNode(
|
||
`所有 IPv4 (0.0.0.0/0)`,
|
||
-1,
|
||
),
|
||
]),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
8,
|
||
[`value`],
|
||
),
|
||
]),
|
||
]),
|
||
_: 1,
|
||
},
|
||
8,
|
||
[`visible`],
|
||
),
|
||
])
|
||
);
|
||
}
|
||
);
|
||
},
|
||
}),
|
||
[[`__scopeId`, `data-v-fe145bae`]],
|
||
);
|
||
export {};
|
||
export default b;
|
||
</script>
|