first commit

This commit is contained in:
cieanyau committed 2026-11-06 00:04:38 +08:00
commit db5762abd4
687 files changed
+171307

No files matched your search

+927
View File
@@ -0,0 +1,927 @@
<script>
import "~/assets/styles/api-keys.Bq3Csr53.css";
import { toDisplayString } from "vue";
import { onMounted } from "vue";
import { onBeforeUnmount } 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 { withModifiers } from "vue";
import { useHead } from "#app";
import { onBeforeRouteLeave } from "vue-router";
import { NuxtLink } from "#components";
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 datatable from "primevue/datatable";
import paginator from "primevue/paginator";
import checkbox from "primevue/checkbox";
import column from "primevue/column";
import textarea from "primevue/textarea";
function D() {
let e = useApi(),
t = ref([]),
r = ref(0),
a = ref(1),
o = ref(!1),
s = ref(!1),
l = ref(!1),
u = ref(!1),
d = ref(``),
f = ref(``),
p = ref(``),
m = ref(null),
h = ref(null),
g = reactive({
enabled: !1,
ip: ``,
}),
_ = ref(null),
v = !0,
y = 0,
b = () => {
m.value = null;
};
onBeforeUnmount(() => {
((v = !1), y++, b());
});
async function x() {
if (!v) return;
let n = ++y;
((o.value = !0), (d.value = ``), (u.value = !1));
try {
let i = await e.get(`/api`, {
page: a.value,
limit: 20,
orderby: `id`,
sort: `desc`,
});
if (!v || n !== y) return;
if (!Array.isArray(i?.list)) throw Error(`密钥列表响应异常,请重试`);
((t.value = i.list),
(r.value = Number(i.count) || 0),
(u.value = Number(i.create_api) === 1),
(l.value = !0));
} catch (e) {
v &&
n === y &&
((d.value = e instanceof Error ? e.message : `加载失败`),
(l.value = !1));
} finally {
v && n === y && (o.value = !1);
}
}
async function C() {
if (!(!v || s.value || m.value)) {
if (((d.value = ``), (f.value = ``), !l.value || o.value)) {
d.value = `权限尚未加载,请刷新列表`;
return;
}
if (!u.value) {
d.value = `当前账户暂未开放 API 密钥创建`;
return;
}
if (!p.value.trim()) {
d.value = `请填写密钥名称`;
return;
}
s.value = !0;
try {
let t = await e.post(`/api`, {
name: p.value.trim(),
});
if (!v) return;
if (!t?.private_key || !t?.token)
throw Error(
`服务端未返回完整密钥,请刷新列表核实创建结果,避免重复创建`,
);
((m.value = t),
(p.value = ``),
(f.value = `密钥已创建,请立即保存私钥。关闭后无法再次查看。`),
(a.value = 1),
await x());
} catch (e) {
v &&
(d.value = `${e instanceof Error ? e.message : `创建失败`}。请先刷新列表核实结果,再决定是否重试。`);
} finally {
v && (s.value = !1);
}
}
}
function w(e) {
s.value ||
((d.value = ``),
(f.value = ``),
(h.value = e.id),
(g.enabled = Number(e.status) === 1),
(g.ip = e.ip || ``));
}
async function T() {
if (!v || s.value || !h.value) return;
if (((d.value = ``), (f.value = ``), g.enabled && !g.ip.trim())) {
d.value = `开启白名单时请填写 IP 地址`;
return;
}
let t = h.value;
s.value = !0;
try {
if (
(await e.put(`/api/${t}/white_list`, {
status: +!!g.enabled,
ip: g.ip.trim(),
}),
!v)
)
return;
((h.value = null), (f.value = `白名单设置已保存`), await x());
} catch (e) {
v && (d.value = e instanceof Error ? e.message : `保存失败`);
} finally {
v && (s.value = !1);
}
}
async function E() {
if (v && !s.value && _.value?.id) {
((d.value = ``), (f.value = ``), (s.value = !0));
try {
if ((await e.del(`/api/${_.value.id}`), !v)) return;
((_.value = null),
(f.value = `密钥已删除`),
t.value.length === 1 && a.value > 1 && a.value--,
await x());
} catch (e) {
v && (d.value = e instanceof Error ? e.message : `删除失败`);
} finally {
v && (s.value = !1);
}
}
}
return {
rows: t,
count: r,
page: a,
limit: 20,
loading: o,
busy: s,
loaded: l,
createAllowed: u,
error: d,
success: f,
name: p,
secret: m,
clearSecret: b,
whitelistId: h,
whitelist: g,
deleteTarget: _,
load: x,
create: C,
editWhitelist: w,
saveWhitelist: T,
remove: E,
};
}
var O = {
class: `api-page account-management-page`,
},
k = {
class: `account-management-head`,
},
A = {
class: `account-management-actions`,
},
j = {
class: `create-key-field`,
},
oe = {
key: 2,
class: `muted`,
},
se = {
class: `list-table`,
},
ce = {
class: `token`,
},
le = {
class: `row-actions`,
},
ue = {
class: `token`,
},
de = {
key: 0,
},
M = _plugin_vue_export_helper_t(
defineComponent({
__name: `api-keys`,
setup(n) {
useHead({
title: `API 密钥`,
});
let {
rows: c,
count: h,
page: S,
limit: T,
loading: M,
busy: N,
loaded: P,
createAllowed: F,
error: I,
success: L,
name: R,
secret: z,
clearSecret: B,
whitelistId: V,
whitelist: H,
deleteTarget: U,
load: W,
create: G,
editWhitelist: fe,
saveWhitelist: K,
remove: pe,
} = D(),
q = ref(!1),
J = ref(``),
me = computed(() => !!z.value),
Y = computed({
get: () => V.value !== null,
set: (e) => {
!e && !N.value && (V.value = null);
},
}),
X = computed({
get: () => !!U.value,
set: (e) => {
!e && !N.value && (U.value = null);
},
});
async function he() {
if (z.value) {
J.value = ``;
try {
(await navigator.clipboard.writeText(z.value.private_key),
(q.value = !0));
} catch {
J.value = `复制失败,请手动选择并复制私钥`;
}
}
}
function Z() {
(B(), (q.value = !1), (J.value = ``));
}
function ge(e) {
((S.value = e.page + 1), W());
}
let _e = (e) => (e ? new Date(e * 1e3).toLocaleString(`zh-CN`) : `—`);
return (
onMounted(W),
onBeforeRouteLeave(
() =>
!z.value ||
window.confirm(
`私钥仅显示一次,请确认已保存后离开。离开后将无法再次查看。`,
),
),
(t, n) => {
let i = NuxtLink,
s = button,
y = message,
b = inputtext,
D = column,
B = datatable,
V = paginator,
Q = textarea,
$ = dialog,
ve = checkbox;
return (
openBlock(),
createElementBlock(`section`, O, [
createElementVNode(`header`, k, [
createElementVNode(`div`, null, [
(n[10] ||= createElementVNode(`h2`, null, `API 密钥`, -1)),
createElementVNode(`p`, null, [
(n[9] ||= createTextVNode(
`管理程序访问凭证及 IP 白名单。`,
-1,
)),
createVNode(
i,
{
to: `/logbook?type=api`,
},
{
default: withCtx(() => [
...(n[8] ||= [
createTextVNode(`查看 API 日志`, -1),
]),
]),
_: 1,
},
),
]),
]),
createElementVNode(`div`, A, [
createVNode(
i,
{
to: `/account`,
class: `account-management-link`,
},
{
default: withCtx(() => [
...(n[11] ||= [
createElementVNode(
`i`,
{
class: `pi pi-angle-left`,
"aria-hidden": `true`,
},
null,
-1,
),
createTextVNode(`账户设置`, -1),
]),
]),
_: 1,
},
),
createVNode(
s,
{
label: `刷新`,
icon: `pi pi-refresh`,
severity: `secondary`,
outlined: ``,
loading: unref(M),
disabled: unref(N),
onClick: unref(W),
},
null,
8,
[`loading`, `disabled`, `onClick`],
),
]),
]),
unref(I)
? (openBlock(),
createBlock(
y,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(I)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
unref(L)
? (openBlock(),
createBlock(
y,
{
key: 1,
severity: `success`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(L)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createElementVNode(
`form`,
{
class: `create-key`,
onSubmit: (n[1] ||= withModifiers(
(...e) => unref(G) && unref(G)(...e),
[`prevent`],
)),
},
[
createElementVNode(`div`, j, [
(n[12] ||= createElementVNode(
`label`,
{
for: `api-name`,
},
`密钥名称`,
-1,
)),
createVNode(
b,
{
id: `api-name`,
modelValue: unref(R),
"onUpdate:modelValue": (n[0] ||= (e) =>
isRef(R) ? (R.value = e) : null),
maxlength: `100`,
placeholder: `例如:自动化管理`,
disabled: unref(N) || !unref(F),
},
null,
8,
[`modelValue`, `disabled`],
),
]),
createVNode(
s,
{
type: `submit`,
label: `创建密钥`,
loading: unref(N),
disabled:
!unref(P) || !unref(F) || unref(M) || !!unref(z),
},
null,
8,
[`loading`, `disabled`],
),
],
32,
),
unref(P) && !unref(F)
? (openBlock(),
createElementBlock(`p`, oe, `当前账户暂未开放创建密钥。`))
: createCommentVNode(``, !0),
createElementVNode(`div`, se, [
createVNode(
B,
{
value: unref(c),
loading: unref(M),
"data-key": `id`,
scrollable: ``,
},
{
empty: withCtx(() => [
createTextVNode(
toDisplayString(
unref(P)
? `暂无 API 密钥`
: `尚未加载密钥,请刷新重试`,
),
1,
),
]),
default: withCtx(() => [
createVNode(D, {
field: `name`,
header: `名称`,
}),
createVNode(
D,
{
field: `token`,
header: `Token`,
},
{
body: withCtx(({ data: t }) => [
createElementVNode(
`code`,
ce,
toDisplayString(t.token),
1,
),
]),
_: 1,
},
),
createVNode(
D,
{
header: `创建时间`,
},
{
body: withCtx(({ data: t }) => [
createTextVNode(
toDisplayString(_e(t.create_time)),
1,
),
]),
_: 1,
},
),
createVNode(
D,
{
header: `IP 白名单`,
},
{
body: withCtx(({ data: t }) => [
createTextVNode(
toDisplayString(
Number(t.status) === 1 ? t.ip : `未开启`,
),
1,
),
]),
_: 1,
},
),
createVNode(
D,
{
header: `操作`,
},
{
body: withCtx(({ data: e }) => [
createElementVNode(`div`, le, [
createVNode(
s,
{
label: `白名单`,
text: ``,
disabled: unref(N),
onClick: (t) => unref(fe)(e),
},
null,
8,
[`disabled`, `onClick`],
),
createVNode(
s,
{
label: `删除`,
severity: `danger`,
text: ``,
disabled: unref(N),
onClick: (t) => (U.value = e),
},
null,
8,
[`disabled`, `onClick`],
),
]),
]),
_: 1,
},
),
]),
_: 1,
},
8,
[`value`, `loading`],
),
]),
unref(h) > unref(T)
? (openBlock(),
createBlock(
V,
{
key: 3,
first: (unref(S) - 1) * unref(T),
rows: unref(T),
"total-records": unref(h),
onPage: ge,
},
null,
8,
[`first`, `rows`, `total-records`],
))
: createCommentVNode(``, !0),
createVNode(
$,
{
visible: unref(me),
header: `保存新私钥`,
modal: ``,
closable: !1,
draggable: !1,
style: {
width: `600px`,
maxWidth: `94vw`,
},
},
{
footer: withCtx(() => [
createVNode(
s,
{
label: unref(q) ? `已复制` : `复制私钥`,
severity: `secondary`,
onClick: he,
},
null,
8,
[`label`],
),
createVNode(s, {
label: `已安全保存,关闭`,
onClick: Z,
}),
]),
default: withCtx(() => [
(n[16] ||= createElementVNode(
`p`,
null,
`私钥仅显示一次。请保存到安全位置后关闭此窗口。`,
-1,
)),
unref(z)
? (openBlock(),
createElementBlock(
Fragment,
{
key: 0,
},
[
createElementVNode(`p`, null, [
(n[13] ||= createElementVNode(
`strong`,
null,
`Token:`,
-1,
)),
createElementVNode(
`code`,
ue,
toDisplayString(unref(z).token),
1,
),
]),
unref(z).api_url
? (openBlock(),
createElementBlock(`p`, de, [
(n[14] ||= createElementVNode(
`strong`,
null,
`API 地址:`,
-1,
)),
createTextVNode(
toDisplayString(unref(z).api_url),
1,
),
]))
: createCommentVNode(``, !0),
(n[15] ||= createElementVNode(
`label`,
{
for: `private-key`,
},
`私钥`,
-1,
)),
createVNode(
Q,
{
id: `private-key`,
"model-value": unref(z).private_key,
readonly: ``,
rows: `8`,
fluid: ``,
},
null,
8,
[`model-value`],
),
],
64,
))
: createCommentVNode(``, !0),
unref(J)
? (openBlock(),
createBlock(
y,
{
key: 1,
severity: `error`,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(J)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
]),
_: 1,
},
8,
[`visible`],
),
createVNode(
$,
{
visible: unref(Y),
"onUpdate:visible": (n[5] ||= (e) =>
isRef(Y) ? (Y.value = e) : null),
header: `API 白名单`,
modal: ``,
closable: !unref(N),
draggable: !1,
style: {
width: `480px`,
maxWidth: `94vw`,
},
},
{
default: withCtx(() => [
createElementVNode(
`form`,
{
class: `whitelist-form`,
onSubmit: (n[4] ||= withModifiers(
(...e) => unref(K) && unref(K)(...e),
[`prevent`],
)),
},
[
createElementVNode(`div`, null, [
createVNode(
ve,
{
modelValue: unref(H).enabled,
"onUpdate:modelValue": (n[2] ||= (e) =>
(unref(H).enabled = e)),
"input-id": `api-whitelist-on`,
binary: ``,
disabled: unref(N),
},
null,
8,
[`modelValue`, `disabled`],
),
(n[17] ||= createTextVNode()),
(n[18] ||= createElementVNode(
`label`,
{
for: `api-whitelist-on`,
},
`启用 IP 白名单`,
-1,
)),
]),
(n[19] ||= createElementVNode(
`label`,
{
for: `api-whitelist-ip`,
},
`允许访问的 IP 地址`,
-1,
)),
createVNode(
Q,
{
id: `api-whitelist-ip`,
modelValue: unref(H).ip,
"onUpdate:modelValue": (n[3] ||= (e) =>
(unref(H).ip = e)),
disabled: unref(N) || !unref(H).enabled,
rows: `4`,
fluid: ``,
},
null,
8,
[`modelValue`, `disabled`],
),
unref(I)
? (openBlock(),
createBlock(
y,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(unref(I)),
1,
),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createVNode(
s,
{
type: `submit`,
label: `保存`,
loading: unref(N),
},
null,
8,
[`loading`],
),
],
32,
),
]),
_: 1,
},
8,
[`visible`, `closable`],
),
createVNode(
$,
{
visible: unref(X),
"onUpdate:visible": (n[7] ||= (e) =>
isRef(X) ? (X.value = e) : null),
header: `删除 API 密钥`,
modal: ``,
closable: !unref(N),
draggable: !1,
style: {
width: `450px`,
maxWidth: `94vw`,
},
},
{
footer: withCtx(() => [
createVNode(
s,
{
label: `取消`,
severity: `secondary`,
disabled: unref(N),
onClick: (n[6] ||= (e) => (U.value = null)),
},
null,
8,
[`disabled`],
),
createVNode(
s,
{
label: `确认删除`,
severity: `danger`,
loading: unref(N),
onClick: unref(pe),
},
null,
8,
[`loading`, `onClick`],
),
]),
default: withCtx(() => [
createElementVNode(
`p`,
null,
`确认删除「` +
toDisplayString(unref(U)?.name) +
`」?使用此密钥的程序将立即失去访问权限。`,
1,
),
unref(I)
? (openBlock(),
createBlock(
y,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(I)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
]),
_: 1,
},
8,
[`visible`, `closable`],
),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-76e29360`]],
);
export {};
export default M;
</script>
+407
View File
@@ -0,0 +1,407 @@
<script>
import "~/assets/styles/WechatConnection.DI5ko1xy.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
import { onMounted } from "vue";
import { onBeforeUnmount } 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 { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
import dialog from "primevue/dialog";
import button from "primevue/button";
import message from "primevue/message";
import { t as oauth_t } from "~/lib/oauth.js";
import { t as useOAuth_t } from "~/lib/useOAuth.js";
import skeleton from "primevue/skeleton";
import WechatConnection from "~/components/WechatConnection.vue";
var D = {
class: `account-management-page`,
},
O = {
class: `account-management-head`,
},
k = {
class: `account-management-actions`,
},
A = {
"aria-label": `其他登录方式`,
},
j = {
key: 0,
class: `connection-list`,
},
M = {
key: 1,
class: `account-management-note`,
},
N = {
key: 2,
class: `connection-list`,
},
P = [`src`],
F = {
class: `connection-provider-main`,
},
I = defineComponent({
__name: `connections`,
setup(h) {
let I = useApi(),
L = ref([]),
R = ref(!1),
z = ref(``),
B = ref(``),
V = ref(!1),
H = ref(!1),
U = ref(null),
{ busy: W, error: G, start: K } = useOAuth_t(),
q = computed({
get: () => !!U.value,
set: (e) => {
!e && !H.value && (U.value = null);
},
}),
J = !0;
onBeforeUnmount(() => {
J = !1;
});
async function Y() {
if (!R.value) {
((R.value = !0), (z.value = ``));
try {
let e = await I.get(`/account`);
if (!J) return;
((L.value = oauth_t(e.account?.oauth)), (V.value = !0));
} catch (e) {
J && (z.value = e instanceof Error ? e.message : `加载失败`);
} finally {
J && (R.value = !1);
}
}
}
async function X() {
if (U.value?.linked && !H.value && !W.value && J) {
((H.value = !0), (z.value = ``), (B.value = ``));
try {
if (
(await I.post(
`/oauth/unbind/${encodeURIComponent(U.value.name)}`,
),
!J)
)
return;
((U.value = null), (B.value = `已取消关联`), await Y());
} catch (e) {
J && (z.value = e instanceof Error ? e.message : `取消关联失败`);
} finally {
J && (H.value = !1);
}
}
}
return (
onMounted(Y),
(n, r) => {
let a = NuxtLink,
c = button,
h = message,
y = WechatConnection,
C = skeleton,
w = dialog;
return (
openBlock(),
createElementBlock(`div`, D, [
createElementVNode(`header`, O, [
(r[3] ||= createElementVNode(
`div`,
null,
[
createElementVNode(`h2`, null, `关联账号`),
createElementVNode(
`p`,
null,
`管理第三方登录方式与微信通知。`,
),
],
-1,
)),
createElementVNode(`div`, k, [
createVNode(
a,
{
to: `/account`,
class: `account-management-link`,
},
{
default: withCtx(() => [
...(r[2] ||= [
createElementVNode(
`i`,
{
class: `pi pi-angle-left`,
"aria-hidden": `true`,
},
null,
-1,
),
createTextVNode(`账户设置`, -1),
]),
]),
_: 1,
},
),
createVNode(
c,
{
label: `刷新`,
icon: `pi pi-refresh`,
severity: `secondary`,
outlined: ``,
loading: unref(R),
disabled: unref(H) || unref(W),
onClick: Y,
},
null,
8,
[`loading`, `disabled`],
),
]),
]),
unref(z) || unref(G)
? (openBlock(),
createBlock(
h,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(unref(z) || unref(G)),
1,
),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
unref(B)
? (openBlock(),
createBlock(
h,
{
key: 1,
severity: `success`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(B)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createVNode(y),
createElementVNode(`section`, A, [
(r[4] ||= createElementVNode(
`h3`,
{
class: `connection-section-title`,
},
`其他登录方式`,
-1,
)),
unref(R) && !unref(V)
? (openBlock(),
createElementBlock(`div`, j, [
createVNode(C, {
height: `60px`,
}),
]))
: unref(V) && !unref(L).length
? (openBlock(),
createElementBlock(`p`, M, `暂无其他第三方登录方式。`))
: (openBlock(),
createElementBlock(`div`, N, [
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(L),
(t) => (
openBlock(),
createElementBlock(
`div`,
{
key: t.name,
class: `connection-provider`,
},
[
t.image
? (openBlock(),
createElementBlock(
`img`,
{
key: 0,
src: t.image,
alt: ``,
referrerpolicy: `no-referrer`,
},
null,
8,
P,
))
: createCommentVNode(``, !0),
createElementVNode(`div`, F, [
createElementVNode(
`strong`,
null,
toDisplayString(t.title),
1,
),
createElementVNode(
`p`,
null,
toDisplayString(
t.linked ? `已关联` : `未关联`,
),
1,
),
]),
t.linked
? (openBlock(),
createBlock(
c,
{
key: 1,
label: `取消关联`,
severity: `secondary`,
outlined: ``,
disabled:
unref(H) || unref(W) || unref(R),
onClick: (e) => (U.value = t),
},
null,
8,
[`disabled`, `onClick`],
))
: (openBlock(),
createBlock(
c,
{
key: 2,
label: `关联账号`,
disabled:
unref(H) || unref(W) || unref(R),
onClick: (e) => unref(K)(t.name),
},
null,
8,
[`disabled`, `onClick`],
)),
],
)
),
),
128,
)),
])),
]),
createVNode(
w,
{
visible: unref(q),
"onUpdate:visible": (r[1] ||= (e) =>
isRef(q) ? (q.value = e) : null),
header: `取消关联`,
modal: ``,
closable: !unref(H),
draggable: !1,
style: {
width: `450px`,
},
},
{
footer: withCtx(() => [
createVNode(
c,
{
label: `保留关联`,
severity: `secondary`,
outlined: ``,
disabled: unref(H),
onClick: (r[0] ||= (e) => (U.value = null)),
},
null,
8,
[`disabled`],
),
createVNode(
c,
{
label: `确认取消关联`,
severity: `danger`,
loading: unref(H),
onClick: X,
},
null,
8,
[`loading`],
),
]),
default: withCtx(() => [
createElementVNode(
`p`,
null,
`确认取消与 ` +
toDisplayString(unref(U)?.title) +
` 的关联?之后将无法使用该方式登录。请先确认手机号或邮箱登录可用。`,
1,
),
unref(z)
? (openBlock(),
createBlock(
h,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(z)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
]),
_: 1,
},
8,
[`visible`, `closable`],
),
])
);
}
);
},
});
export {};
export default I;
</script>
File diff suppressed because it is too large. Load diff
+23
View File
@@ -0,0 +1,23 @@
<script>
import "~/assets/styles/UploadField.BbTCTv6C.css";
import "~/assets/styles/PageSteps.B5K7G9zF.css";
import "~/assets/styles/PaymentDialog.BNFKQF1U.css";
import "~/assets/styles/realname.BpRCvfXO.css";
import { createBlock } from "vue";
import { defineComponent } from "vue";
import { openBlock } from "vue";
import index from "~/pages/account/realname/index.vue";
var i = defineComponent({
__name: `company`,
setup(t) {
return (t, i) => (
openBlock(),
createBlock(index, {
"initial-type": `company`,
})
);
},
});
export {};
export default i;
</script>
File diff suppressed because it is too large. Load diff
+23
View File
@@ -0,0 +1,23 @@
<script>
import "~/assets/styles/UploadField.BbTCTv6C.css";
import "~/assets/styles/PageSteps.B5K7G9zF.css";
import "~/assets/styles/PaymentDialog.BNFKQF1U.css";
import "~/assets/styles/realname.BpRCvfXO.css";
import { createBlock } from "vue";
import { defineComponent } from "vue";
import { openBlock } from "vue";
import index from "~/pages/account/realname/index.vue";
var i = defineComponent({
__name: `personal`,
setup(t) {
return (t, i) => (
openBlock(),
createBlock(index, {
"initial-type": `person`,
})
);
},
});
export {};
export default i;
</script>
+25
View File
@@ -0,0 +1,25 @@
<script>
import "~/assets/styles/PaymentDialog.BNFKQF1U.css";
import "~/assets/styles/_id_.gjoJy9S8.css";
definePageMeta({
key: (e) => e.fullPath,
});
import { createBlock } from "vue";
import { defineComponent } from "vue";
import { openBlock } from "vue";
import { t as composables_t } from "~/lib/composables-2.js";
import _id_ from "~/pages/orders/[id].vue";
var a = defineComponent({
__name: `[id]`,
setup(t) {
return (
composables_t({
key: (e) => e.fullPath,
}),
(t, r) => (openBlock(), createBlock(_id_))
);
},
});
export {};
export default a;
</script>
+38
View File
@@ -0,0 +1,38 @@
<script>
definePageMeta({
redirect: {
redirect: (e) => ({
path: `/finance`,
query: {
...e.query,
tab: `bills`,
},
hash: e.hash,
}),
}.redirect,
});
import { createElementBlock } from "vue";
import { defineComponent } from "vue";
import { openBlock } from "vue";
import { t as composables_t } from "~/lib/composables-2.js";
var i = defineComponent({
__name: `index`,
setup(t) {
return (
composables_t({
redirect: (e) => ({
path: `/finance`,
query: {
...e.query,
tab: `bills`,
},
hash: e.hash,
}),
}),
(t, r) => (openBlock(), createElementBlock(`div`))
);
},
});
export {};
export default i;
</script>
File diff suppressed because it is too large. Load diff
+92
View File
@@ -0,0 +1,92 @@
<script>
import "~/assets/styles/PaymentDialog.BNFKQF1U.css";
import "~/assets/styles/OsIcon.CAi4Iz2S.css";
import "~/assets/styles/HostList.B3U15sJm.css";
import { createBlock } from "vue";
import { withCtx } from "vue";
import { computed } from "vue";
import { createTextVNode } from "vue";
import { unref } from "vue";
import { defineComponent } from "vue";
import { createVNode } from "vue";
import { openBlock } from "vue";
import { useRoute } from "#app";
import { NuxtLink } from "#components";
import button from "primevue/button";
import message from "primevue/message";
import { t as modules_t } from "~/lib/modules.js";
import HostList from "~/components/HostList.vue";
var m = defineComponent({
__name: `index`,
setup(a) {
let m = useRoute(),
h = computed(() =>
/^\d+$/.test(String(m.query.m ?? ``)) &&
Number.isSafeInteger(Number(m.query.m)) &&
Number(m.query.m) > 0
? Number(m.query.m)
: void 0,
);
return (n, a) => {
let c = message,
g = button,
_ = NuxtLink,
v = HostList;
return unref(m).query.m !== void 0 && !unref(h)
? (openBlock(),
createBlock(
c,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
...(a[0] ||= [createTextVNode(`服务器导航参数无效`, -1)]),
]),
_: 1,
},
))
: (openBlock(),
createBlock(
v,
{
key: unref(h) ?? `default`,
"menu-id": unref(h),
modules: (`SERVER_MODULE_GROUPS` in n
? n.SERVER_MODULE_GROUPS
: unref(modules_t)
).cloud,
title: `云服务器`,
description: `管理你的云服务器实例`,
},
{
actions: withCtx(() => [
createVNode(
_,
{
to: `/products`,
},
{
default: withCtx(() => [
createVNode(g, {
label: `订购云服务器`,
icon: `pi pi-plus`,
}),
]),
_: 1,
},
),
]),
_: 1,
},
8,
[`menu-id`, `modules`],
));
};
},
});
export {};
export default m;
</script>
+92
View File
@@ -0,0 +1,92 @@
<script>
import "~/assets/styles/PaymentDialog.BNFKQF1U.css";
import "~/assets/styles/OsIcon.CAi4Iz2S.css";
import "~/assets/styles/HostList.B3U15sJm.css";
import { createBlock } from "vue";
import { withCtx } from "vue";
import { computed } from "vue";
import { createTextVNode } from "vue";
import { unref } from "vue";
import { defineComponent } from "vue";
import { createVNode } from "vue";
import { openBlock } from "vue";
import { useRoute } from "#app";
import { NuxtLink } from "#components";
import button from "primevue/button";
import message from "primevue/message";
import { t as modules_t } from "~/lib/modules.js";
import HostList from "~/components/HostList.vue";
var m = defineComponent({
__name: `index`,
setup(a) {
let m = useRoute(),
h = computed(() =>
/^\d+$/.test(String(m.query.m ?? ``)) &&
Number.isSafeInteger(Number(m.query.m)) &&
Number(m.query.m) > 0
? Number(m.query.m)
: void 0,
);
return (n, a) => {
let c = message,
g = button,
_ = NuxtLink,
v = HostList;
return unref(m).query.m !== void 0 && !unref(h)
? (openBlock(),
createBlock(
c,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
...(a[0] ||= [createTextVNode(`服务器导航参数无效`, -1)]),
]),
_: 1,
},
))
: (openBlock(),
createBlock(
v,
{
key: unref(h) ?? `default`,
"menu-id": unref(h),
modules: (`SERVER_MODULE_GROUPS` in n
? n.SERVER_MODULE_GROUPS
: unref(modules_t)
).dcim,
title: `物理服务器`,
description: `管理你的物理服务器(裸金属)`,
},
{
actions: withCtx(() => [
createVNode(
_,
{
to: `/products`,
},
{
default: withCtx(() => [
createVNode(g, {
label: `订购物理服务器`,
icon: `pi pi-plus`,
}),
]),
_: 1,
},
),
]),
_: 1,
},
8,
[`menu-id`, `modules`],
));
};
},
});
export {};
export default m;
</script>
File diff suppressed because it is too large. Load diff
+520
View File
@@ -0,0 +1,520 @@
<script>
import "~/assets/styles/PhoneCodeField.CwyHXfML.css";
import "~/assets/styles/GeetestField.DiieTibK.css";
import "~/assets/styles/CaptchaField.BSN3ZbsD.css";
definePageMeta({
layout: `blank`,
alias: [`/forgot_password`],
});
import { toDisplayString } from "vue";
import { normalizeClass } from "vue";
import { createBlock } from "vue";
import { ref } from "vue";
import { withCtx } from "vue";
import { createCommentVNode } from "vue";
import { reactive } from "vue";
import { isRef } from "vue";
import { createElementBlock } 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 { withModifiers } from "vue";
import { useRoute } from "#app";
import { useRouter } from "#app";
import { useHead } from "#app";
import { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
import button from "primevue/button";
import message from "primevue/message";
import { n as auth_redirect_n } from "~/lib/auth-redirect.js";
import { t as auth_redirect_t } from "~/lib/auth-redirect.js";
import inputtext from "primevue/inputtext";
import CaptchaField from "~/components/CaptchaField.vue";
import password from "primevue/password";
import PhoneCodeField from "~/components/PhoneCodeField.vue";
import { t as useCaptcha_t } from "~/lib/useCaptcha.js";
import { t as composables_t } from "~/lib/composables-2.js";
import { t as useVerifyCode_t } from "~/lib/useVerifyCode.js";
var D = {
class: `auth-card login-card`,
},
O = {
class: `field`,
},
k = {
class: `seg`,
role: `group`,
"aria-labelledby": `recovery-method-label`,
},
A = [`aria-pressed`, `disabled`],
j = [`aria-pressed`, `disabled`],
M = {
class: `field`,
},
N = {
for: `account`,
},
P = {
class: `field`,
},
F = {
class: `code-row`,
},
I = {
class: `field`,
},
L = {
class: `field`,
},
R = {
class: `login-links center`,
},
z = defineComponent({
__name: `forgot-password`,
setup(f) {
(useHead({
title: `找回密码 - 破碎工坊网络`,
}),
composables_t({
layout: `blank`,
alias: `/forgot_password`,
}));
let z = useApi(),
B = useRouter(),
V = useRoute(),
H = ref(`phone`),
U = reactive({
account: ``,
code: ``,
password: ``,
re_password: ``,
phone_code: `86`,
}),
W = ref(``),
G = ref(!1),
K = useCaptcha_t(`password_reset`),
{
needed: q,
provider: J,
captchaId: Y,
complete: ae,
invalidate: oe,
img: se,
value: X,
loading: ce,
error: Z,
load: le,
handleError: ue,
} = K,
{
sending: Q,
countdown: $,
send: de,
} = useVerifyCode_t({
action: `password_reset`,
captcha: K,
error: W,
phoneCode: () => U.phone_code,
});
function fe() {
if (G.value) return;
let e = U.account.trim();
return de(
H.value,
e,
H.value === `email` ? `请输入邮箱` : `请输入手机号`,
);
}
async function pe() {
if (G.value || Q.value) return;
W.value = ``;
let e = U.account.trim();
if (!e)
return (W.value =
H.value === `email` ? `请输入邮箱` : `请输入手机号`);
if (H.value === `phone` && !/^[1-9]\d{0,3}$/.test(U.phone_code))
return (W.value = `请输入有效的国际电话区号`);
if (!U.code.trim()) return (W.value = `请输入验证码`);
if (!U.password) return (W.value = `请输入新密码`);
if (U.password !== U.re_password)
return (W.value = `两次输入的密码不一致`);
G.value = !0;
try {
(await z.post(`/account/password_reset`, {
type: H.value,
account: e,
phone_code: U.phone_code,
code: U.code.trim(),
password: U.password,
re_password: U.re_password,
}),
await B.push({
path: `/login`,
query: {
reset: `1`,
...auth_redirect_n(V.query.redirect),
},
}));
} catch (e) {
((W.value = e instanceof Error ? e.message : `重置失败`),
await ue(e));
} finally {
G.value = !1;
}
}
return (r, o) => {
let f = PhoneCodeField,
h = inputtext,
g = button,
_ = CaptchaField,
y = password,
b = message,
T = NuxtLink;
return (
openBlock(),
createElementBlock(`div`, D, [
(o[13] ||= createElementVNode(
`div`,
{
class: `login-brand`,
},
[
createElementVNode(`h1`, null, `找回密码`),
createElementVNode(
`p`,
null,
`通过注册手机号或邮箱验证身份,设置新密码。`,
),
],
-1,
)),
createElementVNode(
`form`,
{
class: `login-form`,
onSubmit: withModifiers(pe, [`prevent`]),
},
[
unref(H) === `phone`
? (openBlock(),
createBlock(
f,
{
key: 0,
modelValue: unref(U).phone_code,
"onUpdate:modelValue": (o[0] ||= (e) =>
(unref(U).phone_code = e)),
disabled: unref(G) || unref(Q),
},
null,
8,
[`modelValue`, `disabled`],
))
: createCommentVNode(``, !0),
createElementVNode(`div`, O, [
(o[8] ||= createElementVNode(
`span`,
{
id: `recovery-method-label`,
class: `method-label`,
},
`找回方式`,
-1,
)),
createElementVNode(`div`, k, [
createElementVNode(
`button`,
{
type: `button`,
class: normalizeClass({
active: unref(H) === `phone`,
}),
"aria-pressed": unref(H) === `phone`,
disabled: unref(G) || unref(Q),
onClick: (o[1] ||= (e) => (H.value = `phone`)),
},
` 手机号 `,
10,
A,
),
createElementVNode(
`button`,
{
type: `button`,
class: normalizeClass({
active: unref(H) === `email`,
}),
"aria-pressed": unref(H) === `email`,
disabled: unref(G) || unref(Q),
onClick: (o[2] ||= (e) => (H.value = `email`)),
},
` 邮箱 `,
10,
j,
),
]),
]),
createElementVNode(`div`, M, [
createElementVNode(
`label`,
N,
toDisplayString(unref(H) === `email` ? `邮箱` : `手机号`),
1,
),
createVNode(
h,
{
id: `account`,
modelValue: unref(U).account,
"onUpdate:modelValue": (o[3] ||= (e) =>
(unref(U).account = e)),
placeholder:
unref(H) === `email`
? `请输入注册邮箱`
: `请输入注册手机号`,
autocomplete: `username`,
disabled: unref(G) || unref(Q),
fluid: ``,
},
null,
8,
[`modelValue`, `placeholder`, `disabled`],
),
]),
createElementVNode(`div`, P, [
(o[9] ||= createElementVNode(
`label`,
{
for: `code`,
},
[
createTextVNode(`验证码 `),
createElementVNode(
`span`,
{
class: `req`,
},
`*`,
),
],
-1,
)),
createElementVNode(`div`, F, [
createVNode(
h,
{
id: `code`,
modelValue: unref(U).code,
"onUpdate:modelValue": (o[4] ||= (e) =>
(unref(U).code = e)),
placeholder: `请输入验证码`,
autocomplete: `one-time-code`,
},
null,
8,
[`modelValue`],
),
createVNode(
g,
{
type: `button`,
label: unref($) > 0 ? `${unref($)}s` : `获取验证码`,
severity: `secondary`,
outlined: ``,
loading: unref(Q),
disabled: unref($) > 0 || unref(G),
onClick: fe,
},
null,
8,
[`label`, `loading`, `disabled`],
),
]),
]),
unref(q)
? (openBlock(),
createBlock(
_,
{
key: 1,
modelValue: unref(X),
"onUpdate:modelValue": (o[5] ||= (e) =>
isRef(X) ? (X.value = e) : null),
img: unref(se),
provider: unref(J),
"captcha-id": unref(Y),
disabled: unref(G) || unref(Q),
onVerified: unref(ae),
onInvalid: unref(oe),
loading: unref(ce),
onRefresh: unref(le),
},
null,
8,
[
`modelValue`,
`img`,
`provider`,
`captcha-id`,
`disabled`,
`onVerified`,
`onInvalid`,
`loading`,
`onRefresh`,
],
))
: createCommentVNode(``, !0),
createElementVNode(`div`, I, [
(o[10] ||= createElementVNode(
`label`,
{
for: `password`,
},
[
createTextVNode(`新密码 `),
createElementVNode(
`span`,
{
class: `req`,
},
`*`,
),
],
-1,
)),
createVNode(
y,
{
"input-id": `password`,
modelValue: unref(U).password,
"onUpdate:modelValue": (o[6] ||= (e) =>
(unref(U).password = e)),
placeholder: `输入新密码`,
feedback: !1,
toggleMask: ``,
autocomplete: `new-password`,
fluid: ``,
},
null,
8,
[`modelValue`],
),
]),
createElementVNode(`div`, L, [
(o[11] ||= createElementVNode(
`label`,
{
for: `re-password`,
},
[
createTextVNode(`确认新密码 `),
createElementVNode(
`span`,
{
class: `req`,
},
`*`,
),
],
-1,
)),
createVNode(
y,
{
"input-id": `re-password`,
modelValue: unref(U).re_password,
"onUpdate:modelValue": (o[7] ||= (e) =>
(unref(U).re_password = e)),
placeholder: `再次输入新密码`,
feedback: !1,
toggleMask: ``,
autocomplete: `new-password`,
fluid: ``,
},
null,
8,
[`modelValue`],
),
]),
unref(W)
? (openBlock(),
createBlock(
b,
{
key: 2,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(W)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
unref(Z)
? (openBlock(),
createBlock(
b,
{
key: 3,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(Z)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createVNode(
g,
{
type: `submit`,
label: `重置密码`,
loading: unref(G),
class: `login-btn`,
fluid: ``,
},
null,
8,
[`loading`],
),
createElementVNode(`div`, R, [
createVNode(
T,
{
to: unref(auth_redirect_t)(
`/login`,
unref(V).query.redirect,
),
},
{
default: withCtx(() => [
...(o[12] ||= [
createTextVNode(`想起密码了?返回登录`, -1),
]),
]),
_: 1,
},
8,
[`to`],
),
]),
],
32,
),
])
);
};
},
});
export {};
export default z;
</script>
+741
View File
@@ -0,0 +1,741 @@
<script>
import "~/assets/styles/help.BSOB30LE.css";
definePageMeta({
scrollToTop: !0,
alias: [`/help/article`],
});
import { toDisplayString } from "vue";
import { renderList } from "vue";
import { onMounted } 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 { 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 { useRoute } from "#app";
import { navigateTo } from "#app";
import { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
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 iconfield from "primevue/iconfield";
import inputicon from "primevue/inputicon";
import ListEmpty from "~/components/ListEmpty.vue";
import skeleton from "primevue/skeleton";
import { n as sanitize_n } from "~/lib/sanitize.js";
import { t as sanitize_t } from "~/lib/sanitize.js";
import { n as ticketAttachments_n } from "~/lib/ticketAttachments.js";
import { t as composables_t } from "~/lib/composables-2.js";
import { t as content_route_t } from "~/lib/content-route.js";
var me = {
class: `help-page`,
},
he = {
key: 0,
class: `page-head`,
},
ge = {
class: `help-body`,
},
_e = {
class: `help-nav`,
"aria-label": `帮助目录`,
},
ve = {
class: `nav-heading`,
},
ye = {
key: 0,
},
be = {
key: 0,
class: `nav-sk`,
},
C = {
key: 1,
class: `nav-groups`,
"aria-label": `帮助文章`,
},
w = {
class: `nav-group-title`,
},
T = {
key: 0,
class: `nav-empty`,
},
E = [`aria-busy`],
D = {
key: 0,
class: `content-sk`,
},
O = {
key: 1,
class: `content-error`,
},
k = {
key: 2,
},
A = {
class: `article-category`,
},
j = {
class: `content-title`,
},
M = [`innerHTML`],
N = {
key: 0,
class: `article-attachments`,
"aria-label": `文章附件`,
},
xe = [`href`],
P = {
class: `article-footer`,
},
F = _plugin_vue_export_helper_t(
defineComponent({
__name: `help`,
props: {
embedded: {
type: Boolean,
},
},
setup(h) {
composables_t({
scrollToTop: !0,
alias: `/help/article`,
});
let x = h,
F = useRoute(),
I = useApi(),
L = ref([]),
R = ref(!0),
z = ref(``),
B = ref(``),
V = ref(``),
H = computed(() =>
L.value.reduce((e, t) => e + (t.helps?.length ?? 0), 0),
),
U = computed(() => {
let e = V.value.trim().toLocaleLowerCase();
return L.value
.map((t) => ({
...t,
helps: (t.helps ?? []).filter(
(n) =>
!e ||
t.name.toLocaleLowerCase().includes(e) ||
n.title.toLocaleLowerCase().includes(e),
),
}))
.filter((e) => e.helps.length > 0);
}),
W = ref(null),
G = computed(() =>
ticketAttachments_n(W.value?.attachment).filter((e) => e.href),
),
K = ref(!1),
q = ref(null),
J = 0,
Y = 0,
X = !1,
Se = computed(
() =>
L.value.find((e) => e.helps?.some((e) => e.id === q.value))
?.name ?? ``,
);
function Ce(e) {
return x?.embedded
? `/resources?section=help&id=${e}`
: `/help/article?id=${e}`;
}
async function Z() {
if (X) return;
let e = F.query.id !== void 0 || F.path === `/help/article`,
t = content_route_t(F.query.id);
if (e && !t) {
(J++,
(q.value = null),
(W.value = null),
(K.value = !1),
(z.value = `帮助文档编号无效,请从目录重新选择`));
return;
}
let n = t ?? L.value.flatMap((e) => e.helps ?? [])[0]?.id;
n
? await $(n)
: (J++,
(q.value = null),
(W.value = null),
(K.value = !1),
(z.value = ``));
}
async function Q() {
let e = ++Y;
((R.value = !0), (B.value = ``));
try {
let t = await I.get(`/help`);
if (X || e !== Y) return;
L.value = t.list ?? [];
} catch (t) {
if (X || e !== Y) return;
B.value = t instanceof Error ? t.message : `目录加载失败`;
} finally {
!X && e === Y && (R.value = !1);
}
!X && e === Y && (await Z());
}
async function $(e) {
let t = ++J;
((q.value = e), (K.value = !0), (W.value = null), (z.value = ``));
try {
let n = await I.get(`/help/${e}`);
if (X || t !== J) return;
if (!n.help) throw Error(`帮助文档不存在或已删除`);
W.value = n.help;
} catch (e) {
!X &&
t === J &&
(z.value = e instanceof Error ? e.message : `加载失败`);
} finally {
!X && t === J && (K.value = !1);
}
}
return (
onMounted(Q),
watch(
() => F.query.id,
() => {
R.value || Z();
},
),
onBeforeUnmount(() => {
((X = !0), J++, Y++);
}),
(n, r) => {
let i = button,
o = inputicon,
l = inputtext,
h = iconfield,
v = skeleton,
b = NuxtLink,
x = message,
S = ListEmpty;
return (
openBlock(),
createElementBlock(`div`, me, [
n.$props.embedded
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(`div`, he, [
(r[6] ||= createElementVNode(
`div`,
{
class: `page-heading`,
},
[
createElementVNode(`h2`, null, `帮助中心`),
createElementVNode(
`p`,
null,
`查找产品使用指南与常见问题解答`,
),
],
-1,
)),
createVNode(i, {
label: `创建工单`,
icon: `pi pi-plus`,
severity: `secondary`,
outlined: ``,
onClick: (r[0] ||= (e) =>
(`navigateTo` in n
? n.navigateTo
: unref(navigateTo))(`/tickets/create`)),
}),
])),
createElementVNode(`div`, ge, [
createElementVNode(`aside`, _e, [
createElementVNode(`div`, ve, [
(r[7] ||= createElementVNode(`h3`, null, `帮助目录`, -1)),
unref(R)
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(
`span`,
ye,
toDisplayString(unref(H)) + ` 篇`,
1,
)),
]),
createVNode(
h,
{
class: `help-search`,
},
{
default: withCtx(() => [
createVNode(o, {
class: `pi pi-search`,
}),
createVNode(
l,
{
modelValue: unref(V),
"onUpdate:modelValue": (r[1] ||= (e) =>
isRef(V) ? (V.value = e) : null),
placeholder: `搜索文章或分类`,
"aria-label": `搜索帮助文章或分类`,
},
null,
8,
[`modelValue`],
),
]),
_: 1,
},
),
unref(R)
? (openBlock(),
createElementBlock(`div`, be, [
(openBlock(),
createElementBlock(
Fragment,
null,
renderList(6, (e) =>
createVNode(v, {
key: e,
height: `20px`,
}),
),
64,
)),
]))
: (openBlock(),
createElementBlock(`nav`, C, [
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(U),
(n) => (
openBlock(),
createElementBlock(
`div`,
{
key: n.id,
class: `nav-group`,
},
[
createElementVNode(`div`, w, [
createTextVNode(
toDisplayString(n.name),
1,
),
createElementVNode(
`span`,
null,
toDisplayString(n.helps.length),
1,
),
]),
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
n.helps,
(t) => (
openBlock(),
createBlock(
b,
{
key: t.id,
to: Ce(t.id),
class: normalizeClass([
`nav-item`,
{
active: unref(q) === t.id,
},
]),
"aria-current":
unref(q) === t.id
? `page`
: void 0,
},
{
default: withCtx(() => [
(r[8] ||= createElementVNode(
`i`,
{
class: `pi pi-file`,
"aria-hidden": `true`,
},
null,
-1,
)),
createElementVNode(
`span`,
null,
toDisplayString(t.title),
1,
),
]),
_: 2,
},
1032,
[`to`, `class`, `aria-current`],
)
),
),
128,
)),
],
)
),
),
128,
)),
unref(U).length
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(`div`, T, [
createElementVNode(
`p`,
null,
toDisplayString(
unref(V).trim()
? `没有找到匹配的文章`
: `暂无帮助内容`,
),
1,
),
unref(V).trim()
? (openBlock(),
createBlock(i, {
key: 0,
label: `清除搜索`,
size: `small`,
severity: `secondary`,
text: ``,
onClick: (r[2] ||= (e) => (V.value = ``)),
}))
: createCommentVNode(``, !0),
])),
])),
unref(B)
? (openBlock(),
createBlock(
x,
{
key: 2,
severity: `warn`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(B)), 1),
createVNode(i, {
label: `重试目录`,
text: ``,
onClick: Q,
}),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
(r[9] ||= createElementVNode(
`div`,
{
class: `support-note`,
},
[
createElementVNode(`i`, {
class: `pi pi-question-circle`,
"aria-hidden": `true`,
}),
createElementVNode(
`span`,
null,
`仍有疑问?创建工单,联系我们协助处理。`,
),
],
-1,
)),
]),
createElementVNode(
`section`,
{
class: `help-content`,
"aria-label": `帮助文章正文`,
"aria-busy": unref(K),
},
[
unref(K) || unref(R)
? (openBlock(),
createElementBlock(`div`, D, [
createVNode(v, {
width: `60%`,
height: `28px`,
}),
(openBlock(),
createElementBlock(
Fragment,
null,
renderList(5, (e) =>
createVNode(
v,
{
key: e,
width: e === 5 ? `70%` : `100%`,
height: `18px`,
},
null,
8,
[`width`],
),
),
64,
)),
]))
: unref(z)
? (openBlock(),
createElementBlock(`div`, O, [
createVNode(
S,
{
icon: `pi pi-exclamation-circle`,
title: `帮助内容暂时无法显示`,
description: unref(z),
},
{
default: withCtx(() => [
unref(q)
? (openBlock(),
createBlock(i, {
key: 0,
label: `重新加载文章`,
icon: `pi pi-refresh`,
severity: `secondary`,
outlined: ``,
onClick: (r[3] ||= (e) =>
$(unref(q))),
}))
: (openBlock(),
createBlock(i, {
key: 1,
label: `重新加载帮助目录`,
icon: `pi pi-refresh`,
severity: `secondary`,
outlined: ``,
onClick: Q,
})),
]),
_: 1,
},
8,
[`description`],
),
]))
: unref(W)
? (openBlock(),
createElementBlock(`article`, k, [
createElementVNode(`div`, A, [
(r[10] ||= createElementVNode(
`i`,
{
class: `pi pi-book`,
"aria-hidden": `true`,
},
null,
-1,
)),
createElementVNode(
`span`,
null,
toDisplayString(unref(Se) || `帮助文档`),
1,
),
]),
createElementVNode(
`h3`,
j,
toDisplayString(unref(W).title),
1,
),
createElementVNode(
`div`,
{
class: `content-html`,
innerHTML: (`sanitizeHtml` in n
? n.sanitizeHtml
: unref(sanitize_n))(
(`decodeEntities` in n
? n.decodeEntities
: unref(sanitize_t))(
unref(W).content || ``,
),
),
},
null,
8,
M,
),
unref(G).length
? (openBlock(),
createElementBlock(`section`, N, [
(r[13] ||= createElementVNode(
`h4`,
null,
`相关附件`,
-1,
)),
createElementVNode(`ul`, null, [
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(G),
(t) => (
openBlock(),
createElementBlock(
`li`,
{
key: t.href || t.name,
},
[
createElementVNode(
`a`,
{
href: t.href,
target: `_blank`,
rel: `noopener noreferrer`,
},
[
(r[11] ||=
createElementVNode(
`i`,
{
class: `pi pi-file`,
"aria-hidden": `true`,
},
null,
-1,
)),
createElementVNode(
`span`,
null,
toDisplayString(t.name),
1,
),
(r[12] ||=
createElementVNode(
`i`,
{
class: `pi pi-download`,
"aria-hidden": `true`,
},
null,
-1,
)),
],
8,
xe,
),
],
)
),
),
128,
)),
]),
]))
: createCommentVNode(``, !0),
createElementVNode(`footer`, P, [
(r[14] ||= createElementVNode(
`span`,
null,
`没有解决你的问题?`,
-1,
)),
createVNode(i, {
label: `创建工单`,
size: `small`,
severity: `secondary`,
outlined: ``,
onClick: (r[4] ||= (e) =>
(`navigateTo` in n
? n.navigateTo
: unref(navigateTo))(
`/tickets/create`,
)),
}),
]),
]))
: (openBlock(),
createBlock(
S,
{
key: 3,
icon: `pi pi-book`,
title: unref(H)
? `选择一篇帮助文档`
: `帮助文档正在准备中`,
description: unref(H)
? `从左侧目录选择文章,查看详细操作指引。`
: `如需协助,可以创建工单联系我们。`,
},
{
default: withCtx(() => [
unref(H)
? createCommentVNode(``, !0)
: (openBlock(),
createBlock(i, {
key: 0,
label: `创建工单`,
icon: `pi pi-plus`,
onClick: (r[5] ||= (e) =>
(`navigateTo` in n
? n.navigateTo
: unref(navigateTo))(
`/tickets/create`,
)),
})),
]),
_: 1,
},
8,
[`title`, `description`],
)),
],
8,
E,
),
]),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-2e1fbf8d`]],
);
export {};
export default F;
</script>
+105
View File
@@ -0,0 +1,105 @@
<script setup>
import { onBeforeUnmount, onMounted, ref } from "vue";
import { useHead, useRouter } from "#app";
import { NuxtLink } from "#components";
import Button from "primevue/button";
import Message from "primevue/message";
import { useAuth } from "~/lib/auth.js";
definePageMeta({ layout: "blank" });
useHead({
title: "正在进入会员中心",
meta: [{ name: "referrer", content: "no-referrer" }],
});
const auth = useAuth();
const router = useRouter();
const isVerifying = ref(false);
const verificationError = ref("");
let disposed = false;
async function verifyAndEnterConsole() {
if (isVerifying.value) return;
isVerifying.value = true;
verificationError.value = "";
try {
// The route guard owns credential extraction; this page never reads it from the URL.
await auth.verifyLegacyLogin();
if (!disposed) await router.replace("/");
} catch (error) {
if (!disposed) {
verificationError.value =
error instanceof Error ? error.message : "登录验证失败,请稍后重试。";
}
} finally {
if (!disposed) isVerifying.value = false;
}
}
onMounted(verifyAndEnterConsole);
onBeforeUnmount(() => {
disposed = true;
auth.cancelLegacyLogin();
});
</script>
<template>
<section
class="auth-card legacy-login"
:aria-busy="isVerifying"
aria-labelledby="legacy-login-title"
>
<span class="legacy-login-icon" aria-hidden="true">
<i :class="isVerifying ? 'pi pi-spin pi-spinner' : 'pi pi-user'" />
</span>
<h1 id="legacy-login-title">
{{ verificationError ? "暂时无法进入会员中心" : "正在进入会员中心" }}
</h1>
<Message v-if="verificationError" severity="error" :closable="false">
{{ verificationError }}
</Message>
<p v-else role="status">正在验证登录身份,请稍候…</p>
<div v-if="verificationError" class="legacy-login-actions">
<Button
label="重新验证"
icon="pi pi-refresh"
:loading="isVerifying"
@click="verifyAndEnterConsole"
/>
<NuxtLink to="/login" class="legacy-login-link">使用账号登录</NuxtLink>
</div>
</section>
</template>
<style scoped>
.legacy-login {
text-align: center;
}
.legacy-login-icon {
background: color-mix(in srgb, var(--p-primary-color) 10%, transparent);
width: 56px;
height: 56px;
color: var(--p-primary-color);
border-radius: 50%;
justify-content: center;
align-items: center;
margin-bottom: 20px;
font-size: 1.75em;
display: inline-flex;
}
.legacy-login p {
color: var(--p-text-muted-color);
margin: 0;
line-height: 1.6;
}
.legacy-login-actions {
flex-direction: column;
gap: 20px;
margin-top: 24px;
display: flex;
}
.legacy-login-link {
color: var(--p-primary-color);
text-underline-offset: 3px;
}
</style>
+675
View File
@@ -0,0 +1,675 @@
<script>
import "~/assets/styles/WithdrawDialog.ks0wdKEK.css";
import "~/assets/styles/PaymentDialog.BNFKQF1U.css";
import "~/assets/styles/RechargeDialog.DpsMly4r.css";
import "~/assets/styles/HomeWalletCard.C5SHzGlJ.css";
import "~/assets/styles/HomeTicketPanel.LxPesukM.css";
import "~/assets/styles/HomeServerPanel.Do7AgMDx.css";
import "~/assets/styles/HomeReferralPanel.DsgN33ME.css";
import "~/assets/styles/HomeNoticePanel.e5-qgzAs.css";
import "~/assets/styles/HomeExpiringPanel.Ckflw2Hv.css";
import "~/assets/styles/SensitiveValue.BSZlaRHT.css";
import "~/assets/styles/pages.DytekQXu.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
import { onMounted } from "vue";
import { normalizeClass } from "vue";
import { onBeforeUnmount } from "vue";
import { createBlock } from "vue";
import { ref } from "vue";
import { withCtx } from "vue";
import { computed } 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 { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
import { useAuth } from "~/lib/auth.js";
import { Icon } from "#components";
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 { t as interval_t } from "~/lib/interval.js";
import skeleton from "primevue/skeleton";
import SensitiveValue from "~/components/SensitiveValue.vue";
import HomeExpiringPanel from "~/components/home/HomeExpiringPanel.vue";
import HomeNoticePanel from "~/components/home/HomeNoticePanel.vue";
import HomeReferralPanel from "~/components/home/HomeReferralPanel.vue";
import HomeServerPanel from "~/components/home/HomeServerPanel.vue";
import HomeTicketPanel from "~/components/home/HomeTicketPanel.vue";
import HomeWalletCard from "~/components/home/HomeWalletCard.vue";
function S(e) {
let t = e.getHours();
return t < 5 || t >= 22
? {
title: `夜深了`,
message: `忙碌之余,也记得早点休息。愿今晚的工作顺利收尾。`,
icon: `lucide:moon-star`,
}
: t < 11
? {
title: `早上好`,
message: `新的一天开始了,愿今天的每一份计划都顺利。`,
icon: `lucide:sunrise`,
}
: t < 14
? {
title: `中午好`,
message: `忙碌了半天,记得好好吃饭,给自己一点休息时间。`,
icon: `lucide:sun`,
}
: t < 18
? {
title: `下午好`,
message: `喝杯水,舒展一下,愿接下来的工作从容又顺利。`,
icon: `lucide:sun`,
}
: {
title: `晚上好`,
message: `辛苦一天了,愿你有个轻松的晚上。`,
icon: `lucide:sunset`,
};
}
function C(e) {
let t = e?.previous_login,
n = Number(t?.time),
r = new Date(n * 1e3);
if (!Number.isFinite(n) || n <= 0 || !Number.isFinite(r.getTime()))
return {
available: !1,
time: `暂无历史记录`,
ip: `—`,
};
let i = (e) => String(e).padStart(2, `0`);
return {
available: !0,
time: `${r.getFullYear()}-${i(r.getMonth() + 1)}-${i(r.getDate())} ${i(r.getHours())}:${i(r.getMinutes())}`,
ip: typeof t?.ip == `string` && t.ip.trim() ? t.ip.trim() : `未记录`,
};
}
var w = {
class: `home-page`,
},
T = {
class: `home-grid`,
},
E = {
class: `home-card welcome-card home-welcome`,
"aria-labelledby": `welcome-title`,
},
D = {
class: `home-card-head welcome-head`,
},
O = {
class: `welcome-greeting`,
},
k = {
class: `welcome-heading`,
},
A = {
class: `welcome-icon`,
"aria-hidden": `true`,
},
j = {
id: `welcome-title`,
class: `home-card-title welcome-title`,
},
M = [`aria-label`],
N = {
key: 1,
class: `copy-id-placeholder`,
"aria-hidden": `true`,
},
P = {
class: `welcome-message`,
},
F = {
class: `welcome-meta`,
},
I = {
class: `welcome-login`,
},
L = {
key: 1,
},
R = {
class: `welcome-login`,
},
z = {
key: 1,
},
de = {
class: `home-card-body welcome-body`,
},
B = {
key: 1,
class: `tiles`,
},
V = {
key: 2,
class: `tiles`,
},
H = {
class: `tile-label`,
},
fe = {
class: `tile-value`,
},
pe = {
key: 2,
class: `tile-text`,
},
U = _plugin_vue_export_helper_t(
defineComponent({
__name: `index`,
setup(m) {
let b = useAuth(),
U = useApi(),
W = ref(!1),
G = ref(new Date()),
K = computed(() => S(G.value)),
q = computed(() => C(b.overview)),
J;
function Y() {
G.value = new Date();
}
async function me() {
if (b.overview)
try {
(await navigator.clipboard.writeText(String(b.overview.id)),
(W.value = !0));
} catch {
W.value = !1;
}
}
let X = ref(null),
Z = ref(!0),
Q = ref(``);
function $() {
((Q.value = ``),
b.fetchOverview().catch((e) => {
e?.status !== 401 &&
(Q.value = e instanceof Error ? e.message : `账户信息加载失败`);
}));
}
(onMounted(() => {
(Y(),
(J = interval_t(Y, 6e4)),
document.addEventListener(`visibilitychange`, Y),
$(),
U.get(`/certification/info`)
.then((e) => {
X.value = e;
})
.catch(() => {})
.finally(() => {
Z.value = !1;
}));
}),
onBeforeUnmount(() => {
(clearInterval(J),
document.removeEventListener(`visibilitychange`, Y));
}));
let he = computed(() => {
let e = b.overview;
if (!e) return [];
let t = [
e.phone
? {
label: `手机号`,
value: e.phone,
kind: `phone`,
prefix: `+${e.phone_code || 86}`,
}
: {
label: `手机号`,
value: `未绑定`,
action: `绑定手机`,
to: `/account?tab=phone`,
},
];
t.push(
e.email
? {
label: `邮箱`,
value: e.email,
kind: `email`,
}
: {
label: `邮箱`,
value: `未绑定`,
action: `绑定邮箱`,
to: `/account`,
},
);
let n = X.value;
return (
n
? (t.push(
n.certification_open === 0
? {
label: `实名认证`,
value: `暂未开放`,
}
: n.is_certification
? {
label: `实名认证`,
value: n.person?.card_name || `已认证`,
kind: n.person?.card_name ? `name` : void 0,
}
: n.person?.status
? {
label: `实名认证`,
value: `审核中`,
}
: {
label: `实名认证`,
value: `未认证`,
action: `去认证`,
to: `/account/realname`,
},
),
n.certification_company_open === 1
? t.push(
n.company?.status
? {
label: `组织机构认证`,
value: n.company?.company || `已认证`,
kind: n.company?.company ? `text` : void 0,
}
: {
label: `组织机构认证`,
value: `未认证`,
action: `去认证`,
to: `/account/realname`,
},
)
: t.push({
label: `组织机构认证`,
value: `暂未开放`,
}))
: t.push(
{
label: `实名认证`,
value: `暂不可用`,
loading: Z.value,
},
{
label: `组织机构认证`,
value: `暂不可用`,
loading: Z.value,
},
),
t
);
});
return (n, i) => {
let o = Icon,
c = skeleton,
m = NuxtLink,
v = button,
y = message,
x = SensitiveValue,
S = HomeWalletCard,
C = HomeTicketPanel,
U = HomeServerPanel,
G = HomeExpiringPanel,
J = HomeNoticePanel,
Y = HomeReferralPanel;
return (
openBlock(),
createElementBlock(`div`, w, [
createElementVNode(`div`, T, [
createElementVNode(`section`, E, [
createElementVNode(`div`, D, [
createElementVNode(`div`, O, [
createElementVNode(`div`, k, [
createElementVNode(`span`, A, [
unref(K).icon === `lucide:moon-star`
? (openBlock(),
createBlock(o, {
key: 0,
name: `lucide:moon-star`,
}))
: unref(K).icon === `lucide:sunrise`
? (openBlock(),
createBlock(o, {
key: 1,
name: `lucide:sunrise`,
}))
: unref(K).icon === `lucide:sunset`
? (openBlock(),
createBlock(o, {
key: 2,
name: `lucide:sunset`,
}))
: (openBlock(),
createBlock(o, {
key: 3,
name: `lucide:sun`,
})),
]),
createElementVNode(`h1`, j, [
createTextVNode(
toDisplayString(unref(K).title) +
`,` +
toDisplayString(
unref(b).overview?.username || `会员`,
) +
` `,
1,
),
unref(b).overview
? (openBlock(),
createElementBlock(
`button`,
{
key: 0,
type: `button`,
class: `copy-id`,
"aria-label": unref(W)
? `用户 ID 已复制`
: `复制用户 ID`,
onClick: me,
},
[
createTextVNode(
` #` +
toDisplayString(unref(b).overview.id),
1,
),
createElementVNode(
`i`,
{
class: normalizeClass(
unref(W) ? `pi pi-check` : `pi pi-copy`,
),
"aria-hidden": `true`,
},
null,
2,
),
],
8,
M,
))
: (openBlock(), createElementBlock(`span`, N)),
]),
]),
createElementVNode(
`p`,
P,
toDisplayString(unref(K).message),
1,
),
createElementVNode(`dl`, F, [
createElementVNode(`div`, I, [
createElementVNode(`dt`, null, [
createVNode(o, {
name: `lucide:clock-3`,
"aria-hidden": `true`,
}),
(i[0] ||= createTextVNode(`上次登录`, -1)),
]),
createElementVNode(`dd`, null, [
!unref(b).overview && !unref(Q)
? (openBlock(),
createBlock(c, {
key: 0,
height: `18px`,
width: `138px`,
}))
: (openBlock(),
createElementBlock(
`span`,
L,
toDisplayString(unref(q).time),
1,
)),
]),
]),
createElementVNode(`div`, R, [
createElementVNode(`dt`, null, [
createVNode(o, {
name: `lucide:map-pin`,
"aria-hidden": `true`,
}),
(i[1] ||= createTextVNode(`IP 地址`, -1)),
]),
createElementVNode(`dd`, null, [
!unref(b).overview && !unref(Q)
? (openBlock(),
createBlock(c, {
key: 0,
height: `18px`,
width: `108px`,
}))
: (openBlock(),
createElementBlock(
`span`,
z,
toDisplayString(unref(q).ip),
1,
)),
]),
]),
]),
]),
createVNode(
m,
{
to: `/account`,
class: `home-card-more welcome-settings`,
"aria-label": `账户设置`,
title: `账户设置`,
},
{
default: withCtx(() => [
...(i[2] ||= [
createElementVNode(
`i`,
{
class: `pi pi-cog`,
"aria-hidden": `true`,
},
null,
-1,
),
]),
]),
_: 1,
},
),
]),
createElementVNode(`div`, de, [
unref(Q)
? (openBlock(),
createBlock(
y,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(Q)), 1),
createVNode(v, {
label: `重试`,
size: `small`,
severity: `secondary`,
text: ``,
onClick: $,
}),
]),
_: 1,
},
))
: unref(b).overview
? (openBlock(),
createElementBlock(`dl`, V, [
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(he),
(t) => (
openBlock(),
createElementBlock(
`div`,
{
key: t.label,
class: `tile`,
},
[
createElementVNode(
`dt`,
H,
toDisplayString(t.label),
1,
),
createElementVNode(`dd`, fe, [
t.loading
? (openBlock(),
createBlock(c, {
key: 0,
width: `64px`,
height: `22px`,
}))
: t.kind
? (openBlock(),
createBlock(
x,
{
key: 1,
value: t.value,
kind: t.kind,
prefix: t.prefix,
label: t.label,
"reset-key":
unref(b).overview?.id,
},
null,
8,
[
`value`,
`kind`,
`prefix`,
`label`,
`reset-key`,
],
))
: t.action
? (openBlock(),
createBlock(
m,
{
key: 3,
to: t.to,
class: `tile-action`,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(
t.action,
) + ` `,
1,
),
(i[3] ||=
createElementVNode(
`i`,
{
class: `pi pi-angle-right`,
"aria-hidden": `true`,
},
null,
-1,
)),
]),
_: 2,
},
1032,
[`to`],
))
: (openBlock(),
createElementBlock(
`span`,
pe,
toDisplayString(t.value),
1,
)),
]),
],
)
),
),
128,
)),
]))
: (openBlock(),
createElementBlock(`div`, B, [
(openBlock(),
createElementBlock(
Fragment,
null,
renderList(4, (e) =>
createElementVNode(
`div`,
{
key: e,
class: `tile`,
},
[
createVNode(c, {
height: `14px`,
width: `50%`,
}),
createVNode(c, {
height: `14px`,
width: `75%`,
style: {
"margin-top": `10px`,
},
}),
],
),
),
64,
)),
])),
]),
]),
createVNode(S, {
class: `home-wallet`,
}),
createVNode(C, {
class: `home-tickets`,
}),
createVNode(U, {
class: `home-servers`,
}),
createVNode(G, {
class: `home-expiring`,
}),
createVNode(J, {
class: `home-notices`,
}),
createVNode(Y),
]),
])
);
};
},
}),
[[`__scopeId`, `data-v-b43b228e`]],
);
export {};
export default U;
</script>
File diff suppressed because it is too large. Load diff
+623
View File
@@ -0,0 +1,623 @@
<script>
import "~/assets/styles/invoice.B6sYFYlb.css";
definePageMeta({
key: (e) => e.fullPath,
});
import { toDisplayString } from "vue";
import { onMounted } from "vue";
import { normalizeClass } from "vue";
import { createBlock } from "vue";
import { ref } from "vue";
import { withCtx } from "vue";
import { createCommentVNode } from "vue";
import { computed } from "vue";
import { createElementBlock } 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 { useRoute } from "#app";
import { NuxtLink } from "#components";
import { sanitizeMessage } from "~/lib/auth.js";
import { useApi } from "~/lib/auth.js";
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 { n as dict_n } from "~/lib/dict.js";
import skeleton from "primevue/skeleton";
import { t as composables_t } from "~/lib/composables-2.js";
var T = {
class: `invoice-detail-page`,
},
E = {
key: 1,
class: `invoice-error`,
},
D = {
key: 2,
class: `invoice-paper`,
},
O = {
class: `invoice-number`,
},
k = {
key: 0,
},
A = {
key: 1,
},
j = {
key: 2,
},
M = {
key: 3,
},
N = {
key: 4,
},
P = {
class: `invoice-total`,
},
F = {
class: `invoice-actions`,
},
I = [`href`],
L = {
key: 2,
class: `invoice-file-hint`,
},
R = _plugin_vue_export_helper_t(
defineComponent({
__name: `invoice`,
setup(f) {
composables_t({
key: (e) => e.fullPath,
});
let b = useRoute(),
R = useApi(),
z = ref(null),
B = ref(!0),
V = ref(``),
H = ref(!1),
U = ref(``),
W = {
pending: `待审核`,
unpaid: `待支付`,
wait_create: `待开具`,
creating: `开具中`,
create_fail: `开具失败`,
approved: `已审核`,
reject: `已驳回`,
rejected: `已驳回`,
issued: `已开具`,
wait_send: `待寄出`,
sent: `已寄出`,
cancel: `已取消`,
flushed: `已冲红`,
},
G = computed(() => z.value?.invoice_project === `历史已开票登记`),
K = computed(() =>
G.value
? `历史已开票登记`
: W[String(z.value?.status || ``).toLowerCase()] ||
String(z.value?.status || `—`),
);
function q(e) {
return typeof e != `string` || /[\u0000-\u0020\\]/.test(e)
? ``
: /^https?:\/\//i.test(e) || /^\/(?!\/)/.test(e)
? e
: ``;
}
function J(...e) {
for (let t of e) {
let e = z.value?.[t];
if (e != null && String(e) !== ``) return e;
}
return ``;
}
async function Y(e) {
if (!(G.value || q(z.value?.invoice_url))) {
((H.value = !0), (U.value = ``));
try {
let t = await R.get(`/invoice/${e}/invoice_filename`),
n = t.data && typeof t.data == `object` ? t.data : t,
r = q(n.url ?? n.invoice_url);
if (r && z.value)
z.value = {
...z.value,
invoice_url: r,
};
else throw Error(`正式发票文件尚未提供`);
} catch (e) {
U.value = e instanceof Error ? e.message : `获取发票下载地址失败`;
} finally {
H.value = !1;
}
}
}
async function X() {
((B.value = !0), (V.value = ``));
try {
let e = String(b.query.id || ``);
if (!/^\d+$/.test(e)) throw Error(`请选择要查看的发票`);
let t = await R.get(`/invoice/${e}`),
n = t.invoice && typeof t.invoice == `object` ? t.invoice : t;
if (!n.id && !n.title && !n.invoice_project)
throw Error(`发票不存在或暂不可用`);
((z.value = n), G.value || Y(String(n.id || e)));
} catch (e) {
V.value = e instanceof Error ? e.message : `加载发票失败`;
} finally {
B.value = !1;
}
}
let Z = computed(() => q(z.value?.invoice_url));
return (
onMounted(X),
(t, i) => {
let s = message,
f = skeleton,
h = button,
v = NuxtLink;
return (
openBlock(),
createElementBlock(`div`, T, [
createVNode(
s,
{
severity: `info`,
closable: !1,
},
{
default: withCtx(() => [
...(i[1] ||= [
createTextVNode(
`发票内容以开票机构出具的正式文件为准,请下载后查看完整票面信息。`,
-1,
),
]),
]),
_: 1,
},
),
unref(B)
? (openBlock(),
createBlock(f, {
key: 0,
height: `480px`,
}))
: unref(V)
? (openBlock(),
createElementBlock(`div`, E, [
createVNode(
s,
{
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(V)), 1),
]),
_: 1,
},
),
createVNode(h, {
label: `重新加载`,
icon: `pi pi-refresh`,
severity: `secondary`,
outlined: ``,
onClick: X,
}),
]))
: unref(z)
? (openBlock(),
createElementBlock(`article`, D, [
(i[16] ||= createElementVNode(
`h1`,
null,
`发票详情`,
-1,
)),
createElementVNode(
`p`,
O,
`发票 ID:` +
toDisplayString(unref(z).id || unref(b).query.id),
1,
),
createElementVNode(`dl`, null, [
createElementVNode(`div`, null, [
(i[2] ||= createElementVNode(
`dt`,
null,
`发票抬头`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(unref(z).title || `—`),
1,
),
]),
createElementVNode(`div`, null, [
(i[3] ||= createElementVNode(
`dt`,
null,
`开票项目`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(
unref(z).invoice_project || `—`,
),
1,
),
]),
createElementVNode(`div`, null, [
(i[4] ||= createElementVNode(
`dt`,
null,
`收件地址`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(
unref(z).email || unref(z).address || `—`,
),
1,
),
]),
createElementVNode(`div`, null, [
(i[5] ||= createElementVNode(
`dt`,
null,
`申请日期`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(
unref(z).create_time
? (`formatDate` in t
? t.formatDate
: unref(dict_n))(
Number(unref(z).create_time),
)
: `—`,
),
1,
),
]),
createElementVNode(`div`, null, [
(i[6] ||= createElementVNode(
`dt`,
null,
`格式`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(unref(z).invoice_format || `—`),
1,
),
]),
createElementVNode(`div`, null, [
(i[7] ||= createElementVNode(
`dt`,
null,
`状态`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(unref(K)),
1,
),
]),
J(`tax_price`, `tax_amount`) === ``
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(`div`, k, [
(i[8] ||= createElementVNode(
`dt`,
null,
`税费`,
-1,
)),
createElementVNode(
`dd`,
null,
`¥` +
toDisplayString(
J(`tax_price`, `tax_amount`),
),
1,
),
])),
J(`parcel_price`, `shipping_fee`, `express_fee`) ===
``
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(`div`, A, [
(i[9] ||= createElementVNode(
`dt`,
null,
`快递费`,
-1,
)),
createElementVNode(
`dd`,
null,
`¥` +
toDisplayString(
J(
`parcel_price`,
`shipping_fee`,
`express_fee`,
),
),
1,
),
])),
J(
`express_company`,
`logistics_company`,
`parcel_name`,
) === ``
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(`div`, j, [
(i[10] ||= createElementVNode(
`dt`,
null,
`物流公司`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(
J(
`express_company`,
`logistics_company`,
`parcel_name`,
),
),
1,
),
])),
J(
`express_number`,
`tracking_number`,
`parcel_number`,
) === ``
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(`div`, M, [
(i[11] ||= createElementVNode(
`dt`,
null,
`物流单号`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(
J(
`express_number`,
`tracking_number`,
`parcel_number`,
),
),
1,
),
])),
J(`invoice_order_id`, `fee_order_id`) === ``
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(`div`, N, [
(i[12] ||= createElementVNode(
`dt`,
null,
`费用订单`,
-1,
)),
createElementVNode(
`dd`,
null,
`#` +
toDisplayString(
J(`invoice_order_id`, `fee_order_id`),
),
1,
),
])),
]),
createElementVNode(`div`, P, [
(i[13] ||= createElementVNode(
`span`,
null,
`价税合计`,
-1,
)),
createElementVNode(
`strong`,
null,
`¥` + toDisplayString(unref(z).price ?? `—`),
1,
),
]),
unref(z).reject_reason
? (openBlock(),
createBlock(
s,
{
key: 0,
severity: `warn`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(
unref(sanitizeMessage)(
unref(z).reject_reason,
),
),
1,
),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createElementVNode(`div`, F, [
createVNode(
h,
{
"as-child": ``,
severity: `secondary`,
outlined: ``,
},
{
default: withCtx((e) => [
createVNode(
v,
{
to: `/invoices`,
class: normalizeClass(e.class),
},
{
default: withCtx(() => [
...(i[14] ||= [
createTextVNode(`返回发票列表`, -1),
]),
]),
_: 1,
},
8,
[`class`],
),
]),
_: 1,
},
),
unref(Z)
? (openBlock(),
createBlock(
h,
{
key: 0,
"as-child": ``,
},
{
default: withCtx((e) => [
createElementVNode(
`a`,
{
href: unref(Z),
class: normalizeClass(e.class),
target: `_blank`,
rel: `noopener noreferrer`,
},
[
...(i[15] ||= [
createElementVNode(
`i`,
{
class: `pi pi-download`,
},
null,
-1,
),
createTextVNode(` 下载发票`, -1),
]),
],
10,
I,
),
]),
_: 1,
},
))
: unref(G)
? createCommentVNode(``, !0)
: (openBlock(),
createBlock(
h,
{
key: 1,
label: `获取发票文件`,
icon: `pi pi-download`,
severity: `secondary`,
outlined: ``,
loading: unref(H),
disabled: unref(H),
onClick: (i[0] ||= (e) =>
Y(
String(
unref(z).id ||
unref(b).query.id ||
``,
),
)),
},
null,
8,
[`loading`, `disabled`],
)),
]),
unref(U)
? (openBlock(),
createBlock(
s,
{
key: 1,
severity: `warn`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(unref(U)) +
`。可稍后重试获取。`,
1,
),
]),
_: 1,
},
))
: !unref(Z) && !unref(G)
? (openBlock(),
createElementBlock(
`p`,
L,
`正式发票文件尚未提供,可在交付后查看或下载。`,
))
: createCommentVNode(``, !0),
]))
: createCommentVNode(``, !0),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-3b8f6704`]],
);
export {};
export default R;
</script>
+17
View File
@@ -0,0 +1,17 @@
<script>
import "~/assets/styles/PageInfoNotice._16xRFrB.css";
import "~/assets/styles/PaymentDialog.BNFKQF1U.css";
import "~/assets/styles/invoices.t1P1GXOx.css";
import { createBlock } from "vue";
import { defineComponent } from "vue";
import { openBlock } from "vue";
import index from "~/pages/invoices/index.vue";
var i = defineComponent({
__name: `request`,
setup(t) {
return (t, i) => (openBlock(), createBlock(index));
},
});
export {};
export default i;
</script>
File diff suppressed because it is too large. Load diff
+393
View File
@@ -0,0 +1,393 @@
<script>
import "~/assets/styles/logbook.Y7gm684m.css";
import { toDisplayString } 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 { 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 { operationMessage } from "~/lib/auth.js";
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 { r as dict_r } from "~/lib/dict.js";
import datatable from "primevue/datatable";
import paginator from "primevue/paginator";
import select from "primevue/select";
import column from "primevue/column";
import ListEmpty from "~/components/ListEmpty.vue";
import { t as useListPage_t } from "~/lib/useListPage.js";
var k = {
class: `list-page`,
},
A = {
class: `list-toolbar`,
},
j = {
class: `list-toolbar-right`,
},
M = {
class: `list-table`,
},
N = {
class: `col-mono`,
},
P = {
key: 1,
class: `list-foot`,
},
F = _plugin_vue_export_helper_t(
defineComponent({
__name: `index`,
setup(f) {
let y = ref(``),
F = ref(``),
I = useRoute(),
L = ref(I.query.type === `api` ? `api` : `system`),
R = ref(`id`),
z = ref(`desc`),
{
list: B,
total: V,
loading: H,
error: U,
page: W,
limit: G,
onPage: K,
reload: q,
} = useListPage_t(`/log`, {
limit: 20,
params: () => ({
keywords: F.value,
type: L.value,
orderby: R.value,
sort: z.value,
}),
});
watch(
() => I.query.type,
(e) => {
let t = e === `api` ? `api` : `system`;
L.value !== t && ((L.value = t), q());
},
);
let J = computed(() => !!F.value || L.value !== `system`);
function Y() {
((F.value = y.value.trim()), q());
}
function X() {
((y.value = ``),
(F.value = ``),
(L.value = `system`),
(R.value = `id`),
(z.value = `desc`),
q());
}
return (t, r) => {
let o = select,
f = inputtext,
g = button,
O = ListEmpty,
F = column,
I = datatable,
Z = message,
Q = paginator;
return (
openBlock(),
createElementBlock(`div`, k, [
(r[5] ||= createElementVNode(
`div`,
{
class: `list-head`,
},
[createElementVNode(`h2`, null, `操作日志`)],
-1,
)),
createElementVNode(`div`, A, [
createElementVNode(`div`, j, [
createVNode(
o,
{
modelValue: unref(L),
"onUpdate:modelValue": (r[0] ||= (e) =>
isRef(L) ? (L.value = e) : null),
options: [
{
label: `系统日志`,
value: `system`,
},
{
label: `API 日志`,
value: `api`,
},
],
"option-label": `label`,
"option-value": `value`,
"aria-label": `日志类型`,
onChange: unref(q),
},
null,
8,
[`modelValue`, `onChange`],
),
createVNode(
o,
{
modelValue: unref(R),
"onUpdate:modelValue": (r[1] ||= (e) =>
isRef(R) ? (R.value = e) : null),
options: [
{
label: `记录 ID`,
value: `id`,
},
{
label: `创建时间`,
value: `create_time`,
},
{
label: `操作内容`,
value: `description`,
},
{
label: `IP`,
value: `ip`,
},
],
"option-label": `label`,
"option-value": `value`,
"aria-label": `日志排序字段`,
onChange: unref(q),
},
null,
8,
[`modelValue`, `onChange`],
),
createVNode(
o,
{
modelValue: unref(z),
"onUpdate:modelValue": (r[2] ||= (e) =>
isRef(z) ? (z.value = e) : null),
options: [
{
label: `降序`,
value: `desc`,
},
{
label: `升序`,
value: `asc`,
},
],
"option-label": `label`,
"option-value": `value`,
"aria-label": `日志排序方向`,
onChange: unref(q),
},
null,
8,
[`modelValue`, `onChange`],
),
createVNode(
f,
{
modelValue: unref(y),
"onUpdate:modelValue": (r[3] ||= (e) =>
isRef(y) ? (y.value = e) : null),
class: `list-search`,
placeholder: `输入关键字`,
onKeyup: withKeys(Y, [`enter`]),
},
null,
8,
[`modelValue`],
),
createVNode(g, {
label: `清除筛选`,
icon: `pi pi-filter-slash`,
severity: `secondary`,
text: ``,
size: `small`,
onClick: X,
}),
]),
]),
createElementVNode(`div`, M, [
createVNode(
I,
{
value: unref(B),
loading: unref(H),
lazy: ``,
rows: unref(G),
totalRecords: unref(V),
first: (unref(W) - 1) * unref(G),
dataKey: `id`,
},
{
empty: withCtx(() => [
createVNode(
O,
{
icon: `pi pi-history`,
title: unref(H) ? `加载中…` : `暂无日志`,
description: unref(J)
? `换个关键字试试`
: `账户操作与登录记录会显示在这里`,
},
null,
8,
[`title`, `description`],
),
]),
default: withCtx(() => [
createVNode(
F,
{
header: `创建时间`,
style: {
width: `180px`,
},
},
{
body: withCtx(({ data: n }) => [
createTextVNode(
toDisplayString(
(`formatTime` in t
? t.formatTime
: unref(dict_r))(n.create_time),
),
1,
),
]),
_: 1,
},
),
createVNode(
F,
{
field: `id`,
header: `ID`,
style: {
width: `120px`,
},
},
{
body: withCtx(({ data: t }) => [
createTextVNode(`#` + toDisplayString(t.id), 1),
]),
_: 1,
},
),
createVNode(
F,
{
header: `操作内容`,
style: {
"min-width": `320px`,
},
},
{
body: withCtx(({ data: t }) => [
createTextVNode(
toDisplayString(
unref(operationMessage)(t.description),
),
1,
),
]),
_: 1,
},
),
createVNode(
F,
{
header: `IP`,
style: {
width: `200px`,
},
},
{
body: withCtx(({ data: t }) => [
createElementVNode(
`span`,
N,
toDisplayString(t.ip || `—`),
1,
),
]),
_: 1,
},
),
]),
_: 1,
},
8,
[`value`, `loading`, `rows`, `totalRecords`, `first`],
),
]),
unref(U)
? (openBlock(),
createBlock(
Z,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(U)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
unref(V) > 0
? (openBlock(),
createElementBlock(`div`, P, [
(r[4] ||= createElementVNode(
`div`,
{
class: `list-bulk`,
},
null,
-1,
)),
createVNode(
Q,
{
rows: unref(G),
totalRecords: unref(V),
first: (unref(W) - 1) * unref(G),
rowsPerPageOptions: [20, 50, 100],
onPage: unref(K),
},
null,
8,
[`rows`, `totalRecords`, `first`, `onPage`],
),
]))
: createCommentVNode(``, !0),
])
);
};
},
}),
[[`__scopeId`, `data-v-0e32233b`]],
);
export {};
export default F;
</script>
+568
View File
@@ -0,0 +1,568 @@
<script setup>
import {
computed,
onBeforeUnmount,
onMounted,
reactive,
ref,
watch,
} from "vue";
import { useHead, useRoute, useRouter } from "#app";
import { NuxtLink } from "#components";
import Button from "primevue/button";
import InputText from "primevue/inputtext";
import Message from "primevue/message";
import Password from "primevue/password";
import AuthProviders from "~/components/AuthProviders.vue";
import CaptchaField from "~/components/CaptchaField.vue";
import GenericOAuthProviders from "~/components/GenericOAuthProviders.vue";
import SecurityVerificationDialog from "~/components/SecurityVerificationDialog.vue";
import { loginErrorMessage, securityChallenge, useAuth } from "~/lib/auth.js";
import { r as safeRedirect, t as withRedirect } from "~/lib/auth-redirect.js";
import { t as useCaptcha } from "~/lib/useCaptcha.js";
import { t as useSiteConfig } from "~/lib/useSiteConfig.js";
import { t as useVerifyCode } from "~/lib/useVerifyCode.js";
import "~/assets/styles/SecurityVerificationDialog.skYFo4v7.css";
import "~/assets/styles/GenericOAuthProviders.Bv8oNwXP.css";
import "~/assets/styles/GeetestField.DiieTibK.css";
import "~/assets/styles/CaptchaField.BSN3ZbsD.css";
import "~/assets/styles/AuthProviders.CgAWouSf.css";
import "~/assets/styles/login.Clyo2_3T.css";
definePageMeta({ layout: "blank" });
useHead({ title: "登录 - 破碎工坊网络" });
const auth = useAuth();
const route = useRoute();
const router = useRouter();
const site = useSiteConfig();
const { config, error: configError } = site;
const form = reactive({ account: "", password: "", code: "" });
const loginMode = ref("password");
const phoneCode = ref("86");
const errorMessage = ref("");
const submitting = ref(false);
const authProviders = ref(null);
const securityDialogVisible = ref(false);
const challenge = ref({});
const pendingCredentials = ref(null);
const pendingVerification = ref(null);
let attemptVersion = 0;
const phonePasswordEnabled = computed(
() =>
config.value?.login_phone_password === undefined ||
site.on(config.value.login_phone_password),
);
const emailPasswordEnabled = computed(
() =>
config.value?.login_email_password === undefined ||
site.on(config.value.login_email_password),
);
const passwordLoginEnabled = computed(
() => phonePasswordEnabled.value || emailPasswordEnabled.value,
);
const codeLoginEnabled = computed(() =>
site.on(config.value?.login_phone_verify),
);
const registrationEnabled = computed(
() =>
config.value?.register_phone === undefined ||
config.value?.register_email === undefined ||
site.on(config.value.register_phone) ||
site.on(config.value.register_email),
);
const isPhoneAccount = computed(
() => loginMode.value === "code" || !form.account.includes("@"),
);
const formLocked = computed(
() =>
submitting.value ||
securityDialogVisible.value ||
!!pendingVerification.value,
);
const securityContext = computed(() => ({
scene: "login",
account: pendingCredentials.value?.account,
phoneCode: pendingCredentials.value?.phoneCode,
}));
const passwordWasReset = computed(() => route.query.reset === "1");
const registrationSucceeded = computed(() => route.query.registered === "1");
const captcha = useCaptcha("login");
const {
needed: captchaNeeded,
provider: captchaProvider,
captchaId,
img: captchaImage,
token: captchaToken,
value: captchaValue,
loading: captchaLoading,
configLoading: captchaConfigLoading,
error: captchaError,
load: loadCaptcha,
complete: completeCaptcha,
invalidate: invalidateCaptcha,
} = captcha;
const {
sending: sendingCode,
countdown: codeCountdown,
send: sendCode,
} = useVerifyCode({
action: "login",
captcha,
error: errorMessage,
phoneCode: () => phoneCode.value,
});
const fieldsDisabled = computed(() => formLocked.value || sendingCode.value);
const captchaBusy = computed(
() => captchaConfigLoading.value || captchaLoading.value,
);
onMounted(() => site.load());
watch(
config,
(settings) => {
if (formLocked.value || sendingCode.value) return;
if (
codeLoginEnabled.value &&
(settings?.first_login_method === "code" || !passwordLoginEnabled.value)
) {
loginMode.value = "code";
} else if (!codeLoginEnabled.value) {
loginMode.value = "password";
}
},
{ immediate: true },
);
watch([() => form.account, loginMode], () => {
form.code = "";
errorMessage.value = "";
});
function setLoginMode(mode) {
if (
fieldsDisabled.value ||
!(mode === "code" ? codeLoginEnabled.value : passwordLoginEnabled.value)
)
return;
loginMode.value = mode;
}
function sendLoginCode() {
if (loginMode.value !== "code" || fieldsDisabled.value) return;
if (!config.value) {
errorMessage.value = "登录设置暂不可用,请重新加载";
return;
}
if (!codeLoginEnabled.value) {
errorMessage.value = "当前登录方式未开放,请选择其他方式";
return;
}
const account = form.account.trim();
if (account.includes("@")) {
errorMessage.value = "请输入手机号";
return;
}
return sendCode("phone", account, "请输入手机号");
}
function cancelLogin() {
attemptVersion += 1;
securityDialogVisible.value = false;
pendingCredentials.value = null;
pendingVerification.value = null;
submitting.value = false;
errorMessage.value = "";
}
onBeforeUnmount(cancelLogin);
async function attemptLogin(credentials, verification) {
const methodEnabled =
credentials.type === "code"
? codeLoginEnabled.value
: credentials.account.includes("@")
? emailPasswordEnabled.value
: phonePasswordEnabled.value;
if (!config.value || !methodEnabled) {
cancelLogin();
errorMessage.value = config.value
? "当前登录方式未开放,请选择其他方式"
: "登录设置暂不可用,请重新加载";
return;
}
const version = attemptVersion;
submitting.value = true;
errorMessage.value = "";
try {
await auth.login(
credentials.account,
credentials.password,
credentials.captcha,
credentials.token,
credentials.type,
credentials.code,
credentials.phoneCode,
verification,
);
if (version !== attemptVersion) return;
authProviders.value?.resetQr();
securityDialogVisible.value = false;
pendingCredentials.value = null;
pendingVerification.value = null;
await router.push(safeRedirect(route.query.redirect));
} catch (error) {
if (version !== attemptVersion) return;
const nextChallenge = securityChallenge(error);
if (nextChallenge) {
pendingCredentials.value = credentials;
pendingVerification.value = null;
challenge.value = nextChallenge;
securityDialogVisible.value = true;
} else {
errorMessage.value = loginErrorMessage(error);
const captchaRequired = await captcha.handleError(error);
if (version !== attemptVersion) return;
if (
captchaRequired &&
verification?.security_verify_method === "certification"
) {
pendingCredentials.value = credentials;
pendingVerification.value = { ...verification };
securityDialogVisible.value = false;
}
}
} finally {
if (version === attemptVersion) submitting.value = false;
}
}
async function confirmSecurityVerification(verification) {
const credentials = pendingCredentials.value;
if (!credentials || submitting.value || pendingVerification.value) return;
if (
verification.security_verify_method === "certification" &&
credentials.type === "password" &&
captchaNeeded.value
) {
// Certification returns to this form for a fresh CAPTCHA before continuing the same login.
pendingVerification.value = { ...verification };
securityDialogVisible.value = false;
errorMessage.value = "";
invalidateCaptcha();
await loadCaptcha();
return;
}
await attemptLogin(credentials, verification);
}
async function submitLogin() {
if (submitting.value || sendingCode.value || securityDialogVisible.value)
return;
errorMessage.value = "";
if (pendingVerification.value && pendingCredentials.value) {
const validationError = captcha.validationError();
if (validationError) {
errorMessage.value = validationError;
return;
}
await attemptLogin(
{
...pendingCredentials.value,
captcha: captchaValue.value.trim(),
token: captchaToken.value,
},
pendingVerification.value,
);
return;
}
if (!form.account.trim()) {
errorMessage.value = "请输入手机号或邮箱";
return;
}
if (!config.value) {
errorMessage.value = "登录设置暂不可用,请重新加载";
return;
}
if (
(loginMode.value === "code" && !codeLoginEnabled.value) ||
(loginMode.value === "password" &&
!(isPhoneAccount.value
? phonePasswordEnabled.value
: emailPasswordEnabled.value))
) {
errorMessage.value = "当前登录方式未开放,请选择其他方式";
return;
}
if (isPhoneAccount.value && !/^[1-9]\d{0,3}$/.test(phoneCode.value)) {
errorMessage.value = "请输入有效的国际电话区号";
return;
}
if (loginMode.value === "code" && form.account.includes("@")) {
errorMessage.value = "请输入手机号";
return;
}
if (loginMode.value === "code" && !form.code.trim()) {
errorMessage.value = "请输入验证码";
return;
}
if (loginMode.value === "password" && !form.password) {
errorMessage.value = "请输入密码";
return;
}
const validationError =
loginMode.value === "password" ? captcha.validationError() : "";
if (validationError) {
errorMessage.value = validationError;
return;
}
attemptVersion += 1;
await attemptLogin({
account: form.account.trim(),
password: loginMode.value === "password" ? form.password : "",
captcha: captchaValue.value.trim(),
token: captchaToken.value,
type: loginMode.value,
code: form.code.trim(),
phoneCode: isPhoneAccount.value ? phoneCode.value : "",
});
}
</script>
<template>
<div class="auth-shell">
<div class="auth-card login-card">
<div class="login-main">
<div class="login-brand"><h1>欢迎回来</h1></div>
<form class="login-form" @submit.prevent="submitLogin">
<div class="seg" role="group" aria-label="登录方式">
<button
type="button"
:class="{ active: loginMode === 'password' }"
:aria-pressed="loginMode === 'password'"
:disabled="!passwordLoginEnabled || fieldsDisabled"
@click="setLoginMode('password')"
>
密码登录
</button>
<button
type="button"
:class="{ active: loginMode === 'code' }"
:aria-pressed="loginMode === 'code'"
:disabled="!codeLoginEnabled || fieldsDisabled"
@click="setLoginMode('code')"
>
验证码登录
</button>
</div>
<Message
v-if="passwordWasReset && !errorMessage"
severity="success"
:closable="false"
icon="pi pi-check-circle"
>密码已重置,请使用新密码登录</Message
>
<Message
v-if="registrationSucceeded && !errorMessage"
severity="success"
:closable="false"
>注册成功,请登录</Message
>
<Message v-if="configError" severity="error" :closable="false">
{{ configError }}
<Button label="重新加载" text @click="site.load" />
</Message>
<Message
v-else-if="!passwordLoginEnabled && !codeLoginEnabled"
severity="info"
:closable="false"
>账号登录暂未开放,请使用已开放的其他登录方式</Message
>
<Message v-if="pendingVerification" severity="info" :closable="false"
>实名认证已通过,请完成新的人机验证后继续登录。</Message
>
<div class="field">
<label for="account">账号</label>
<InputText
id="account"
v-model="form.account"
:placeholder="
loginMode === 'code' ? '输入手机号' : '输入手机号/邮箱'
"
autocomplete="username"
:invalid="!!errorMessage"
:aria-describedby="errorMessage ? 'login-error' : undefined"
:disabled="fieldsDisabled"
fluid
/>
</div>
<div v-if="loginMode === 'code'" class="field">
<label for="login-code">验证码</label>
<!-- Retain the deployed scoped selectors without changing the shared stylesheet. -->
<div class="code-row" data-v-7896f676>
<InputText
id="login-code"
v-model="form.code"
autocomplete="one-time-code"
placeholder="请输入验证码"
:disabled="fieldsDisabled"
fluid
/>
<Button
type="button"
:label="codeCountdown > 0 ? `${codeCountdown}s` : '获取验证码'"
:loading="sendingCode"
:disabled="
codeCountdown > 0 ||
fieldsDisabled ||
!codeLoginEnabled ||
captchaBusy
"
severity="secondary"
outlined
@click="sendLoginCode"
/>
</div>
</div>
<div v-if="loginMode === 'password'" class="field">
<div class="field-label-row">
<label for="password">密码</label>
<NuxtLink
:to="withRedirect('/forgot-password', route.query.redirect)"
class="field-link"
>忘记密码?</NuxtLink
>
</div>
<Password
input-id="password"
v-model="form.password"
placeholder="请输入密码"
:feedback="false"
toggle-mask
autocomplete="current-password"
:invalid="!!errorMessage"
:input-props="{
'aria-describedby': errorMessage ? 'login-error' : undefined,
}"
:disabled="fieldsDisabled"
fluid
/>
</div>
<div class="login-captcha" data-v-7896f676 :aria-busy="captchaBusy">
<button
v-if="captchaBusy || captchaError"
type="button"
class="captcha-placeholder"
data-v-7896f676
:class="{ 'has-error': !!captchaError }"
:disabled="
captchaBusy ||
submitting ||
sendingCode ||
securityDialogVisible ||
!captchaNeeded
"
:aria-label="captchaError || '正在加载验证…'"
@click="loadCaptcha"
>
<i
:class="
captchaError
? 'pi pi-exclamation-circle'
: 'pi pi-spin pi-spinner'
"
aria-hidden="true"
/>
<span
:role="captchaError ? 'alert' : 'status'"
aria-live="polite"
aria-atomic="true"
>{{ captchaError || "正在加载验证…" }}</span
>
</button>
<CaptchaField
v-else-if="captchaNeeded"
v-model="captchaValue"
:img="captchaImage"
:provider="captchaProvider"
:captcha-id="captchaId"
:disabled="submitting || sendingCode || securityDialogVisible"
@verified="completeCaptcha"
@invalid="invalidateCaptcha"
@refresh="loadCaptcha"
/>
</div>
<Message
v-if="errorMessage"
id="login-error"
severity="error"
:closable="false"
icon="pi pi-exclamation-circle"
class="login-error"
>{{ errorMessage }}</Message
>
<Button
type="submit"
:label="
submitting
? '正在登录…'
: pendingVerification
? '继续登录'
: '登录'
"
:loading="submitting"
:disabled="
submitting ||
sendingCode ||
securityDialogVisible ||
captchaBusy ||
!config ||
(!passwordLoginEnabled && !codeLoginEnabled)
"
class="login-btn"
fluid
/>
<Button
v-if="pendingVerification"
label="取消本次登录"
type="button"
severity="secondary"
text
:disabled="submitting"
@click="cancelLogin"
/>
<Button
v-if="registrationEnabled"
v-slot="{ class: buttonClass, a11yAttrs }"
as-child
severity="secondary"
fluid
>
<NuxtLink
:to="withRedirect('/register', route.query.redirect)"
:class="[buttonClass, 'register-btn']"
v-bind="a11yAttrs"
><span class="p-button-label">注册账号</span></NuxtLink
>
</Button>
<SecurityVerificationDialog
v-model:visible="securityDialogVisible"
:challenge="challenge"
:context="securityContext"
:busy="submitting"
:request-error="errorMessage"
@verified="confirmSecurityVerification"
@cancel="cancelLogin"
/>
</form>
</div>
</div>
<AuthProviders ref="authProviders" />
<GenericOAuthProviders />
</div>
</template>
+904
View File
@@ -0,0 +1,904 @@
<script>
import "~/assets/styles/NotificationPreferences.DcH8Noxd.css";
import "~/assets/styles/messages.asRdYcVu.css";
definePageMeta({
key: (e) => e.fullPath,
});
import { toDisplayString } from "vue";
import { onMounted } from "vue";
import { normalizeClass } 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 { withModifiers } 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 { r as dict_r } from "~/lib/dict.js";
import datatable from "primevue/datatable";
import paginator from "primevue/paginator";
import iconfield from "primevue/iconfield";
import inputicon from "primevue/inputicon";
import select from "primevue/select";
import column from "primevue/column";
import ListEmpty from "~/components/ListEmpty.vue";
import { t as useListPage_t } from "~/lib/useListPage.js";
import tag from "primevue/tag";
import skeleton from "primevue/skeleton";
import NotificationPreferences from "~/components/NotificationPreferences.vue";
import { n as sanitize_n } from "~/lib/sanitize.js";
import { t as useMailbox_t } from "~/lib/useMailbox.js";
import { t as composables_t } from "~/lib/composables-2.js";
var ge = {
class: `list-page`,
},
_e = {
class: `list-head`,
},
ve = {
class: `list-head-actions`,
},
ye = {
class: `message-panel`,
},
be = {
class: `list-toolbar`,
},
xe = {
class: `selection-toolbar`,
},
Se = {
class: `selection-summary`,
},
Ce = {
class: `bulk-actions`,
},
we = {
key: 1,
class: `message-error`,
},
Te = {
key: 2,
class: `list-table`,
},
Ee = [`onClick`],
De = {
key: 0,
class: `mail-dot`,
},
Oe = {
key: 3,
class: `list-foot`,
},
ke = {
key: 0,
class: `detail-state`,
},
w = {
key: 1,
class: `detail-state`,
},
Ae = {
class: `detail-meta`,
},
je = [`innerHTML`],
Me = {
class: `detail-nav`,
},
Ne = {
class: `confirm-text`,
},
T = _plugin_vue_export_helper_t(
defineComponent({
__name: `index`,
setup(p) {
composables_t({
key: (e) => e.fullPath,
});
let y = useApi(),
T = useRoute(),
{ types: E, refresh: D } = useMailbox_t(),
O = ref(null),
k = ref(``),
A = ref(``),
j = ref(``),
{
list: M,
total: N,
loading: P,
error: F,
page: I,
limit: L,
onPage: Pe,
reload: R,
} = useListPage_t(`/client_care/mail/list`, {
limit: 10,
params: () => ({
orderby: `id`,
sort: `desc`,
read: O.value ?? ``,
type: k.value,
keywords: j.value,
}),
}),
z = ref([]),
B = ref(!1),
V = computed(() => O.value !== null || !!k.value || !!j.value),
Fe = [
{
label: `全部消息`,
value: null,
},
{
label: `未读`,
value: 0,
},
{
label: `已读`,
value: 1,
},
],
Ie = computed(() => [
{
label: `全部类型`,
value: ``,
},
...E.value.map((e) => ({
label: e.name_lang || e.name,
value: e.name,
})),
]);
function H(e) {
return E.value.find((t) => t.name === e)?.name_lang || e || `—`;
}
function Le() {
((j.value = A.value.trim()), R());
}
function Re() {
((A.value = ``),
(j.value = ``),
(O.value = null),
(k.value = ``),
R());
}
async function ze(e) {
if (!(!e.length || B.value || G.value)) {
((B.value = !0), (U.value = ``));
try {
(await y.put(`/client_care/mail/read`, {
id: e,
}),
(z.value = []),
await R(),
await D());
} catch (e) {
U.value = e instanceof Error ? e.message : `操作失败`;
} finally {
B.value = !1;
}
}
}
async function Be() {
if (!(B.value || G.value)) {
((B.value = !0), (U.value = ``));
try {
(await y.put(`/client_care/mail/read`, {
all: 1,
}),
(z.value = []),
await R(),
await D());
} catch (e) {
U.value = e instanceof Error ? e.message : `操作失败`;
} finally {
B.value = !1;
}
}
}
let U = ref(``),
W = ref(!1),
G = ref(!1);
function Ve() {
!z.value.length ||
B.value ||
G.value ||
((U.value = ``), (W.value = !0));
}
async function He() {
if (!z.value.length || G.value || B.value) return;
let e = z.value.map((e) => e.id);
((G.value = !0), (U.value = ``));
try {
(await y.del(`/client_care/mail`, {
id: e,
}),
(W.value = !1),
(z.value = []),
await R(),
await D());
} catch (e) {
((W.value = !1),
(U.value = e instanceof Error ? e.message : `删除失败`));
} finally {
G.value = !1;
}
}
let K = ref(!1),
q = ref(null),
J = ref(!1),
Y = ref(``),
X = ref(null),
Z = 0;
async function Q(e) {
let t = ++Z;
((X.value = e),
(K.value = !0),
(J.value = !0),
(Y.value = ``),
(q.value = null));
try {
let n = await y.get(`/client_care/mail/${e}`);
if (t !== Z) return;
if (!n?.mail?.id) {
Y.value = `消息不存在或已删除`;
return;
}
((q.value = n.mail), (J.value = !1));
let r = M.value.find((t) => t.id === e);
if (!r || r.read === 0)
try {
(await y.put(`/client_care/mail/read`, {
id: [e],
}),
r && (r.read = 1),
await D());
} catch {}
} catch (e) {
t === Z && (Y.value = e instanceof Error ? e.message : `加载失败`);
} finally {
t === Z && (J.value = !1);
}
}
return (
onMounted(async () => {
await D();
let e = Number(T.query.id);
e && Q(e);
}),
(t, i) => {
let s = button,
p = NotificationPreferences,
_ = inputicon,
v = inputtext,
y = iconfield,
x = select,
S = message,
C = column,
T = ListEmpty,
E = datatable,
D = paginator,
j = skeleton,
Z = tag,
$ = dialog;
return (
openBlock(),
createElementBlock(`div`, ge, [
createElementVNode(`div`, _e, [
(i[12] ||= createElementVNode(
`div`,
{
class: `page-heading`,
},
[
createElementVNode(`h2`, null, `消息`),
createElementVNode(
`p`,
null,
`查看产品、订单与账户的重要通知`,
),
],
-1,
)),
createElementVNode(`div`, ve, [
createVNode(
s,
{
label: `全部已读`,
icon: `pi pi-check`,
size: `small`,
severity: `secondary`,
outlined: ``,
disabled: !unref(N) || unref(P) || unref(G),
loading: unref(B),
onClick: Be,
},
null,
8,
[`disabled`, `loading`],
),
]),
]),
createVNode(p),
createElementVNode(`section`, ye, [
createElementVNode(`div`, be, [
createElementVNode(
`form`,
{
class: `list-toolbar-right`,
onSubmit: withModifiers(Le, [`prevent`]),
},
[
createVNode(
y,
{
class: `search-field`,
},
{
default: withCtx(() => [
createVNode(_, {
class: `pi pi-search`,
}),
createVNode(
v,
{
modelValue: unref(A),
"onUpdate:modelValue": (i[0] ||= (e) =>
isRef(A) ? (A.value = e) : null),
class: `list-search`,
placeholder: `搜索消息标题`,
"aria-label": `搜索消息标题`,
},
null,
8,
[`modelValue`],
),
]),
_: 1,
},
),
createVNode(
x,
{
modelValue: unref(O),
"onUpdate:modelValue": (i[1] ||= (e) =>
isRef(O) ? (O.value = e) : null),
class: `list-select`,
options: Fe,
optionLabel: `label`,
optionValue: `value`,
"aria-label": `阅读状态`,
onChange: unref(R),
},
null,
8,
[`modelValue`, `onChange`],
),
createVNode(
x,
{
modelValue: unref(k),
"onUpdate:modelValue": (i[2] ||= (e) =>
isRef(k) ? (k.value = e) : null),
class: `list-select`,
options: unref(Ie),
optionLabel: `label`,
optionValue: `value`,
"aria-label": `消息类型`,
onChange: unref(R),
},
null,
8,
[`modelValue`, `options`, `onChange`],
),
createVNode(s, {
type: `submit`,
label: `搜索`,
severity: `secondary`,
outlined: ``,
}),
unref(V)
? (openBlock(),
createBlock(s, {
key: 0,
label: `清除筛选`,
icon: `pi pi-filter-slash`,
severity: `secondary`,
text: ``,
onClick: Re,
}))
: createCommentVNode(``, !0),
],
32,
),
]),
createElementVNode(`div`, xe, [
createElementVNode(
`span`,
Se,
toDisplayString(
unref(z).length
? `已选择 ${unref(z).length} 条消息`
: `共 ${unref(N)} 条消息`,
),
1,
),
createElementVNode(`div`, Ce, [
createVNode(
s,
{
label: `标记已读`,
icon: `pi pi-envelope-open`,
size: `small`,
severity: `secondary`,
text: ``,
disabled: !unref(z).length || unref(B) || unref(G),
onClick: (i[3] ||= (e) =>
ze(unref(z).map((e) => e.id))),
},
null,
8,
[`disabled`],
),
createVNode(
s,
{
label: `删除`,
icon: `pi pi-trash`,
size: `small`,
severity: `danger`,
text: ``,
disabled: !unref(z).length || unref(B) || unref(G),
onClick: Ve,
},
null,
8,
[`disabled`],
),
]),
]),
unref(U)
? (openBlock(),
createBlock(
S,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(U)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
unref(F)
? (openBlock(),
createElementBlock(`div`, we, [
createVNode(
S,
{
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(F)), 1),
]),
_: 1,
},
),
createVNode(
s,
{
label: `重新加载消息`,
icon: `pi pi-refresh`,
severity: `secondary`,
outlined: ``,
onClick: unref(R),
},
null,
8,
[`onClick`],
),
]))
: (openBlock(),
createElementBlock(`div`, Te, [
createVNode(
E,
{
selection: unref(z),
"onUpdate:selection": (i[4] ||= (e) =>
isRef(z) ? (z.value = e) : null),
value: unref(M),
loading: unref(P),
lazy: ``,
scrollable: ``,
rows: unref(L),
totalRecords: unref(N),
first: (unref(I) - 1) * unref(L),
dataKey: `id`,
},
{
empty: withCtx(() => [
createVNode(
T,
{
icon: `pi pi-envelope`,
title: unref(P)
? `加载中…`
: unref(V)
? `没有符合条件的消息`
: `还没有消息`,
description: unref(V)
? `换个筛选条件试试`
: `订单、产品、财务等重要通知会出现在这里`,
},
null,
8,
[`title`, `description`],
),
]),
default: withCtx(() => [
createVNode(C, {
selectionMode: `multiple`,
style: {
width: `3rem`,
},
}),
createVNode(
C,
{
header: `标题`,
style: {
"min-width": `260px`,
},
},
{
body: withCtx(({ data: t }) => [
createElementVNode(
`button`,
{
type: `button`,
class: normalizeClass([
`mail-title`,
{
unread: t.read === 0,
},
]),
onClick: (e) => Q(t.id),
},
[
t.read === 0
? (openBlock(),
createElementBlock(`span`, De))
: createCommentVNode(``, !0),
createTextVNode(
` ` + toDisplayString(t.title),
1,
),
],
10,
Ee,
),
]),
_: 1,
},
),
createVNode(
C,
{
header: `类型`,
style: {
width: `150px`,
},
},
{
body: withCtx(({ data: t }) => [
createTextVNode(
toDisplayString(H(t.type)),
1,
),
]),
_: 1,
},
),
createVNode(
C,
{
header: `接收时间`,
style: {
"min-width": `160px`,
},
},
{
body: withCtx(({ data: n }) => [
createTextVNode(
toDisplayString(
(`formatTime` in t
? t.formatTime
: unref(dict_r))(n.create_time),
),
1,
),
]),
_: 1,
},
),
]),
_: 1,
},
8,
[
`selection`,
`value`,
`loading`,
`rows`,
`totalRecords`,
`first`,
],
),
])),
unref(N) > 0
? (openBlock(),
createElementBlock(`div`, Oe, [
(i[13] ||= createElementVNode(
`div`,
{
class: `list-bulk`,
},
null,
-1,
)),
createVNode(
D,
{
rows: unref(L),
totalRecords: unref(N),
first: (unref(I) - 1) * unref(L),
rowsPerPageOptions: [10, 20, 50],
onPage: unref(Pe),
},
null,
8,
[`rows`, `totalRecords`, `first`, `onPage`],
),
]))
: createCommentVNode(``, !0),
]),
createVNode(
$,
{
visible: unref(K),
"onUpdate:visible": (i[9] ||= (e) =>
isRef(K) ? (K.value = e) : null),
header: unref(q)?.title || `消息详情`,
modal: ``,
style: {
width: `680px`,
},
draggable: !1,
},
{
footer: withCtx(() => [
createElementVNode(`div`, Me, [
createVNode(
s,
{
label: `上一条`,
icon: `pi pi-angle-left`,
size: `small`,
severity: `secondary`,
text: ``,
disabled: !unref(q)?.prev?.id,
onClick: (i[6] ||= (e) =>
unref(q)?.prev?.id && Q(unref(q).prev.id)),
},
null,
8,
[`disabled`],
),
createVNode(
s,
{
label: `下一条`,
icon: `pi pi-angle-right`,
iconPos: `right`,
size: `small`,
severity: `secondary`,
text: ``,
disabled: !unref(q)?.next?.id,
onClick: (i[7] ||= (e) =>
unref(q)?.next?.id && Q(unref(q).next.id)),
},
null,
8,
[`disabled`],
),
]),
createVNode(s, {
label: `关闭`,
severity: `secondary`,
text: ``,
onClick: (i[8] ||= (e) => (K.value = !1)),
}),
]),
default: withCtx(() => [
unref(J)
? (openBlock(),
createElementBlock(`div`, ke, [
createVNode(j, {
height: `18px`,
width: `45%`,
}),
createVNode(j, {
height: `150px`,
}),
]))
: unref(Y)
? (openBlock(),
createElementBlock(`div`, w, [
createVNode(
S,
{
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(unref(Y)),
1,
),
]),
_: 1,
},
),
createVNode(s, {
label: `重新加载消息`,
icon: `pi pi-refresh`,
severity: `secondary`,
outlined: ``,
onClick: (i[5] ||= (e) =>
unref(X) && Q(unref(X))),
}),
]))
: unref(q)
? (openBlock(),
createElementBlock(
Fragment,
{
key: 2,
},
[
createElementVNode(`div`, Ae, [
createVNode(
Z,
{
value: H(unref(q).type),
severity: `secondary`,
},
null,
8,
[`value`],
),
createElementVNode(
`span`,
null,
toDisplayString(
(`formatTime` in t
? t.formatTime
: unref(dict_r))(
unref(q).create_time,
),
),
1,
),
]),
createElementVNode(
`div`,
{
class: `detail-body`,
innerHTML: (`sanitizeHtml` in t
? t.sanitizeHtml
: unref(sanitize_n))(unref(q).content),
},
null,
8,
je,
),
],
64,
))
: createCommentVNode(``, !0),
]),
_: 1,
},
8,
[`visible`, `header`],
),
createVNode(
$,
{
visible: unref(W),
"onUpdate:visible": (i[11] ||= (e) =>
isRef(W) ? (W.value = e) : null),
header: `删除消息`,
modal: ``,
closable: !unref(G),
closeOnEscape: !unref(G),
draggable: !1,
style: {
width: `420px`,
},
},
{
footer: withCtx(() => [
createVNode(
s,
{
label: `取消`,
severity: `secondary`,
text: ``,
disabled: unref(G),
onClick: (i[10] ||= (e) => (W.value = !1)),
},
null,
8,
[`disabled`],
),
createVNode(
s,
{
label: `确认删除`,
severity: `danger`,
loading: unref(G),
onClick: He,
},
null,
8,
[`loading`],
),
]),
default: withCtx(() => [
createElementVNode(
`p`,
Ne,
`确认删除选中的 ` +
toDisplayString(unref(z).length) +
` 条消息?删除后不可恢复。`,
1,
),
]),
_: 1,
},
8,
[`visible`, `closable`, `closeOnEscape`],
),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-e9dbafec`]],
);
export {};
export default T;
</script>
+576
View File
@@ -0,0 +1,576 @@
<script>
import "~/assets/styles/news.CbIzdrjt.css";
definePageMeta({
scrollToTop: !0,
alias: [`/news/article`],
});
import { toDisplayString } from "vue";
import { renderList } 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 { 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 { useRouter } from "#app";
import { NuxtLink } from "#components";
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 { r as dict_r } from "~/lib/dict.js";
import paginator from "primevue/paginator";
import ListSegments from "~/components/ListSegments.vue";
import ListEmpty from "~/components/ListEmpty.vue";
import { t as useListPage_t } from "~/lib/useListPage.js";
import tag from "primevue/tag";
import skeleton from "primevue/skeleton";
import { n as sanitize_n } from "~/lib/sanitize.js";
import { t as sanitize_t } from "~/lib/sanitize.js";
import { t as composables_t } from "~/lib/composables-2.js";
import { t as content_route_t } from "~/lib/content-route.js";
var fe = {
class: `list-page`,
},
pe = {
key: 0,
class: `list-head`,
},
me = {
class: `list-toolbar`,
},
he = {
class: `list-toolbar-right`,
},
ge = {
class: `list-search`,
},
_e = {
key: 2,
class: `news-list`,
},
ve = {
key: 3,
class: `news-list`,
},
ye = [`src`],
be = {
class: `news-main`,
},
D = {
class: `news-title`,
},
O = {
class: `news-excerpt`,
},
k = {
class: `news-meta`,
},
A = {
key: 5,
class: `list-foot`,
},
j = {
key: 0,
},
M = {
key: 2,
class: `news-detail`,
},
N = {
class: `news-detail-time`,
},
xe = [`innerHTML`],
P = _plugin_vue_export_helper_t(
defineComponent({
__name: `news`,
props: {
embedded: {
type: Boolean,
},
},
setup(h) {
composables_t({
scrollToTop: !0,
alias: `/news/article`,
});
let x = h,
P = useRoute(),
Se = useRouter(),
F = useApi(),
I = ref([]),
L = ref(``),
R = ref(``),
{
list: z,
total: B,
loading: Ce,
error: V,
page: we,
limit: H,
onPage: Te,
reload: U,
} = useListPage_t(`/news`, {
limit: 10,
params: () => ({
addon_idcsmart_news_type_id: L.value,
keywords: R.value,
}),
});
async function Ee() {
try {
let e = await F.get(`/news/type`);
I.value = e.list ?? [];
} catch {
I.value = [];
}
}
function De(e) {
((L.value = e === `` || e === void 0 ? `` : Number(e)), U());
}
function Oe(e) {
return sanitize_t(e || ``)
.replace(/<[^>]*>/g, ``)
.replace(/<[^>]*$/, ``)
.replace(/&(?:#(?:x[\da-f]*|\d*)|[a-z]+)(?=(?:…|\.{3})?\s*$)/gi, ``)
.replace(/&nbsp;/g, ` `)
.replace(/\s+/g, ` `)
.trim();
}
let W = ref(!1),
G = ref(!1),
K = ref(null),
q = ref(``),
J = ref(null),
Y = 0,
X = !1;
function ke(e) {
return x?.embedded
? `/resources?section=news&id=${e}`
: `/news/article?id=${e}`;
}
async function Z() {
if (X) return;
let e = P.query.id !== void 0 || P.path === `/news/article`,
t = content_route_t(P.query.id);
if (!e || !t) {
(Y++,
(W.value = e),
(G.value = !1),
(K.value = null),
(J.value = null),
(q.value = e ? `新闻编号无效,请从列表重新选择` : ``));
return;
}
await $({
id: t,
title: `新闻详情`,
create_time: 0,
});
}
function Q() {
if (
((W.value = !1),
Y++,
(G.value = !1),
P.query.id !== void 0 || P.path === `/news/article`)
) {
let e = {
...P.query,
};
(delete e.id,
Se.replace({
path: x?.embedded ? `/resources` : `/news`,
query: e,
}));
}
}
async function $(e) {
let t = ++Y;
((W.value = !0),
(G.value = !0),
(J.value = e),
(q.value = ``),
(K.value = null));
try {
let n = await F.get(`/news/${e.id}`);
if (X || t !== Y) return;
if (!n.news) throw Error(`新闻不存在或已删除`);
K.value = n.news;
} catch (e) {
!X &&
t === Y &&
(q.value =
e instanceof Error ? e.message : `正文加载失败,请重试`);
} finally {
!X && t === Y && (G.value = !1);
}
}
return (
onMounted(async () => {
await Promise.all([Ee(), Z()]);
}),
watch(
() => P.query.id,
() => {
Z();
},
),
onBeforeUnmount(() => {
((X = !0), Y++);
}),
(n, r) => {
let i = ListSegments,
o = inputtext,
h = button,
v = message,
y = skeleton,
b = tag,
x = NuxtLink,
C = ListEmpty,
T = paginator,
E = dialog;
return (
openBlock(),
createElementBlock(`div`, fe, [
n.$props.embedded
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(`div`, pe, [
...(r[3] ||= [
createElementVNode(`h2`, null, `新闻中心`, -1),
]),
])),
createElementVNode(`div`, me, [
createVNode(
i,
{
options: [
{
label: `全部`,
value: ``,
},
...unref(I).map((e) => ({
label: `${e.name}(${e.news_num})`,
value: e.id,
})),
],
"model-value": unref(L),
"onUpdate:modelValue": De,
},
null,
8,
[`options`, `model-value`],
),
createElementVNode(`div`, he, [
createElementVNode(`span`, ge, [
createVNode(
o,
{
modelValue: unref(R),
"onUpdate:modelValue": (r[0] ||= (e) =>
isRef(R) ? (R.value = e) : null),
placeholder: `搜索标题`,
onKeyup: withKeys(unref(U), [`enter`]),
},
null,
8,
[`modelValue`, `onKeyup`],
),
]),
createVNode(
h,
{
label: `查询`,
size: `small`,
severity: `secondary`,
outlined: ``,
onClick: unref(U),
},
null,
8,
[`onClick`],
),
]),
]),
unref(V)
? (openBlock(),
createBlock(
v,
{
key: 1,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(V)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
unref(Ce) && !unref(z).length
? (openBlock(),
createElementBlock(`div`, _e, [
(openBlock(),
createElementBlock(
Fragment,
null,
renderList(4, (e) =>
createElementVNode(
`div`,
{
key: e,
class: `news-card`,
},
[
createVNode(y, {
height: `72px`,
}),
],
),
),
64,
)),
]))
: unref(z).length
? (openBlock(),
createElementBlock(`div`, ve, [
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(z),
(t) => (
openBlock(),
createBlock(
x,
{
key: t.id,
to: ke(t.id),
class: `news-card`,
},
{
default: withCtx(() => [
t.img
? (openBlock(),
createElementBlock(
`img`,
{
key: 0,
src: t.img,
class: `news-img`,
alt: ``,
},
null,
8,
ye,
))
: createCommentVNode(``, !0),
createElementVNode(`div`, be, [
createElementVNode(
`div`,
D,
toDisplayString(t.title),
1,
),
createElementVNode(
`div`,
O,
toDisplayString(Oe(t.excerpt)),
1,
),
createElementVNode(`div`, k, [
t.type
? (openBlock(),
createBlock(
b,
{
key: 0,
value: t.type,
severity: `secondary`,
},
null,
8,
[`value`],
))
: createCommentVNode(``, !0),
createElementVNode(
`span`,
null,
toDisplayString(
(`formatTime` in n
? n.formatTime
: unref(dict_r))(t.create_time),
),
1,
),
]),
]),
]),
_: 2,
},
1032,
[`to`],
)
),
),
128,
)),
]))
: (openBlock(),
createBlock(C, {
key: 4,
icon: `pi pi-megaphone`,
title: `暂无新闻`,
description: `站点发布的新闻与公告会显示在这里`,
})),
unref(B) > 0
? (openBlock(),
createElementBlock(`div`, A, [
(r[4] ||= createElementVNode(
`div`,
{
class: `list-bulk`,
},
null,
-1,
)),
createVNode(
T,
{
rows: unref(H),
totalRecords: unref(B),
first: (unref(we) - 1) * unref(H),
rowsPerPageOptions: [10, 20, 50],
onPage: unref(Te),
},
null,
8,
[`rows`, `totalRecords`, `first`, `onPage`],
),
]))
: createCommentVNode(``, !0),
createVNode(
E,
{
visible: unref(W),
header: unref(K)?.title || unref(J)?.title || `新闻详情`,
modal: ``,
draggable: !1,
style: {
width: `760px`,
},
"onUpdate:visible": (r[2] ||= (e) => {
e || Q();
}),
},
{
footer: withCtx(() => [
unref(q) && unref(J)
? (openBlock(),
createBlock(h, {
key: 0,
label: `重新加载正文`,
icon: `pi pi-refresh`,
severity: `secondary`,
onClick: (r[1] ||= (e) => $(unref(J))),
}))
: createCommentVNode(``, !0),
createVNode(h, {
label: `关闭`,
severity: `secondary`,
text: ``,
onClick: Q,
}),
]),
default: withCtx(() => [
unref(G)
? (openBlock(),
createElementBlock(`div`, j, [
createVNode(y, {
height: `220px`,
}),
]))
: unref(q)
? (openBlock(),
createBlock(
v,
{
key: 1,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(q)), 1),
]),
_: 1,
},
))
: unref(K)
? (openBlock(),
createElementBlock(`div`, M, [
createElementVNode(
`div`,
N,
toDisplayString(
(`formatTime` in n
? n.formatTime
: unref(dict_r))(unref(K).create_time),
),
1,
),
createElementVNode(
`div`,
{
class: `news-detail-html`,
innerHTML: (`sanitizeHtml` in n
? n.sanitizeHtml
: unref(sanitize_n))(
(`decodeEntities` in n
? n.decodeEntities
: unref(sanitize_t))(
unref(K).content || ``,
),
),
},
null,
8,
xe,
),
]))
: createCommentVNode(``, !0),
]),
_: 1,
},
8,
[`visible`, `header`],
),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-0f61b69d`]],
);
export {};
export default P;
</script>
+543
View File
@@ -0,0 +1,543 @@
<script>
import "~/assets/styles/notices.BXNGOj-Q.css";
definePageMeta({
key: (e) => e.fullPath,
alias: [`/notices/notice`],
});
import { toDisplayString } from "vue";
import { renderList } 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 { 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 { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
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 { r as dict_r } from "~/lib/dict.js";
import paginator from "primevue/paginator";
import select from "primevue/select";
import ListEmpty from "~/components/ListEmpty.vue";
import skeleton from "primevue/skeleton";
import { n as sanitize_n } from "~/lib/sanitize.js";
import { t as composables_t } from "~/lib/composables-2.js";
import { t as content_route_t } from "~/lib/content-route.js";
var ue = {
key: 0,
class: `list-page notices-page`,
},
w = {
class: `list-toolbar`,
},
de = {
key: 1,
class: `notice-list`,
},
fe = {
key: 2,
class: `notice-list`,
},
T = {
class: `notice-title`,
},
pe = {
class: `notice-time`,
},
me = {
key: 1,
class: `notice-article`,
},
he = {
key: 0,
},
ge = {
class: `detail-time`,
},
E = [`innerHTML`],
D = _plugin_vue_export_helper_t(
defineComponent({
__name: `index`,
props: {
embedded: {
type: Boolean,
},
},
setup(h) {
let b = h,
D = useApi(),
O = useRoute();
composables_t({
alias: `/notices/notice`,
key: (e) => e.fullPath,
});
let k = ref([]),
A = ref(0),
j = ref(!0),
M = ref(``),
N = ref(1),
P = ref(10),
F = ref(``),
I = ref(``),
L = ref(`desc`),
R = ref(null),
z = ref([]),
B = ref(``),
V = 0,
H = !1;
async function U() {
B.value = ``;
try {
let e = await D.get(`/announcement/type`);
H || (z.value = e.list || []);
} catch (e) {
H ||
(B.value = e instanceof Error ? e.message : `公告分类加载失败`);
}
}
function W() {
((I.value = F.value.trim()), (N.value = 1), Z());
}
let G = ref(!1),
K = ref(null),
q = ref(!1),
J = ref(``),
Y = ref(null),
X = 0;
async function Z() {
let e = ++V;
((j.value = !0), (M.value = ``));
try {
let t = await D.get(`/announcement`, {
page: N.value,
limit: P.value,
keywords: I.value,
addon_idcsmart_announcement_type_id: R.value ?? void 0,
orderby: `id`,
sort: L.value,
});
if (H || e !== V) return;
((k.value = t.list), (A.value = t.count));
} catch (t) {
if (H || e !== V) return;
((M.value = t instanceof Error ? t.message : `加载公告失败`),
(k.value = []),
(A.value = 0));
} finally {
!H && e === V && (j.value = !1);
}
}
async function Q(e) {
let t = ++X;
((G.value = !0),
(q.value = !0),
(Y.value = e),
(J.value = ``),
(K.value = null));
try {
let n = await D.get(`/announcement/${e.id}`);
if (t !== X) return;
if (!n.announcement) throw Error(`公告不存在或已删除`);
K.value = n.announcement;
} catch (e) {
t === X &&
(J.value =
e instanceof Error ? e.message : `正文加载失败,请重试`);
} finally {
t === X && (q.value = !1);
}
}
function _e(e) {
((N.value = e.page + 1), (P.value = e.rows), Z());
}
async function $() {
if (H) return;
let e = O.query.id !== void 0 || O.path === `/notices/notice`,
t = content_route_t(O.query.id);
if (!e || !t) {
(X++,
(G.value = e),
(q.value = !1),
(K.value = null),
(Y.value = null),
(J.value = e ? `公告编号无效,请从列表重新选择` : ``));
return;
}
await Q(
k.value.find((e) => e.id === t) ?? {
id: t,
title: `公告详情`,
create_time: 0,
},
);
}
return (
onMounted(async () => {
(U(), await Promise.all([Z(), $()]));
}),
watch(() => O.query.id, $),
onBeforeUnmount(() => {
((H = !0), (X += 1), V++);
}),
(n, r) => {
let i = inputtext,
o = select,
h = button,
v = message,
y = skeleton,
S = NuxtLink,
C = ListEmpty,
D = paginator;
return unref(G)
? (openBlock(),
createElementBlock(`article`, me, [
createVNode(
S,
{
to: b.embedded
? `/resources?section=notices`
: `/notices`,
class: `article-back`,
},
{
default: withCtx(() => [
...(r[5] ||= [
createElementVNode(
`i`,
{
class: `pi pi-arrow-left`,
},
null,
-1,
),
createTextVNode(` 返回公告`, -1),
]),
]),
_: 1,
},
8,
[`to`],
),
createElementVNode(
`h1`,
null,
toDisplayString(
unref(K)?.title || unref(Y)?.title || `公告详情`,
),
1,
),
unref(q)
? (openBlock(),
createElementBlock(`div`, he, [
createVNode(y, {
height: `180px`,
}),
]))
: unref(J)
? (openBlock(),
createBlock(
v,
{
key: 1,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(J)), 1),
unref(Y)
? (openBlock(),
createBlock(h, {
key: 0,
label: `重新加载正文`,
text: ``,
onClick: (r[3] ||= (e) => Q(unref(Y))),
}))
: createCommentVNode(``, !0),
]),
_: 1,
},
))
: unref(K)
? (openBlock(),
createElementBlock(
Fragment,
{
key: 2,
},
[
createElementVNode(
`p`,
ge,
`发布时间 ` +
toDisplayString(
(`formatTime` in n
? n.formatTime
: unref(dict_r))(unref(K).create_time),
),
1,
),
createElementVNode(
`div`,
{
class: `detail-content`,
innerHTML: (`sanitizeHtml` in n
? n.sanitizeHtml
: unref(sanitize_n))(
unref(K).content || ``,
),
},
null,
8,
E,
),
],
64,
))
: createCommentVNode(``, !0),
]))
: (openBlock(),
createElementBlock(`div`, ue, [
(r[4] ||= createElementVNode(
`div`,
{
class: `notice-heading`,
},
[
createElementVNode(`h2`, null, `公告`),
createElementVNode(
`p`,
null,
`我们会在这里发布重要公告与服务信息,帮助您及时了解平台动态。`,
),
],
-1,
)),
createElementVNode(`div`, w, [
createVNode(
i,
{
modelValue: unref(F),
"onUpdate:modelValue": (r[0] ||= (e) =>
isRef(F) ? (F.value = e) : null),
placeholder: `搜索公告标题`,
"aria-label": `搜索公告标题`,
onKeyup: withKeys(W, [`enter`]),
},
null,
8,
[`modelValue`],
),
createVNode(
o,
{
modelValue: unref(R),
"onUpdate:modelValue": (r[1] ||= (e) =>
isRef(R) ? (R.value = e) : null),
options: [
{
id: null,
name: `全部分类`,
},
...unref(z),
],
"option-label": `name`,
"option-value": `id`,
"aria-label": `公告分类`,
onChange: W,
},
null,
8,
[`modelValue`, `options`],
),
createVNode(
o,
{
modelValue: unref(L),
"onUpdate:modelValue": (r[2] ||= (e) =>
isRef(L) ? (L.value = e) : null),
options: [
{
label: `最新优先`,
value: `desc`,
},
{
label: `最早优先`,
value: `asc`,
},
],
"option-label": `label`,
"option-value": `value`,
"aria-label": `公告排序`,
onChange: W,
},
null,
8,
[`modelValue`],
),
createVNode(h, {
label: `搜索`,
text: ``,
onClick: W,
}),
]),
unref(B)
? (openBlock(),
createBlock(
v,
{
key: 0,
severity: `warn`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(B)) + ` `, 1),
createVNode(h, {
label: `重试分类`,
text: ``,
onClick: U,
}),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
unref(j) && !unref(k).length
? (openBlock(),
createElementBlock(`div`, de, [
(openBlock(),
createElementBlock(
Fragment,
null,
renderList(5, (e) =>
createVNode(y, {
key: e,
height: `48px`,
}),
),
64,
)),
]))
: unref(k).length
? (openBlock(),
createElementBlock(`div`, fe, [
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(k),
(t) => (
openBlock(),
createBlock(
S,
{
key: t.id,
to: b.embedded
? `/resources?section=notices&id=${t.id}`
: `/notices/notice?id=${t.id}`,
class: `notice-item`,
},
{
default: withCtx(() => [
createElementVNode(
`span`,
T,
toDisplayString(t.title),
1,
),
createElementVNode(
`span`,
pe,
toDisplayString(
(`formatTime` in n
? n.formatTime
: unref(dict_r))(t.create_time),
),
1,
),
]),
_: 2,
},
1032,
[`to`],
)
),
),
128,
)),
]))
: unref(M)
? (openBlock(),
createBlock(
v,
{
key: 3,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(unref(M)) + ` `,
1,
),
createVNode(h, {
label: `重试`,
text: ``,
onClick: Z,
}),
]),
_: 1,
},
))
: (openBlock(),
createBlock(C, {
key: 4,
icon: `pi pi-megaphone`,
title: `暂无公告`,
description: `平台公告与通知会显示在这里`,
})),
unref(A) > 0
? (openBlock(),
createBlock(
D,
{
key: 5,
rows: unref(P),
totalRecords: unref(A),
first: (unref(N) - 1) * unref(P),
rowsPerPageOptions: [10, 20, 50],
onPage: _e,
},
null,
8,
[`rows`, `totalRecords`, `first`],
))
: createCommentVNode(``, !0),
]));
}
);
},
}),
[[`__scopeId`, `data-v-4ba0157b`]],
);
export {};
export default D;
</script>
+614
View File
@@ -0,0 +1,614 @@
<script>
import "~/assets/styles/PhoneCodeField.CwyHXfML.css";
import "~/assets/styles/GeetestField.DiieTibK.css";
import "~/assets/styles/CaptchaField.BSN3ZbsD.css";
import "~/assets/styles/bind.CFZQE2UH.css";
definePageMeta({
layout: `blank`,
});
import { useRuntimeConfig } from "#app";
import { toDisplayString } from "vue";
import { onMounted } 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 { isRef } from "vue";
import { createElementBlock } 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 { withModifiers } from "vue";
import { useRoute } from "#app";
import { navigateTo } from "#app";
import { useHead } from "#app";
import { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
import { isValidJwt } from "~/lib/auth.js";
import { useAuth } from "~/lib/auth.js";
import { t as useSiteConfig_t } from "~/lib/useSiteConfig.js";
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 { n as wechat_binding_n } from "~/lib/wechat-binding.js";
import { t as wechat_binding_t } from "~/lib/wechat-binding.js";
import { r as auth_redirect_r } from "~/lib/auth-redirect.js";
import inputtext from "primevue/inputtext";
import CaptchaField from "~/components/CaptchaField.vue";
import checkbox from "primevue/checkbox";
import { n as oauth_n } from "~/lib/oauth.js";
import PhoneCodeField from "~/components/PhoneCodeField.vue";
import { t as useCaptcha_t } from "~/lib/useCaptcha.js";
import { t as composables_t } from "~/lib/composables-2.js";
import { t as useVerifyCode_t } from "~/lib/useVerifyCode.js";
var ve = {
class: `auth-card bind-card`,
},
ye = {
key: 1,
class: `session-status`,
},
be = [`disabled`],
xe = {
class: `seg`,
},
Se = [`aria-pressed`],
Ce = [`aria-pressed`],
we = {
class: `field`,
},
w = {
for: `bind-account`,
},
T = {
class: `input-row`,
},
E = {
class: `field`,
},
D = {
class: `input-row`,
},
O = {
class: `agreement`,
},
k = {
for: `bind-agreement`,
},
Te = [`href`],
Ee = {
key: 1,
},
De = [`href`],
Oe = {
key: 3,
},
A = _plugin_vue_export_helper_t(
defineComponent({
__name: `bind`,
setup(g) {
(composables_t({
layout: `blank`,
}),
useHead({
title: `绑定账号 - 破碎工坊网络`,
}));
let S = useApi(),
A = useAuth(),
j = useSiteConfig_t(),
M = useRoute(),
N = M.query.provider === `wechat`,
P = N ? wechat_binding_n(sessionStorage) : null,
F = auth_redirect_r(M.query.redirect),
I = ref(`phone`),
L = ref(``),
R = ref(`86`),
z = ref(``),
B = ref(!1),
V = ref(!1),
H = ref(``),
U = ref(!1),
W = ref(!1),
G = !0;
onBeforeUnmount(() => {
G = !1;
});
async function K() {
if (!W.value && G) {
((W.value = !0), (U.value = !1), (H.value = ``));
try {
if (N) {
if (!P) throw Error(`扫码会话已过期,请返回登录重新扫码`);
let e = await S.get(`/mp_weixin_notice/qrcode/status`, {
ticket: P.ticket,
token: P.token,
});
if (!G) return;
if (e.status === `Success` && isValidJwt(e.jwt)) {
(wechat_binding_t(sessionStorage),
A.loginWithJwt(e.jwt),
await navigateTo(F));
return;
}
if (e.status !== `WaitBind`)
throw Error(`扫码会话已失效,请返回登录重新扫码`);
U.value = !0;
return;
}
let t = await S.get(`/oauth/token`);
if (!G) return;
let n = oauth_n(
t,
window.location.origin,
new URL(
String(
useRuntimeConfig().public.apiBase || window.location.origin,
),
window.location.origin,
).origin,
);
if (n.kind === `login`) {
(A.loginWithJwt(n.jwt), await navigateTo(`/`));
return;
}
if (n.kind !== `bind`)
throw Error(`授权会话已过期,请返回登录并重新授权`);
U.value = !0;
} catch (e) {
G &&
(H.value = e instanceof Error ? e.message : `验证授权会话失败`);
} finally {
G && (W.value = !1);
}
}
}
let q = useCaptcha_t(`oauth`),
{
sending: J,
countdown: Y,
send: ke,
} = useVerifyCode_t({
action: `oauth`,
captcha: q,
error: H,
phoneCode: () => R.value,
}),
{
needed: Ae,
provider: je,
captchaId: Me,
complete: Ne,
invalidate: X,
value: Z,
img: Pe,
loading: Fe,
error: Q,
load: Ie,
} = q,
$ = computed(() => {
let e = j.config.value,
t = (e) =>
typeof e == `string` && /^https?:\/\//i.test(e) ? e : ``;
return {
service: t(e?.terms_service_url),
privacy: t(e?.terms_privacy_url),
};
});
watch(I, () => {
((L.value = ``), (z.value = ``), (H.value = ``));
});
async function Le() {
if (!V.value && G) {
if (((H.value = ``), !U.value)) {
H.value = `请先验证授权会话;会话失效时请返回登录重新授权`;
return;
}
if (!L.value.trim() || !z.value.trim()) {
H.value = `请填写账号和验证码`;
return;
}
if (!B.value) {
H.value = `请先阅读并同意服务协议和隐私协议`;
return;
}
if (I.value === `phone` && !/^[1-9]\d{0,3}$/.test(R.value)) {
H.value = `请输入有效的国际电话区号`;
return;
}
V.value = !0;
try {
let e = q.validationError?.();
if (e) throw Error(e);
let t = await S.post(
N
? `/mp_weixin_notice/qrcode/bind_client`
: `/oauth/client/bind`,
{
type: I.value,
account: L.value.trim(),
phone_code: R.value,
code: z.value.trim(),
...(N && P
? {
ticket: P.ticket,
token: P.token,
captcha: q.value.value,
}
: {}),
},
);
if (!G) return;
if (!isValidJwt(t?.jwt))
throw Error(`绑定会话已失效,请返回登录后重新发起第三方授权`);
(N && wechat_binding_t(sessionStorage),
A.loginWithJwt(t.jwt),
await navigateTo(F));
} catch (e) {
G &&
((H.value =
e instanceof Error ? e.message : `绑定失败,请重试`),
await q.handleError?.(e));
} finally {
G && (V.value = !1);
}
}
}
return (
onMounted(() => {
(j.load(), K());
}),
(e, n) => {
let i = message,
a = button,
s = NuxtLink,
u = PhoneCodeField,
g = inputtext,
y = CaptchaField,
b = checkbox;
return (
openBlock(),
createElementBlock(`section`, ve, [
(n[13] ||= createElementVNode(`h1`, null, `绑定账号`, -1)),
(n[14] ||= createElementVNode(
`p`,
{
class: `bind-intro`,
},
`请使用已注册的手机号或邮箱接收验证码,完成与第三方账号的绑定。`,
-1,
)),
unref(H)
? (openBlock(),
createBlock(
i,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(H)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
unref(U)
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(`div`, ye, [
createVNode(
a,
{
label: `验证授权会话`,
loading: unref(W),
onClick: K,
},
null,
8,
[`loading`],
),
createVNode(
s,
{
to: `/login`,
},
{
default: withCtx(() => [
...(n[8] ||= [createTextVNode(`返回登录`, -1)]),
]),
_: 1,
},
),
])),
unref(U)
? (openBlock(),
createElementBlock(
`form`,
{
key: 2,
class: `bind-form`,
onSubmit: withModifiers(Le, [`prevent`]),
},
[
createElementVNode(
`fieldset`,
{
disabled: unref(V),
},
[
(n[9] ||= createElementVNode(
`legend`,
null,
`验证方式`,
-1,
)),
createElementVNode(`div`, xe, [
createElementVNode(
`button`,
{
type: `button`,
class: normalizeClass({
active: unref(I) === `email`,
}),
"aria-pressed": unref(I) === `email`,
onClick: (n[0] ||= (e) =>
(I.value = `email`)),
},
` 邮箱 `,
10,
Se,
),
createElementVNode(
`button`,
{
type: `button`,
class: normalizeClass({
active: unref(I) === `phone`,
}),
"aria-pressed": unref(I) === `phone`,
onClick: (n[1] ||= (e) =>
(I.value = `phone`)),
},
` 手机号 `,
10,
Ce,
),
]),
],
8,
be,
),
createElementVNode(`div`, we, [
createElementVNode(
`label`,
w,
toDisplayString(
unref(I) === `phone` ? `手机号` : `邮箱`,
),
1,
),
createElementVNode(`div`, T, [
unref(I) === `phone`
? (openBlock(),
createBlock(
u,
{
key: 0,
modelValue: unref(R),
"onUpdate:modelValue": (n[2] ||= (e) =>
isRef(R) ? (R.value = e) : null),
disabled: unref(V) || unref(J),
},
null,
8,
[`modelValue`, `disabled`],
))
: createCommentVNode(``, !0),
createVNode(
g,
{
id: `bind-account`,
modelValue: unref(L),
"onUpdate:modelValue": (n[3] ||= (e) =>
isRef(L) ? (L.value = e) : null),
type: unref(I) === `email` ? `email` : `tel`,
placeholder:
unref(I) === `phone`
? `输入手机号`
: `输入邮箱`,
autocomplete: `username`,
disabled: unref(V),
required: ``,
},
null,
8,
[`modelValue`, `type`, `placeholder`, `disabled`],
),
]),
]),
createElementVNode(`div`, E, [
(n[10] ||= createElementVNode(
`label`,
{
for: `bind-code`,
},
`验证码`,
-1,
)),
createElementVNode(`div`, D, [
createVNode(
g,
{
id: `bind-code`,
modelValue: unref(z),
"onUpdate:modelValue": (n[4] ||= (e) =>
isRef(z) ? (z.value = e) : null),
placeholder: `输入验证码`,
autocomplete: `one-time-code`,
disabled: unref(V),
required: ``,
},
null,
8,
[`modelValue`, `disabled`],
),
createVNode(
a,
{
label: unref(Y) ? `${unref(Y)}s` : `获取验证码`,
loading: unref(J),
disabled:
unref(V) || !unref(L).trim() || unref(Y) > 0,
onClick: (n[5] ||= (e) =>
unref(ke)(
unref(I),
unref(L).trim(),
`请输入账号`,
)),
},
null,
8,
[`label`, `loading`, `disabled`],
),
]),
]),
unref(Ae)
? (openBlock(),
createBlock(
y,
{
key: 0,
modelValue: unref(Z),
"onUpdate:modelValue": (n[6] ||= (e) =>
isRef(Z) ? (Z.value = e) : null),
img: unref(Pe),
provider: unref(je),
"captcha-id": unref(Me),
disabled: unref(V) || unref(J),
onVerified: unref(Ne),
onInvalid: unref(X),
loading: unref(Fe),
onRefresh: unref(Ie),
},
null,
8,
[
`modelValue`,
`img`,
`provider`,
`captcha-id`,
`disabled`,
`onVerified`,
`onInvalid`,
`loading`,
`onRefresh`,
],
))
: createCommentVNode(``, !0),
unref(Q)
? (openBlock(),
createBlock(
i,
{
key: 1,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(Q)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createElementVNode(`div`, O, [
createVNode(
b,
{
modelValue: unref(B),
"onUpdate:modelValue": (n[7] ||= (e) =>
isRef(B) ? (B.value = e) : null),
"input-id": `bind-agreement`,
binary: ``,
disabled: unref(V),
},
null,
8,
[`modelValue`, `disabled`],
),
createElementVNode(`label`, k, [
(n[11] ||= createTextVNode(`阅读并同意 `, -1)),
unref($).service
? (openBlock(),
createElementBlock(
`a`,
{
key: 0,
href: unref($).service,
target: `_blank`,
rel: `noopener noreferrer`,
},
`《服务协议》`,
8,
Te,
))
: (openBlock(),
createElementBlock(`span`, Ee, `《服务协议》`)),
(n[12] ||= createTextVNode(` 和 `, -1)),
unref($).privacy
? (openBlock(),
createElementBlock(
`a`,
{
key: 2,
href: unref($).privacy,
target: `_blank`,
rel: `noopener noreferrer`,
},
`《隐私协议》`,
8,
De,
))
: (openBlock(),
createElementBlock(`span`, Oe, `《隐私协议》`)),
]),
]),
createVNode(
a,
{
type: `submit`,
label: `确认绑定`,
loading: unref(V),
disabled:
!unref(B) || !unref(L).trim() || !unref(z).trim(),
fluid: ``,
},
null,
8,
[`loading`, `disabled`],
),
],
32,
))
: createCommentVNode(``, !0),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-146faed1`]],
);
export {};
export default A;
</script>
+149
View File
@@ -0,0 +1,149 @@
<script>
definePageMeta({
layout: `blank`,
});
import { useRuntimeConfig } from "#app";
import { toDisplayString } from "vue";
import { onMounted } from "vue";
import { onBeforeUnmount } from "vue";
import { createBlock } from "vue";
import { ref } from "vue";
import { withCtx } from "vue";
import { createCommentVNode } from "vue";
import { createElementBlock } 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 { useHead } from "#app";
import { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
import { useAuth } from "~/lib/auth.js";
import button from "primevue/button";
import message from "primevue/message";
import { n as oauth_n } from "~/lib/oauth.js";
import { t as composables_t } from "~/lib/composables-2.js";
var w = {
class: `auth-card`,
},
T = defineComponent({
__name: `callback`,
setup(f) {
(composables_t({
layout: `blank`,
}),
useHead({
title: `恢复第三方登录`,
}));
let T = useApi(),
E = useAuth(),
D = ref(!1),
O = ref(``),
k = !0;
onBeforeUnmount(() => {
k = !1;
});
async function A() {
if (k && !D.value) {
((D.value = !0), (O.value = ``));
try {
let t = await T.get(`/oauth/token`);
if (!k) return;
let n = oauth_n(
t,
window.location.origin,
new URL(
String(
useRuntimeConfig().public.apiBase || window.location.origin,
),
window.location.origin,
).origin,
);
if (n.kind === `login`) {
E.loginWithJwt(n.jwt);
try {
sessionStorage.removeItem(`console-oauth-pending`);
} catch {}
await navigateTo(`/`);
} else if (n.kind === `bind`) {
if (new URL(n.url).pathname === `/oauth/callback`)
throw Error(`授权返回地址异常,请重新发起授权`);
window.location.replace(n.url);
} else throw Error(`授权会话已过期,请返回登录并重新授权`);
} catch (e) {
k && (O.value = e instanceof Error ? e.message : `恢复登录失败`);
} finally {
k && (D.value = !1);
}
}
}
return (
onMounted(A),
(e, n) => {
let r = message,
a = button,
f = NuxtLink;
return (
openBlock(),
createElementBlock(`section`, w, [
(n[1] ||= createElementVNode(`h1`, null, `恢复第三方登录`, -1)),
(n[2] ||= createElementVNode(
`p`,
null,
`正在验证授权会话。`,
-1,
)),
unref(O)
? (openBlock(),
createBlock(
r,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(O)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createVNode(
a,
{
label: `重新验证`,
loading: unref(D),
onClick: A,
},
null,
8,
[`loading`],
),
createElementVNode(`p`, null, [
createVNode(
f,
{
to: `/login`,
},
{
default: withCtx(() => [
...(n[0] ||= [createTextVNode(`返回登录`, -1)]),
]),
_: 1,
},
),
]),
])
);
}
);
},
});
export {};
export default T;
</script>
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+25
View File
@@ -0,0 +1,25 @@
<script>
import "~/assets/styles/PaymentDialog.BNFKQF1U.css";
import "~/assets/styles/_id_.gjoJy9S8.css";
definePageMeta({
key: (e) => e.fullPath,
});
import { createBlock } from "vue";
import { defineComponent } from "vue";
import { openBlock } from "vue";
import { t as composables_t } from "~/lib/composables-2.js";
import _id_ from "~/pages/orders/[id].vue";
var a = defineComponent({
__name: `index`,
setup(t) {
return (
composables_t({
key: (e) => e.fullPath,
}),
(t, r) => (openBlock(), createBlock(_id_))
);
},
});
export {};
export default a;
</script>
+25
View File
@@ -0,0 +1,25 @@
<script>
import "~/assets/styles/PaymentDialog.BNFKQF1U.css";
import "~/assets/styles/_id_.gjoJy9S8.css";
definePageMeta({
key: (e) => e.fullPath,
});
import { createBlock } from "vue";
import { defineComponent } from "vue";
import { openBlock } from "vue";
import { t as composables_t } from "~/lib/composables-2.js";
import _id_ from "~/pages/orders/[id].vue";
var a = defineComponent({
__name: `paid`,
setup(t) {
return (
composables_t({
key: (e) => e.fullPath,
}),
(t, r) => (openBlock(), createBlock(_id_))
);
},
});
export {};
export default a;
</script>
+25
View File
@@ -0,0 +1,25 @@
<script>
import "~/assets/styles/PaymentDialog.BNFKQF1U.css";
import "~/assets/styles/_id_.gjoJy9S8.css";
definePageMeta({
key: (e) => e.fullPath,
});
import { createBlock } from "vue";
import { defineComponent } from "vue";
import { openBlock } from "vue";
import { t as composables_t } from "~/lib/composables-2.js";
import _id_ from "~/pages/orders/[id].vue";
var a = defineComponent({
__name: `pay`,
setup(t) {
return (
composables_t({
key: (e) => e.fullPath,
}),
(t, r) => (openBlock(), createBlock(_id_))
);
},
});
export {};
export default a;
</script>
+460
View File
@@ -0,0 +1,460 @@
<script>
import "~/assets/styles/MfDcimConfig.BhC_eZ0V.css";
import "~/assets/styles/MfCloudConfig.DbgUu8qr.css";
import "~/assets/styles/KkyEcsConfig.BFaAwQPZ.css";
import "~/assets/styles/ConfigResourceOption.Dj4VopnA.css";
import "~/assets/styles/ConfigDiskAdd.C92e9ZuF.css";
import "~/assets/styles/ConfigBandwidth.CWSH1WJZ.css";
import "~/assets/styles/JyCloudConfig.kGfttaHi.css";
import "~/assets/styles/FinanceConfig.CLwX1CC8.css";
import "~/assets/styles/ConfigDiskCard.Dx9IqqQ_.css";
import "~/assets/styles/PromoCodeField.qFEkB6Hm.css";
import "~/assets/styles/ProductCustomFields.B2QMnPil.css";
import "~/assets/styles/OsIcon.CAi4Iz2S.css";
import "~/assets/styles/CommonConfig.Cfik-oMe.css";
import "~/assets/styles/PaymentDialog.BNFKQF1U.css";
import "~/assets/styles/_id_.CSXsMADH.css";
definePageMeta({
key: (e) => e.fullPath,
});
import { toDisplayString } from "vue";
import { onMounted } from "vue";
import { onScopeDispose } 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 { useRoute } from "#app";
import { useRouter } from "#app";
import { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
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 { n as product_name_n } from "~/lib/product-name.js";
import PaymentDialog from "~/components/PaymentDialog.vue";
import { t as product_order_module_t } from "~/lib/product-order-module.js";
import ConfigLoadingShell from "~/components/config/ConfigLoadingShell.vue";
import { n as cart_edit_n } from "~/lib/cart-edit.js";
import CommonConfig from "~/components/config/CommonConfig.vue";
import FinanceConfig from "~/components/config/FinanceConfig.vue";
import JyCloudConfig from "~/components/config/JyCloudConfig.vue";
import KkyEcsConfig from "~/components/config/KkyEcsConfig.vue";
import MfCloudConfig from "~/components/config/MfCloudConfig.vue";
import { r as order_image_continuation_r } from "~/lib/order-image-continuation.js";
import MfDcimConfig from "~/components/config/MfDcimConfig.vue";
import { t as composables_t } from "~/lib/composables-2.js";
import { t as product_description_data_t } from "~/lib/product-description-data.js";
var j = {
class: `page product-order-page`,
},
M = {
class: `product-head`,
},
N = {
class: `product-links`,
"aria-label": `订购导航`,
},
P = {
key: 1,
class: `panel config-load-error`,
},
F = {
key: 6,
class: `panel`,
},
I = _plugin_vue_export_helper_t(
defineComponent({
__name: `[id]`,
setup(f) {
let b = useRoute(),
I = useRouter(),
L = useApi(),
R = String(b.params.id ?? b.query.id ?? b.query.product_id ?? ``),
z = order_image_continuation_r(b.query.order_image),
B = b.fullPath;
composables_t({
key: (e) => e.fullPath,
});
function V() {
let e = {},
t = b.query.g ?? b.query.first_group_id,
n = b.query.sg ?? b.query.second_group_id;
return (
typeof t == `string` && /^[1-9]\d*$/.test(t) && (e.g = t),
typeof n == `string` && /^[1-9]\d*$/.test(n) && (e.sg = n),
{
path: `/products`,
query: e,
}
);
}
let H = ref(null),
U = ref(null),
W = ref(!0),
G = ref(``),
K = computed(() => {
let e = b.query.edit_position;
if (typeof e != `string` || !/^\d+$/.test(e)) return null;
let t = Number(e);
return Number.isInteger(t) && t >= 0 ? t : null;
}),
q = computed(() => {
if (K.value !== null && U.value)
return {
position: K.value,
item: U.value,
};
}),
J = ref(!1),
Y = ref(null),
X = ref(``),
Z = 0;
onScopeDispose(() => {
Z++;
});
async function Q() {
let e = ++Z;
((W.value = !0), (G.value = ``), (H.value = null), (U.value = null));
try {
if (!/^[1-9]\d*$/.test(R)) throw Error(`商品编号无效`);
let t = await L.get(`/product/${R}`);
if (e !== Z) return;
let n = t?.product ?? t;
if (
!n ||
typeof n != `object` ||
!n.id ||
Number(n.id) !== Number(R)
)
throw Error(`商品不存在或已下架`);
let r = n;
if (r.order_allowed === !1)
throw Error(r.order_block_reason || `该产品已停止订购`);
if (K.value !== null) {
let t = (await L.get(`/cart`))?.list?.[K.value];
if (!t || Number(t.product_id) !== Number(R))
throw Error(`购物车商品已变化,请返回购物车重新编辑`);
if (e !== Z) return;
U.value = cart_edit_n(t);
}
let i = await product_order_module_t(L, r);
if (e !== Z) return;
((H.value = {
...r,
module: i,
}),
product_description_data_t(L, r).then((t) => {
e === Z && H.value && (H.value.description = t);
}));
} catch (t) {
e === Z &&
(G.value = t instanceof Error ? t.message : `加载商品失败`);
} finally {
e === Z && (W.value = !1);
}
}
function $(e) {
((Y.value = e.order_id), (X.value = e.amount), (J.value = !0));
}
function se() {
I.push(`/orders`);
}
return (
onMounted(Q),
(t, n) => {
let i = NuxtLink,
o = ConfigLoadingShell,
f = message,
h = button,
g = MfCloudConfig,
v = MfDcimConfig,
b = CommonConfig,
C = FinanceConfig,
T = KkyEcsConfig,
O = PaymentDialog;
return (
openBlock(),
createElementBlock(`div`, j, [
createElementVNode(`header`, M, [
createElementVNode(`div`, null, [
createElementVNode(
`h1`,
null,
toDisplayString(
unref(product_name_n)(unref(H)?.name) || `配置产品`,
),
1,
),
createElementVNode(
`p`,
null,
toDisplayString(
unref(q)
? `编辑购物车商品配置,保存后返回购物车结算。`
: `选择所需资源,确认配置与费用后购买。`,
),
1,
),
]),
createElementVNode(`nav`, N, [
createVNode(
i,
{
to: V(),
class: `back`,
},
{
default: withCtx(() => [
...(n[1] ||= [
createElementVNode(
`i`,
{
class: `pi pi-angle-left`,
"aria-hidden": `true`,
},
null,
-1,
),
createTextVNode(`返回产品列表`, -1),
]),
]),
_: 1,
},
8,
[`to`],
),
createVNode(
i,
{
to: `/cart`,
class: `cart-link`,
},
{
default: withCtx(() => [
...(n[2] ||= [
createElementVNode(
`i`,
{
class: `pi pi-shopping-cart`,
"aria-hidden": `true`,
},
null,
-1,
),
createTextVNode(`购物车`, -1),
]),
]),
_: 1,
},
),
]),
]),
unref(W)
? (openBlock(),
createBlock(o, {
key: 0,
}))
: unref(G) && !unref(H)
? (openBlock(),
createElementBlock(`div`, P, [
createVNode(
f,
{
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(G)), 1),
]),
_: 1,
},
),
createVNode(h, {
label: `重新加载商品`,
icon: `pi pi-refresh`,
severity: `secondary`,
text: ``,
onClick: Q,
}),
]))
: unref(H)
? (openBlock(),
createElementBlock(
Fragment,
{
key: 2,
},
[
unref(H).module === `mf_cloud` ||
unref(H).module === `remf_cloud`
? (openBlock(),
createBlock(
g,
{
key: 0,
"product-id": unref(R),
module: unref(H).module,
"initial-image-id": unref(z),
"continuation-path": unref(B),
"initial-product": unref(H),
edit: unref(q),
onSettled: $,
},
null,
8,
[
`product-id`,
`module`,
`initial-image-id`,
`continuation-path`,
`initial-product`,
`edit`,
],
))
: unref(H).module === `mf_dcim` ||
unref(H).module === `remf_dcim`
? (openBlock(),
createBlock(
v,
{
key: 1,
"product-id": unref(R),
module: unref(H).module,
"initial-product": unref(H),
edit: unref(q),
onSettled: $,
},
null,
8,
[
`product-id`,
`module`,
`initial-product`,
`edit`,
],
))
: unref(H).module === `jy_cloud` ||
unref(H).module === `jy_dcim`
? (openBlock(),
createBlock(
JyCloudConfig,
{
key: 2,
"product-id": unref(R),
module: unref(H).module,
edit: unref(q),
onSettled: $,
},
null,
8,
[`product-id`, `module`, `edit`],
))
: unref(H).module === `idcsmart_common`
? (openBlock(),
createBlock(
b,
{
key: 3,
"product-id": unref(R),
edit: unref(q),
onSettled: $,
},
null,
8,
[`product-id`, `edit`],
))
: [
`remf_finance`,
`remf_finance_dcim`,
`remf_finance_common`,
].includes(unref(H).module || ``)
? (openBlock(),
createBlock(
C,
{
key: 4,
"product-id": unref(R),
module: unref(H).module,
edit: unref(q),
onSettled: $,
},
null,
8,
[`product-id`, `module`, `edit`],
))
: unref(H).module === `kky_ecs`
? (openBlock(),
createBlock(
T,
{
key: 5,
"product-id": unref(R),
edit: unref(q),
onSettled: $,
},
null,
8,
[`product-id`, `edit`],
))
: (openBlock(),
createElementBlock(`div`, F, [
createVNode(
f,
{
severity: `warn`,
closable: !1,
},
{
default: withCtx(() => [
...(n[3] ||= [
createTextVNode(
` 该商品暂不支持在线配置,请联系客服下单。 `,
-1,
),
]),
]),
_: 1,
},
),
])),
],
64,
))
: createCommentVNode(``, !0),
createVNode(
O,
{
modelValue: unref(J),
"onUpdate:modelValue": (n[0] ||= (e) =>
isRef(J) ? (J.value = e) : null),
"order-id": unref(Y),
amount: unref(X),
onSuccess: se,
},
null,
8,
[`modelValue`, `order-id`, `amount`],
),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-49461ec8`]],
);
export {};
export default I;
</script>
+494
View File
@@ -0,0 +1,494 @@
<script>
import "~/assets/styles/MfCloudConfig.DbgUu8qr.css";
import "~/assets/styles/KkyEcsConfig.BFaAwQPZ.css";
import "~/assets/styles/ConfigResourceOption.Dj4VopnA.css";
import "~/assets/styles/ConfigDiskAdd.C92e9ZuF.css";
import "~/assets/styles/ConfigBandwidth.CWSH1WJZ.css";
import "~/assets/styles/JyCloudConfig.kGfttaHi.css";
import "~/assets/styles/FinanceConfig.CLwX1CC8.css";
import "~/assets/styles/ConfigDiskCard.Dx9IqqQ_.css";
import "~/assets/styles/PromoCodeField.qFEkB6Hm.css";
import "~/assets/styles/ProductCustomFields.B2QMnPil.css";
import "~/assets/styles/OsIcon.CAi4Iz2S.css";
import "~/assets/styles/CommonConfig.Cfik-oMe.css";
import "~/assets/styles/CloudProductPicker.CAm9rmUl.css";
import "~/assets/styles/ProductGroupLabel.B1kWpFA-.css";
import "~/assets/styles/PaymentDialog.BNFKQF1U.css";
import "~/assets/styles/cloud-server.CcwlOXJ0.css";
definePageMeta({
key: (e) => e.fullPath,
alias: [`/products/cvm`],
});
import { toDisplayString } from "vue";
import { onMounted } from "vue";
import { resolveDynamicComponent } from "vue";
import { onScopeDispose } from "vue";
import { createBlock } from "vue";
import { ref } from "vue";
import { withCtx } from "vue";
import { createCommentVNode } from "vue";
import { mergeProps } from "vue";
import { computed } from "vue";
import { isRef } from "vue";
import { createElementBlock } from "vue";
import { toHandlers } 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 { useRoute } from "#app";
import { useRouter } from "#app";
import { useHead } from "#app";
import { useApi } from "~/lib/auth.js";
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 PaymentDialog from "~/components/PaymentDialog.vue";
import { t as product_order_module_t } from "~/lib/product-order-module.js";
import { n as ProductGroupLabel_n } from "~/components/ProductGroupLabel.vue";
import CloudProductPicker from "~/components/config/CloudProductPicker.vue";
import ConfigLoadingShell from "~/components/config/ConfigLoadingShell.vue";
import CommonConfig from "~/components/config/CommonConfig.vue";
import FinanceConfig from "~/components/config/FinanceConfig.vue";
import JyCloudConfig from "~/components/config/JyCloudConfig.vue";
import KkyEcsConfig from "~/components/config/KkyEcsConfig.vue";
import MfCloudConfig from "~/components/config/MfCloudConfig.vue";
import { t as composables_t } from "~/lib/composables-2.js";
import { t as product_description_data_t } from "~/lib/product-description-data.js";
var ge = {
class: `cloud-order-page`,
},
_e = {
key: 0,
class: `product-switch-status`,
role: `status`,
},
S = _plugin_vue_export_helper_t(
defineComponent({
__name: `cloud-server`,
setup(m) {
let y = new Set([
`云服务器`,
`轻量云服务器`,
`国内云服务器`,
`香港云服务器`,
`弹性云服务器`,
]);
(composables_t({
alias: `/products/cvm`,
key: (e) => e.fullPath,
}),
useHead({
title: `创建云服务器实例 - 破碎工坊网络`,
}));
let S = useApi(),
C = useRoute(),
ve = useRouter(),
w = ref([]),
T = ref(null),
E = ref([]),
D = ref(null),
O = ref(null),
k = ref(!0),
A = ref(!1),
j = ref(!1),
M = ref(``),
N = ref(`groups`),
P = ref(``),
F = ref(0),
ye = computed(() => j.value || !!P.value || O.value !== D.value?.id),
I = ref(0),
L = ref(1),
R = 0,
z = 0;
onScopeDispose(() => {
(R++, z++);
});
let be = {
mf_cloud: MfCloudConfig,
remf_cloud: MfCloudConfig,
jy_cloud: JyCloudConfig,
jy_dcim: JyCloudConfig,
kky_ecs: KkyEcsConfig,
idcsmart_common: CommonConfig,
remf_finance: FinanceConfig,
},
B = computed(() => be[D.value?.module ?? ``]),
V = computed(() =>
D.value?.module === `mf_cloud` || D.value?.module === `remf_cloud`
? D.value.module
: void 0,
),
H = computed(() =>
D.value?.module === `jy_cloud` || D.value?.module === `jy_dcim`
? D.value.module
: void 0,
),
U = (e) => Number(e.stock_control) === 1 && !(Number(e.qty) > 0),
W = (e) =>
e.qty == null || !Number.isFinite(Number(e.qty))
? `库存待确认`
: `暂时缺货`,
G = (e) =>
Number(e.hidden ?? 0) !== 1 &&
(e.product_group_id_second == null ||
Number(e.product_group_id_second) === T.value);
async function K(e) {
let t = E.value.find((t) => t.id === e);
if (
!t ||
!G(t) ||
U(t) ||
(O.value === e && (j.value || (D.value?.id === e && !P.value)))
)
return;
let n = ++z;
((O.value = e), (P.value = ``), (j.value = !0));
try {
let r = await S.get(`/product/${e}`);
if (n !== z) return;
let i = r?.product ?? r;
if (!i?.id || Number(i.id) !== e) throw Error(`商品不存在或已下架`);
let a = {
...t,
...i,
};
if (
(typeof i.description != `string` &&
typeof t.description == `string` &&
(a.description = t.description),
!G(a))
)
throw Error(`商品不存在或已下架`);
if (U(a)) throw Error(`当前商品${W(a)},请稍后重试`);
let o = await product_order_module_t(S, a);
if (n !== z) return;
(F.value++,
(D.value = {
...a,
module: o,
}),
product_description_data_t(S, a).then((e) => {
n === z && D.value && (D.value.description = e);
}));
} catch (e) {
n === z &&
(P.value = e instanceof Error ? e.message : `商品配置加载失败`);
} finally {
n === z && (j.value = !1);
}
}
async function q() {
let e = ++R;
(z++,
(E.value = []),
(D.value = null),
(O.value = null),
(j.value = !1),
(P.value = ``),
(I.value = 0),
(M.value = ``),
(N.value = `products`),
(A.value = !0));
try {
if (!T.value || !w.value.some((e) => e.id === T.value)) return;
let t = await S.get(`/product`, {
id: T.value,
page: L.value,
limit: 24,
});
if (e !== R) return;
((E.value = (t.list ?? []).filter(G)),
(I.value = Number(t.count) || 0),
(A.value = !1));
let n = Number(C.query.id ?? C.query.product_id),
r =
E.value.find((e) => e.id === n && !U(e)) ??
E.value.find((e) => !U(e));
r && (await K(r.id));
} catch (t) {
e === R &&
(M.value = t instanceof Error ? t.message : `产品加载失败`);
} finally {
e === R && (A.value = !1);
}
}
async function J() {
let e = ++R;
(z++,
(k.value = !0),
(M.value = ``),
(N.value = `groups`),
(P.value = ``),
(w.value = []),
(T.value = null),
(E.value = []),
(D.value = null),
(O.value = null),
(I.value = 0),
(L.value = 1),
(j.value = !1),
(A.value = !1));
try {
let [t, n] = await Promise.all([
S.get(`/product/group/first`),
S.get(`/product/group/second`),
]);
if (e !== R) return;
let r = new Set(
(t.list ?? [])
.filter((e) => y.has(ProductGroupLabel_n(e.name).name))
.map((e) => Number(e.id)),
);
((w.value = (n.list ?? []).filter((e) =>
r.has(Number(e.parent_id)),
)),
(T.value = w.value[0]?.id ?? null),
(k.value = !1),
await q());
} catch (t) {
e === R &&
(M.value = t instanceof Error ? t.message : `线路加载失败`);
} finally {
e === R && (k.value = !1);
}
}
function xe(e) {
T.value !== e &&
w.value.some((t) => t.id === e) &&
((T.value = e), (L.value = 1), q());
}
function Se(e) {
((L.value = e.page + 1), q());
}
let Y = ref(!1),
X = ref(null),
Z = ref(``);
function Ce(e) {
((X.value = e.order_id), (Z.value = e.amount), (Y.value = !0));
}
function we() {
((Y.value = !1),
X.value && ve.push(`/orders/order/paid?id=${X.value}`));
}
let Q = computed(() => ({
groups: w.value,
activeGroup: T.value,
products: E.value,
selectedId: O.value,
loading: k.value,
productLoading: A.value,
error: M.value,
errorSource: N.value,
total: I.value,
page: L.value,
soldOut: U,
stockLabel: W,
})),
$ = {
"choose-group": xe,
"choose-product": K,
"retry-groups": J,
"retry-products": q,
paginate: Se,
};
return (
onMounted(J),
(t, r) => {
let a = CloudProductPicker,
l = button,
m = message,
_ = ConfigLoadingShell,
v = PaymentDialog;
return (
openBlock(),
createElementBlock(`div`, ge, [
(r[4] ||= createElementVNode(
`h2`,
null,
`创建云服务器实例`,
-1,
)),
unref(D) && unref(B)
? (openBlock(),
createBlock(
resolveDynamicComponent(unref(B)),
mergeProps(
{
key: `${unref(D).id}-${unref(F)}`,
"product-id": unref(D).id,
"identity-locked": unref(ye),
},
unref(V)
? {
module: unref(V),
initialProduct: unref(D),
}
: unref(H)
? {
module: unref(H),
}
: {},
{
onSettled: Ce,
},
),
{
catalog: withCtx(({ disabled: t }) => [
createVNode(
a,
mergeProps(
unref(Q),
{
disabled: t,
},
toHandlers($),
),
null,
16,
[`disabled`],
),
unref(j)
? (openBlock(),
createElementBlock(
`p`,
_e,
`正在加载所选商品,原配置暂不可操作。`,
))
: unref(P)
? (openBlock(),
createBlock(
m,
{
key: 1,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(unref(P)),
1,
),
unref(O)
? (openBlock(),
createBlock(l, {
key: 0,
label: `重新加载配置`,
severity: `secondary`,
text: ``,
onClick: (r[0] ||= (e) =>
K(unref(O))),
}))
: createCommentVNode(``, !0),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
]),
_: 1,
},
16,
[`product-id`, `identity-locked`],
))
: (openBlock(),
createBlock(
_,
{
key: 1,
busy: unref(k) || unref(A) || unref(j),
},
{
catalog: withCtx(() => [
createVNode(
a,
mergeProps(unref(Q), toHandlers($)),
null,
16,
),
unref(P)
? (openBlock(),
createBlock(
m,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(unref(P)),
1,
),
unref(O)
? (openBlock(),
createBlock(l, {
key: 0,
label: `重新加载配置`,
severity: `secondary`,
text: ``,
onClick: (r[1] ||= (e) =>
K(unref(O))),
}))
: createCommentVNode(``, !0),
]),
_: 1,
},
))
: unref(D)
? (openBlock(),
createBlock(
m,
{
key: 1,
severity: `warn`,
closable: !1,
},
{
default: withCtx(() => [
...(r[3] ||= [
createTextVNode(
`当前商品暂不支持在线配置,请通过工单咨询。`,
-1,
),
]),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
]),
_: 1,
},
8,
[`busy`],
)),
createVNode(
v,
{
modelValue: unref(Y),
"onUpdate:modelValue": (r[2] ||= (e) =>
isRef(Y) ? (Y.value = e) : null),
"order-id": unref(X),
amount: unref(Z),
onSuccess: we,
},
null,
8,
[`modelValue`, `order-id`, `amount`],
),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-ef3931f0`]],
);
export {};
export default S;
</script>
@@ -0,0 +1,255 @@
<script>
import "~/assets/styles/dedicated-server.CiJfMYtk.css";
import "~/assets/styles/ConfigDiskCard.Dx9IqqQ_.css";
import "~/assets/styles/PromoCodeField.qFEkB6Hm.css";
import "~/assets/styles/ProductCustomFields.B2QMnPil.css";
import "~/assets/styles/OsIcon.CAi4Iz2S.css";
import "~/assets/styles/MfDcimConfig.BhC_eZ0V.css";
import "~/assets/styles/PaymentDialog.BNFKQF1U.css";
import "~/assets/styles/customize.Cc123Awf.css";
definePageMeta({
key: (e) => e.fullPath,
});
import { toDisplayString } from "vue";
import { onMounted } from "vue";
import { onScopeDispose } 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 { createElementVNode } from "vue";
import { createTextVNode } from "vue";
import { unref } from "vue";
import { defineComponent } from "vue";
import { createVNode } from "vue";
import { openBlock } from "vue";
import { useRoute } from "#app";
import { useRouter } from "#app";
import { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
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 PaymentDialog from "~/components/PaymentDialog.vue";
import skeleton from "primevue/skeleton";
import { t as product_order_module_t } from "~/lib/product-order-module.js";
import MfDcimConfig from "~/components/config/MfDcimConfig.vue";
import { t as composables_t } from "~/lib/composables-2.js";
import { t as product_description_data_t } from "~/lib/product-description-data.js";
import index from "~/pages/products/dedicated-server/index.vue";
var A = {
key: 1,
class: `dedicated-customize`,
},
j = {
class: `product-links`,
},
M = {
key: 0,
class: `panel`,
},
N = {
key: 1,
class: `panel config-load-error`,
},
P = _plugin_vue_export_helper_t(
defineComponent({
__name: `customize`,
setup(p) {
let x = useRoute(),
P = useRouter(),
F = useApi(),
I = computed(() => String(x.query.id ?? x.query.product_id ?? ``));
composables_t({
key: (e) => e.fullPath,
});
let L = ref(null),
R = ref(!0),
z = ref(``),
B = ref(!1),
V = ref(null),
H = ref(``),
U = 0;
onScopeDispose(() => {
U++;
});
async function W() {
let e = ++U,
t = I.value;
((R.value = !0), (z.value = ``), (L.value = null));
try {
if (!/^[1-9]\d*$/.test(t)) throw Error(`商品编号无效`);
let n = await F.get(`/product/${t}`);
if (e !== U) return;
let r = n?.product ?? n;
if (!r || Number(r.id) !== Number(t) || r.hidden === 1)
throw Error(`商品不存在或已下架`);
let i = r,
a = await product_order_module_t(F, i);
if (a !== `mf_dcim` && a !== `remf_dcim`)
throw Error(`该商品不支持物理服务器定制,请返回列表重新选择`);
((L.value = {
...i,
module: a,
}),
product_description_data_t(F, L.value).then((t) => {
e === U && L.value && (L.value.description = t);
}));
} catch (t) {
e === U &&
(z.value = t instanceof Error ? t.message : `加载商品失败`);
} finally {
e === U && (R.value = !1);
}
}
function G(e) {
((V.value = e.order_id), (H.value = e.amount), (B.value = !0));
}
return (
onMounted(() => {
I.value ? W() : (R.value = !1);
}),
(t, n) => {
let i = NuxtLink,
s = skeleton,
p = message,
g = button,
_ = MfDcimConfig,
y = PaymentDialog;
return unref(I)
? (openBlock(),
createElementBlock(`div`, A, [
createElementVNode(`header`, null, [
(n[4] ||= createElementVNode(
`h1`,
null,
`定制物理服务器`,
-1,
)),
createElementVNode(`div`, j, [
createVNode(
i,
{
to: `/products/dedicated-server`,
},
{
default: withCtx(() => [
...(n[2] ||= [
createElementVNode(
`i`,
{
class: `pi pi-angle-left`,
"aria-hidden": `true`,
},
null,
-1,
),
createTextVNode(`返回物理服务器列表`, -1),
]),
]),
_: 1,
},
),
createVNode(
i,
{
to: `/cart`,
},
{
default: withCtx(() => [
...(n[3] ||= [
createElementVNode(
`i`,
{
class: `pi pi-shopping-cart`,
"aria-hidden": `true`,
},
null,
-1,
),
createTextVNode(`购物车`, -1),
]),
]),
_: 1,
},
),
]),
]),
unref(R)
? (openBlock(),
createElementBlock(`div`, M, [
createVNode(s, {
height: `360px`,
}),
]))
: unref(z)
? (openBlock(),
createElementBlock(`div`, N, [
createVNode(
p,
{
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(z)), 1),
]),
_: 1,
},
),
createVNode(g, {
label: `重新加载商品`,
icon: `pi pi-refresh`,
severity: `secondary`,
text: ``,
onClick: W,
}),
]))
: unref(L)
? (openBlock(),
createBlock(
_,
{
key: 2,
"product-id": unref(I),
module: unref(L).module,
"initial-product": unref(L),
onSettled: G,
},
null,
8,
[`product-id`, `module`, `initial-product`],
))
: createCommentVNode(``, !0),
createVNode(
y,
{
modelValue: unref(B),
"onUpdate:modelValue": (n[0] ||= (e) =>
isRef(B) ? (B.value = e) : null),
"order-id": unref(V),
amount: unref(H),
onSuccess: (n[1] ||= (e) => unref(P).push(`/orders`)),
},
null,
8,
[`modelValue`, `order-id`, `amount`],
),
]))
: (openBlock(),
createBlock(index, {
key: 0,
}));
}
);
},
}),
[[`__scopeId`, `data-v-5618b27d`]],
);
export {};
export default P;
</script>
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+907
View File
@@ -0,0 +1,907 @@
<script>
import "~/assets/styles/PhoneCodeField.CwyHXfML.css";
import "~/assets/styles/GeetestField.DiieTibK.css";
import "~/assets/styles/CaptchaField.BSN3ZbsD.css";
definePageMeta({
layout: `blank`,
alias: [`/signup`],
});
import { toDisplayString } from "vue";
import { renderList } from "vue";
import { onMounted } from "vue";
import { normalizeClass } 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 { withModifiers } from "vue";
import { useRoute } from "#app";
import { useRouter } from "#app";
import { useHead } from "#app";
import { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
import { t as useSiteConfig_t } from "~/lib/useSiteConfig.js";
import button from "primevue/button";
import message from "primevue/message";
import { n as auth_redirect_n } from "~/lib/auth-redirect.js";
import { t as auth_redirect_t } from "~/lib/auth-redirect.js";
import inputtext from "primevue/inputtext";
import CaptchaField from "~/components/CaptchaField.vue";
import select from "primevue/select";
import checkbox from "primevue/checkbox";
import password from "primevue/password";
import textarea from "primevue/textarea";
import PhoneCodeField from "~/components/PhoneCodeField.vue";
import { i as product_fields_i } from "~/lib/product-fields.js";
import { t as useCaptcha_t } from "~/lib/useCaptcha.js";
import { t as composables_t } from "~/lib/composables-2.js";
import { t as useVerifyCode_t } from "~/lib/useVerifyCode.js";
var w = (e) => e === !0 || e === 1 || e === `1`,
T = (e) => (typeof e == `string` || typeof e == `number` ? String(e) : ``);
function _e(e) {
if (e == null)
return {
fields: [],
error: ``,
};
if (!Array.isArray(e))
return {
fields: [],
error: `注册资料配置暂不可用,请联系客服`,
};
let t = [];
for (let n of e) {
if (!n || typeof n != `object` || Array.isArray(n))
return {
fields: t,
error: `注册资料配置暂不可用,请联系客服`,
};
let e = n;
if (e.show_register !== void 0 && !w(e.show_register)) continue;
let r = T(e.field ?? e.key ?? e.id ?? e.name),
i = T(e.title ?? e.label ?? e.field_name ?? e.name) || `注册资料`,
a = {
string: `text`,
text: `text`,
password: `password`,
textarea: `textarea`,
select: `select`,
dropdown: `select`,
checkbox: `boolean`,
tickbox: `boolean`,
explain: `explain`,
link: `link`,
}[T(e.type ?? e.field_type) || `text`];
if (
!r ||
[`__proto__`, `prototype`, `constructor`].includes(r) ||
!a ||
t.some((e) => e.id === r)
)
return {
fields: t,
error: `${i}的配置暂不受支持,请联系客服`,
};
let o = e.options ?? e.field_option,
s = Array.isArray(o)
? o.map((e) => {
if (e && typeof e == `object`) {
let t = e;
return {
value: T(t.value ?? t.id),
label: T(t.label ?? t.name ?? t.value),
};
}
return {
value: T(e),
label: T(e),
};
})
: o && typeof o == `object`
? Object.entries(o).map(([e, t]) => ({
value: e,
label: T(t),
}))
: T(o)
.split(/[,\n]/)
.map((e) => ({
value: e.trim(),
label: e.trim(),
}))
.filter((e) => e.value);
t.push({
id: r,
label: i,
kind: a,
required: w(e.required ?? e.is_required),
options: s,
pattern: T(e.regexpr ?? e.pattern) || void 0,
description: T(e.description ?? e.tip),
explain: T(e.explain_content),
});
}
return {
fields: t,
error: ``,
};
}
var ve = {
class: `auth-card login-card`,
},
ye = {
class: `login-brand`,
},
be = {
class: `seg`,
role: `group`,
"aria-label": `注册方式`,
},
xe = [`aria-pressed`, `disabled`],
E = [`aria-pressed`, `disabled`],
D = {
class: `field`,
},
O = {
for: `account`,
},
k = [`for`],
A = {
key: 0,
class: `req`,
},
j = {
key: 0,
},
M = {
key: 5,
},
Se = {
key: 4,
class: `field`,
},
Ce = {
class: `code-row`,
},
we = {
class: `field`,
},
Te = {
class: `field`,
},
Ee = {
class: `field`,
},
De = {
class: `login-links center`,
},
N = defineComponent({
__name: `register`,
setup(m) {
(useHead({
title: `注册账号 - 破碎工坊网络`,
}),
composables_t({
layout: `blank`,
alias: `/signup`,
}));
let w = useApi(),
T = useRouter(),
N = useRoute(),
P = ref(`phone`),
F = reactive({
account: ``,
code: ``,
username: ``,
password: ``,
re_password: ``,
phone_code: `86`,
customfield: {},
}),
I = ref(``),
L = ref(!1),
R = useCaptcha_t(`register`),
{
needed: Oe,
provider: ke,
captchaId: Ae,
complete: je,
invalidate: Me,
img: Ne,
value: z,
loading: Pe,
error: B,
load: Fe,
handleError: Ie,
} = R,
{
siteName: Le,
config: V,
error: H,
load: U,
on: W,
} = useSiteConfig_t(),
G = computed(() => ({
phone:
V.value?.register_phone === void 0 || W(V.value?.register_phone),
email:
V.value?.register_email === void 0 || W(V.value?.register_email),
})),
K = computed(() =>
P.value === `phone`
? V.value?.code_client_phone_register === void 0 ||
W(V.value?.code_client_phone_register)
: V.value?.code_client_email_register === void 0 ||
W(V.value?.code_client_email_register),
),
q = computed(() => _e(V.value?.custom_fields)),
J = computed(() => q.value.fields),
Y = computed(() => !!V.value && (G.value.phone || G.value.email));
async function X() {
(await U(), !G.value.phone && G.value.email && (P.value = `email`));
}
onMounted(X);
let {
sending: Z,
countdown: Q,
send: Re,
} = useVerifyCode_t({
action: `register`,
captcha: R,
error: I,
phoneCode: () => F.phone_code,
});
function ze() {
if (!Y.value || !G.value[P.value] || !K.value || L.value) return;
let e = F.account.trim();
return Re(
P.value,
e,
P.value === `email` ? `请输入邮箱` : `请输入手机号`,
);
}
function $(e) {
!G.value[e] || L.value || Z.value || ((P.value = e), (F.code = ``));
}
async function Be() {
if (L.value || Z.value) return;
if (((I.value = ``), !V.value))
return (I.value = `请先重新加载注册设置`);
if (!G.value[P.value]) return (I.value = `当前注册方式未开放`);
if (q.value.error) return (I.value = q.value.error);
let e = F.account.trim();
if (!e)
return (I.value =
P.value === `email` ? `请输入邮箱` : `请输入手机号`);
if (P.value === `phone` && !/^[1-9]\d{0,3}$/.test(F.phone_code))
return (I.value = `请输入有效的国际电话区号`);
if (K.value && !F.code.trim()) return (I.value = `请输入验证码`);
if (!F.password) return (I.value = `请输入密码`);
if (F.password !== F.re_password)
return (I.value = `两次输入的密码不一致`);
try {
product_fields_i(J.value, F.customfield);
} catch (e) {
I.value = e instanceof Error ? e.message : `请检查注册资料`;
return;
}
L.value = !0;
try {
(await w.post(`/register`, {
type: P.value,
account: e,
phone_code: F.phone_code,
code: K.value ? F.code.trim() : ``,
username: F.username.trim(),
password: F.password,
re_password: F.re_password,
customfield: Object.fromEntries(
J.value
.filter((e) => e.kind !== `explain`)
.map((e) => [e.id, F.customfield[e.id] || ``]),
),
}),
await T.push({
path: `/login`,
query: {
registered: `1`,
...auth_redirect_n(N.query.redirect),
},
}));
} catch (e) {
((I.value = e instanceof Error ? e.message : `注册失败`),
await Ie(e));
} finally {
L.value = !1;
}
}
return (n, a) => {
let c = button,
l = message,
m = PhoneCodeField,
_ = inputtext,
v = textarea,
y = select,
b = checkbox,
x = CaptchaField,
S = password,
C = NuxtLink;
return (
openBlock(),
createElementBlock(`div`, ve, [
createElementVNode(`div`, ye, [
(a[9] ||= createElementVNode(`h1`, null, `创建账户`, -1)),
createElementVNode(
`p`,
null,
`只需几分钟,即可开始使用` +
toDisplayString(unref(Le)) +
`服务。`,
1,
),
]),
createElementVNode(
`form`,
{
class: `login-form`,
onSubmit: withModifiers(Be, [`prevent`]),
},
[
unref(H)
? (openBlock(),
createBlock(
l,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(H)) + ` `, 1),
createVNode(c, {
label: `重新加载`,
text: ``,
onClick: X,
}),
]),
_: 1,
},
))
: unref(V) && !unref(Y)
? (openBlock(),
createBlock(
l,
{
key: 1,
severity: `info`,
closable: !1,
},
{
default: withCtx(() => [
...(a[10] ||= [
createTextVNode(`本站暂未开放新用户注册`, -1),
]),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
unref(q).error
? (openBlock(),
createBlock(
l,
{
key: 2,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(q).error), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createElementVNode(`div`, be, [
createElementVNode(
`button`,
{
type: `button`,
class: normalizeClass({
active: unref(P) === `email`,
}),
"aria-pressed": unref(P) === `email`,
onClick: (a[0] ||= (e) => $(`email`)),
disabled: !unref(G).email || unref(L) || unref(Z),
},
` 邮箱 `,
10,
xe,
),
createElementVNode(
`button`,
{
type: `button`,
class: normalizeClass({
active: unref(P) === `phone`,
}),
"aria-pressed": unref(P) === `phone`,
onClick: (a[1] ||= (e) => $(`phone`)),
disabled: !unref(G).phone || unref(L) || unref(Z),
},
` 手机号 `,
10,
E,
),
]),
unref(P) === `phone`
? (openBlock(),
createBlock(
m,
{
key: 3,
modelValue: unref(F).phone_code,
"onUpdate:modelValue": (a[2] ||= (e) =>
(unref(F).phone_code = e)),
disabled: unref(L) || unref(Z),
},
null,
8,
[`modelValue`, `disabled`],
))
: createCommentVNode(``, !0),
createElementVNode(`div`, D, [
createElementVNode(
`label`,
O,
toDisplayString(unref(P) === `email` ? `邮箱` : `手机号`),
1,
),
createVNode(
_,
{
id: `account`,
modelValue: unref(F).account,
"onUpdate:modelValue": (a[3] ||= (e) =>
(unref(F).account = e)),
placeholder:
unref(P) === `email` ? `请输入邮箱` : `请输入手机号`,
autocomplete: `username`,
disabled: unref(L) || unref(Z),
fluid: ``,
},
null,
8,
[`modelValue`, `placeholder`, `disabled`],
),
]),
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(J),
(t) => (
openBlock(),
createElementBlock(
`div`,
{
key: t.id,
class: `field`,
},
[
createElementVNode(
`label`,
{
for: `custom-${t.id}`,
},
[
createTextVNode(
toDisplayString(t.label || t.id) + ` `,
1,
),
t.required
? (openBlock(),
createElementBlock(`span`, A, `*`))
: createCommentVNode(``, !0),
],
8,
k,
),
t.kind === `explain`
? (openBlock(),
createElementBlock(
`p`,
j,
toDisplayString(t.explain || t.description),
1,
))
: t.kind === `textarea`
? (openBlock(),
createBlock(
v,
{
key: 1,
id: `custom-${t.id}`,
modelValue: unref(F).customfield[t.id],
"onUpdate:modelValue": (e) =>
(unref(F).customfield[t.id] = e),
disabled: unref(L),
fluid: ``,
},
null,
8,
[
`id`,
`modelValue`,
`onUpdate:modelValue`,
`disabled`,
],
))
: t.kind === `select`
? (openBlock(),
createBlock(
y,
{
key: 2,
"input-id": `custom-${t.id}`,
modelValue: unref(F).customfield[t.id],
"onUpdate:modelValue": (e) =>
(unref(F).customfield[t.id] = e),
options: t.options,
"option-label": `label`,
"option-value": `value`,
disabled: unref(L),
fluid: ``,
},
null,
8,
[
`input-id`,
`modelValue`,
`onUpdate:modelValue`,
`options`,
`disabled`,
],
))
: t.kind === `boolean`
? (openBlock(),
createBlock(
b,
{
key: 3,
"input-id": `custom-${t.id}`,
modelValue: unref(F).customfield[t.id],
"onUpdate:modelValue": (e) =>
(unref(F).customfield[t.id] = e),
binary: ``,
"true-value": `1`,
"false-value": ``,
disabled: unref(L),
},
null,
8,
[
`input-id`,
`modelValue`,
`onUpdate:modelValue`,
`disabled`,
],
))
: (openBlock(),
createBlock(
_,
{
key: 4,
id: `custom-${t.id}`,
modelValue: unref(F).customfield[t.id],
"onUpdate:modelValue": (e) =>
(unref(F).customfield[t.id] = e),
type:
t.kind === `password`
? `password`
: `text`,
disabled: unref(L),
fluid: ``,
},
null,
8,
[
`id`,
`modelValue`,
`onUpdate:modelValue`,
`type`,
`disabled`,
],
)),
t.description && t.kind !== `explain`
? (openBlock(),
createElementBlock(
`small`,
M,
toDisplayString(t.description),
1,
))
: createCommentVNode(``, !0),
],
)
),
),
128,
)),
unref(K)
? (openBlock(),
createElementBlock(`div`, Se, [
(a[11] ||= createElementVNode(
`label`,
{
for: `code`,
},
[
createTextVNode(`验证码 `),
createElementVNode(
`span`,
{
class: `req`,
},
`*`,
),
],
-1,
)),
createElementVNode(`div`, Ce, [
createVNode(
_,
{
id: `code`,
modelValue: unref(F).code,
"onUpdate:modelValue": (a[4] ||= (e) =>
(unref(F).code = e)),
placeholder: `请输入验证码`,
autocomplete: `one-time-code`,
},
null,
8,
[`modelValue`],
),
createVNode(
c,
{
type: `button`,
label: unref(Q) > 0 ? `${unref(Q)}s` : `获取验证码`,
severity: `secondary`,
outlined: ``,
loading: unref(Z),
disabled:
unref(Q) > 0 ||
unref(L) ||
!unref(Y) ||
!unref(G)[unref(P)],
onClick: ze,
},
null,
8,
[`label`, `loading`, `disabled`],
),
]),
]))
: createCommentVNode(``, !0),
unref(Oe) && unref(K)
? (openBlock(),
createBlock(
x,
{
key: 5,
modelValue: unref(z),
"onUpdate:modelValue": (a[5] ||= (e) =>
isRef(z) ? (z.value = e) : null),
img: unref(Ne),
provider: unref(ke),
"captcha-id": unref(Ae),
disabled: unref(L) || unref(Z),
onVerified: unref(je),
onInvalid: unref(Me),
loading: unref(Pe),
onRefresh: unref(Fe),
},
null,
8,
[
`modelValue`,
`img`,
`provider`,
`captcha-id`,
`disabled`,
`onVerified`,
`onInvalid`,
`loading`,
`onRefresh`,
],
))
: createCommentVNode(``, !0),
createElementVNode(`div`, we, [
(a[12] ||= createElementVNode(
`label`,
{
for: `username`,
},
`姓名`,
-1,
)),
createVNode(
_,
{
id: `username`,
modelValue: unref(F).username,
"onUpdate:modelValue": (a[6] ||= (e) =>
(unref(F).username = e)),
placeholder: `选填`,
fluid: ``,
},
null,
8,
[`modelValue`],
),
]),
createElementVNode(`div`, Te, [
(a[13] ||= createElementVNode(
`label`,
{
for: `password`,
},
[
createTextVNode(`密码 `),
createElementVNode(
`span`,
{
class: `req`,
},
`*`,
),
],
-1,
)),
createVNode(
S,
{
"input-id": `password`,
modelValue: unref(F).password,
"onUpdate:modelValue": (a[7] ||= (e) =>
(unref(F).password = e)),
feedback: !1,
toggleMask: ``,
autocomplete: `new-password`,
fluid: ``,
},
null,
8,
[`modelValue`],
),
]),
createElementVNode(`div`, Ee, [
(a[14] ||= createElementVNode(
`label`,
{
for: `re-password`,
},
[
createTextVNode(`确认密码 `),
createElementVNode(
`span`,
{
class: `req`,
},
`*`,
),
],
-1,
)),
createVNode(
S,
{
"input-id": `re-password`,
modelValue: unref(F).re_password,
"onUpdate:modelValue": (a[8] ||= (e) =>
(unref(F).re_password = e)),
feedback: !1,
toggleMask: ``,
autocomplete: `new-password`,
fluid: ``,
},
null,
8,
[`modelValue`],
),
]),
unref(I)
? (openBlock(),
createBlock(
l,
{
key: 6,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(I)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
unref(B)
? (openBlock(),
createBlock(
l,
{
key: 7,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(B)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createVNode(
c,
{
type: `submit`,
label: `创建账户`,
loading: unref(L),
disabled: !unref(Y) || !!unref(q).error || unref(Z),
class: `login-btn`,
fluid: ``,
},
null,
8,
[`loading`, `disabled`],
),
createElementVNode(`div`, De, [
createVNode(
C,
{
to: unref(auth_redirect_t)(
`/login`,
unref(N).query.redirect,
),
},
{
default: withCtx(() => [
...(a[15] ||= [
createTextVNode(`已有账号?返回登录`, -1),
]),
]),
_: 1,
},
8,
[`to`],
),
]),
],
32,
),
])
);
};
},
});
export {};
export default N;
</script>
+427
View File
@@ -0,0 +1,427 @@
<script>
import "~/assets/styles/RemoteConsole.DLmX5UZu.css";
import "~/assets/styles/remote-console.zcxaOxxS.css";
definePageMeta({
layout: !1,
});
import { toDisplayString } 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 { withModifiers } from "vue";
import { useRoute } from "#app";
import { useHead } from "#app";
import { NuxtLink } from "#components";
import button from "primevue/button";
import { t as _plugin_vue_export_helper_t } from "~/lib/_plugin-vue_export-helper.js";
import inputtext from "primevue/inputtext";
import OperatePasswordGate from "~/components/OperatePasswordGate.vue";
import RemoteConsole from "~/components/instance/RemoteConsole.vue";
import { logoUrl } from "~/utils/brand.js";
import { t as composables_t } from "~/lib/composables-2.js";
var O = {
class: `standalone-console`,
},
k = {
class: `console-page-header`,
},
A = {
key: 0,
class: `console-page-host`,
},
j = {
class: `console-page-content`,
},
M = {
key: 0,
class: `console-intro`,
"aria-labelledby": `console-invalid-title`,
},
N = {
key: 1,
class: `console-intro`,
"aria-labelledby": `console-closed-title`,
},
P = _plugin_vue_export_helper_t(
defineComponent({
__name: `remote-console`,
setup(m) {
(composables_t({
layout: !1,
}),
useHead({
title: `远程控制台 - 破碎工坊云`,
}));
let S = useRoute(),
P = ref(!0),
F = ref(``),
I = ref(!1),
L = computed(() =>
/^\d+$/.test(String(S.query.hostId || ``))
? String(S.query.hostId)
: ``,
),
R = new Set([
`kky_ecs`,
`mf_cloud`,
`remf_cloud`,
`mf_dcim`,
`remf_dcim`,
`remf_finance`,
`remf_finance_dcim`,
`remf_finance_common`,
`idcsmart_common`,
]),
z = computed(() =>
R.has(String(S.query.module)) ? String(S.query.module) : ``,
),
B = computed(() =>
L.value
? {
path: `/servers/server`,
query: {
id: L.value,
},
}
: {
path: `/cloud-servers`,
},
),
V = computed(() => z.value === `mf_dcim` && !I.value);
function H() {
((I.value = !0), (P.value = !0));
}
function U() {
((F.value = ``), (I.value = !1), (P.value = !0));
}
return (
watch(P, (e) => {
e || ((F.value = ``), (I.value = !1));
}),
watch([L, z], () => {
((F.value = ``), (I.value = !1), (P.value = !0));
}),
onBeforeUnmount(() => {
F.value = ``;
}),
(t, n) => {
let i = RemoteConsole,
s = NuxtLink,
m = button,
v = inputtext,
y = OperatePasswordGate;
return (
openBlock(),
createElementBlock(`main`, O, [
unref(L) && unref(z) && unref(P) && !unref(V)
? (openBlock(),
createBlock(
i,
{
key: 0,
visible: unref(P),
"onUpdate:visible": (n[0] ||= (e) =>
isRef(P) ? (P.value = e) : null),
"host-id": unref(L),
module: unref(z),
"operate-password": unref(F),
},
null,
8,
[`visible`, `host-id`, `module`, `operate-password`],
))
: (openBlock(),
createElementBlock(
Fragment,
{
key: 1,
},
[
createElementVNode(`header`, k, [
createVNode(
s,
{
to: unref(B),
class: `console-page-brand`,
},
{
default: withCtx(() => [
...(n[2] ||= [
createElementVNode(
`img`,
{
src: logoUrl,
alt: `破碎工坊云`,
},
null,
-1,
),
]),
]),
_: 1,
},
8,
[`to`],
),
unref(L)
? (openBlock(),
createElementBlock(
`span`,
A,
`实例 #` + toDisplayString(unref(L)),
1,
))
: createCommentVNode(``, !0),
]),
createElementVNode(`div`, j, [
!unref(L) || !unref(z)
? (openBlock(),
createElementBlock(`section`, M, [
(n[4] ||= createElementVNode(
`i`,
{
class: `pi pi-link intro-icon`,
"aria-hidden": `true`,
},
null,
-1,
)),
(n[5] ||= createElementVNode(
`h1`,
{
id: `console-invalid-title`,
},
`控制台链接已失效`,
-1,
)),
(n[6] ||= createElementVNode(
`p`,
null,
`请从实例详情页重新打开远程控制台。`,
-1,
)),
createVNode(
s,
{
to: `/cloud-servers`,
class: `console-return`,
},
{
default: withCtx(() => [
...(n[3] ||= [
createTextVNode(`返回云服务器`, -1),
createElementVNode(
`i`,
{
class: `pi pi-arrow-right`,
"aria-hidden": `true`,
},
null,
-1,
),
]),
]),
_: 1,
},
),
]))
: unref(P)
? (openBlock(),
createElementBlock(
`form`,
{
key: 2,
class: `console-intro console-operation`,
"aria-labelledby": `console-operation-title`,
onSubmit: withModifiers(H, [`prevent`]),
},
[
(n[14] ||= createElementVNode(
`i`,
{
class: `pi pi-desktop intro-icon`,
"aria-hidden": `true`,
},
null,
-1,
)),
(n[15] ||= createElementVNode(
`h1`,
{
id: `console-operation-title`,
},
`连接远程控制台`,
-1,
)),
(n[16] ||= createElementVNode(
`p`,
null,
`在此标签页中查看和操作实例屏幕。`,
-1,
)),
createVNode(y, null, {
default: withCtx(() => [
(n[11] ||= createElementVNode(
`label`,
{
for: `console-operation-password`,
},
[
createTextVNode(`操作密码`),
createElementVNode(
`span`,
null,
`选填`,
),
],
-1,
)),
createVNode(
v,
{
id: `console-operation-password`,
modelValue: unref(F),
"onUpdate:modelValue": (n[1] ||= (
e,
) =>
isRef(F) ? (F.value = e) : null),
type: `password`,
autocomplete: `off`,
fluid: ``,
"aria-describedby": `console-operation-help`,
},
null,
8,
[`modelValue`],
),
(n[12] ||= createElementVNode(
`p`,
{
id: `console-operation-help`,
class: `operation-help`,
},
`若此实例开启了操作验证,请填写操作密码。`,
-1,
)),
]),
_: 1,
}),
createVNode(m, {
type: `submit`,
label: `连接控制台`,
icon: `pi pi-arrow-right`,
fluid: ``,
}),
createVNode(
s,
{
to: unref(B),
class: `console-return`,
},
{
default: withCtx(() => [
...(n[13] ||= [
createTextVNode(`返回实例详情`, -1),
]),
]),
_: 1,
},
8,
[`to`],
),
],
32,
))
: (openBlock(),
createElementBlock(`section`, N, [
(n[8] ||= createElementVNode(
`i`,
{
class: `pi pi-desktop intro-icon`,
"aria-hidden": `true`,
},
null,
-1,
)),
(n[9] ||= createElementVNode(
`h1`,
{
id: `console-closed-title`,
},
`远程控制台已关闭`,
-1,
)),
(n[10] ||= createElementVNode(
`p`,
null,
`远程会话已结束,实例继续运行。您可以关闭此标签页,或重新打开控制台。`,
-1,
)),
createVNode(m, {
label: `重新打开控制台`,
icon: `pi pi-refresh`,
onClick: U,
}),
createVNode(
s,
{
to: unref(B),
class: `console-return`,
},
{
default: withCtx(() => [
...(n[7] ||= [
createTextVNode(`返回实例详情`, -1),
createElementVNode(
`i`,
{
class: `pi pi-arrow-right`,
"aria-hidden": `true`,
},
null,
-1,
),
]),
]),
_: 1,
},
8,
[`to`],
),
])),
]),
(n[17] ||= createElementVNode(
`footer`,
{
class: `console-page-footer`,
},
`关闭控制台不会关闭实例`,
-1,
)),
],
64,
)),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-5f7e2625`]],
);
export {};
export default P;
</script>
+263
View File
@@ -0,0 +1,263 @@
<script>
import "~/assets/styles/notices.BXNGOj-Q.css";
import "~/assets/styles/news.CbIzdrjt.css";
import "~/assets/styles/help.BSOB30LE.css";
import "~/assets/styles/resources._aUrMsvM.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
import { onMounted } 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 { 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 { useRoute } from "#app";
import { useRouter } from "#app";
import { navigateTo } from "#app";
import { useApi } from "~/lib/auth.js";
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 skeleton from "primevue/skeleton";
import { n as content_route_n } from "~/lib/content-route.js";
import { r as content_route_r } from "~/lib/content-route.js";
import help from "~/pages/help.vue";
import news from "~/pages/news.vue";
import index from "~/pages/notices/index.vue";
var j = {
class: `resources-page`,
},
M = {
class: `page-head`,
},
N = {
class: `resource-tabs`,
role: `tablist`,
"aria-label": `资源分类`,
},
P = [
`id`,
`aria-selected`,
`aria-controls`,
`tabindex`,
`onClick`,
`onKeydown`,
],
F = [`id`, `aria-labelledby`],
I = _plugin_vue_export_helper_t(
defineComponent({
__name: `resources`,
setup(h) {
let C = useRoute(),
I = useRouter(),
L = useApi(),
R = ref(null),
z = ref(``),
B = ref(!1),
V = [
{
value: `help`,
label: `帮助文档`,
},
{
value: `news`,
label: `新闻资讯`,
},
{
value: `notices`,
label: `平台公告`,
},
],
H = 0;
async function U() {
let e = ++H;
((z.value = ``), (R.value = null), (B.value = !0));
try {
if (C.query.section !== void 0)
R.value = content_route_r(C.query.section);
else if (C.query.plugin !== void 0) {
let t = await L.get(`/plugin`);
if (e !== H) return;
R.value = content_route_n(C.query.plugin, t.list ?? []);
} else R.value = `help`;
R.value || (z.value = `资源入口无效或插件未启用,请重新选择分类`);
} catch (t) {
e === H &&
(z.value = t instanceof Error ? t.message : `资源分类加载失败`);
} finally {
e === H && (B.value = !1);
}
}
function W(e) {
((R.value = e),
I.push({
path: `/resources`,
query: {
section: e,
},
}));
}
(onMounted(U),
watch(() => [C.query.section, C.query.plugin], U),
onBeforeUnmount(() => {
H++;
}));
function G(e, t) {
let n = t;
if (e.key === `ArrowRight`) n = (t + 1) % V.length;
else if (e.key === `ArrowLeft`) n = (t + V.length - 1) % V.length;
else if (e.key === `Home`) n = 0;
else if (e.key === `End`) n = V.length - 1;
else return;
(e.preventDefault(),
W(V[n].value),
document.getElementById(`resource-tab-${V[n].value}`)?.focus());
}
return (n, i) => {
let a = button,
s = skeleton,
h = message;
return (
openBlock(),
createElementBlock(`div`, j, [
createElementVNode(`div`, M, [
(i[1] ||= createElementVNode(
`div`,
{
class: `page-heading`,
},
[
createElementVNode(`h2`, null, `资源中心`),
createElementVNode(`p`, null, `查找帮助文档与站点资讯`),
],
-1,
)),
unref(R) === `help`
? (openBlock(),
createBlock(a, {
key: 0,
label: `创建工单`,
icon: `pi pi-plus`,
severity: `secondary`,
outlined: ``,
onClick: (i[0] ||= (e) =>
(`navigateTo` in n ? n.navigateTo : unref(navigateTo))(
`/tickets/create`,
)),
}))
: createCommentVNode(``, !0),
]),
createElementVNode(`div`, N, [
(openBlock(),
createElementBlock(
Fragment,
null,
renderList(V, (t, n) =>
createElementVNode(
`button`,
{
id: `resource-tab-${t.value}`,
key: t.value,
type: `button`,
role: `tab`,
class: normalizeClass([
`resource-tab`,
{
active: unref(R) === t.value,
},
]),
"aria-selected": unref(R) === t.value,
"aria-controls": `resource-panel-${t.value}`,
tabindex: unref(R) === t.value ? 0 : -1,
onClick: (e) => W(t.value),
onKeydown: (e) => G(e, n),
},
toDisplayString(t.label),
43,
P,
),
),
64,
)),
]),
unref(B)
? (openBlock(),
createBlock(s, {
key: 0,
height: `120px`,
}))
: unref(z)
? (openBlock(),
createBlock(
h,
{
key: 1,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(z)), 1),
createVNode(a, {
label: `重试`,
text: ``,
onClick: U,
}),
]),
_: 1,
},
))
: unref(R)
? (openBlock(),
createElementBlock(
`div`,
{
key: 2,
id: `resource-panel-${unref(R)}`,
role: `tabpanel`,
"aria-labelledby": `resource-tab-${unref(R)}`,
class: `resource-content`,
},
[
unref(R) === `help`
? (openBlock(),
createBlock(help, {
key: 0,
embedded: ``,
}))
: unref(R) === `news`
? (openBlock(),
createBlock(news, {
key: 1,
embedded: ``,
}))
: (openBlock(),
createBlock(index, {
key: 2,
embedded: ``,
})),
],
8,
F,
))
: createCommentVNode(``, !0),
])
);
};
},
}),
[[`__scopeId`, `data-v-7390ef3e`]],
);
export {};
export default I;
</script>
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+413
View File
@@ -0,0 +1,413 @@
<script setup>
import {
computed,
onBeforeUnmount,
onMounted,
reactive,
ref,
useId,
} from "vue";
import Button from "primevue/button";
import Column from "primevue/column";
import DataTable from "primevue/datatable";
import Dialog from "primevue/dialog";
import InputText from "primevue/inputtext";
import Message from "primevue/message";
import Paginator from "primevue/paginator";
import Textarea from "primevue/textarea";
import ListEmpty from "~/components/ListEmpty.vue";
import { useApi } from "~/lib/auth.js";
import { n as loadCompleteList } from "~/lib/complete-list.js";
import "~/assets/styles/ssh-keys.WWTOf2zG.css";
const api = useApi();
const fieldId = useId();
const draftSearch = ref("");
const appliedSearch = ref("");
const rows = ref([]);
const totalRows = ref(0);
const currentPage = ref(1);
const pageSize = ref(10);
const listLoading = ref(true);
const searchLoading = ref(false);
const listError = ref("");
const actionError = ref("");
const editorVisible = ref(false);
const saving = ref(false);
const editingKey = ref(null);
const keyForm = reactive({ name: "", public_key: "" });
const copiedKeyId = ref(null);
let copyResetTimer;
let requestGeneration = 0;
let disposed = false;
const firstRecord = computed(() => (currentPage.value - 1) * pageSize.value);
const visibleRows = computed(() =>
appliedSearch.value
? rows.value.slice(firstRecord.value, firstRecord.value + pageSize.value)
: rows.value,
);
function isCurrentLoad(generation) {
return !disposed && generation === requestGeneration;
}
function clampPage() {
const lastPage = Math.max(1, Math.ceil(totalRows.value / pageSize.value));
const nextPage = Math.max(1, Math.min(currentPage.value, lastPage));
if (nextPage === currentPage.value) return false;
currentPage.value = nextPage;
return true;
}
async function loadKeys() {
if (disposed) return;
if (appliedSearch.value) return loadSearchResults();
const generation = ++requestGeneration;
searchLoading.value = false;
listLoading.value = true;
listError.value = "";
try {
const result = await api.get("/ssh_key", {
page: currentPage.value,
limit: pageSize.value,
});
if (!isCurrentLoad(generation)) return;
totalRows.value = result.count;
if (clampPage()) {
await loadKeys();
return;
}
rows.value = result.list;
} catch (error) {
if (!isCurrentLoad(generation)) return;
listError.value =
error instanceof Error ? error.message : "加载密钥列表失败";
rows.value = [];
totalRows.value = 0;
} finally {
if (isCurrentLoad(generation)) listLoading.value = false;
}
}
async function loadSearchResults() {
const query = appliedSearch.value;
const generation = ++requestGeneration;
const isCurrent = () => isCurrentLoad(generation);
listLoading.value = true;
searchLoading.value = true;
listError.value = "";
try {
// Search owns the complete result set; only visibleRows applies local pagination.
const completeRows = await loadCompleteList(
(page, limit) => api.get("/ssh_key", { page, limit }),
isCurrent,
);
if (!isCurrent()) return;
rows.value = completeRows.filter((row) =>
(row.id + " " + row.name + " " + row.public_key)
.toLowerCase()
.includes(query),
);
totalRows.value = rows.value.length;
clampPage();
} catch (error) {
if (!isCurrent()) return;
listError.value = error instanceof Error ? error.message : "搜索密钥失败";
rows.value = [];
totalRows.value = 0;
} finally {
if (isCurrent()) {
searchLoading.value = false;
listLoading.value = false;
}
}
}
async function submitSearch() {
appliedSearch.value = draftSearch.value.trim().toLowerCase();
currentPage.value = 1;
await loadKeys();
}
async function clearSearch() {
draftSearch.value = "";
appliedSearch.value = "";
currentPage.value = 1;
await loadKeys();
}
function changePage(event) {
currentPage.value = event.rows === pageSize.value ? event.page + 1 : 1;
pageSize.value = event.rows;
if (appliedSearch.value) clampPage();
else void loadKeys();
}
function openCreateDialog() {
editingKey.value = null;
keyForm.name = "";
keyForm.public_key = "";
actionError.value = "";
editorVisible.value = true;
}
function openEditDialog(key) {
editingKey.value = key;
keyForm.name = key.name;
keyForm.public_key = key.public_key;
actionError.value = "";
editorVisible.value = true;
}
async function saveKey() {
if (saving.value || disposed) return;
actionError.value = "";
if (!keyForm.name.trim()) {
actionError.value = "请输入密钥名称";
return;
}
if ([...keyForm.name.trim()].length > 10) {
actionError.value = "密钥名称最多 10 个字符";
return;
}
if (!keyForm.public_key.trim()) {
actionError.value = "请输入公钥内容";
return;
}
saving.value = true;
try {
if (editingKey.value) {
await api.put("/ssh_key/" + editingKey.value.id, {
id: editingKey.value.id,
...keyForm,
});
} else {
await api.post("/ssh_key", { ...keyForm });
}
if (disposed) return;
editorVisible.value = false;
await loadKeys();
} catch (error) {
if (!disposed)
actionError.value = error instanceof Error ? error.message : "保存失败";
} finally {
if (!disposed) saving.value = false;
}
}
async function deleteKey(key) {
if (disposed) return;
try {
await api.del("/ssh_key/" + key.id);
await loadKeys();
} catch (error) {
if (!disposed)
actionError.value = error instanceof Error ? error.message : "删除失败";
}
}
async function copyPublicKey(key) {
try {
await navigator.clipboard.writeText(key.public_key);
if (disposed) return;
clearTimeout(copyResetTimer);
copiedKeyId.value = key.id;
copyResetTimer = setTimeout(() => (copiedKeyId.value = null), 2000);
} catch {
// Clipboard access can be unavailable; the public key remains selectable.
}
}
onMounted(loadKeys);
onBeforeUnmount(() => {
disposed = true;
requestGeneration++;
clearTimeout(copyResetTimer);
});
</script>
<template>
<!-- Keep the imported page styles scoped, including the teleported editor. -->
<div class="list-page" data-v-31489433>
<div class="list-head" data-v-31489433>
<h2 data-v-31489433>SSH 密钥</h2>
<div class="list-head-actions" data-v-31489433>
<Button label="添加密钥" icon="pi pi-plus" @click="openCreateDialog" />
</div>
</div>
<Message v-if="listError" severity="error" :closable="false">
{{ listError }}
<Button label="重试" text @click="loadKeys" />
</Message>
<Message
v-if="actionError && !editorVisible"
severity="error"
:closable="false"
>
{{ actionError }}
</Message>
<div class="list-toolbar" data-v-31489433>
<InputText
v-model="draftSearch"
class="list-search"
placeholder="输入关键字"
aria-label="搜索 SSH 密钥"
data-v-31489433
@keyup.enter="submitSearch"
/>
<Button
label="搜索"
icon="pi pi-search"
severity="secondary"
outlined
:loading="searchLoading"
@click="submitSearch"
/>
<Button
label="清除筛选"
icon="pi pi-filter-slash"
severity="secondary"
outlined
@click="clearSearch"
/>
</div>
<div class="list-table" data-v-31489433>
<DataTable
:value="visibleRows"
:loading="listLoading"
lazy
:rows="pageSize"
:total-records="totalRows"
:first="firstRecord"
data-key="id"
>
<template #empty>
<ListEmpty
icon="pi pi-key"
title="还没有 SSH 密钥"
description="添加公钥后可在开通云服务器时选用"
/>
</template>
<Column field="id" header="ID" style="width: 120px">
<template #body="{ data }">#{{ data.id }}</template>
</Column>
<Column header="名称" style="width: 180px; min-width: 140px">
<template #body="{ data }">{{ data.name }}</template>
</Column>
<Column header="公钥">
<template #body="{ data }">
<span class="key-text" data-v-31489433>{{ data.public_key }}</span>
</template>
</Column>
<Column header="指纹" style="width: 220px">
<template #body="{ data }">
<span class="fingerprint" data-v-31489433>{{
data.finger_print || "--"
}}</span>
</template>
</Column>
<Column header="操作" style="width: 200px">
<template #body="{ data }">
<div class="row-actions" data-v-31489433>
<Button
:label="copiedKeyId === data.id ? '已复制' : '复制'"
size="small"
severity="secondary"
outlined
@click="copyPublicKey(data)"
/>
<Button
label="编辑"
size="small"
severity="secondary"
text
@click="openEditDialog(data)"
/>
<Button
icon="pi pi-trash"
aria-label="删除密钥"
size="small"
severity="danger"
text
@click="deleteKey(data)"
/>
</div>
</template>
</Column>
</DataTable>
</div>
<div v-if="totalRows > 0" class="list-foot" data-v-31489433>
<div class="list-bulk" data-v-31489433 />
<Paginator
:rows="pageSize"
:total-records="totalRows"
:first="firstRecord"
:rows-per-page-options="[10, 20, 50]"
@page="changePage"
/>
</div>
<Dialog
v-model:visible="editorVisible"
:header="editingKey ? '编辑密钥' : '添加密钥'"
modal
:draggable="false"
style="width: 448px"
>
<div class="form" data-v-31489433>
<div class="field" data-v-31489433>
<label :for="fieldId + '-name'" data-v-31489433>
名称 <span class="req" data-v-31489433>*</span>
</label>
<InputText
:id="fieldId + '-name'"
v-model="keyForm.name"
:aria-describedby="fieldId + '-name-hint'"
aria-required="true"
placeholder="便于识别的名称"
maxlength="10"
fluid
/>
<span :id="fieldId + '-name-hint'" class="hint" data-v-31489433>
最多 10 个字符
</span>
</div>
<div class="field" data-v-31489433>
<label :for="fieldId + '-public-key'" data-v-31489433>
公钥内容 <span class="req" data-v-31489433>*</span>
</label>
<Textarea
:id="fieldId + '-public-key'"
v-model="keyForm.public_key"
:aria-describedby="fieldId + '-key-hint'"
aria-required="true"
rows="8"
placeholder="输入公钥"
auto-resize
fluid
/>
<span :id="fieldId + '-key-hint'" class="hint" data-v-31489433>
粘贴 ~/.ssh/id_rsa.pub 等公钥文件内容
</span>
</div>
<Message v-if="actionError" severity="error" :closable="false">
{{ actionError }}
</Message>
</div>
<template #footer>
<Button
label="取消"
severity="secondary"
text
@click="editorVisible = false"
/>
<Button label="保存" :loading="saving" @click="saveKey" />
</template>
</Dialog>
</div>
</template>
File diff suppressed because it is too large. Load diff
+778
View File
@@ -0,0 +1,778 @@
<script>
import "~/assets/styles/TicketAttachments.DtbJGLkE.css";
import "~/assets/styles/RichTextEditor.NkmgPT5m.css";
import "~/assets/styles/_id_.C-0--Lx-.css";
definePageMeta({
key: (e) => e.fullPath,
});
import { toDisplayString } from "vue";
import { renderList } from "vue";
import { onMounted } from "vue";
import { normalizeClass } 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 { normalizeStyle } from "vue";
import { useRoute } from "#app";
import { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
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 { r as dict_r } from "~/lib/dict.js";
import skeleton from "primevue/skeleton";
import { n as sanitize_n } from "~/lib/sanitize.js";
import RichTextEditor from "~/components/RichTextEditor.vue";
import { n as ticketAttachments_n } from "~/lib/ticketAttachments.js";
import TicketAttachments from "~/components/TicketAttachments.vue";
import TicketNotice from "~/components/TicketNotice.vue";
import { t as composables_t } from "~/lib/composables-2.js";
var le = {
class: `page`,
},
ue = {
class: `page-head`,
},
de = {
key: 0,
class: `panel`,
},
fe = {
class: `panel head-panel`,
},
pe = {
class: `ticket-head`,
},
me = {
class: `ticket-title`,
},
he = {
class: `ticket-num`,
},
ge = {
class: `ticket-meta`,
},
w = {
class: `panel thread-panel`,
},
T = {
class: `thread`,
},
E = {
class: `message-head`,
},
D = {
class: `sender`,
},
O = {
key: 0,
class: `badge`,
},
k = {
class: `time`,
},
A = [`innerHTML`],
j = {
key: 0,
class: `message-quote`,
},
M = [`innerHTML`],
N = {
class: `reply-box`,
},
P = {
key: 0,
class: `reply-quote`,
role: `status`,
},
F = [`innerHTML`],
I = {
class: `reply-attachments`,
},
L = {
class: `reply-upload`,
for: `reply-files`,
},
_e = [`disabled`],
R = {
key: 0,
class: `muted`,
},
ve = {
key: 1,
class: `reply-file-list`,
},
ye = {
class: `reply-actions`,
},
be = {
class: `buttons`,
},
z = _plugin_vue_export_helper_t(
defineComponent({
__name: `[id]`,
setup(m) {
let y = useRoute(),
z = useApi(),
B = String(y.params.id ?? y.query.id ?? ``);
composables_t({
key: (e) => e.fullPath,
});
let V = ref(null),
H = ref(!0),
U = ref(``),
W = ref(``),
G = ref(!1),
K = ref(!1),
q = ref([]),
J = ref(!1),
Y = ref(0);
async function X() {
try {
let e = await z.get(`/ticket/${B}`);
if (!e.ticket || !e.ticket.id) {
U.value = `工单不存在或已删除`;
return;
}
V.value = e.ticket;
} catch (e) {
U.value = e instanceof Error ? e.message : `加载失败`;
} finally {
H.value = !1;
}
}
let Z = computed(() => {
let e = V.value;
return e
? [
{
id: 0,
content: e.content,
attachment: e.attachment || [],
create_time: e.create_time,
type: `Client`,
client_name: e.username,
admin_name: ``,
client_id: e.client_id,
quote_reply_id: 0,
quote_info: null,
},
...(e.replies || []).filter((e) => e.id !== 0),
]
.sort((e, t) => e.create_time - t.create_time)
.map((e) => ({
...e,
files: ticketAttachments_n(e.attachment),
}))
: [];
});
function Q(e) {
return e.type === `Admin`
? e.admin_name || `客服`
: e.client_name || `我`;
}
let $ = computed(() => Z.value.find((e) => e.id === Y.value));
async function xe(e) {
if (J.value || G.value || V.value?.status === `已关闭`) return;
let t = e.target,
n = Array.from(t.files ?? []);
if (n.length) {
((J.value = !0), (U.value = ``));
try {
for (let e of n) {
let t = await z.upload(e);
if (!t?.save_name)
throw Error(`附件「${e.name}」上传失败,请重新选择文件`);
q.value.push({
save_name: t.save_name,
name: e.name,
});
}
} catch (e) {
U.value = e instanceof Error ? e.message : `附件上传失败`;
} finally {
((J.value = !1), (t.value = ``));
}
}
}
function Se(e) {
G.value || q.value.splice(e, 1);
}
function Ce(e) {
e.id > 0 && V.value?.status !== `已关闭` && (Y.value = e.id);
}
function we(e) {
return Number(e.quote_info?.is_deleted) === 1
? `该回复已删除`
: e.quote_info?.content ||
Z.value.find((t) => t.id === e.quote_reply_id)?.content ||
`原回复 #${e.quote_reply_id}(内容暂不可用)`;
}
function Te(e) {
let t = e.quote_info,
n = Z.value.find((t) => t.id === e.quote_reply_id);
return (
t?.sender_name ||
t?.admin_name ||
t?.client_name ||
(n ? Q(n) : `回复 #${e.quote_reply_id}`)
);
}
function Ee(e) {
return Number(e.quote_info?.is_deleted) === 1
? []
: ticketAttachments_n(
e.quote_info?.attachment ||
Z.value.find((t) => t.id === e.quote_reply_id)?.attachment ||
[],
);
}
async function De() {
if (!(
G.value ||
!W.value.trim() ||
J.value ||
V.value?.status === `已关闭`
)) {
G.value = !0;
try {
(await z.post(`/ticket/${B}/reply`, {
content: W.value.trim(),
attachment: q.value.map((e) => e.save_name),
quote_reply_id: Y.value,
}),
(W.value = ``),
(q.value = []),
(Y.value = 0),
await X());
} catch (e) {
U.value = e instanceof Error ? e.message : `回复失败`;
} finally {
G.value = !1;
}
}
}
async function Oe() {
K.value = !0;
try {
(await z.put(`/ticket/${B}/urge`), await X());
} catch (e) {
U.value = e instanceof Error ? e.message : `催单失败`;
} finally {
K.value = !1;
}
}
async function ke() {
K.value = !0;
try {
(await z.put(`/ticket/${B}/close`), await X());
} catch (e) {
U.value = e instanceof Error ? e.message : `关闭失败`;
} finally {
K.value = !1;
}
}
return (
onMounted(X),
(n, a) => {
let c = TicketNotice,
m = NuxtLink,
_ = skeleton,
v = message,
y = button,
S = TicketAttachments,
C = RichTextEditor;
return (
openBlock(),
createElementBlock(`div`, le, [
createVNode(c),
createElementVNode(`div`, ue, [
createVNode(
m,
{
to: `/tickets`,
class: `back`,
},
{
default: withCtx(() => [
...(a[2] ||= [
createElementVNode(
`i`,
{
class: `pi pi-arrow-left`,
},
null,
-1,
),
createTextVNode(` 返回工单列表 `, -1),
]),
]),
_: 1,
},
),
]),
unref(H)
? (openBlock(),
createElementBlock(`div`, de, [
createVNode(_, {
height: `220px`,
}),
]))
: unref(U) && !unref(V)
? (openBlock(),
createBlock(
v,
{
key: 1,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(U)), 1),
]),
_: 1,
},
))
: unref(V)
? (openBlock(),
createElementBlock(
Fragment,
{
key: 2,
},
[
createElementVNode(`div`, fe, [
createElementVNode(`div`, pe, [
createElementVNode(`div`, me, [
createElementVNode(
`span`,
he,
`#` + toDisplayString(unref(V).ticket_num),
1,
),
createElementVNode(
`h2`,
null,
toDisplayString(unref(V).title),
1,
),
]),
unref(V).status === `已关闭`
? createCommentVNode(``, !0)
: (openBlock(),
createBlock(
y,
{
key: 0,
label: `关闭工单`,
severity: `danger`,
outlined: ``,
size: `small`,
loading: unref(K),
onClick: ke,
},
null,
8,
[`loading`],
)),
createElementVNode(
`span`,
{
class: `status-tag`,
style: normalizeStyle({
color: `var(--p-text-color)`,
borderColor: unref(V).color,
}),
},
toDisplayString(unref(V).status),
5,
),
]),
createElementVNode(`div`, ge, [
createElementVNode(
`span`,
null,
`提交时间:` +
toDisplayString(
(`formatTime` in n
? n.formatTime
: unref(dict_r))(unref(V).create_time),
),
1,
),
]),
]),
createElementVNode(`div`, w, [
createElementVNode(`div`, T, [
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(Z),
(t, a) => (
openBlock(),
createElementBlock(
`div`,
{
key: `${t.id}-${a}`,
class: normalizeClass([
`message`,
{
admin: t.type === `Admin`,
},
]),
},
[
createElementVNode(`div`, E, [
createElementVNode(
`span`,
{
class: normalizeClass([
`sender-avatar`,
{
staff: t.type === `Admin`,
},
]),
},
toDisplayString(Q(t).slice(0, 1)),
3,
),
createElementVNode(
`span`,
D,
toDisplayString(Q(t)),
1,
),
t.type === `Admin`
? (openBlock(),
createElementBlock(
`span`,
O,
`客服`,
))
: createCommentVNode(``, !0),
createElementVNode(
`span`,
k,
toDisplayString(
(`formatTime` in n
? n.formatTime
: unref(dict_r))(
t.create_time,
),
),
1,
),
]),
createElementVNode(
`div`,
{
class: `message-body`,
innerHTML: (`sanitizeHtml` in n
? n.sanitizeHtml
: unref(sanitize_n))(t.content),
},
null,
8,
A,
),
t.quote_reply_id
? (openBlock(),
createElementBlock(`div`, j, [
createElementVNode(
`span`,
null,
`引用 ` +
toDisplayString(Te(t)) +
`:`,
1,
),
createElementVNode(
`div`,
{
innerHTML:
(`sanitizeHtml` in n
? n.sanitizeHtml
: unref(sanitize_n))(
we(t),
),
},
null,
8,
M,
),
createVNode(
S,
{
files: Ee(t),
},
null,
8,
[`files`],
),
]))
: createCommentVNode(``, !0),
createVNode(
S,
{
files: t.files,
},
null,
8,
[`files`],
),
t.id > 0 &&
unref(V).status !== `已关闭`
? (openBlock(),
createBlock(
y,
{
key: 1,
label: `引用`,
icon: `pi pi-reply`,
text: ``,
size: `small`,
class: `quote-button`,
onClick: (e) => Ce(t),
},
null,
8,
[`onClick`],
))
: createCommentVNode(``, !0),
],
2,
)
),
),
128,
)),
]),
createElementVNode(`div`, N, [
unref($)
? (openBlock(),
createElementBlock(`div`, P, [
createElementVNode(`div`, null, [
createElementVNode(
`span`,
null,
`引用 ` +
toDisplayString(Q(unref($))) +
`:`,
1,
),
createElementVNode(
`div`,
{
innerHTML: (`sanitizeHtml` in n
? n.sanitizeHtml
: unref(sanitize_n))(
unref($).content,
),
},
null,
8,
F,
),
]),
createVNode(y, {
icon: `pi pi-times`,
text: ``,
rounded: ``,
size: `small`,
"aria-label": `取消引用`,
onClick: (a[0] ||= (e) =>
(Y.value = 0)),
}),
]))
: createCommentVNode(``, !0),
createVNode(
C,
{
modelValue: unref(W),
"onUpdate:modelValue": (a[1] ||= (e) =>
isRef(W) ? (W.value = e) : null),
label: `回复内容`,
"min-height": 140,
placeholder: `输入回复内容…`,
disabled: unref(V).status === `已关闭`,
},
null,
8,
[`modelValue`, `disabled`],
),
createElementVNode(`div`, I, [
createElementVNode(`label`, L, [
(a[3] ||= createElementVNode(
`i`,
{
class: `pi pi-paperclip`,
},
null,
-1,
)),
(a[4] ||= createTextVNode(`添加附件`, -1)),
createElementVNode(
`input`,
{
id: `reply-files`,
type: `file`,
multiple: ``,
disabled:
unref(G) ||
unref(J) ||
unref(V).status === `已关闭`,
onChange: xe,
},
null,
40,
_e,
),
]),
unref(J)
? (openBlock(),
createElementBlock(
`span`,
R,
`正在上传…`,
))
: createCommentVNode(``, !0),
unref(q).length
? (openBlock(),
createElementBlock(`ul`, ve, [
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(q),
(t, n) => (
openBlock(),
createElementBlock(
`li`,
{
key: t.save_name,
},
[
createElementVNode(
`span`,
null,
toDisplayString(t.name),
1,
),
createVNode(
y,
{
icon: `pi pi-times`,
text: ``,
rounded: ``,
size: `small`,
"aria-label": `移除附件 ${t.name}`,
disabled: unref(G),
onClick: (e) => Se(n),
},
null,
8,
[
`aria-label`,
`disabled`,
`onClick`,
],
),
],
)
),
),
128,
)),
]))
: createCommentVNode(``, !0),
]),
createElementVNode(`div`, ye, [
unref(U)
? (openBlock(),
createBlock(
v,
{
key: 0,
severity: `error`,
closable: !1,
class: `inline-error`,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(unref(U)),
1,
),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createElementVNode(`div`, be, [
unref(V).status === `已关闭`
? createCommentVNode(``, !0)
: (openBlock(),
createBlock(
y,
{
key: 0,
label: `催单`,
severity: `secondary`,
outlined: ``,
size: `small`,
loading: unref(K),
onClick: Oe,
},
null,
8,
[`loading`],
)),
createVNode(
y,
{
label: `提交回复`,
loading: unref(G),
disabled:
unref(V).status === `已关闭` ||
!unref(W).trim() ||
unref(J),
onClick: De,
},
null,
8,
[`loading`, `disabled`],
),
]),
]),
]),
]),
],
64,
))
: createCommentVNode(``, !0),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-3743a20f`]],
);
export {};
export default z;
</script>
+675
View File
@@ -0,0 +1,675 @@
<script>
import "~/assets/styles/RichTextEditor.NkmgPT5m.css";
import "~/assets/styles/create.gX6gpD00.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
import { onMounted } 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 { 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 { withModifiers } from "vue";
import { useRouter } from "#app";
import { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
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 radiobutton from "primevue/radiobutton";
import inputtext from "primevue/inputtext";
import { n as product_name_n } from "~/lib/product-name.js";
import select from "primevue/select";
import RichTextEditor from "~/components/RichTextEditor.vue";
import TicketNotice from "~/components/TicketNotice.vue";
import chip from "primevue/chip";
import multiselect from "primevue/multiselect";
var le = {
class: `ticket-create`,
},
ue = {
class: `ticket-section`,
},
de = {
class: `section-fields`,
},
fe = {
key: 0,
role: `status`,
},
C = {
class: `type-options`,
},
w = {
class: `field`,
},
T = {
key: 2,
class: `field`,
},
E = {
class: `field`,
},
D = {
class: `host-opt`,
},
O = {
class: `host-opt-meta`,
},
k = {
class: `ticket-section`,
},
A = {
class: `section-fields`,
},
j = {
class: `field`,
},
M = {
class: `field`,
},
N = {
class: `upload-drop`,
for: `files`,
},
P = [`disabled`],
F = {
key: 0,
class: `files`,
},
I = {
class: `fname`,
},
L = [`aria-label`, `disabled`, `onClick`],
R = {
class: `actions`,
},
z = _plugin_vue_export_helper_t(
defineComponent({
__name: `create`,
setup(m) {
let b = useApi(),
z = useRouter(),
B = ref([]),
V = ref([]),
H = ref([]),
U = reactive({
title: ``,
ticket_type_id: null,
admin_role_id: null,
host_ids: [],
content: ``,
}),
W = ref(!1),
G = ref(``),
K = ref(!1),
q = ref(!1);
function J(e) {
return e.dedicate_ip || e.assign_ip || ``;
}
function pe(e) {
return [`#${e.id}`, e.name, J(e)].filter(Boolean).join(` / `);
}
let Y = computed(() => new Map(H.value.map((e) => [e.id, e])));
function me(e) {
let t = Y.value.get(e);
return t
? `${product_name_n(t.product_name) || t.name} (#${t.id})`
: `#${e}`;
}
let X = ref([]),
Z = ref(!1);
async function he(e) {
if (Z.value || W.value) return;
let t = e.target,
n = Array.from(t.files ?? []);
if (n.length) {
((Z.value = !0), (G.value = ``));
try {
for (let e of n) {
let t = await b.upload(e);
if (!t?.save_name)
throw Error(`附件「${e.name}」上传失败,请重新选择文件`);
X.value.push({
save_name: t.save_name,
name: e.name,
});
}
} catch (e) {
G.value = e instanceof Error ? e.message : `上传失败`;
} finally {
((Z.value = !1), (t.value = ``));
}
}
}
function ge(e) {
W.value || X.value.splice(e, 1);
}
async function Q() {
if (!K.value) {
((K.value = !0), (q.value = !1), (G.value = ``));
try {
let [e, t] = await Promise.all([
b.get(`/ticket/type`),
b.get(`/ticket/department`),
]);
((B.value = e.list ?? []), (V.value = t.list ?? []));
try {
let e = [],
t = 1,
n = 0;
do {
let r = await b.get(`/host`, {
page: t,
limit: 200,
});
(e.push(...(r.list ?? [])),
(n = Number(r.count ?? e.length)),
(t += 1));
} while (e.length < n && t <= 100);
H.value = e;
} catch {
H.value = [];
}
((U.ticket_type_id = B.value[0]?.id ?? null),
(U.admin_role_id = V.value[0]?.id ?? null),
(q.value = !0));
} catch (e) {
G.value =
e instanceof Error ? e.message : `加载工单选项失败,请重试`;
} finally {
K.value = !1;
}
}
}
async function $() {
if (!(W.value || Z.value || K.value || !q.value)) {
if (((G.value = ``), !U.title.trim())) {
G.value = `请输入工单标题`;
return;
}
if (!U.content.trim()) {
G.value = `请输入工单内容`;
return;
}
W.value = !0;
try {
let e = await b.post(`/ticket`, {
title: U.title.trim(),
content: U.content.trim(),
ticket_type_id: U.ticket_type_id,
admin_role_id: U.admin_role_id,
host_ids: U.host_ids,
attachment: X.value.map((e) => e.save_name),
});
z.push(`/tickets/${e.id}`);
} catch (e) {
G.value = e instanceof Error ? e.message : `提交失败`;
} finally {
W.value = !1;
}
}
}
return (
onMounted(Q),
(n, i) => {
let s = TicketNotice,
c = button,
m = radiobutton,
_ = inputtext,
y = select,
b = chip,
z = multiselect,
J = RichTextEditor,
Y = message,
_e = NuxtLink;
return (
openBlock(),
createElementBlock(`div`, le, [
createVNode(s),
(i[18] ||= createElementVNode(`h2`, null, `创建工单`, -1)),
createElementVNode(
`form`,
{
onSubmit: withModifiers($, [`prevent`]),
},
[
createElementVNode(`section`, ue, [
(i[9] ||= createElementVNode(
`aside`,
null,
[
createElementVNode(`h3`, null, `基本信息`),
createElementVNode(
`p`,
null,
`请提供一些基本信息,这有助于我们快速定位您的问题并将其转交给正确的团队处理。`,
),
],
-1,
)),
createElementVNode(`div`, de, [
unref(K)
? (openBlock(),
createElementBlock(`div`, fe, `正在加载工单选项…`))
: unref(q)
? createCommentVNode(``, !0)
: (openBlock(),
createBlock(c, {
key: 1,
label: `重新加载工单选项`,
severity: `secondary`,
outlined: ``,
onClick: Q,
})),
createElementVNode(`fieldset`, C, [
(i[5] ||= createElementVNode(
`legend`,
null,
`工单类型`,
-1,
)),
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(B),
(t) => (
openBlock(),
createElementBlock(
`label`,
{
key: t.id,
},
[
createVNode(
m,
{
modelValue: unref(U).ticket_type_id,
"onUpdate:modelValue": (i[0] ||= (e) =>
(unref(U).ticket_type_id = e)),
"input-id": `ticket-type-${t.id}`,
value: t.id,
disabled: !unref(q),
},
null,
8,
[
`modelValue`,
`input-id`,
`value`,
`disabled`,
],
),
createElementVNode(
`span`,
null,
toDisplayString(t.name),
1,
),
],
)
),
),
128,
)),
]),
createElementVNode(`div`, w, [
(i[6] ||= createElementVNode(
`label`,
{
for: `title`,
},
`工单标题`,
-1,
)),
createVNode(
_,
{
id: `title`,
modelValue: unref(U).title,
"onUpdate:modelValue": (i[1] ||= (e) =>
(unref(U).title = e)),
placeholder: `输入工单标题`,
maxlength: `100`,
fluid: ``,
},
null,
8,
[`modelValue`],
),
]),
unref(V).length > 1
? (openBlock(),
createElementBlock(`div`, T, [
(i[7] ||= createElementVNode(
`label`,
{
for: `department`,
},
`服务部门`,
-1,
)),
createVNode(
y,
{
"input-id": `department`,
modelValue: unref(U).admin_role_id,
"onUpdate:modelValue": (i[2] ||= (e) =>
(unref(U).admin_role_id = e)),
options: unref(V),
optionLabel: `name`,
optionValue: `id`,
disabled: !unref(q),
fluid: ``,
},
null,
8,
[`modelValue`, `options`, `disabled`],
),
]))
: createCommentVNode(``, !0),
createElementVNode(`div`, E, [
(i[8] ||= createElementVNode(
`label`,
{
for: `ticket-hosts`,
},
`关联产品`,
-1,
)),
createVNode(
z,
{
"input-id": `ticket-hosts`,
modelValue: unref(U).host_ids,
"onUpdate:modelValue": (i[3] ||= (e) =>
(unref(U).host_ids = e)),
options: unref(H),
optionLabel: `name`,
optionValue: `id`,
filter: ``,
filterFields: [
`product_name`,
`name`,
`dedicate_ip`,
`assign_ip`,
`id`,
],
filterPlaceholder: `输入关键字`,
placeholder: `选择关联产品`,
display: `chip`,
fluid: ``,
},
{
option: withCtx(({ option: t }) => [
createElementVNode(`div`, D, [
createElementVNode(
`span`,
null,
toDisplayString(
unref(product_name_n)(t.product_name) ||
t.name,
),
1,
),
createElementVNode(
`span`,
O,
toDisplayString(pe(t)),
1,
),
]),
]),
chip: withCtx(
({ value: e, removeCallback: t }) => [
createVNode(
b,
{
label: me(e),
removable: ``,
onRemove: (n) => t(n, e),
},
null,
8,
[`label`, `onRemove`],
),
],
),
_: 1,
},
8,
[`modelValue`, `options`],
),
]),
]),
]),
createElementVNode(`section`, k, [
(i[17] ||= createElementVNode(
`aside`,
null,
[
createElementVNode(`h3`, null, `详细信息`),
createElementVNode(`p`, null, [
createTextVNode(
`请尽可能详细地描述您的问题,包括您已经尝试过的解决方案。`,
),
createElementVNode(`br`),
createTextVNode(
`如果您有相关的文件或图片,请上传它们,这将有助于我们更快地诊断问题并为您提供解决方案。`,
),
]),
],
-1,
)),
createElementVNode(`div`, A, [
createElementVNode(`div`, j, [
(i[10] ||= createElementVNode(
`label`,
{
for: `content`,
},
`工单内容`,
-1,
)),
createVNode(
J,
{
id: `content`,
modelValue: unref(U).content,
"onUpdate:modelValue": (i[4] ||= (e) =>
(unref(U).content = e)),
label: `工单内容`,
placeholder: `请描述问题、复现步骤和报错信息`,
disabled: unref(W),
},
null,
8,
[`modelValue`, `disabled`],
),
]),
createElementVNode(`div`, M, [
(i[15] ||= createElementVNode(
`label`,
{
for: `files`,
},
`附件`,
-1,
)),
createElementVNode(`label`, N, [
(i[11] ||= createElementVNode(
`i`,
{
class: `pi pi-upload`,
},
null,
-1,
)),
(i[12] ||= createElementVNode(
`span`,
null,
[
createElementVNode(`b`, null, `点击此处`),
createTextVNode(`上传相关文件`),
],
-1,
)),
createElementVNode(
`small`,
null,
toDisplayString(
unref(Z)
? `正在上传,请稍候…`
: `支持多选;上传完成后再提交工单`,
),
1,
),
createElementVNode(
`input`,
{
id: `files`,
type: `file`,
multiple: ``,
disabled: unref(Z) || unref(W),
onChange: he,
},
null,
40,
P,
),
]),
unref(X).length
? (openBlock(),
createElementBlock(`ul`, F, [
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(X),
(t, n) => (
openBlock(),
createElementBlock(
`li`,
{
key: t.save_name,
},
[
(i[14] ||= createElementVNode(
`i`,
{
class: `pi pi-paperclip`,
},
null,
-1,
)),
createElementVNode(
`span`,
I,
toDisplayString(t.name),
1,
),
createElementVNode(
`button`,
{
type: `button`,
class: `rm`,
"aria-label": `移除附件 ${t.name}`,
disabled: unref(W),
onClick: (e) => ge(n),
},
[
...(i[13] ||= [
createElementVNode(
`i`,
{
class: `pi pi-times`,
},
null,
-1,
),
]),
],
8,
L,
),
],
)
),
),
128,
)),
]))
: createCommentVNode(``, !0),
]),
unref(G)
? (openBlock(),
createBlock(
Y,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(G)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createElementVNode(`div`, R, [
createVNode(
_e,
{
to: `/tickets`,
class: `cancel-link`,
},
{
default: withCtx(() => [
...(i[16] ||= [createTextVNode(`取消`, -1)]),
]),
_: 1,
},
),
createVNode(
c,
{
type: `submit`,
label: `创建工单`,
loading: unref(W),
disabled: unref(Z) || unref(K) || !unref(q),
},
null,
8,
[`loading`, `disabled`],
),
]),
]),
]),
],
32,
),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-710c7c49`]],
);
export {};
export default z;
</script>
+623
View File
@@ -0,0 +1,623 @@
<script>
import "~/assets/styles/tickets.Dz8JrDhQ.css";
import { toDisplayString } from "vue";
import { onMounted } 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 { createElementVNode } from "vue";
import { createTextVNode } from "vue";
import { unref } from "vue";
import { defineComponent } from "vue";
import { createVNode } from "vue";
import { openBlock } from "vue";
import { normalizeStyle } from "vue";
import { withKeys } from "vue";
import { useRoute } from "#app";
import { NuxtLink } from "#components";
import { useApi } from "~/lib/auth.js";
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 { r as dict_r } from "~/lib/dict.js";
import datatable from "primevue/datatable";
import paginator from "primevue/paginator";
import select from "primevue/select";
import column from "primevue/column";
import ListEmpty from "~/components/ListEmpty.vue";
import { t as useListPage_t } from "~/lib/useListPage.js";
var ie = {
class: `list-page`,
},
D = {
class: `list-head`,
},
O = {
class: `list-head-actions`,
},
ae = {
class: `list-toolbar`,
},
oe = {
class: `list-toolbar-right`,
},
k = {
class: `list-table`,
},
A = {
key: 3,
class: `list-foot`,
},
j = _plugin_vue_export_helper_t(
defineComponent({
__name: `index`,
setup(f) {
let v = useApi(),
j = useRoute(),
M = ref([]),
N = ref([]),
P = ref({}),
F = ref(``),
I = ref(!1),
L = ref(``),
R = ref(!1),
z = ref(``),
B = ref(``),
V = Number(j.query.status),
H = ref(V > 0 ? V : null),
U = ref(null),
{
list: W,
total: G,
loading: K,
error: q,
page: J,
limit: Y,
onPage: se,
reload: X,
} = useListPage_t(`/ticket`, {
limit: 10,
params: () => ({
keywords: B.value,
status: H.value ? [H.value] : [],
ticket_type_id: U.value ?? ``,
}),
});
async function Z() {
if (!I.value) {
((I.value = !0), (F.value = ``));
try {
let [e, t] = await Promise.allSettled([
v.get(`/ticket/status`),
v.get(`/ticket/type`),
]);
(e.status === `fulfilled` && (M.value = e.value.list ?? []),
t.status === `fulfilled` && (N.value = t.value.list ?? []));
let n = [e, t].flatMap((e, t) =>
e.status === `rejected`
? [
`${t === 0 ? `工单状态` : `工单类型`}加载失败:${e.reason instanceof Error ? e.reason.message : `请稍后重试`}`,
]
: [],
);
F.value = n.join(`;`);
} finally {
I.value = !1;
}
}
}
async function Q() {
if (!R.value) {
((R.value = !0), (L.value = ``));
try {
P.value = await v.get(`/ticket/statistic`);
} catch (e) {
L.value = e instanceof Error ? e.message : `工单统计加载失败`;
} finally {
R.value = !1;
}
}
}
computed(() =>
M.value
.filter((e) => P.value[e.id] !== void 0)
.map((e) => ({
...e,
count: P.value[e.id] ?? 0,
})),
);
let ce = computed(() => [
{
label: `全部状态`,
value: null,
},
...M.value.map((e) => ({
label: e.name,
value: e.id,
})),
]),
le = computed(() => [
{
label: `全部类型`,
value: null,
},
...N.value.map((e) => ({
label: e.name,
value: e.id,
})),
]),
$ = computed(() => !!B.value || !!H.value || !!U.value);
function ue() {
((B.value = z.value.trim()), X());
}
function de() {
((z.value = ``),
(B.value = ``),
(H.value = null),
(U.value = null),
X());
}
return (
onMounted(() => {
(Z(), Q());
}),
(t, r) => {
let o = message,
f = button,
g = NuxtLink,
_ = inputtext,
v = select,
E = ListEmpty,
j = column,
M = datatable,
N = paginator;
return (
openBlock(),
createElementBlock(`div`, ie, [
createVNode(
o,
{
severity: `info`,
closable: !1,
class: `support-notice`,
},
{
default: withCtx(() => [
...(r[3] ||= [
createElementVNode(
`div`,
null,
[
createTextVNode(
`尊敬的客户您好:为了更好地为您服务,请详细描述您遇到的问题,并提供相关截图或报错信息。`,
),
createElementVNode(`br`),
createTextVNode(
`请勿在工单中发送账户密码等敏感信息。我们会尽快为您处理。`,
),
],
-1,
),
]),
]),
_: 1,
},
),
createElementVNode(`div`, D, [
(r[4] ||= createElementVNode(`h2`, null, `工单`, -1)),
createElementVNode(`div`, O, [
createVNode(
g,
{
to: `/tickets/create`,
},
{
default: withCtx(() => [
createVNode(f, {
label: `创建工单`,
icon: `pi pi-plus`,
}),
]),
_: 1,
},
),
]),
]),
unref(L)
? (openBlock(),
createBlock(
o,
{
key: 0,
severity: `warn`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
` 工单统计暂不可用:` +
toDisplayString(unref(L)) +
` `,
1,
),
createVNode(
f,
{
label: `重试统计`,
size: `small`,
severity: `secondary`,
text: ``,
loading: unref(R),
onClick: Q,
},
null,
8,
[`loading`],
),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
unref(F)
? (openBlock(),
createBlock(
o,
{
key: 1,
severity: `warn`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(F)) + ` `, 1),
createVNode(
f,
{
label: `重试筛选选项`,
size: `small`,
severity: `secondary`,
text: ``,
loading: unref(I),
onClick: Z,
},
null,
8,
[`loading`],
),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createElementVNode(`div`, ae, [
createElementVNode(`div`, oe, [
createVNode(
_,
{
modelValue: unref(z),
"onUpdate:modelValue": (r[0] ||= (e) =>
isRef(z) ? (z.value = e) : null),
class: `list-search`,
placeholder: `输入关键字`,
onKeyup: withKeys(ue, [`enter`]),
},
null,
8,
[`modelValue`],
),
createVNode(
v,
{
modelValue: unref(U),
"onUpdate:modelValue": (r[1] ||= (e) =>
isRef(U) ? (U.value = e) : null),
class: `list-select`,
options: unref(le),
optionLabel: `label`,
optionValue: `value`,
onChange: unref(X),
},
null,
8,
[`modelValue`, `options`, `onChange`],
),
createVNode(
v,
{
modelValue: unref(H),
"onUpdate:modelValue": (r[2] ||= (e) =>
isRef(H) ? (H.value = e) : null),
class: `list-select`,
options: unref(ce),
optionLabel: `label`,
optionValue: `value`,
onChange: unref(X),
},
null,
8,
[`modelValue`, `options`, `onChange`],
),
createVNode(f, {
label: `清除筛选`,
icon: `pi pi-filter-slash`,
severity: `secondary`,
text: ``,
size: `small`,
onClick: de,
}),
]),
]),
createElementVNode(`div`, k, [
createVNode(
M,
{
value: unref(W),
loading: unref(K),
lazy: ``,
rows: unref(Y),
totalRecords: unref(G),
first: (unref(J) - 1) * unref(Y),
dataKey: `id`,
},
{
empty: withCtx(() => [
createVNode(
E,
{
icon: `pi pi-comments`,
title: unref(K) ? `加载中…` : `还没有工单`,
description: unref($)
? `换个筛选条件试试`
: `遇到问题可以提交工单联系我们`,
},
{
default: withCtx(() => [
unref($)
? createCommentVNode(``, !0)
: (openBlock(),
createBlock(
g,
{
key: 0,
to: `/tickets/create`,
},
{
default: withCtx(() => [
createVNode(f, {
label: `创建工单`,
icon: `pi pi-plus`,
size: `small`,
}),
]),
_: 1,
},
)),
]),
_: 1,
},
8,
[`title`, `description`],
),
]),
default: withCtx(() => [
createVNode(
j,
{
header: `ID`,
style: {
width: `120px`,
},
},
{
body: withCtx(({ data: t }) => [
createVNode(
g,
{
to: `/tickets/${t.id}`,
class: `col-id`,
},
{
default: withCtx(() => [
createTextVNode(
`#` + toDisplayString(t.ticket_num),
1,
),
]),
_: 2,
},
1032,
[`to`],
),
]),
_: 1,
},
),
createVNode(
j,
{
header: `标题`,
style: {
"min-width": `220px`,
},
},
{
body: withCtx(({ data: t }) => [
createVNode(
g,
{
to: `/tickets/${t.id}`,
class: `title-link`,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(t.title),
1,
),
]),
_: 2,
},
1032,
[`to`],
),
]),
_: 1,
},
),
createVNode(
j,
{
header: `工单类型`,
style: {
width: `130px`,
},
},
{
body: withCtx(({ data: t }) => [
createTextVNode(
toDisplayString(t.name || `—`),
1,
),
]),
_: 1,
},
),
createVNode(
j,
{
header: `提交时间`,
style: {
"min-width": `160px`,
},
},
{
body: withCtx(({ data: n }) => [
createTextVNode(
toDisplayString(
(`formatTime` in t
? t.formatTime
: unref(dict_r))(n.post_time),
),
1,
),
]),
_: 1,
},
),
createVNode(
j,
{
header: `最近回复`,
style: {
"min-width": `160px`,
},
},
{
body: withCtx(({ data: n }) => [
createTextVNode(
toDisplayString(
n.last_reply_time
? (`formatTime` in t
? t.formatTime
: unref(dict_r))(n.last_reply_time)
: `—`,
),
1,
),
]),
_: 1,
},
),
createVNode(
j,
{
header: `状态`,
style: {
width: `110px`,
},
},
{
body: withCtx(({ data: t }) => [
createElementVNode(
`span`,
{
class: `status-tag`,
style: normalizeStyle({
color: `var(--p-text-color)`,
borderColor: t.color,
}),
},
toDisplayString(t.status),
5,
),
]),
_: 1,
},
),
]),
_: 1,
},
8,
[`value`, `loading`, `rows`, `totalRecords`, `first`],
),
]),
unref(q)
? (openBlock(),
createBlock(
o,
{
key: 2,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(q)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
unref(G) > 0
? (openBlock(),
createElementBlock(`div`, A, [
(r[5] ||= createElementVNode(
`div`,
{
class: `list-bulk`,
},
null,
-1,
)),
createVNode(
N,
{
rows: unref(Y),
totalRecords: unref(G),
first: (unref(J) - 1) * unref(Y),
rowsPerPageOptions: [10, 20, 50],
onPage: unref(se),
},
null,
8,
[`rows`, `totalRecords`, `first`, `onPage`],
),
]))
: createCommentVNode(``, !0),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-1ff3e04d`]],
);
export {};
export default j;
</script>
+17
View File
@@ -0,0 +1,17 @@
<script>
import "~/assets/styles/TicketAttachments.DtbJGLkE.css";
import "~/assets/styles/RichTextEditor.NkmgPT5m.css";
import "~/assets/styles/_id_.C-0--Lx-.css";
import { createBlock } from "vue";
import { defineComponent } from "vue";
import { openBlock } from "vue";
import _id_ from "~/pages/tickets/[id].vue";
var i = defineComponent({
__name: `ticket`,
setup(t) {
return (t, i) => (openBlock(), createBlock(_id_));
},
});
export {};
export default i;
</script>
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff