741 lines
27 KiB
Vue
741 lines
27 KiB
Vue
<script>
|
||
/**
|
||
* 云实例 IPv4/IPv6 列表及附加地址购买;addresses 事件供父级更新可选 IP,pay 事件交接订单。
|
||
* 仅支持的云模块开放附加地址操作,upgradeAllowed 来自父级业务状态而非本地授权。
|
||
*/
|
||
import "~/assets/styles/MfCloudIps.jhFnuvcs.css";
|
||
import { toDisplayString } from "vue";
|
||
import { onMounted } 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 { 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 datatable from "primevue/datatable";
|
||
import inputnumber from "primevue/inputnumber";
|
||
import column from "primevue/column";
|
||
import OperationNotice from "~/components/OperationNotice.vue";
|
||
import { i as cloud_instance_management_i } from "~/lib/cloud-instance-management.js";
|
||
import { s as cloud_instance_management_s } from "~/lib/cloud-instance-management.js";
|
||
import { t as cloud_instance_management_t } from "~/lib/cloud-instance-management.js";
|
||
var ce = {
|
||
class: `ips`,
|
||
},
|
||
C = {
|
||
class: `ips-head network-head`,
|
||
},
|
||
w = {
|
||
class: `ips-title`,
|
||
},
|
||
T = {
|
||
key: 0,
|
||
class: `count`,
|
||
},
|
||
E = {
|
||
class: `network-actions`,
|
||
},
|
||
le = {
|
||
class: `col-mono`,
|
||
},
|
||
ue = {
|
||
class: `col-mono`,
|
||
},
|
||
de = {
|
||
class: `col-mono`,
|
||
},
|
||
fe = {
|
||
class: `ipv6-group`,
|
||
},
|
||
pe = {
|
||
class: `ips-title ipv6-title`,
|
||
},
|
||
me = {
|
||
key: 0,
|
||
class: `count`,
|
||
},
|
||
he = {
|
||
class: `col-mono`,
|
||
},
|
||
ge = {
|
||
class: `dlg`,
|
||
},
|
||
_e = {
|
||
class: `form-row`,
|
||
},
|
||
ve = {
|
||
class: `form-row`,
|
||
},
|
||
ye = {
|
||
class: `quote`,
|
||
},
|
||
be = {
|
||
class: `quote-value`,
|
||
},
|
||
D = {
|
||
key: 0,
|
||
class: `pi pi-spin pi-spinner`,
|
||
},
|
||
O = Object.assign(
|
||
_plugin_vue_export_helper_t(
|
||
defineComponent({
|
||
__name: `MfCloudIps`,
|
||
props: {
|
||
hostId: {},
|
||
module: {
|
||
default: `mf_cloud`,
|
||
},
|
||
upgradeAllowed: {
|
||
type: Boolean,
|
||
default: !0,
|
||
},
|
||
},
|
||
emits: [`pay`, `addresses`],
|
||
setup(h, { emit: b }) {
|
||
let O = h,
|
||
k = b,
|
||
A = useApi(),
|
||
j = ref(``),
|
||
M = ref([]),
|
||
N = ref([]),
|
||
P = ref(!1),
|
||
F = ref(``),
|
||
I = ref(``),
|
||
L = 0,
|
||
R = 0,
|
||
z = 0,
|
||
B = !1;
|
||
/** 两类地址并行加载、分别显示错误;每类以 100 条分页完整拉取,实例变化使旧结果失效。 */
|
||
async function V() {
|
||
let e = ++L,
|
||
t = `/${O.module}/${O.hostId}`;
|
||
((P.value = !0), (F.value = ``), (I.value = ``));
|
||
// count 缺失时不足一页结束;有 count 却提前返回空页时不能当作完整列表。
|
||
async function n(n) {
|
||
let r = [],
|
||
i = 1;
|
||
for (;;) {
|
||
let a = await A.get(`${t}/${n}`, {
|
||
page: i,
|
||
limit: 100,
|
||
});
|
||
if (B || e !== L) return null;
|
||
if (!Array.isArray(a.list)) throw Error(`地址列表无效,请重试`);
|
||
if (
|
||
(r.push(...a.list),
|
||
a.count === void 0
|
||
? a.list.length < 100
|
||
: r.length >= Number(a.count))
|
||
)
|
||
return r;
|
||
if (!a.list.length) throw Error(`地址列表不完整,请重试`);
|
||
i++;
|
||
}
|
||
}
|
||
(await Promise.allSettled([
|
||
n(`ip`)
|
||
.then((e) => {
|
||
e &&
|
||
((M.value = e),
|
||
k(`addresses`, e.map((e) => e.ip).filter(Boolean)));
|
||
})
|
||
.catch((t) => {
|
||
!B &&
|
||
e === L &&
|
||
(F.value =
|
||
t instanceof Error ? t.message : `IPv4 加载失败`);
|
||
}),
|
||
...(cloud_instance_management_i(O.module)
|
||
? [
|
||
n(`ipv6`)
|
||
.then((e) => {
|
||
e && (N.value = e);
|
||
})
|
||
.catch((t) => {
|
||
!B &&
|
||
e === L &&
|
||
(I.value =
|
||
t instanceof Error ? t.message : `IPv6 加载失败`);
|
||
}),
|
||
]
|
||
: []),
|
||
]),
|
||
!B && e === L && (P.value = !1));
|
||
}
|
||
let H = ref(!1),
|
||
U = ref(!1),
|
||
W = ref(``),
|
||
G = reactive({
|
||
ip_num: 1,
|
||
ipv6_num: 0,
|
||
}),
|
||
K = ref(``),
|
||
q = ref(!1),
|
||
J = ref(``),
|
||
// 数量、权限、实例和弹窗共同构成取价快照,任何一项变化都必须重新报价。
|
||
Y = computed(() =>
|
||
JSON.stringify([
|
||
O.hostId,
|
||
O.module,
|
||
O.upgradeAllowed,
|
||
H.value,
|
||
G,
|
||
]),
|
||
),
|
||
// IPv4/IPv6 可分别为 0,但总数必须大于 0;不能提交负数或非整数数量。
|
||
X = computed(
|
||
() =>
|
||
Number.isInteger(G.ip_num) &&
|
||
G.ip_num >= 0 &&
|
||
Number.isInteger(G.ipv6_num) &&
|
||
G.ipv6_num >= 0 &&
|
||
G.ip_num + G.ipv6_num > 0,
|
||
),
|
||
Z = computed(
|
||
() =>
|
||
O.upgradeAllowed !== !1 &&
|
||
H.value &&
|
||
X.value &&
|
||
!!J.value &&
|
||
J.value === Y.value &&
|
||
!q.value &&
|
||
!U.value,
|
||
);
|
||
// 清除报价并递增序号,使较早的取价响应无法恢复过期的可提交状态。
|
||
function Q() {
|
||
(R++, (K.value = ``), (J.value = ``), (q.value = !1));
|
||
}
|
||
watch(Y, Q, {
|
||
flush: `sync`,
|
||
});
|
||
// 每次打开恢复为一枚 IPv4,并立即获取对应价格。
|
||
async function xe() {
|
||
O.upgradeAllowed !== !1 &&
|
||
!U.value &&
|
||
cloud_instance_management_i(O.module) &&
|
||
((G.ip_num = 1),
|
||
(G.ipv6_num = 0),
|
||
(W.value = ``),
|
||
(H.value = !0),
|
||
await $());
|
||
}
|
||
/** 询价只更新与当前数量快照一致的响应,合法的零价格也能通过金额适配器。 */
|
||
async function $() {
|
||
if ((Q(), O.upgradeAllowed === !1 || !H.value || !X.value)) return;
|
||
let e = ++R,
|
||
t = Y.value;
|
||
((q.value = !0), (W.value = ``));
|
||
try {
|
||
let n = await A.get(`/${O.module}/${O.hostId}/ip_num`, {
|
||
ip_num: G.ip_num,
|
||
ipv6_num: G.ipv6_num,
|
||
});
|
||
if (B || e !== R || t !== Y.value) return;
|
||
((K.value = cloud_instance_management_t(n.price)), (J.value = t));
|
||
} catch (t) {
|
||
!B &&
|
||
e === R &&
|
||
(W.value = t instanceof Error ? t.message : `询价失败`);
|
||
} finally {
|
||
!B && e === R && (q.value = !1);
|
||
}
|
||
}
|
||
/** 有有效报价才创建 ip_num/order,校验订单号后关闭弹窗并发 pay;不自动重复下单。 */
|
||
async function Se() {
|
||
if (!Z.value || B) return;
|
||
let e = Y.value,
|
||
t = K.value,
|
||
n = ++z;
|
||
((U.value = !0), (W.value = ``), (j.value = ``));
|
||
try {
|
||
let n = await A.post(`/${O.module}/${O.hostId}/ip_num/order`, {
|
||
ip_num: G.ip_num,
|
||
ipv6_num: G.ipv6_num,
|
||
});
|
||
if (B || e !== Y.value) return;
|
||
let r = cloud_instance_management_s(n.id);
|
||
((H.value = !1),
|
||
k(`pay`, {
|
||
order_id: r,
|
||
amount: t,
|
||
}));
|
||
} catch (t) {
|
||
!B &&
|
||
e === Y.value &&
|
||
(W.value = t instanceof Error ? t.message : `下单失败`);
|
||
} finally {
|
||
!B && n === z && (U.value = !1);
|
||
}
|
||
}
|
||
return (
|
||
onMounted(V),
|
||
watch(
|
||
() => [O.hostId, O.module],
|
||
() => {
|
||
(z++,
|
||
(H.value = !1),
|
||
(U.value = !1),
|
||
(M.value = []),
|
||
(N.value = []),
|
||
V());
|
||
},
|
||
),
|
||
// 离开面板后作废地址请求和报价请求,防止回填已销毁界面。
|
||
onBeforeUnmount(() => {
|
||
((B = !0), L++, Q());
|
||
}),
|
||
// 两张地址表共享加载态但分别呈现错误;购买弹窗展示当前数量、报价和重报价入口。
|
||
(t, n) => {
|
||
let r = OperationNotice,
|
||
a = button,
|
||
c = message,
|
||
l = column,
|
||
v = datatable,
|
||
b = inputnumber,
|
||
x = dialog;
|
||
return (
|
||
openBlock(),
|
||
createElementBlock(`div`, ce, [
|
||
createVNode(
|
||
r,
|
||
{
|
||
message: unref(j),
|
||
severity: `error`,
|
||
},
|
||
null,
|
||
8,
|
||
[`message`],
|
||
),
|
||
createElementVNode(`div`, C, [
|
||
createElementVNode(`div`, w, [
|
||
(n[4] ||= createTextVNode(`IP 列表`, -1)),
|
||
unref(M).length
|
||
? (openBlock(),
|
||
createElementBlock(
|
||
`span`,
|
||
T,
|
||
`(` + toDisplayString(unref(M).length) + `)`,
|
||
1,
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
]),
|
||
createElementVNode(`div`, E, [
|
||
h.upgradeAllowed === !1
|
||
? createCommentVNode(``, !0)
|
||
: (openBlock(),
|
||
createBlock(a, {
|
||
key: 0,
|
||
label: `购买附加 IP`,
|
||
icon: `pi pi-plus`,
|
||
size: `small`,
|
||
onClick: xe,
|
||
})),
|
||
createVNode(
|
||
a,
|
||
{
|
||
label: `刷新地址`,
|
||
icon: `pi pi-refresh`,
|
||
size: `small`,
|
||
text: ``,
|
||
loading: unref(P),
|
||
onClick: V,
|
||
},
|
||
null,
|
||
8,
|
||
[`loading`],
|
||
),
|
||
]),
|
||
]),
|
||
unref(F)
|
||
? (openBlock(),
|
||
createBlock(
|
||
c,
|
||
{
|
||
key: 0,
|
||
severity: `error`,
|
||
closable: !1,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(toDisplayString(unref(F)), 1),
|
||
]),
|
||
_: 1,
|
||
},
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
createVNode(
|
||
v,
|
||
{
|
||
class: `network-table ipv4-table`,
|
||
value: unref(M),
|
||
loading: unref(P),
|
||
size: `small`,
|
||
scrollable: ``,
|
||
},
|
||
{
|
||
empty: withCtx(() => [
|
||
...(n[5] ||= [
|
||
createElementVNode(
|
||
`div`,
|
||
{
|
||
class: `empty`,
|
||
},
|
||
`暂无 IP 信息`,
|
||
-1,
|
||
),
|
||
]),
|
||
]),
|
||
default: withCtx(() => [
|
||
createVNode(
|
||
l,
|
||
{
|
||
field: `ip`,
|
||
header: `IP 地址`,
|
||
style: {
|
||
width: `40%`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createElementVNode(
|
||
`span`,
|
||
le,
|
||
toDisplayString(t.ip),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
l,
|
||
{
|
||
field: `subnet_mask`,
|
||
header: `子网掩码`,
|
||
style: {
|
||
width: `30%`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createElementVNode(
|
||
`span`,
|
||
ue,
|
||
toDisplayString(t.subnet_mask || `—`),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
createVNode(
|
||
l,
|
||
{
|
||
field: `gateway`,
|
||
header: `网关`,
|
||
style: {
|
||
width: `30%`,
|
||
},
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createElementVNode(
|
||
`span`,
|
||
de,
|
||
toDisplayString(t.gateway || `—`),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
8,
|
||
[`value`, `loading`],
|
||
),
|
||
createElementVNode(`div`, fe, [
|
||
createElementVNode(`div`, pe, [
|
||
(n[6] ||= createTextVNode(`IPv6 地址`, -1)),
|
||
unref(N).length
|
||
? (openBlock(),
|
||
createElementBlock(
|
||
`span`,
|
||
me,
|
||
`(` + toDisplayString(unref(N).length) + `)`,
|
||
1,
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
]),
|
||
unref(I)
|
||
? (openBlock(),
|
||
createBlock(
|
||
c,
|
||
{
|
||
key: 0,
|
||
severity: `error`,
|
||
closable: !1,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(toDisplayString(unref(I)), 1),
|
||
createVNode(a, {
|
||
label: `重试`,
|
||
text: ``,
|
||
size: `small`,
|
||
onClick: V,
|
||
}),
|
||
]),
|
||
_: 1,
|
||
},
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
createVNode(
|
||
v,
|
||
{
|
||
class: `network-table ipv6-table`,
|
||
"show-headers": !1,
|
||
value: unref(N),
|
||
loading: unref(P),
|
||
size: `small`,
|
||
scrollable: ``,
|
||
},
|
||
{
|
||
empty: withCtx(() => [
|
||
...(n[7] ||= [
|
||
createElementVNode(
|
||
`div`,
|
||
{
|
||
class: `empty`,
|
||
},
|
||
`暂无 IPv6 地址`,
|
||
-1,
|
||
),
|
||
]),
|
||
]),
|
||
default: withCtx(() => [
|
||
createVNode(
|
||
l,
|
||
{
|
||
field: `ipv6`,
|
||
header: `IPv6 地址`,
|
||
},
|
||
{
|
||
body: withCtx(({ data: t }) => [
|
||
createElementVNode(
|
||
`span`,
|
||
he,
|
||
toDisplayString(t.ipv6),
|
||
1,
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
8,
|
||
[`value`, `loading`],
|
||
),
|
||
]),
|
||
h.upgradeAllowed === !1
|
||
? createCommentVNode(``, !0)
|
||
: (openBlock(),
|
||
createBlock(
|
||
x,
|
||
{
|
||
key: 1,
|
||
visible: unref(H),
|
||
"onUpdate:visible": (n[3] ||= (e) =>
|
||
isRef(H) ? (H.value = e) : null),
|
||
header: `购买附加 IP`,
|
||
modal: ``,
|
||
style: {
|
||
width: `420px`,
|
||
},
|
||
draggable: !1,
|
||
closable: !unref(U),
|
||
"close-on-escape": !unref(U),
|
||
},
|
||
{
|
||
footer: withCtx(() => [
|
||
createVNode(
|
||
a,
|
||
{
|
||
label: `取消`,
|
||
severity: `secondary`,
|
||
text: ``,
|
||
disabled: unref(U),
|
||
onClick: (n[2] ||= (e) => (H.value = !1)),
|
||
},
|
||
null,
|
||
8,
|
||
[`disabled`],
|
||
),
|
||
createVNode(
|
||
a,
|
||
{
|
||
label: `提交订单`,
|
||
loading: unref(U),
|
||
disabled: !unref(Z),
|
||
onClick: Se,
|
||
},
|
||
null,
|
||
8,
|
||
[`loading`, `disabled`],
|
||
),
|
||
]),
|
||
default: withCtx(() => [
|
||
createElementVNode(`div`, ge, [
|
||
createElementVNode(`div`, _e, [
|
||
(n[8] ||= createElementVNode(
|
||
`label`,
|
||
null,
|
||
`IPv4 数量`,
|
||
-1,
|
||
)),
|
||
createVNode(
|
||
b,
|
||
{
|
||
modelValue: unref(G).ip_num,
|
||
"onUpdate:modelValue": [
|
||
(n[0] ||= (e) => (unref(G).ip_num = e)),
|
||
$,
|
||
],
|
||
min: 0,
|
||
disabled: unref(U),
|
||
showButtons: ``,
|
||
buttonLayout: `horizontal`,
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`, `disabled`],
|
||
),
|
||
]),
|
||
createElementVNode(`div`, ve, [
|
||
(n[9] ||= createElementVNode(
|
||
`label`,
|
||
null,
|
||
`IPv6 数量`,
|
||
-1,
|
||
)),
|
||
createVNode(
|
||
b,
|
||
{
|
||
modelValue: unref(G).ipv6_num,
|
||
"onUpdate:modelValue": [
|
||
(n[1] ||= (e) => (unref(G).ipv6_num = e)),
|
||
$,
|
||
],
|
||
min: 0,
|
||
disabled: unref(U),
|
||
showButtons: ``,
|
||
buttonLayout: `horizontal`,
|
||
},
|
||
null,
|
||
8,
|
||
[`modelValue`, `disabled`],
|
||
),
|
||
]),
|
||
createElementVNode(`div`, ye, [
|
||
(n[10] ||= createElementVNode(
|
||
`span`,
|
||
null,
|
||
`应付金额`,
|
||
-1,
|
||
)),
|
||
createElementVNode(`span`, be, [
|
||
unref(q)
|
||
? (openBlock(), createElementBlock(`i`, D))
|
||
: (openBlock(),
|
||
createElementBlock(
|
||
Fragment,
|
||
{
|
||
key: 1,
|
||
},
|
||
[
|
||
createTextVNode(
|
||
toDisplayString(
|
||
unref(K) ? `¥${unref(K)}` : `—`,
|
||
),
|
||
1,
|
||
),
|
||
],
|
||
64,
|
||
)),
|
||
]),
|
||
]),
|
||
unref(W)
|
||
? (openBlock(),
|
||
createBlock(
|
||
c,
|
||
{
|
||
key: 0,
|
||
severity: `error`,
|
||
closable: !1,
|
||
},
|
||
{
|
||
default: withCtx(() => [
|
||
createTextVNode(
|
||
toDisplayString(unref(W)),
|
||
1,
|
||
),
|
||
createVNode(
|
||
a,
|
||
{
|
||
label: `重新报价`,
|
||
text: ``,
|
||
size: `small`,
|
||
disabled: unref(U),
|
||
onClick: $,
|
||
},
|
||
null,
|
||
8,
|
||
[`disabled`],
|
||
),
|
||
]),
|
||
_: 1,
|
||
},
|
||
))
|
||
: createCommentVNode(``, !0),
|
||
]),
|
||
]),
|
||
_: 1,
|
||
},
|
||
8,
|
||
[`visible`, `closable`, `close-on-escape`],
|
||
)),
|
||
])
|
||
);
|
||
}
|
||
);
|
||
},
|
||
}),
|
||
[[`__scopeId`, `data-v-9ba8a1b3`]],
|
||
),
|
||
{
|
||
__name: `MfCloudIps`,
|
||
},
|
||
);
|
||
export {};
|
||
export default O;
|
||
</script>
|