Files
console-dev/app/components/instance/DcimBilling.vue
T

994 lines
39 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script>
// DCIM 计费入口集中处理自动续费和停用申请,停用操作可进入安全验证后重试。
import "~/assets/styles/InstanceRenewDialog.BHDAL6d3.css";
import "~/assets/styles/GeetestField.DiieTibK.css";
import "~/assets/styles/CaptchaField.BSN3ZbsD.css";
import "~/assets/styles/SecurityVerificationDialog.skYFo4v7.css";
import "~/assets/styles/DcimBilling.QV7PUjnH.css";
import { toDisplayString } from "vue";
import { onMounted } from "vue";
import { useId } 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 { 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 { navigateTo } from "#app";
import { NuxtLink } from "#components";
import { sanitizeMessage } from "~/lib/auth.js";
import { useApi } from "~/lib/auth.js";
import { assertSecurityComplete } from "~/lib/auth.js";
import { securityChallenge } 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 { c as dict_c } from "~/lib/dict.js";
import { l as dict_l } from "~/lib/dict.js";
import { r as dict_r } from "~/lib/dict.js";
import select from "primevue/select";
import OperationNotice from "~/components/OperationNotice.vue";
import textarea from "primevue/textarea";
import tag from "primevue/tag";
import SecurityVerificationDialog from "~/components/SecurityVerificationDialog.vue";
import InstanceRenewDialog from "~/components/instance/InstanceRenewDialog.vue";
var he = {
class: `billing`,
},
ge = {
class: `rows`,
},
_e = {
key: 1,
},
ve = {
key: 0,
},
ye = {
key: 0,
},
be = {
key: 1,
},
xe = {
class: `order-row`,
},
Se = {
key: 1,
},
Ce = {
class: `muted`,
},
we = {
class: `form`,
},
Te = {
key: 1,
},
Ee = [`id`],
De = [`for`],
Oe = [`id`],
ke = {
key: 3,
},
C = Object.assign(
_plugin_vue_export_helper_t(
defineComponent({
__name: `DcimBilling`,
props: {
hostId: {},
host: {},
},
emits: [`refresh`],
setup(g, { emit: S }) {
let C = g,
w = S,
T = useApi(),
E = ref(``),
D = useId(),
O = ref(!1),
k = ref(null),
A = ref(``),
j = ref(``),
M = ref(``),
N = ref(``),
P = ref(!1),
F = ref(!1),
I = ref(null),
L = ref(`Expire`),
R = ref(null),
z = ref(``),
B = ref(!1),
V = ref({}),
H = ref(null),
U = ref(null),
W = ref(null),
Ae = {
Pending: `待审核`,
Suspending: `待停用`,
Suspend: `停用中`,
Suspended: `已停用`,
Refund: `已退款`,
Reject: `已驳回`,
Cancelled: `已取消`,
},
je = computed(
() =>
!C.host.renew_forbidden &&
![`Deleted`, `Unpaid`, `Cancelled`, `Failed`].includes(
C.host.status,
),
),
G = computed(
() =>
!F.value &&
!j.value &&
Number(U.value?.allow_refund) === 1 &&
(!W.value || [`Reject`, `Cancelled`].includes(W.value.status)),
),
K = computed(
() =>
!F.value &&
!j.value &&
[`Pending`, `Suspending`, `Suspend`].includes(
W.value?.status || ``,
),
),
q = 0,
J = 0,
Y = !1,
X = (e, t) =>
!Y && e === C.hostId && String(C.host.id) === e && t === J;
// 三项信息独立加载,自动续费失败不遮蔽退款规则或现有停用记录的错误。
async function Z() {
let e = ++q,
t = C.hostId;
((F.value = !0),
(k.value = null),
(U.value = null),
(W.value = null),
(A.value = ``),
(j.value = ``));
let n = await Promise.allSettled([
T.get(`/host/${t}/renew/auto`),
T.get(`/refund`, {
host_id: t,
}),
T.get(`/refund/host/${t}/refund`),
]);
if (Y || e !== q || t !== C.hostId) return;
let [r, i, a] = n;
(r.status === `fulfilled` &&
[0, 1].includes(Number(r.value?.status))
? (k.value = Number(r.value.status))
: (A.value =
r.status === `rejected` && r.reason instanceof Error
? r.reason.message
: `自动续费状态不可用`),
i.status === `fulfilled`
? (U.value = i.value)
: (j.value =
i.reason instanceof Error
? i.reason.message
: `停用信息暂不可用`),
a.status === `fulfilled` && a.value && `refund` in a.value
? (W.value = a.value.refund)
: (j.value =
a.status === `rejected` && a.reason instanceof Error
? a.reason.message
: `停用记录暂不可用`),
(F.value = !1));
}
function Q(e) {
P.value ||
F.value ||
B.value ||
String(C.host.id) !== C.hostId ||
(e === `auto` && k.value === null) ||
(e === `refund` && !G.value) ||
(e === `cancel` && !K.value) ||
((I.value = e),
(M.value = ``),
(E.value = ``),
(N.value = ``),
(L.value = `Expire`),
(z.value = ``),
(R.value = null),
(H.value = null));
}
function $() {
!P.value &&
!B.value &&
((I.value = null), (M.value = ``), (H.value = null));
}
// 根据确认动作分派自动续费、撤销或提交停用;提交前校验实例及原因有效性。
async function Me() {
if (
P.value ||
B.value ||
!I.value ||
Y ||
String(C.host.id) !== C.hostId
)
return;
let e = I.value,
t = C.hostId,
n = J,
r = () => X(t, n);
if (
((M.value = ``),
(E.value = ``),
(e === `auto` && k.value === null) ||
(e === `refund` && !G.value) ||
(e === `cancel` && !K.value))
)
return;
if (
e === `refund` &&
(U.value?.reason_custom ? !z.value.trim() : !R.value)
) {
M.value = `请填写或选择停用原因`;
return;
}
let i = {
host_id: Number(t),
type: L.value,
suspend_reason: U.value?.reason_custom
? z.value.trim()
: [R.value],
};
P.value = !0;
try {
if (e === `auto`) {
let e = k.value === 1 ? 0 : 1;
(await T.put(`/host/${t}/renew/auto`, {
status: e,
}),
r() && (k.value = e));
} else if (e === `cancel`)
await T.put(`/refund/${W.value.id}/cancel`);
else {
let e = await T.post(`/refund`, i);
if (!r()) return;
assertSecurityComplete(e);
}
if (!r()) return;
((I.value = null),
(N.value =
e === `auto`
? `自动续费设置已更新`
: e === `cancel`
? `停用申请已取消`
: `停用申请已提交`),
e !== `auto` && (await Z()));
} catch (t) {
if (!r()) return;
let n = e === `refund` ? securityChallenge(t) : null;
n
? ((H.value = i), (V.value = n), (B.value = !0))
: (E.value =
t instanceof Error ? t.message : `操作失败,请重试`);
} finally {
r() && (P.value = !1);
}
}
// 只给当前实例保留的申请补充验证字段;后台再次要求挑战时继续保留验证状态。
async function Ne(e) {
let t = H.value,
n = C.hostId,
r = J;
if (
t &&
!P.value &&
B.value &&
Number(n) === t.host_id &&
X(n, r)
) {
((P.value = !0), (E.value = ``));
try {
let i = await T.post(`/refund`, {
...t,
...e,
});
if (!X(n, r)) return;
(assertSecurityComplete(i),
(H.value = null),
(B.value = !1),
(I.value = null),
(N.value = `停用申请已提交`),
await Z(),
X(n, r) && w(`refresh`));
} catch (e) {
if (!X(n, r)) return;
let t = securityChallenge(e);
t
? ((V.value = t), (E.value = `安全验证尚未完成,请重试`))
: (E.value =
e instanceof Error ? e.message : `操作失败,请重试`);
} finally {
X(n, r) && (P.value = !1);
}
}
}
return (
watch(
() => C.hostId,
() => {
(J++,
(I.value = null),
(P.value = !1),
(O.value = !1),
(H.value = null),
(B.value = !1),
(V.value = {}),
(E.value = ``),
(N.value = ``),
Z());
},
{
flush: `sync`,
},
),
onMounted(Z),
onBeforeUnmount(() => {
((Y = !0), q++, J++, (H.value = null), (B.value = !1));
}),
(t, n) => {
let r = OperationNotice,
i = tag,
o = button,
l = NuxtLink,
y = InstanceRenewDialog,
b = message,
x = select,
S = textarea,
C = dialog,
T = SecurityVerificationDialog;
return (
openBlock(),
createElementBlock(`div`, he, [
createVNode(
r,
{
message: unref(E),
severity: `error`,
},
null,
8,
[`message`],
),
createVNode(
r,
{
message: unref(N),
severity: `success`,
},
null,
8,
[`message`],
),
(n[24] ||= createElementVNode(`h3`, null, `计费信息`, -1)),
createElementVNode(`dl`, ge, [
createElementVNode(`div`, null, [
(n[12] ||= createElementVNode(
`dt`,
null,
`计费方式`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(
g.host.billing_cycle_name ||
g.host.billing_cycle ||
`—`,
),
1,
),
]),
createElementVNode(`div`, null, [
(n[13] ||= createElementVNode(`dt`, null, `状态`, -1)),
createElementVNode(`dd`, null, [
createVNode(
i,
{
value: (`statusLabel` in t
? t.statusLabel
: unref(dict_c))(g.host.status),
severity: (`statusSeverity` in t
? t.statusSeverity
: unref(dict_l))(g.host.status),
},
null,
8,
[`value`, `severity`],
),
]),
]),
createElementVNode(`div`, null, [
(n[14] ||= createElementVNode(
`dt`,
null,
`自动续费`,
-1,
)),
createElementVNode(`dd`, null, [
createElementVNode(
`span`,
null,
toDisplayString(
unref(F)
? `加载中…`
: unref(k) === null
? `状态不可用`
: unref(k)
? `已开启`
: `已关闭`,
),
1,
),
unref(k) === null
? createCommentVNode(``, !0)
: (openBlock(),
createBlock(
o,
{
key: 0,
label: unref(k) ? `关闭` : `开启`,
text: ``,
size: `small`,
disabled: unref(P) || unref(F),
onClick: (n[0] ||= (e) => Q(`auto`)),
},
null,
8,
[`label`, `disabled`],
)),
unref(A)
? (openBlock(),
createElementBlock(
`small`,
_e,
toDisplayString(unref(A)),
1,
))
: createCommentVNode(``, !0),
]),
]),
createElementVNode(`div`, null, [
(n[15] ||= createElementVNode(
`dt`,
null,
`续费金额`,
-1,
)),
createElementVNode(`dd`, null, [
createTextVNode(
`¥` + toDisplayString(g.host.renew_amount),
1,
),
createVNode(
o,
{
label: `续费`,
size: `small`,
text: ``,
disabled: !unref(je),
onClick: (n[1] ||= (e) => (O.value = !0)),
},
null,
8,
[`disabled`],
),
]),
]),
createElementVNode(`div`, null, [
(n[16] ||= createElementVNode(
`dt`,
null,
`开通时间`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(
(`formatTime` in t ? t.formatTime : unref(dict_r))(
g.host.active_time,
),
),
1,
),
]),
createElementVNode(`div`, null, [
(n[17] ||= createElementVNode(
`dt`,
null,
`到期时间`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(
(`formatTime` in t ? t.formatTime : unref(dict_r))(
g.host.due_time,
),
),
1,
),
]),
createElementVNode(`div`, null, [
(n[18] ||= createElementVNode(
`dt`,
null,
`停用 / 退款`,
-1,
)),
createElementVNode(`dd`, null, [
unref(F)
? (openBlock(),
createElementBlock(`span`, ve, `加载中…`))
: unref(j)
? (openBlock(),
createElementBlock(
Fragment,
{
key: 1,
},
[
createElementVNode(
`span`,
null,
toDisplayString(unref(j)),
1,
),
createVNode(o, {
label: `重试`,
text: ``,
size: `small`,
onClick: Z,
}),
],
64,
))
: (openBlock(),
createElementBlock(
Fragment,
{
key: 2,
},
[
unref(W)
? (openBlock(),
createElementBlock(`span`, ye, [
createTextVNode(
toDisplayString(
Ae[unref(W).status] ||
unref(W).status,
),
1,
),
unref(W).reject_reason
? (openBlock(),
createElementBlock(
Fragment,
{
key: 0,
},
[
createTextVNode(
` · ` +
toDisplayString(
unref(sanitizeMessage)(
unref(W).reject_reason,
),
),
1,
),
],
64,
))
: createCommentVNode(``, !0),
]))
: unref(G)
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(
`span`,
be,
`当前实例不支持申请停用 / 退款`,
)),
unref(G)
? (openBlock(),
createBlock(o, {
key: 2,
label: `申请停用`,
text: ``,
severity: `danger`,
size: `small`,
onClick: (n[2] ||= (e) => Q(`refund`)),
}))
: createCommentVNode(``, !0),
unref(K)
? (openBlock(),
createBlock(o, {
key: 3,
label: `取消申请`,
text: ``,
size: `small`,
onClick: (n[3] ||= (e) => Q(`cancel`)),
}))
: createCommentVNode(``, !0),
],
64,
)),
]),
]),
]),
(n[25] ||= createElementVNode(`h3`, null, `关联订单`, -1)),
createElementVNode(`div`, xe, [
(n[19] ||= createElementVNode(
`span`,
null,
`开通订单`,
-1,
)),
g.host.order_id
? (openBlock(),
createBlock(
l,
{
key: 0,
to: `/orders/${g.host.order_id}`,
},
{
default: withCtx(() => [
createTextVNode(
`#` + toDisplayString(g.host.order_id),
1,
),
]),
_: 1,
},
8,
[`to`],
))
: (openBlock(),
createElementBlock(`span`, Se, `暂无关联订单`)),
]),
createElementVNode(`p`, Ce, [
(n[21] ||= createTextVNode(`续费及变更订单可前往`, -1)),
createVNode(
l,
{
to: `/orders`,
},
{
default: withCtx(() => [
...(n[20] ||= [createTextVNode(`订单管理`, -1)]),
]),
_: 1,
},
),
(n[22] ||= createTextVNode(`查看。`, -1)),
]),
createVNode(
y,
{
modelValue: unref(O),
"onUpdate:modelValue": (n[4] ||= (e) =>
isRef(O) ? (O.value = e) : null),
"host-id": g.hostId,
onPaid: (n[5] ||= (e) => {
(w(`refresh`),
(`navigateTo` in t
? t.navigateTo
: unref(navigateTo))(`/orders`));
}),
},
null,
8,
[`modelValue`, `host-id`],
),
createVNode(
C,
{
visible: !!unref(I),
header:
unref(I) === `auto`
? `确认自动续费设置`
: unref(I) === `refund`
? `确认申请停用`
: `确认取消申请`,
modal: ``,
style: {
width: `500px`,
},
closable: !unref(P) && !unref(B),
"close-on-escape": !unref(P) && !unref(B),
"onUpdate:visible": (n[9] ||= (e) => {
e || $();
}),
},
{
footer: withCtx(() => [
createVNode(
o,
{
label: `取消`,
outlined: ``,
severity: `secondary`,
disabled: unref(P) || unref(B),
onClick: $,
},
null,
8,
[`disabled`],
),
createVNode(
o,
{
label: `确认`,
severity:
unref(I) === `refund` ? `danger` : `primary`,
loading: unref(P),
disabled: unref(P) || unref(B),
onClick: Me,
},
null,
8,
[`severity`, `loading`, `disabled`],
),
]),
default: withCtx(() => [
createElementVNode(`div`, we, [
createElementVNode(
`p`,
null,
`实例「` +
toDisplayString(g.host.name) +
`」(#` +
toDisplayString(g.hostId) +
`)`,
1,
),
unref(M)
? (openBlock(),
createBlock(
b,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(unref(M)),
1,
),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
unref(I) === `auto`
? (openBlock(),
createElementBlock(
`p`,
Te,
`确认` +
toDisplayString(unref(k) ? `关闭` : `开启`) +
`自动续费?开启后将按平台规则扣取余额续费。`,
1,
))
: unref(I) === `refund`
? (openBlock(),
createElementBlock(
Fragment,
{
key: 2,
},
[
createVNode(
b,
{
severity: `warn`,
closable: !1,
},
{
default: withCtx(() => [
...(n[23] ||= [
createTextVNode(
`停用后服务将不可用,请提前备份数据。退款金额以审核结果为准。`,
-1,
),
]),
]),
_: 1,
},
),
createElementVNode(
`span`,
{
id: `${unref(D)}-type-label`,
},
`停用时间`,
8,
Ee,
),
createVNode(
x,
{
modelValue: unref(L),
"onUpdate:modelValue": (n[6] ||= (e) =>
isRef(L) ? (L.value = e) : null),
"aria-labelledby": `${unref(D)}-type-label`,
options: [
{
label: `到期停用`,
value: `Expire`,
},
{
label: `立即停用`,
value: `Immediate`,
},
],
"option-label": `label`,
"option-value": `value`,
disabled: unref(P) || unref(B),
},
null,
8,
[
`modelValue`,
`aria-labelledby`,
`disabled`,
],
),
unref(U)?.reason_custom
? (openBlock(),
createElementBlock(
Fragment,
{
key: 0,
},
[
createElementVNode(
`label`,
{
for: `${unref(D)}-reason`,
},
`停用原因`,
8,
De,
),
createVNode(
S,
{
id: `${unref(D)}-reason`,
modelValue: unref(z),
"onUpdate:modelValue": (n[7] ||=
(e) =>
isRef(z)
? (z.value = e)
: null),
disabled: unref(P) || unref(B),
rows: `3`,
},
null,
8,
[`id`, `modelValue`, `disabled`],
),
],
64,
))
: (openBlock(),
createElementBlock(
Fragment,
{
key: 1,
},
[
createElementVNode(
`span`,
{
id: `${unref(D)}-reason-label`,
},
`停用原因`,
8,
Oe,
),
createVNode(
x,
{
modelValue: unref(R),
"onUpdate:modelValue": (n[8] ||=
(e) =>
isRef(R)
? (R.value = e)
: null),
"aria-labelledby": `${unref(D)}-reason-label`,
options:
unref(U)?.reasons || [],
"option-label": `content`,
"option-value": `id`,
disabled: unref(P) || unref(B),
},
null,
8,
[
`modelValue`,
`aria-labelledby`,
`options`,
`disabled`,
],
),
],
64,
)),
],
64,
))
: (openBlock(),
createElementBlock(
`p`,
ke,
`确认取消尚未处理的停用申请?`,
)),
]),
]),
_: 1,
},
8,
[`visible`, `header`, `closable`, `close-on-escape`],
),
createVNode(
T,
{
visible: unref(B),
"onUpdate:visible": (n[10] ||= (e) =>
isRef(B) ? (B.value = e) : null),
challenge: unref(V),
busy: unref(P),
"request-error": unref(E),
context: {
scene: `account`,
action: `host_refund`,
},
onVerified: Ne,
onCancel: (n[11] ||= (e) => (H.value = null)),
},
null,
8,
[`visible`, `challenge`, `busy`, `request-error`],
),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-9a8e05c2`]],
),
{
__name: `DcimBilling`,
},
);
export {};
export default C;
</script>