285 lines
7.8 KiB
Vue
285 lines
7.8 KiB
Vue
<template>
|
||
<div
|
||
class="flex h-screen w-screen items-center justify-center"
|
||
v-if="$resources.saasProduct.loading"
|
||
>
|
||
<Spinner class="mr-2 w-4" />
|
||
<p class="text-gray-800">加载中</p>
|
||
</div>
|
||
<div class="flex h-screen overflow-hidden sm:bg-gray-50" v-else>
|
||
<div class="w-full overflow-auto">
|
||
<LoginBox
|
||
v-if="saasProduct"
|
||
title="让我们设置您的站点"
|
||
:logo="saasProduct?.logo"
|
||
>
|
||
<template v-slot:logo v-if="saasProduct">
|
||
<div class="mx-auto flex items-center space-x-2">
|
||
<img
|
||
class="inline-block h-7 w-7 rounded-sm"
|
||
:src="saasProduct?.logo"
|
||
/>
|
||
<span
|
||
class="select-none text-xl font-semibold tracking-tight text-gray-900"
|
||
>
|
||
{{ saasProduct?.title }}
|
||
</span>
|
||
</div>
|
||
</template>
|
||
<template v-slot:default>
|
||
<form class="w-full space-y-4" @submit.prevent="createSite">
|
||
<div class="w-full space-y-1.5">
|
||
<label class="block text-xs text-ink-gray-5">
|
||
输入您站点的子域名
|
||
</label>
|
||
<div class="col-span-2 flex w-full">
|
||
<input
|
||
class="dark:[color-scheme:dark] z-10 h-7 w-full rounded rounded-r-none border border-outline-gray-2 bg-surface-white py-1.5 pl-2 pr-2 text-base text-ink-gray-8 placeholder-ink-gray-4 transition-colors hover:border-outline-gray-3 hover:shadow-sm focus:border-outline-gray-4 focus:bg-surface-white focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3"
|
||
:placeholder="`${saasProduct.name}-site`"
|
||
v-model="subdomain"
|
||
/>
|
||
<div
|
||
class="flex items-center rounded-r bg-gray-100 px-2 text-base"
|
||
>
|
||
.{{ saasProduct.domain }}
|
||
</div>
|
||
</div>
|
||
<div class="mt-1">
|
||
<div
|
||
v-if="$resources.subdomainExists.loading"
|
||
class="text-sm text-gray-600"
|
||
>
|
||
检查中...
|
||
</div>
|
||
<template
|
||
v-else-if="
|
||
!$resources.subdomainExists.error &&
|
||
$resources.subdomainExists.data != null
|
||
"
|
||
>
|
||
<div
|
||
v-if="$resources.subdomainExists.data"
|
||
class="text-sm text-green-600"
|
||
>
|
||
{{ subdomain }}.{{ saasProduct.domain }} 可用
|
||
</div>
|
||
<div v-else class="text-sm text-red-600">
|
||
{{ subdomain }}.{{ saasProduct.domain }} 不可用
|
||
</div>
|
||
</template>
|
||
<ErrorMessage :message="$resources.subdomainExists.error" />
|
||
</div>
|
||
</div>
|
||
<FormControl
|
||
label="电子邮件地址(将作为您的登录ID)"
|
||
:modelValue="$team.pg.user"
|
||
:disabled="true"
|
||
variant="outline"
|
||
class="mb-4"
|
||
/>
|
||
<SaaSSignupFields
|
||
v-if="saasProductSignupFields.length > 0"
|
||
:fields="saasProductSignupFields"
|
||
v-model="signupValues"
|
||
></SaaSSignupFields>
|
||
<ErrorMessage
|
||
class="mt-2"
|
||
:message="$resources.createSite?.error"
|
||
/>
|
||
<Button
|
||
class="mt-8 w-full"
|
||
variant="solid"
|
||
type="submit"
|
||
:loading="findingClosestServer || $resources.createSite?.loading"
|
||
loadingText="提交中..."
|
||
>
|
||
下一步
|
||
</Button>
|
||
</form>
|
||
</template>
|
||
<template v-slot:footer>
|
||
<div
|
||
class="mt-2 flex w-full items-center justify-center text-sm text-gray-600"
|
||
>
|
||
由 今果 Jingrow 提供支持
|
||
</div>
|
||
</template>
|
||
</LoginBox>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<script>
|
||
import { toast } from 'vue-sonner';
|
||
import { debounce } from 'jingrow-ui';
|
||
import LoginBox from '../../components/auth/LoginBox.vue';
|
||
import SaaSSignupFields from '../../components/SaaSSignupFields.vue';
|
||
import { validateSubdomain } from '../../utils/site';
|
||
|
||
export default {
|
||
name: 'SignupSetup',
|
||
props: ['productId'],
|
||
components: {
|
||
LoginBox,
|
||
SaaSSignupFields,
|
||
},
|
||
data() {
|
||
return {
|
||
progressErrorCount: 0,
|
||
findingClosestServer: false,
|
||
closestCluster: null,
|
||
signupValues: {},
|
||
initialSubdomain: '',
|
||
subdomain: '',
|
||
};
|
||
},
|
||
watch: {
|
||
subdomain: {
|
||
handler: debounce(function (value) {
|
||
let invalidMessage = validateSubdomain(value);
|
||
this.$resources.subdomainExists.error = invalidMessage;
|
||
if (!invalidMessage && value !== this.initialSubdomain) {
|
||
this.$resources.subdomainExists.submit();
|
||
}
|
||
}, 500),
|
||
},
|
||
},
|
||
resources: {
|
||
siteRequest() {
|
||
return {
|
||
url: 'jcloud.api.product_trial.get_request',
|
||
params: {
|
||
product: this.productId,
|
||
account_request: this.$team.pg.account_request,
|
||
},
|
||
auto: true,
|
||
initialData: {},
|
||
onSuccess: (data) => {
|
||
if (data?.status !== 'Pending') {
|
||
this.$router.push({
|
||
name: 'SignupLoginToSite',
|
||
params: { productId: this.productId },
|
||
query: {
|
||
product_trial_request: data.name,
|
||
},
|
||
});
|
||
}
|
||
},
|
||
onError(error) {
|
||
toast.error(error.messages.join('\n'));
|
||
},
|
||
};
|
||
},
|
||
saasProduct() {
|
||
return {
|
||
type: 'document',
|
||
pagetype: 'Product Trial',
|
||
name: this.productId,
|
||
auto: true,
|
||
onSuccess: (pg) => {
|
||
if (pg.site) {
|
||
this.subdomain = pg.site.split('.')[0];
|
||
this.initialSubdomain = pg.site.split('.')[0];
|
||
}
|
||
},
|
||
};
|
||
},
|
||
subdomainExists() {
|
||
return {
|
||
url: 'jcloud.api.site.exists',
|
||
makeParams() {
|
||
return {
|
||
domain: this.saasProduct?.domain,
|
||
subdomain: this.subdomain,
|
||
};
|
||
},
|
||
validate() {
|
||
let error = validateSubdomain(this.subdomain);
|
||
if (error) {
|
||
return new DashboardError(error);
|
||
}
|
||
},
|
||
transform(data) {
|
||
return !Boolean(data);
|
||
},
|
||
};
|
||
},
|
||
createSite() {
|
||
return {
|
||
url: 'jcloud.api.client.run_pg_method',
|
||
makeParams: () => {
|
||
return {
|
||
dt: 'Product Trial Request',
|
||
dn: this.$resources.siteRequest.data.name,
|
||
method: 'create_site',
|
||
args: {
|
||
subdomain: this.subdomain,
|
||
cluster: this.closestCluster ?? 'Default',
|
||
signup_values: this.signupValues,
|
||
},
|
||
};
|
||
},
|
||
auto: false,
|
||
onSuccess: (data) => {
|
||
this.$router.push({
|
||
name: 'SignupLoginToSite',
|
||
params: { productId: this.productId },
|
||
query: {
|
||
product_trial_request: this.$resources.siteRequest.data.name,
|
||
},
|
||
});
|
||
},
|
||
};
|
||
},
|
||
},
|
||
computed: {
|
||
saasProduct() {
|
||
return this.$resources.saasProduct.pg;
|
||
},
|
||
saasProductSignupFields() {
|
||
return this.saasProduct?.signup_fields ?? [];
|
||
},
|
||
},
|
||
methods: {
|
||
async createSite() {
|
||
await this.getClosestCluster();
|
||
return this.$resources.createSite.submit();
|
||
},
|
||
async getClosestCluster() {
|
||
if (this.closestCluster) return this.closestCluster;
|
||
let proxyServers = Object.keys(this.saasProduct.proxy_servers);
|
||
if (proxyServers.length > 0) {
|
||
this.findingClosestServer = true;
|
||
let promises = proxyServers.map((server) => this.getPingTime(server));
|
||
let results = await Promise.allSettled(promises);
|
||
let fastestServer = results.reduce((a, b) =>
|
||
a.value.pingTime < b.value.pingTime ? a : b,
|
||
);
|
||
let closestServer = fastestServer.value.server;
|
||
let closestCluster = this.saasProduct.proxy_servers[closestServer];
|
||
if (!this.closestCluster) {
|
||
this.closestCluster = closestCluster;
|
||
}
|
||
this.findingClosestServer = false;
|
||
}
|
||
return this.closestCluster;
|
||
},
|
||
async getPingTime(server) {
|
||
let pingTime = 999999;
|
||
try {
|
||
let t1 = new Date().getTime();
|
||
await fetch(`https://${server}`);
|
||
let t2 = new Date().getTime();
|
||
pingTime = t2 - t1;
|
||
} catch (error) {
|
||
console.warn(error);
|
||
}
|
||
return { server, pingTime };
|
||
},
|
||
redirectToLogin() {
|
||
this.$router.push({
|
||
name: '登录',
|
||
});
|
||
},
|
||
},
|
||
};
|
||
</script> |