设置菜单及页面实现多语言支持
This commit is contained in:
parent
d911a973eb
commit
369e89fc6d
@ -195,7 +195,7 @@ export default {
|
||||
disabled: enforce2FA,
|
||||
},
|
||||
{
|
||||
name: '设置',
|
||||
name: this.$t('Settings'),
|
||||
icon: () => h(Settings),
|
||||
route: '/settings',
|
||||
isActive: routeName.startsWith('Settings'),
|
||||
|
||||
@ -5,11 +5,11 @@
|
||||
class="mx-auto min-w-[48rem] max-w-3xl space-y-6 rounded-md border p-4"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-xl font-semibold">API 访问</div>
|
||||
<div class="text-xl font-semibold">{{ $t('API Access') }}</div>
|
||||
<Button @click="showCreateSecretDialog = true">{{
|
||||
$team.pg?.user_info?.api_key
|
||||
? '重新生成 API 密钥'
|
||||
: '创建新的 API 密钥'
|
||||
? $t('Regenerate API Key')
|
||||
: $t('Create New API Key')
|
||||
}}</Button>
|
||||
</div>
|
||||
<div v-if="$team.pg?.user_info?.api_key">
|
||||
@ -19,18 +19,18 @@
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="pb-2 text-base text-gray-700">
|
||||
您还没有 API 密钥。点击上面的按钮创建一个。
|
||||
{{ $t("You don't have an API key yet. Click the button above to create one.") }}
|
||||
</div>
|
||||
<Dialog
|
||||
v-model="showCreateSecretDialog"
|
||||
:options="{
|
||||
title: 'API 访问',
|
||||
title: $t('API Access'),
|
||||
size: 'xl',
|
||||
actions: [
|
||||
{
|
||||
label: $team.pg.user_info.api_key
|
||||
? '重新生成 API 密钥'
|
||||
: '创建新的 API 密钥',
|
||||
? $t('Regenerate API Key')
|
||||
: $t('Create New API Key'),
|
||||
variant: 'solid',
|
||||
disabled: createSecret.data,
|
||||
loading: createSecret.loading,
|
||||
@ -44,7 +44,7 @@
|
||||
<template #body-content>
|
||||
<div v-if="createSecret.data">
|
||||
<p class="text-base">
|
||||
请立即复制 API 密钥。您将无法再次查看它!
|
||||
{{ $t('Please copy the API key immediately. You will not be able to view it again!') }}
|
||||
</p>
|
||||
<label class="block pt-2">
|
||||
<span class="mb-2 block text-sm leading-4 text-gray-700"
|
||||
@ -60,9 +60,9 @@
|
||||
</label>
|
||||
</div>
|
||||
<div v-else class="text-base text-gray-700">
|
||||
API key 和 API secret 可用于访问
|
||||
{{ $t('API key and API secret can be used to access') }}
|
||||
<a href="/docs/api" class="underline" target="_blank"
|
||||
>今果 Jingrow API</a
|
||||
>{{ $t('Jingrow API') }}</a
|
||||
>.
|
||||
</div>
|
||||
</template>
|
||||
@ -72,7 +72,7 @@
|
||||
class="mx-auto min-w-[48rem] max-w-3xl space-y-6 rounded-md border p-4"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-xl font-semibold">SSH 密钥</div>
|
||||
<div class="text-xl font-semibold">{{ $t('SSH Keys') }}</div>
|
||||
</div>
|
||||
<ObjectList :options="sshKeyListOptions" />
|
||||
</div>
|
||||
@ -81,7 +81,7 @@
|
||||
class="mx-auto min-w-[48rem] max-w-3xl space-y-6 rounded-md border p-4"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-xl font-semibold">Webhooks</div>
|
||||
<div class="text-xl font-semibold">{{ $t('Webhooks') }}</div>
|
||||
</div>
|
||||
<ObjectList :options="webhookListOptions" />
|
||||
<AddNewWebhookDialog
|
||||
@ -114,7 +114,7 @@
|
||||
<script setup>
|
||||
import { Badge, createResource } from 'jingrow-ui';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { computed, h, onMounted, ref } from 'vue';
|
||||
import { computed, h, onMounted, ref, getCurrentInstance } from 'vue';
|
||||
import { confirmDialog, icon } from '../../utils/components';
|
||||
import ObjectList from '../ObjectList.vue';
|
||||
import { getTeam } from '../../data/team';
|
||||
@ -127,6 +127,9 @@ import { useRouter } from 'vue-router';
|
||||
import WebhookAttemptsDialog from './WebhookAttemptsDialog.vue';
|
||||
import { session } from '../../data/session';
|
||||
|
||||
const instance = getCurrentInstance();
|
||||
const $t = instance?.appContext.config.globalProperties.$t || ((key) => key);
|
||||
|
||||
const $team = getTeam();
|
||||
const router = useRouter();
|
||||
let showCreateSecretDialog = ref(false);
|
||||
@ -140,9 +143,9 @@ const createSecret = createResource({
|
||||
url: 'jcloud.api.account.create_api_secret',
|
||||
onSuccess() {
|
||||
if ($team.pg.user_info.api_key) {
|
||||
toast.success('API 密钥已成功重新生成');
|
||||
toast.success($t('API key regenerated successfully'));
|
||||
} else {
|
||||
toast.success('API 密钥已成功创建');
|
||||
toast.success($t('API key created successfully'));
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -150,13 +153,13 @@ const createSecret = createResource({
|
||||
const addSSHKey = createResource({
|
||||
url: 'jcloud.api.client.insert',
|
||||
onSuccess() {
|
||||
toast.success('SSH 密钥已成功添加');
|
||||
toast.success($t('SSH key added successfully'));
|
||||
},
|
||||
onError(err) {
|
||||
toast.error(
|
||||
err.messages.length
|
||||
? err.messages.join('\n')
|
||||
: 'SSH 密钥无法添加'
|
||||
: $t('Failed to add SSH key')
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -164,13 +167,13 @@ const addSSHKey = createResource({
|
||||
const makeKeyDefault = createResource({
|
||||
url: 'jcloud.api.account.mark_key_as_default',
|
||||
onSuccess() {
|
||||
toast.success('SSH 密钥已成功更新');
|
||||
toast.success($t('SSH key updated successfully'));
|
||||
},
|
||||
onError(err) {
|
||||
toast.error(
|
||||
err.messages.length
|
||||
? err.messages.join('\n')
|
||||
: 'SSH 密钥无法设置为默认'
|
||||
: $t('Failed to set SSH key as default')
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -178,13 +181,13 @@ const makeKeyDefault = createResource({
|
||||
const deleteSSHKey = createResource({
|
||||
url: 'jcloud.api.client.delete',
|
||||
onSuccess() {
|
||||
toast.success('SSH 密钥已成功删除');
|
||||
toast.success($t('SSH key deleted successfully'));
|
||||
},
|
||||
onError(err) {
|
||||
toast.error(
|
||||
err.messages.length
|
||||
? err.messages.join('\n')
|
||||
: 'SSH 密钥无法删除'
|
||||
: $t('Failed to delete SSH key')
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -199,14 +202,14 @@ const sshKeyListOptions = computed(() => ({
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
label: 'SSH 指纹',
|
||||
label: $t('SSH Fingerprint'),
|
||||
fieldname: 'ssh_fingerprint',
|
||||
class: 'font-mono',
|
||||
format: value => `SHA256:${value}`,
|
||||
suffix(row) {
|
||||
return row.is_default
|
||||
? h(Badge, {
|
||||
label: '默认',
|
||||
label: $t('Default'),
|
||||
theme: 'green',
|
||||
class: 'ml-2'
|
||||
})
|
||||
@ -214,7 +217,7 @@ const sshKeyListOptions = computed(() => ({
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '添加时间',
|
||||
label: $t('Added Time'),
|
||||
fieldname: 'creation',
|
||||
width: 0.1,
|
||||
format: value => date(value, 'll')
|
||||
@ -222,7 +225,7 @@ const sshKeyListOptions = computed(() => ({
|
||||
],
|
||||
primaryAction({ listResource }) {
|
||||
return {
|
||||
label: '添加 SSH 密钥',
|
||||
label: $t('Add SSH Key'),
|
||||
slots: { prefix: icon('plus') },
|
||||
onClick: () => renderAddNewKeyDialog(listResource)
|
||||
};
|
||||
@ -230,7 +233,7 @@ const sshKeyListOptions = computed(() => ({
|
||||
rowActions({ row }) {
|
||||
return [
|
||||
{
|
||||
label: '设为默认',
|
||||
label: $t('Set as Default'),
|
||||
condition: () => !row.is_default,
|
||||
onClick() {
|
||||
makeKeyDefault.submit({
|
||||
@ -239,7 +242,7 @@ const sshKeyListOptions = computed(() => ({
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
label: $t('Delete'),
|
||||
onClick() {
|
||||
deleteSSHKey.submit({
|
||||
pagetype: 'User SSH Key',
|
||||
@ -253,23 +256,22 @@ const sshKeyListOptions = computed(() => ({
|
||||
|
||||
function renderAddNewKeyDialog(listResource) {
|
||||
confirmDialog({
|
||||
title: '添加新 SSH 密钥',
|
||||
message: '向您的账户添加新的 SSH 密钥',
|
||||
title: $t('Add New SSH Key'),
|
||||
message: $t('Add a new SSH key to your account'),
|
||||
fields: [
|
||||
{
|
||||
label: 'SSH 密钥',
|
||||
label: $t('SSH Key'),
|
||||
fieldname: 'sshKey',
|
||||
type: 'textarea',
|
||||
placeholder:
|
||||
"以 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'ssh-ed25519', 'sk-ecdsa-sha2-nistp256@openssh.com', 或 'sk-ssh-ed25519@openssh.com' 开头",
|
||||
placeholder: $t("Starts with 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'ssh-ed25519', 'sk-ecdsa-sha2-nistp256@openssh.com', or 'sk-ssh-ed25519@openssh.com'"),
|
||||
required: true
|
||||
}
|
||||
],
|
||||
primaryAction: {
|
||||
label: '添加 SSH 密钥',
|
||||
label: $t('Add SSH Key'),
|
||||
variant: 'solid',
|
||||
onClick({ hide, values }) {
|
||||
if (!values.sshKey) throw new Error('SSH 密钥是必填项');
|
||||
if (!values.sshKey) throw new Error($t('SSH key is required'));
|
||||
addSSHKey
|
||||
.submit({
|
||||
pg: {
|
||||
@ -303,14 +305,14 @@ const webhookListResource = createResource({
|
||||
const deleteWebhook = createResource({
|
||||
url: 'jcloud.api.client.delete',
|
||||
onSuccess() {
|
||||
toast.success('Webhook 删除成功');
|
||||
toast.success($t('Webhook deleted successfully'));
|
||||
webhookListResource.reload();
|
||||
},
|
||||
onError(err) {
|
||||
toast.error(
|
||||
err.messages.length
|
||||
? err.messages.join('\n')
|
||||
: 'Webhook 无法删除'
|
||||
: $t('Failed to delete webhook')
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -319,13 +321,13 @@ const webhookListOptions = computed(() => ({
|
||||
data: () => webhookListResource.data,
|
||||
columns: [
|
||||
{
|
||||
label: '端点',
|
||||
label: $t('Endpoint'),
|
||||
fieldname: 'endpoint',
|
||||
width: 1,
|
||||
format: value => value.substring(0, 50)
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
label: $t('Status'),
|
||||
fieldname: 'enabled',
|
||||
width: 0.1,
|
||||
type: 'Component',
|
||||
@ -333,11 +335,11 @@ const webhookListOptions = computed(() => ({
|
||||
component({ row }) {
|
||||
return row.enabled
|
||||
? h(Badge, {
|
||||
label: '启用',
|
||||
label: $t('Enabled'),
|
||||
theme: 'green'
|
||||
})
|
||||
: h(Badge, {
|
||||
label: '禁用',
|
||||
label: $t('Disabled'),
|
||||
theme: 'red'
|
||||
});
|
||||
}
|
||||
@ -346,7 +348,7 @@ const webhookListOptions = computed(() => ({
|
||||
rowActions({ row }) {
|
||||
return [
|
||||
{
|
||||
label: '激活',
|
||||
label: $t('Activate'),
|
||||
condition: () => !Boolean(row.enabled),
|
||||
onClick() {
|
||||
selectedWebhook.value = row;
|
||||
@ -354,14 +356,14 @@ const webhookListOptions = computed(() => ({
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '禁用',
|
||||
label: $t('Disable'),
|
||||
condition: () => Boolean(row.enabled),
|
||||
onClick: () => {
|
||||
confirmDialog({
|
||||
title: '禁用 Webhook',
|
||||
message: `端点 - ${row.endpoint}<br>确定要禁用该 webhook 吗?<br>`,
|
||||
title: $t('Disable Webhook'),
|
||||
message: $t('Endpoint - {endpoint}<br>Are you sure you want to disable this webhook?<br>', { endpoint: row.endpoint }),
|
||||
primaryAction: {
|
||||
label: '禁用',
|
||||
label: $t('Disable'),
|
||||
variant: 'solid',
|
||||
theme: 'red',
|
||||
onClick({ hide }) {
|
||||
@ -379,27 +381,27 @@ const webhookListOptions = computed(() => ({
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '尝试',
|
||||
label: $t('Attempts'),
|
||||
onClick: () => {
|
||||
selectedWebhook.value = row;
|
||||
showWebhookAttempts.value = true;
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '编辑',
|
||||
label: $t('Edit'),
|
||||
onClick() {
|
||||
selectedWebhook.value = row;
|
||||
showEditWebhookDialog.value = true;
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
label: $t('Delete'),
|
||||
onClick() {
|
||||
confirmDialog({
|
||||
title: '删除 Webhook',
|
||||
message: `端点 - ${row.endpoint}<br>确定要删除该 webhook 吗?<br>`,
|
||||
title: $t('Delete Webhook'),
|
||||
message: $t('Endpoint - {endpoint}<br>Are you sure you want to delete this webhook?<br>', { endpoint: row.endpoint }),
|
||||
primaryAction: {
|
||||
label: '删除',
|
||||
label: $t('Delete'),
|
||||
variant: 'solid',
|
||||
theme: 'red',
|
||||
onClick({ hide }) {
|
||||
@ -419,14 +421,14 @@ const webhookListOptions = computed(() => ({
|
||||
},
|
||||
primaryAction() {
|
||||
return {
|
||||
label: '添加 Webhook',
|
||||
label: $t('Add Webhook'),
|
||||
slots: { prefix: icon('plus') },
|
||||
onClick: () => (showAddWebhookDialog.value = true)
|
||||
};
|
||||
},
|
||||
secondaryAction() {
|
||||
return {
|
||||
label: '刷新',
|
||||
label: $t('Refresh'),
|
||||
icon: 'refresh-ccw',
|
||||
onClick: () => webhookListResource.reload()
|
||||
};
|
||||
@ -436,14 +438,14 @@ const webhookListOptions = computed(() => ({
|
||||
const disableWebhook = createResource({
|
||||
url: 'jcloud.api.client.run_pg_method',
|
||||
onSuccess() {
|
||||
toast.success('Webhook 禁用成功');
|
||||
toast.success($t('Webhook disabled successfully'));
|
||||
webhookListResource.reload();
|
||||
},
|
||||
onError(err) {
|
||||
toast.error(
|
||||
err.messages.length
|
||||
? err.messages.join('\n')
|
||||
: 'Webhook 无法禁用'
|
||||
: $t('Failed to disable webhook')
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<Dialog
|
||||
:options="{
|
||||
title: '添加新成员',
|
||||
title: $t('Add New Member'),
|
||||
actions: [
|
||||
{
|
||||
label: '邀请成员',
|
||||
label: $t('Invite Member'),
|
||||
variant: 'solid',
|
||||
onClick: inviteMember,
|
||||
},
|
||||
@ -14,7 +14,7 @@
|
||||
>
|
||||
<template #body-content>
|
||||
<div class="space-y-4">
|
||||
<FormControl label="用户名" v-model="username" />
|
||||
<FormControl :label="$t('Username')" v-model="username" />
|
||||
<div
|
||||
v-if="$resources.roles.data?.length > 0"
|
||||
class="flex items-center space-x-2"
|
||||
@ -22,12 +22,12 @@
|
||||
<FormControl
|
||||
class="w-full"
|
||||
type="autocomplete"
|
||||
label="选择角色"
|
||||
:label="$t('Select Role')"
|
||||
:options="roleOptions"
|
||||
v-model="selectedRole"
|
||||
/>
|
||||
<Button
|
||||
label="添加"
|
||||
:label="$t('Add')"
|
||||
icon-left="plus"
|
||||
:disabled="!selectedRole"
|
||||
@click="addRole"
|
||||
@ -109,12 +109,12 @@ export default {
|
||||
},
|
||||
inviteMember() {
|
||||
if (!this.username) {
|
||||
toast.error('用户名为必填项');
|
||||
toast.error(this.$t('Username is required'));
|
||||
return;
|
||||
}
|
||||
if (this.$team.inviteTeamMember.loading) return;
|
||||
|
||||
toast.success('已添加成员到团队', { duration: 2000 });
|
||||
toast.success(this.$t('Member added to team'), { duration: 2000 });
|
||||
this.show = false;
|
||||
this.$team.inviteTeamMember.submit({
|
||||
username: this.username,
|
||||
|
||||
@ -8,11 +8,11 @@
|
||||
<FTabs
|
||||
:tabs="[
|
||||
{
|
||||
label: '成员',
|
||||
label: $t('Members'),
|
||||
value: 'members',
|
||||
},
|
||||
{
|
||||
label: '设置',
|
||||
label: $t('Settings'),
|
||||
value: 'settings',
|
||||
},
|
||||
]"
|
||||
@ -30,28 +30,28 @@
|
||||
<div v-if="tab.value === 'members'" class="text-base">
|
||||
<div class="my-4 flex gap-2">
|
||||
<div class="flex-1">
|
||||
<Autocomplete
|
||||
:options="autoCompleteList"
|
||||
v-model="member"
|
||||
placeholder="选择要添加的成员"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
variant="solid"
|
||||
label="添加成员"
|
||||
:disabled="!member?.value"
|
||||
:loading="$resources.role.addUser?.loading"
|
||||
@click="() => addUser(member.value)"
|
||||
<Autocomplete
|
||||
:options="autoCompleteList"
|
||||
v-model="member"
|
||||
:placeholder="$t('Select member to add')"
|
||||
/>
|
||||
</div>
|
||||
<div class="rounded border px-3">
|
||||
<div class="mt-2 text-gray-600">成员</div>
|
||||
<div
|
||||
v-if="roleUsers.length === 0"
|
||||
class="p-6 text-center text-gray-500"
|
||||
>
|
||||
<span>此角色尚未添加任何成员。</span>
|
||||
</div>
|
||||
<Button
|
||||
variant="solid"
|
||||
:label="$t('Add Member')"
|
||||
:disabled="!member?.value"
|
||||
:loading="$resources.role.addUser?.loading"
|
||||
@click="() => addUser(member.value)"
|
||||
/>
|
||||
</div>
|
||||
<div class="rounded border px-3">
|
||||
<div class="mt-2 text-gray-600">{{ $t('Members') }}</div>
|
||||
<div
|
||||
v-if="roleUsers.length === 0"
|
||||
class="p-6 text-center text-gray-500"
|
||||
>
|
||||
<span>{{ $t('No members have been added to this role yet.') }}</span>
|
||||
</div>
|
||||
<div v-else class="flex flex-col divide-y">
|
||||
<div
|
||||
v-for="user in roleUsers"
|
||||
@ -78,46 +78,46 @@
|
||||
<Switch
|
||||
class="ml-2"
|
||||
v-model="adminAccess"
|
||||
label="管理员权限"
|
||||
description="授予成员类似团队所有者的权限。包括访问所有页面和设置。"
|
||||
:label="$t('Admin Access')"
|
||||
:description="$t('Grant members permissions similar to team owner. Includes access to all pages and settings.')"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-1 rounded border p-4">
|
||||
<h2 class="mb-2 ml-2 font-semibold">页面访问权限</h2>
|
||||
<h2 class="mb-2 ml-2 font-semibold">{{ $t('Page Access Permissions') }}</h2>
|
||||
<Switch
|
||||
v-model="allowBilling"
|
||||
label="允许访问计费"
|
||||
:label="$t('Allow Billing Access')"
|
||||
:disabled="adminAccess"
|
||||
/>
|
||||
<Switch
|
||||
v-model="allowApps"
|
||||
label="允许访问应用"
|
||||
:label="$t('Allow Apps Access')"
|
||||
:disabled="adminAccess"
|
||||
/>
|
||||
<Switch
|
||||
v-if="$team.pg.jerp_partner"
|
||||
v-model="allowPartner"
|
||||
label="允许访问合作伙伴"
|
||||
:label="$t('Allow Partner Access')"
|
||||
:disabled="adminAccess"
|
||||
/>
|
||||
<Switch
|
||||
v-model="allowSiteCreation"
|
||||
label="允许创建站点"
|
||||
:label="$t('Allow Site Creation')"
|
||||
:disabled="adminAccess"
|
||||
/>
|
||||
<Switch
|
||||
v-model="allowBenchCreation"
|
||||
label="允许创建站点分组"
|
||||
:label="$t('Allow Release Group Creation')"
|
||||
:disabled="adminAccess"
|
||||
/>
|
||||
<Switch
|
||||
v-model="allowServerCreation"
|
||||
label="允许创建服务器"
|
||||
:label="$t('Allow Server Creation')"
|
||||
:disabled="adminAccess"
|
||||
/>
|
||||
<Switch
|
||||
v-model="allowWebhookConfiguration"
|
||||
label="允许配置Webhook"
|
||||
:label="$t('Allow Webhook Configuration')"
|
||||
:disabled="adminAccess"
|
||||
/>
|
||||
</div>
|
||||
@ -290,7 +290,7 @@ export default {
|
||||
if (!user) return;
|
||||
if (this.$resources.role.addUser.loading) return;
|
||||
|
||||
toast.success(`已添加 ${user} 到 ${this.role.title}`, { duration: 2000 });
|
||||
toast.success(this.$t('Added {user} to {role}', { user, role: this.role.title }), { duration: 2000 });
|
||||
this.member = {};
|
||||
this.$resources.role.addUser.submit({ user });
|
||||
// 刷新角色数据
|
||||
@ -304,7 +304,7 @@ export default {
|
||||
if (!user) return;
|
||||
if (this.$resources.role.removeUser.loading) return;
|
||||
|
||||
toast.success(`已从 ${this.role.title} 中移除 ${user}`, { duration: 2000 });
|
||||
toast.success(this.$t('Removed {user} from {role}', { user, role: this.role.title }), { duration: 2000 });
|
||||
this.$resources.role.removeUser.submit({ user });
|
||||
// 刷新角色数据
|
||||
setTimeout(() => {
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="jsx">
|
||||
import { h, ref } from 'vue';
|
||||
import { h, ref, getCurrentInstance } from 'vue';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { FeatherIcon } from 'jingrow-ui';
|
||||
import { icon, renderDialog, confirmDialog } from '../../utils/components';
|
||||
@ -13,6 +13,9 @@ import router from '../../router';
|
||||
import UserAvatarGroup from '../AvatarGroup.vue';
|
||||
import { getToastErrorMessage } from '../../utils/toast';
|
||||
|
||||
const instance = getCurrentInstance();
|
||||
const $t = instance?.appContext.config.globalProperties.$t || ((key) => key);
|
||||
|
||||
const listOptions = ref({
|
||||
pagetype: 'Jcloud Role',
|
||||
fields: [
|
||||
@ -22,7 +25,7 @@ const listOptions = ref({
|
||||
documentation: 'https://jingrow.com/docs/role-permissions',
|
||||
columns: [
|
||||
{
|
||||
label: '角色',
|
||||
label: $t('Role'),
|
||||
fieldname: 'title',
|
||||
width: 1,
|
||||
suffix: (row) => {
|
||||
@ -32,7 +35,7 @@ const listOptions = ref({
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '成员',
|
||||
label: $t('Members'),
|
||||
type: 'Component',
|
||||
component: ({ row }) => {
|
||||
return (
|
||||
@ -44,7 +47,7 @@ const listOptions = ref({
|
||||
class="flex h-6 items-center space-x-2"
|
||||
>
|
||||
<UserAvatarGroup users={row.users} />
|
||||
<Button label="添加成员">
|
||||
<Button label={$t('Add Member')}>
|
||||
{{ icon: () => <i-lucide-plus class="h-4 w-4 text-gray-600" /> }}
|
||||
</Button>
|
||||
</div>
|
||||
@ -56,7 +59,7 @@ const listOptions = ref({
|
||||
rowActions({ row, listResource: roleListResource }) {
|
||||
return [
|
||||
{
|
||||
label: '编辑权限',
|
||||
label: $t('Edit Permissions'),
|
||||
onClick() {
|
||||
router.push({
|
||||
name: 'SettingsPermissionRolePermissions',
|
||||
@ -65,24 +68,24 @@ const listOptions = ref({
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '配置角色',
|
||||
label: $t('Configure Role'),
|
||||
onClick: () => configureRole(row),
|
||||
},
|
||||
{
|
||||
label: '删除角色',
|
||||
label: $t('Delete Role'),
|
||||
onClick() {
|
||||
if (roleListResource.delete.loading) return;
|
||||
confirmDialog({
|
||||
title: '删除角色',
|
||||
message: `确定要删除角色 <b>${row.title}</b> 吗?`,
|
||||
title: $t('Delete Role'),
|
||||
message: $t('Are you sure you want to delete role <b>{role}</b>?', { role: row.title }),
|
||||
onSuccess({ hide }) {
|
||||
if (roleListResource.delete.loading) return;
|
||||
toast.promise(roleListResource.delete.submit(row.name), {
|
||||
loading: '正在删除角色...',
|
||||
loading: $t('Deleting role...'),
|
||||
success: () => {
|
||||
roleListResource.reload();
|
||||
hide();
|
||||
return `角色 ${row.title} 已删除`;
|
||||
return $t('Role {role} deleted', { role: row.title });
|
||||
},
|
||||
error: (e) => getToastErrorMessage(e),
|
||||
});
|
||||
@ -100,18 +103,18 @@ const listOptions = ref({
|
||||
},
|
||||
primaryAction({ listResource: groups }) {
|
||||
return {
|
||||
label: '新建角色',
|
||||
label: $t('New Role'),
|
||||
variant: 'solid',
|
||||
slots: {
|
||||
prefix: icon('plus'),
|
||||
},
|
||||
onClick() {
|
||||
confirmDialog({
|
||||
title: '创建角色',
|
||||
title: $t('Create Role'),
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'title',
|
||||
label: '角色',
|
||||
label: $t('Role'),
|
||||
autocomplete: 'off',
|
||||
},
|
||||
],
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="mb-5 flex items-center gap-2">
|
||||
<Tooltip text="所有角色">
|
||||
<Tooltip :text="$t('All Roles')">
|
||||
<Button :route="{ name: 'SettingsPermissionRoles' }">
|
||||
<template #icon>
|
||||
<i-lucide-arrow-left class="h-4 w-4 text-gray-700" />
|
||||
@ -10,7 +10,7 @@
|
||||
<h3 class="text-lg font-medium text-gray-900">
|
||||
{{ role.pg?.title }}
|
||||
</h3>
|
||||
<Tooltip text="管理员角色" v-if="role.pg.admin_access">
|
||||
<Tooltip :text="$t('Admin Role')" v-if="role.pg.admin_access">
|
||||
<FeatherIcon name="shield" class="h-5 w-5 text-gray-700" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
@ -41,7 +41,7 @@ import {
|
||||
createDocumentResource,
|
||||
createResource,
|
||||
} from 'jingrow-ui';
|
||||
import { computed, h, ref } from 'vue';
|
||||
import { computed, h, ref, getCurrentInstance } from 'vue';
|
||||
import LucideAppWindow from '~icons/lucide/app-window';
|
||||
import ObjectList from '../ObjectList.vue';
|
||||
import { toast } from 'vue-sonner';
|
||||
@ -49,6 +49,9 @@ import { getToastErrorMessage } from '../../utils/toast';
|
||||
import { confirmDialog, icon, renderDialog } from '../../utils/components';
|
||||
import RoleConfigureDialog from './RoleConfigureDialog.vue';
|
||||
|
||||
const instance = getCurrentInstance();
|
||||
const $t = instance?.appContext.config.globalProperties.$t || ((key) => key);
|
||||
|
||||
let selectedItems = ref(new Set());
|
||||
|
||||
const props = defineProps({
|
||||
@ -71,13 +74,13 @@ const docInsert = createResource({
|
||||
});
|
||||
|
||||
const dropdownOptions = [
|
||||
{ label: '允许的站点', pagetype: 'Site', fieldname: 'site' },
|
||||
{ label: $t('Allowed Sites'), pagetype: 'Site', fieldname: 'site' },
|
||||
{
|
||||
label: '允许的发布组',
|
||||
label: $t('Allowed Release Groups'),
|
||||
pagetype: 'Release Group',
|
||||
fieldname: 'release_group',
|
||||
},
|
||||
{ label: '允许的服务器', pagetype: 'Server', fieldname: 'server' },
|
||||
{ label: $t('Allowed Servers'), pagetype: 'Server', fieldname: 'server' },
|
||||
];
|
||||
const currentDropdownOption = ref(dropdownOptions[0]);
|
||||
function getDropdownOptions(listResource) {
|
||||
@ -122,12 +125,13 @@ const rolePermissions = ref({
|
||||
selectable: true,
|
||||
columns: [
|
||||
{
|
||||
label: computed(() =>
|
||||
currentDropdownOption.value.pagetype.replace(
|
||||
'Release Group',
|
||||
'发布组',
|
||||
),
|
||||
),
|
||||
label: computed(() => {
|
||||
const pagetype = currentDropdownOption.value.pagetype;
|
||||
if (pagetype === 'Release Group') {
|
||||
return $t('Release Group');
|
||||
}
|
||||
return pagetype;
|
||||
}),
|
||||
format(_value, row) {
|
||||
return (
|
||||
row.site_host_name ||
|
||||
@ -141,7 +145,7 @@ const rolePermissions = ref({
|
||||
actions({ listResource: permissions }) {
|
||||
return [
|
||||
{
|
||||
label: '配置',
|
||||
label: $t('Configure'),
|
||||
slots: {
|
||||
prefix: icon('settings'),
|
||||
},
|
||||
@ -150,8 +154,8 @@ const rolePermissions = ref({
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
slots: {
|
||||
label: $t('Delete'),
|
||||
slots: {
|
||||
prefix: icon('trash-2'),
|
||||
},
|
||||
condition: () => selectedItems.value.size > 0,
|
||||
@ -162,7 +166,7 @@ slots: {
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
toast.success('项目删除成功');
|
||||
toast.success($t('Items deleted successfully'));
|
||||
selectedItems.value.clear();
|
||||
},
|
||||
},
|
||||
@ -170,20 +174,20 @@ slots: {
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '添加',
|
||||
label: $t('Add'),
|
||||
slots: {
|
||||
prefix: icon('plus'),
|
||||
},
|
||||
onClick() {
|
||||
const pagetypeLabel = currentDropdownOption.value.pagetype === 'Release Group'
|
||||
? $t('Release Group')
|
||||
: currentDropdownOption.value.pagetype;
|
||||
confirmDialog({
|
||||
title: '添加权限',
|
||||
title: $t('Add Permission'),
|
||||
message: '',
|
||||
fields: [
|
||||
{
|
||||
label: `选择 ${currentDropdownOption.value.pagetype.replace(
|
||||
'Release Group',
|
||||
'Bench Group',
|
||||
)}`,
|
||||
label: $t('Select {type}', { type: pagetypeLabel }),
|
||||
type: 'link',
|
||||
fieldname: 'document_name',
|
||||
options: {
|
||||
@ -201,7 +205,7 @@ slots: {
|
||||
},
|
||||
],
|
||||
primaryAction: {
|
||||
label: '添加',
|
||||
label: $t('Add'),
|
||||
onClick({ values }) {
|
||||
let key = currentDropdownOption.value.fieldname;
|
||||
|
||||
@ -214,10 +218,10 @@ slots: {
|
||||
},
|
||||
}),
|
||||
{
|
||||
loading: '正在添加权限...',
|
||||
loading: $t('Adding permission...'),
|
||||
success() {
|
||||
permissions.reload();
|
||||
return '权限添加成功';
|
||||
return $t('Permission added successfully');
|
||||
},
|
||||
error: (e) => getToastErrorMessage(e),
|
||||
},
|
||||
|
||||
@ -5,13 +5,16 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineAsyncComponent, h, ref } from 'vue';
|
||||
import { defineAsyncComponent, h, ref, getCurrentInstance } from 'vue';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { getTeam } from '../../data/team';
|
||||
import { confirmDialog, renderDialog } from '../../utils/components';
|
||||
import ObjectList from '../ObjectList.vue';
|
||||
import UserWithAvatarCell from '../UserWithAvatarCell.vue';
|
||||
|
||||
const instance = getCurrentInstance();
|
||||
const $t = instance?.appContext.config.globalProperties.$t || ((key) => key);
|
||||
|
||||
const team = getTeam();
|
||||
team.getTeamMembers.submit();
|
||||
const teamMembersListOptions = ref({
|
||||
@ -20,7 +23,7 @@ const teamMembersListOptions = ref({
|
||||
list: team.getTeamMembers,
|
||||
columns: [
|
||||
{
|
||||
label: '用户',
|
||||
label: $t('User'),
|
||||
type: 'Component',
|
||||
component: ({ row }) => {
|
||||
return h(UserWithAvatarCell, {
|
||||
@ -38,15 +41,15 @@ const teamMembersListOptions = ref({
|
||||
return [];
|
||||
return [
|
||||
{
|
||||
label: '移除成员',
|
||||
label: $t('Remove Member'),
|
||||
condition: () => row.name !== team.pg.user,
|
||||
onClick() {
|
||||
if (team.removeTeamMember.loading) return;
|
||||
confirmDialog({
|
||||
title: '移除成员',
|
||||
message: `确定要将 <b>${row.full_name}</b> 从团队中移除吗?`,
|
||||
title: $t('Remove Member'),
|
||||
message: $t('Are you sure you want to remove <b>{name}</b> from the team?', { name: row.full_name }),
|
||||
onSuccess({ hide }) {
|
||||
toast.success('成员已被删除', { duration: 2000 });
|
||||
toast.success($t('Member removed'), { duration: 2000 });
|
||||
hide();
|
||||
team.removeTeamMember.submit({ member: row.name });
|
||||
setTimeout(() => {
|
||||
@ -61,7 +64,7 @@ const teamMembersListOptions = ref({
|
||||
actions() {
|
||||
return [
|
||||
{
|
||||
label: '设置',
|
||||
label: $t('Settings'),
|
||||
iconLeft: 'settings',
|
||||
onClick() {
|
||||
const TeamSettingsDialog = defineAsyncComponent(() =>
|
||||
@ -71,7 +74,7 @@ const teamMembersListOptions = ref({
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '添加成员',
|
||||
label: $t('Add Member'),
|
||||
variant: 'solid',
|
||||
iconLeft: 'plus',
|
||||
onClick() {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<Dialog
|
||||
:options="{
|
||||
title: '设置'
|
||||
title: $t('Settings')
|
||||
}"
|
||||
v-model="show"
|
||||
>
|
||||
@ -9,8 +9,8 @@
|
||||
<div class="mt-8 flex flex-col gap-4">
|
||||
<Switch
|
||||
v-model="enforce2FA"
|
||||
label="强制启用双因素认证"
|
||||
description="要求所有团队成员启用双因素认证"
|
||||
:label="$t('Enforce Two-Factor Authentication')"
|
||||
:description="$t('Require all team members to enable two-factor authentication')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<Card
|
||||
v-if="!$team.pg?.jerp_partner"
|
||||
title="Jingrow 合作伙伴"
|
||||
subtitle="与您的账户关联的 Jingrow 合作伙伴"
|
||||
:title="$t('Jingrow Partner')"
|
||||
:subtitle="$t('Jingrow partner associated with your account')"
|
||||
class="mx-auto max-w-3xl"
|
||||
>
|
||||
<template #actions>
|
||||
@ -11,14 +11,14 @@
|
||||
icon-left="edit"
|
||||
@click="showAddPartnerCodeDialog = true"
|
||||
>
|
||||
添加合作伙伴代码
|
||||
{{ $t('Add Partner Code') }}
|
||||
</Button>
|
||||
<Button
|
||||
v-else
|
||||
icon-left="trash-2"
|
||||
@click="showRemovePartnerDialog = true"
|
||||
>
|
||||
取消关联合作伙伴
|
||||
{{ $t('Unlink Partner') }}
|
||||
</Button>
|
||||
</template>
|
||||
<div class="py-4">
|
||||
@ -26,8 +26,9 @@
|
||||
class="text-base font-medium text-gray-700"
|
||||
v-if="!$team.pg?.partner_email"
|
||||
>
|
||||
有 Jingrow 合作伙伴推荐代码吗?点击
|
||||
<strong>添加合作伙伴代码</strong> 以与您的合作伙伴团队关联。
|
||||
{{ $t('Have a Jingrow partner referral code? Click') }}
|
||||
<strong>{{ $t('Add Partner Code') }}</strong>
|
||||
{{ $t('to associate with your partner team.') }}
|
||||
</span>
|
||||
<ListItem
|
||||
v-else
|
||||
@ -37,10 +38,10 @@
|
||||
</div>
|
||||
<Dialog
|
||||
:options="{
|
||||
title: '关联合作伙伴账户',
|
||||
title: $t('Link Partner Account'),
|
||||
actions: [
|
||||
{
|
||||
label: '提交',
|
||||
label: $t('Submit'),
|
||||
variant: 'solid',
|
||||
onClick: () => $resources.addPartnerCode.submit(),
|
||||
},
|
||||
@ -50,24 +51,24 @@
|
||||
>
|
||||
<template v-slot:body-content>
|
||||
<p class="pb-2 text-p-base">
|
||||
输入您的合作伙伴提供的合作伙伴代码
|
||||
{{ $t('Enter the partner code provided by your partner') }}
|
||||
</p>
|
||||
<div class="rounded border border-gray-200 bg-gray-100 p-2 mb-4">
|
||||
<span class="text-sm leading-[1.5] text-gray-700">
|
||||
<strong>注意</strong>: 与合作伙伴关联后,以下信息将与您的合作伙伴团队共享:
|
||||
<strong>{{ $t('Note') }}</strong>: {{ $t('After linking with a partner, the following information will be shared with your partner team:') }}
|
||||
<br />
|
||||
<li>账单名称</li>
|
||||
<li>月度账单金额</li>
|
||||
<li>{{ $t('Billing Name') }}</li>
|
||||
<li>{{ $t('Monthly Billing Amount') }}</li>
|
||||
</span>
|
||||
</div>
|
||||
<FormControl
|
||||
placeholder="例如:rGjw3hJ81b"
|
||||
:placeholder="$t('For example: rGjw3hJ81b')"
|
||||
v-model="code"
|
||||
@input="referralCodeChange"
|
||||
/>
|
||||
<div class="mt-1">
|
||||
<div v-if="partnerExists" class="text-sm text-green-600" role="alert">
|
||||
推荐代码 {{ code }} 属于 {{ partner }}
|
||||
{{ $t('Referral code {code} belongs to {partner}', { code, partner }) }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -76,10 +77,10 @@
|
||||
<Dialog
|
||||
v-model="showRemovePartnerDialog"
|
||||
:options="{
|
||||
title: '移除合作伙伴',
|
||||
title: $t('Remove Partner'),
|
||||
actions: [
|
||||
{
|
||||
label: '移除',
|
||||
label: $t('Remove'),
|
||||
variant: 'solid',
|
||||
theme: 'red',
|
||||
onClick: () => {
|
||||
@ -91,7 +92,7 @@
|
||||
>
|
||||
<template v-slot:body-content>
|
||||
<p class="text-p-base pb-3">
|
||||
这将移除与您的账户关联的合作伙伴。您确定要移除该合作伙伴吗?
|
||||
{{ $t('This will remove the partner associated with your account. Are you sure you want to remove this partner?') }}
|
||||
</p>
|
||||
</template>
|
||||
</Dialog>
|
||||
@ -127,13 +128,13 @@ export default {
|
||||
onSuccess(data) {
|
||||
this.showAddPartnerCodeDialog = false;
|
||||
if (data === 'Request already sent') {
|
||||
toast.error('Approval Request has already been sent to Partner');
|
||||
toast.error(this.$t('Approval Request has already been sent to Partner'));
|
||||
} else {
|
||||
toast.success('Approval Request has been sent to Partner');
|
||||
toast.success(this.$t('Approval Request has been sent to Partner'));
|
||||
}
|
||||
},
|
||||
onError() {
|
||||
throw new DashboardError('Failed to add Partner Code');
|
||||
throw new DashboardError(this.$t('Failed to add Partner Code'));
|
||||
},
|
||||
};
|
||||
},
|
||||
@ -148,13 +149,13 @@ export default {
|
||||
},
|
||||
removePartner() {
|
||||
return {
|
||||
url: 'jcloud.api.partner.remove_partner',
|
||||
url: 'jcloud.api.partner.remove_partner',
|
||||
onSuccess() {
|
||||
this.showRemovePartnerDialog = false;
|
||||
toast.success('合作伙伴已成功移除');
|
||||
toast.success(this.$t('Partner removed successfully'));
|
||||
},
|
||||
onError() {
|
||||
throw new DashboardError('移除合作伙伴失败');
|
||||
throw new DashboardError(this.$t('Failed to remove partner'));
|
||||
},
|
||||
};
|
||||
},
|
||||
@ -177,7 +178,7 @@ url: 'jcloud.api.partner.remove_partner',
|
||||
this.referralCode = code;
|
||||
this.partner = partnerName;
|
||||
} else {
|
||||
this.errorMessage = `${code} 是无效的推荐码`;
|
||||
this.errorMessage = this.$t('{code} is an invalid referral code', { code });
|
||||
}
|
||||
}, 500),
|
||||
},
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Card title="个人资料" v-if="user" class="mx-auto max-w-3xl">
|
||||
<Card :title="$t('Profile')" v-if="user" class="mx-auto max-w-3xl">
|
||||
<div class="flex items-center border-b pb-3">
|
||||
<div class="relative">
|
||||
<Avatar size="2xl" :label="user.first_name" :image="user.user_image" />
|
||||
@ -20,7 +20,7 @@
|
||||
:class="{ 'opacity-50': uploading }"
|
||||
>
|
||||
<span v-if="uploading">{{ progress }}%</span>
|
||||
<span v-else>编辑</span>
|
||||
<span v-else>{{ $t('Edit') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
@ -30,58 +30,58 @@
|
||||
<h3 class="text-base font-semibold">
|
||||
{{ user.first_name }} {{ user.last_name }}
|
||||
</h3>
|
||||
<p class="mt-1 text-base text-gray-600">用户名:{{ user.username }}</p>
|
||||
<p class="mt-1 text-base text-gray-600">手机:{{ user.mobile_no }}</p>
|
||||
<p class="mt-1 text-base text-gray-600">邮箱:{{ user.email }}</p>
|
||||
<p class="mt-1 text-base text-gray-600">{{ $t('Username') }}: {{ user.username }}</p>
|
||||
<p class="mt-1 text-base text-gray-600">{{ $t('Phone') }}: {{ user.mobile_no }}</p>
|
||||
<p class="mt-1 text-base text-gray-600">{{ $t('Email') }}: {{ user.email }}</p>
|
||||
</div>
|
||||
<div class="ml-auto">
|
||||
<Button icon-left="edit" @click="showProfileEditDialog = true">
|
||||
编辑
|
||||
{{ $t('Edit') }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<ListItem
|
||||
title="应用市场开发者"
|
||||
subtitle="开发者可以在应用市场发布自己的应用,供用户付费或免费订阅。"
|
||||
:title="$t('Marketplace Developer')"
|
||||
:subtitle="$t('Developers can publish their apps on the marketplace for users to subscribe to, either paid or free.')"
|
||||
v-if="!$team.pg.is_developer"
|
||||
>
|
||||
<template #actions>
|
||||
<Button @click="confirmPublisherAccount">
|
||||
<span>成为开发者</span>
|
||||
<span>{{ $t('Become a Developer') }}</span>
|
||||
</Button>
|
||||
</template>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
:title="user.is_2fa_enabled ? '禁用双因素认证' : '启用双因素认证'"
|
||||
:title="user.is_2fa_enabled ? $t('Disable Two-Factor Authentication') : $t('Enable Two-Factor Authentication')"
|
||||
:subtitle="
|
||||
user.is_2fa_enabled
|
||||
? '为您的账户禁用双因素认证'
|
||||
: '为您的账户启用双因素认证以增加额外的安全层'
|
||||
? $t('Disable two-factor authentication for your account')
|
||||
: $t('Enable two-factor authentication for your account to add an extra layer of security')
|
||||
"
|
||||
>
|
||||
<template #actions>
|
||||
<Button @click="show2FADialog = true">
|
||||
{{ user.is_2fa_enabled ? '禁用' : '启用' }}
|
||||
{{ user.is_2fa_enabled ? $t('Disable') : $t('Enable') }}
|
||||
</Button>
|
||||
</template>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
title="重置密码"
|
||||
subtitle="更改您的账户登录密码"
|
||||
:title="$t('Reset Password')"
|
||||
:subtitle="$t('Change your account login password')"
|
||||
>
|
||||
<template #actions>
|
||||
<Button @click="showResetPasswordDialog = true">
|
||||
重置密码
|
||||
{{ $t('Reset Password') }}
|
||||
</Button>
|
||||
</template>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
:title="teamEnabled ? '禁用账户' : '启用账户'"
|
||||
:title="teamEnabled ? $t('Disable Account') : $t('Enable Account')"
|
||||
:subtitle="
|
||||
teamEnabled
|
||||
? '禁用您的账户并停止计费'
|
||||
: '启用您的账户并恢复计费'
|
||||
? $t('Disable your account and stop billing')
|
||||
: $t('Enable your account and resume billing')
|
||||
"
|
||||
>
|
||||
<template #actions>
|
||||
@ -97,7 +97,7 @@
|
||||
"
|
||||
>
|
||||
<span :class="{ 'text-red-600': teamEnabled }">{{
|
||||
teamEnabled ? '禁用' : '启用'
|
||||
teamEnabled ? $t('Disable') : $t('Enable')
|
||||
}}</span>
|
||||
</Button>
|
||||
</template>
|
||||
@ -105,11 +105,11 @@
|
||||
</div>
|
||||
<Dialog
|
||||
:options="{
|
||||
title: '更新个人资料信息',
|
||||
title: $t('Update Profile Information'),
|
||||
actions: [
|
||||
{
|
||||
variant: 'solid',
|
||||
label: '保存更改',
|
||||
label: $t('Save Changes'),
|
||||
onClick: () => $resources.updateProfile.submit(),
|
||||
},
|
||||
],
|
||||
@ -118,10 +118,10 @@
|
||||
>
|
||||
<template v-slot:body-content>
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<FormControl label="个人名称" v-model="user.first_name" />
|
||||
<FormControl label="用户名" v-model="user.username" />
|
||||
<FormControl label="手机" v-model="user.mobile_no" />
|
||||
<FormControl label="邮箱" v-model="user.email" />
|
||||
<FormControl :label="$t('First Name')" v-model="user.first_name" />
|
||||
<FormControl :label="$t('Username')" v-model="user.username" />
|
||||
<FormControl :label="$t('Phone')" v-model="user.mobile_no" />
|
||||
<FormControl :label="$t('Email')" v-model="user.email" />
|
||||
</div>
|
||||
<ErrorMessage class="mt-4" :message="$resources.updateProfile.error" />
|
||||
</template>
|
||||
@ -129,10 +129,10 @@
|
||||
|
||||
<Dialog
|
||||
:options="{
|
||||
title: '禁用账户',
|
||||
title: $t('Disable Account'),
|
||||
actions: [
|
||||
{
|
||||
label: '禁用账户',
|
||||
label: $t('Disable Account'),
|
||||
variant: 'solid',
|
||||
theme: 'red',
|
||||
loading: $resources.disableAccount.loading,
|
||||
@ -141,23 +141,23 @@
|
||||
],
|
||||
}"
|
||||
v-model="showDisableAccountDialog"
|
||||
>
|
||||
>
|
||||
<template v-slot:body-content>
|
||||
<div class="prose text-base">
|
||||
确认此操作后:
|
||||
{{ $t('After confirming this action:') }}
|
||||
<ul>
|
||||
<li>您的账户将被禁用</li>
|
||||
<li>{{ $t('Your account will be disabled') }}</li>
|
||||
<li>
|
||||
您激活的站点将立即暂停,并在一周后被删除。
|
||||
{{ $t('Your activated sites will be suspended immediately and deleted after one week.') }}
|
||||
</li>
|
||||
<li>您的账户计费将停止</li>
|
||||
<li>{{ $t('Your account billing will stop') }}</li>
|
||||
</ul>
|
||||
您可以稍后登录以重新启用您的账户。您要继续吗?
|
||||
{{ $t('You can log in later to re-enable your account. Do you want to continue?') }}
|
||||
</div>
|
||||
<FormControl
|
||||
v-if="user.is_2fa_enabled"
|
||||
class="mt-4"
|
||||
label="输入您的2FA代码以确认"
|
||||
:label="$t('Enter your 2FA code to confirm')"
|
||||
v-model="disableAccount2FACode"
|
||||
/>
|
||||
<ErrorMessage class="mt-2" :message="$resources.disableAccount.error" />
|
||||
@ -166,10 +166,10 @@
|
||||
|
||||
<Dialog
|
||||
:options="{
|
||||
title: '启用账户',
|
||||
title: $t('Enable Account'),
|
||||
actions: [
|
||||
{
|
||||
label: '启用账户',
|
||||
label: $t('Enable Account'),
|
||||
variant: 'solid',
|
||||
loading: $resources.enableAccount.loading,
|
||||
onClick: () => $resources.enableAccount.submit(),
|
||||
@ -180,13 +180,13 @@
|
||||
>
|
||||
<template v-slot:body-content>
|
||||
<div class="prose text-base">
|
||||
确认此操作后:
|
||||
{{ $t('After confirming this action:') }}
|
||||
<ul>
|
||||
<li>您的账户将被启用</li>
|
||||
<li>您暂停的站点将重新激活</li>
|
||||
<li>您的账户计费将恢复</li>
|
||||
<li>{{ $t('Your account will be enabled') }}</li>
|
||||
<li>{{ $t('Your suspended sites will be reactivated') }}</li>
|
||||
<li>{{ $t('Your account billing will resume') }}</li>
|
||||
</ul>
|
||||
您要继续吗?
|
||||
{{ $t('Do you want to continue?') }}
|
||||
</div>
|
||||
<ErrorMessage class="mt-2" :message="$resources.enableAccount.error" />
|
||||
</template>
|
||||
@ -263,6 +263,9 @@ export default {
|
||||
this.showProfileEditDialog = false;
|
||||
this.notifySuccess();
|
||||
},
|
||||
onError() {
|
||||
// Error handling
|
||||
},
|
||||
};
|
||||
},
|
||||
disableAccount: {
|
||||
@ -274,22 +277,22 @@ export default {
|
||||
() => import('../../ChurnFeedbackDialog.vue'),
|
||||
);
|
||||
|
||||
renderDialog(
|
||||
h(ChurnFeedbackDialog, {
|
||||
team: this.$team.pg.name,
|
||||
onUpdated: () => {
|
||||
toast.success('您的反馈已成功提交');
|
||||
},
|
||||
}),
|
||||
);
|
||||
toast.success('您的账户已成功禁用');
|
||||
renderDialog(
|
||||
h(ChurnFeedbackDialog, {
|
||||
team: this.$team.pg.name,
|
||||
onUpdated: () => {
|
||||
toast.success(this.$t('Your feedback has been submitted successfully'));
|
||||
},
|
||||
}),
|
||||
);
|
||||
toast.success(this.$t('Your account has been disabled successfully'));
|
||||
this.reloadAccount();
|
||||
},
|
||||
},
|
||||
enableAccount: {
|
||||
url: 'jcloud.api.account.enable_account',
|
||||
onSuccess() {
|
||||
toast.success('您的账户已成功启用');
|
||||
toast.success(this.$t('Your account has been enabled successfully'));
|
||||
this.reloadAccount();
|
||||
this.showEnableAccountDialog = false;
|
||||
},
|
||||
@ -332,7 +335,7 @@ url: 'jcloud.api.billing.get_unpaid_invoices',
|
||||
this.notifySuccess();
|
||||
},
|
||||
notifySuccess() {
|
||||
toast.success('您的个人资料已成功更新');
|
||||
toast.success(this.$t('Your profile has been updated successfully'));
|
||||
},
|
||||
deactivateAccount(disableAccount2FACode) {
|
||||
const currency = this.$team.pg.currency;
|
||||
@ -343,56 +346,53 @@ url: 'jcloud.api.billing.get_unpaid_invoices',
|
||||
);
|
||||
renderDialog(h(finalizeInvoicesDialog));
|
||||
} else if (this.unpaidInvoices) {
|
||||
if (this.unpaidInvoices.length > 1) {
|
||||
this.showDisableAccountDialog = false;
|
||||
if (this.$team.pg.payment_mode === 'Prepaid Credits') {
|
||||
this.showAddPrepaidCreditsDialog = true;
|
||||
} else {
|
||||
confirmDialog({
|
||||
title: '多张未支付发票',
|
||||
message:
|
||||
'您有多张未支付的发票。请从发票页面支付它们',
|
||||
primaryAction: {
|
||||
label: '前往发票',
|
||||
variant: 'solid',
|
||||
onClick: ({ hide }) => {
|
||||
router.push({ name: 'BillingInvoices' });
|
||||
hide();
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
let invoice = this.unpaidInvoices;
|
||||
if (invoice.amount_due > minAmount) {
|
||||
if (this.unpaidInvoices.length > 1) {
|
||||
this.showDisableAccountDialog = false;
|
||||
confirmDialog({
|
||||
title: '清除未支付发票',
|
||||
message: `您有一张未支付的发票,金额为${
|
||||
invoice.currency === 'CNY' ? '¥' : '$'
|
||||
} ${
|
||||
invoice.amount_due
|
||||
}。请在停用账户前结清它。`,
|
||||
primaryAction: {
|
||||
label: '立即结算',
|
||||
variant: 'solid',
|
||||
onClick: ({ hide }) => {
|
||||
if (
|
||||
invoice.stripe_invoice_url &&
|
||||
this.$team.pg.payment_mode === 'Card'
|
||||
) {
|
||||
window.open(
|
||||
`/api/action/jcloud.api.client.run_pg_method?dt=Invoice&dn=${invoice.name}&method=stripe_payment_url`,
|
||||
);
|
||||
} else {
|
||||
this.showAddPrepaidCreditsDialog = true;
|
||||
}
|
||||
hide();
|
||||
if (this.$team.pg.payment_mode === 'Prepaid Credits') {
|
||||
this.showAddPrepaidCreditsDialog = true;
|
||||
} else {
|
||||
confirmDialog({
|
||||
title: this.$t('Multiple Unpaid Invoices'),
|
||||
message: this.$t('You have multiple unpaid invoices. Please pay them from the invoices page.'),
|
||||
primaryAction: {
|
||||
label: this.$t('Go to Invoices'),
|
||||
variant: 'solid',
|
||||
onClick: ({ hide }) => {
|
||||
router.push({ name: 'BillingInvoices' });
|
||||
hide();
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
let invoice = this.unpaidInvoices;
|
||||
if (invoice.amount_due > minAmount) {
|
||||
this.showDisableAccountDialog = false;
|
||||
confirmDialog({
|
||||
title: this.$t('Clear Unpaid Invoice'),
|
||||
message: this.$t('You have an unpaid invoice of {amount}. Please settle it before deactivating your account.', {
|
||||
amount: `${invoice.currency === 'CNY' ? '¥' : '$'} ${invoice.amount_due}`
|
||||
}),
|
||||
primaryAction: {
|
||||
label: this.$t('Settle Now'),
|
||||
variant: 'solid',
|
||||
onClick: ({ hide }) => {
|
||||
if (
|
||||
invoice.stripe_invoice_url &&
|
||||
this.$team.pg.payment_mode === 'Card'
|
||||
) {
|
||||
window.open(
|
||||
`/api/action/jcloud.api.client.run_pg_method?dt=Invoice&dn=${invoice.name}&method=stripe_payment_url`,
|
||||
);
|
||||
} else {
|
||||
this.showAddPrepaidCreditsDialog = true;
|
||||
}
|
||||
hide();
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validate if any active servers
|
||||
@ -409,9 +409,8 @@ url: 'jcloud.api.billing.get_unpaid_invoices',
|
||||
},
|
||||
confirmPublisherAccount() {
|
||||
confirmDialog({
|
||||
title: '成为市场应用开发者?',
|
||||
message:
|
||||
'确认后,您将能够将应用发布到我们的市场。',
|
||||
title: this.$t('Become a Marketplace Developer?'),
|
||||
message: this.$t('After confirmation, you will be able to publish apps to our marketplace.'),
|
||||
onSuccess: ({ hide }) => {
|
||||
toast.promise(
|
||||
this.$team.setValue.submit(
|
||||
@ -431,9 +430,9 @@ url: 'jcloud.api.billing.get_unpaid_invoices',
|
||||
},
|
||||
),
|
||||
{
|
||||
success: '您现在可以将应用发布到我们的市场',
|
||||
error: '标记您为开发者失败',
|
||||
loading: '正在将您设为开发者...',
|
||||
success: this.$t('You can now publish apps to our marketplace'),
|
||||
error: this.$t('Failed to mark you as a developer'),
|
||||
loading: this.$t('Setting you as a developer...'),
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@ -1,18 +1,17 @@
|
||||
<template>
|
||||
<Card
|
||||
v-if="referralLink"
|
||||
title="推荐有礼"
|
||||
subtitle="您的专属推荐链接"
|
||||
:title="$t('Referral Program')"
|
||||
:subtitle="$t('Your exclusive referral link')"
|
||||
class="mx-auto max-w-3xl"
|
||||
>
|
||||
<div class="flex flex-col space-y-4 overflow-hidden">
|
||||
<ClickToCopyField :textContent="referralLink" />
|
||||
<span class="text-sm font-medium leading-4 text-gray-700">
|
||||
邀请他人加入 今果 Jingrow,
|
||||
{{ $t('Invite others to join Jingrow,') }}
|
||||
<strong>
|
||||
当他们注册并消费至少 ¥100 时,
|
||||
您将获得 ¥20 </strong
|
||||
>
|
||||
{{ $t('when they register and spend at least ¥100, you will get ¥20') }}
|
||||
</strong>
|
||||
</span>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<Header class="sticky top-0 z-10 bg-white">
|
||||
<div class="flex items-center space-x-2">
|
||||
<Breadcrumbs :items="[{ label: '设置', route: '/settings' }]" />
|
||||
<Breadcrumbs :items="[{ label: $t('Settings'), route: '/settings' }]" />
|
||||
</div>
|
||||
</Header>
|
||||
<div>
|
||||
@ -20,18 +20,22 @@ import { icon } from '../utils/components';
|
||||
import TabsWithRouter from '../components/TabsWithRouter.vue';
|
||||
import { getTeam } from '../data/team';
|
||||
import { session } from '../data/session';
|
||||
import { getCurrentInstance } from 'vue';
|
||||
|
||||
const instance = getCurrentInstance();
|
||||
const $t = instance?.appContext.config.globalProperties.$t || ((key) => key);
|
||||
|
||||
let $team = getTeam();
|
||||
let $session = session || {};
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
label: '个人资料',
|
||||
label: $t('Profile'),
|
||||
icon: icon('user'),
|
||||
routeName: 'SettingsProfile'
|
||||
},
|
||||
{
|
||||
label: '团队',
|
||||
label: $t('Team'),
|
||||
icon: icon('users'),
|
||||
routeName: 'SettingsTeam',
|
||||
condition: () =>
|
||||
@ -40,7 +44,7 @@ const tabs = [
|
||||
$session.isSystemUser
|
||||
},
|
||||
{
|
||||
label: '权限',
|
||||
label: $t('Permissions'),
|
||||
icon: icon('lock'),
|
||||
routeName: 'SettingsPermission',
|
||||
childrenRoutes: [
|
||||
@ -53,7 +57,7 @@ const tabs = [
|
||||
$session.isSystemUser
|
||||
},
|
||||
{
|
||||
label: '开发者',
|
||||
label: $t('Developer'),
|
||||
icon: icon('code'),
|
||||
routeName: 'SettingsDeveloper'
|
||||
}
|
||||
|
||||
@ -822,4 +822,159 @@ Country and City,国家和城市,
|
||||
State and Postal Code,州和邮政编码,
|
||||
{field} is required,{field} 是必填项,
|
||||
Billing information updated,账单信息已更新,
|
||||
|
||||
Profile,个人资料,
|
||||
Team,团队,
|
||||
Permissions,权限,
|
||||
Add New Member,添加新成员,
|
||||
Invite Member,邀请成员,
|
||||
Username,用户名,
|
||||
Select Role,选择角色,
|
||||
Username is required,用户名为必填项,
|
||||
Member added to team,已添加成员到团队,
|
||||
Remove Member,移除成员,
|
||||
Are you sure you want to remove <b>{name}</b> from the team?,确定要将 <b>{name}</b> 从团队中移除吗?,
|
||||
Member removed,成员已被删除,
|
||||
Add Member,添加成员,
|
||||
Enforce Two-Factor Authentication,强制启用双因素认证,
|
||||
Require all team members to enable two-factor authentication,要求所有团队成员启用双因素认证,
|
||||
API Access,API 访问,
|
||||
Regenerate API Key,重新生成 API 密钥,
|
||||
Create New API Key,创建新的 API 密钥,
|
||||
You don't have an API key yet. Click the button above to create one.,您还没有 API 密钥。点击上面的按钮创建一个。,
|
||||
Please copy the API key immediately. You will not be able to view it again!,请立即复制 API 密钥。您将无法再次查看它!,
|
||||
API key and API secret can be used to access,API key 和 API secret 可用于访问,
|
||||
Jingrow API,今果 Jingrow API,
|
||||
API key regenerated successfully,API 密钥已成功重新生成,
|
||||
API key created successfully,API 密钥已成功创建,
|
||||
SSH Keys,SSH 密钥,
|
||||
SSH key added successfully,SSH 密钥已成功添加,
|
||||
Failed to add SSH key,SSH 密钥无法添加,
|
||||
SSH key updated successfully,SSH 密钥已成功更新,
|
||||
Failed to set SSH key as default,SSH 密钥无法设置为默认,
|
||||
SSH key deleted successfully,SSH 密钥已成功删除,
|
||||
Failed to delete SSH key,SSH 密钥无法删除,
|
||||
SSH Fingerprint,SSH 指纹,
|
||||
Added Time,添加时间,
|
||||
Add SSH Key,添加 SSH 密钥,
|
||||
Add New SSH Key,添加新 SSH 密钥,
|
||||
Add a new SSH key to your account,向您的账户添加新的 SSH 密钥,
|
||||
SSH Key,SSH 密钥,
|
||||
Starts with 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'ssh-ed25519', 'sk-ecdsa-sha2-nistp256@openssh.com', or 'sk-ssh-ed25519@openssh.com',以 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'ssh-ed25519', 'sk-ecdsa-sha2-nistp256@openssh.com', 或 'sk-ssh-ed25519@openssh.com' 开头,
|
||||
SSH key is required,SSH 密钥是必填项,
|
||||
Webhooks,Webhooks,
|
||||
Webhook deleted successfully,Webhook 删除成功,
|
||||
Failed to delete webhook,Webhook 无法删除,
|
||||
Endpoint,端点,
|
||||
Enabled,启用,
|
||||
Disabled,禁用,
|
||||
Activate,激活,
|
||||
Disable,禁用,
|
||||
Disable Webhook,禁用 Webhook,
|
||||
Endpoint - {endpoint}<br>Are you sure you want to disable this webhook?<br>,端点 - {endpoint}<br>确定要禁用该 webhook 吗?<br>,
|
||||
Webhook disabled successfully,Webhook 禁用成功,
|
||||
Failed to disable webhook,Webhook 无法禁用,
|
||||
Attempts,尝试,
|
||||
Add Webhook,添加 Webhook,
|
||||
Refresh,刷新,
|
||||
Delete Webhook,删除 Webhook,
|
||||
Endpoint - {endpoint}<br>Are you sure you want to delete this webhook?<br>,端点 - {endpoint}<br>确定要删除该 webhook 吗?<br>,
|
||||
First Name,个人名称,
|
||||
Phone,手机,
|
||||
Marketplace Developer,应用市场开发者,
|
||||
Developers can publish their apps on the marketplace for users to subscribe to, either paid or free.,开发者可以在应用市场发布自己的应用,供用户付费或免费订阅。,
|
||||
Become a Developer,成为开发者,
|
||||
Disable Two-Factor Authentication,禁用双因素认证,
|
||||
Enable Two-Factor Authentication,启用双因素认证,
|
||||
Disable two-factor authentication for your account,为您的账户禁用双因素认证,
|
||||
Enable two-factor authentication for your account to add an extra layer of security,为您的账户启用双因素认证以增加额外的安全层,
|
||||
Reset Password,重置密码,
|
||||
Change your account login password,更改您的账户登录密码,
|
||||
Update Profile Information,更新个人资料信息,
|
||||
Save Changes,保存更改,
|
||||
After confirming this action:,确认此操作后:,
|
||||
Your account will be disabled,您的账户将被禁用,
|
||||
Your activated sites will be suspended immediately and deleted after one week.,您激活的站点将立即暂停,并在一周后被删除。,
|
||||
Your account billing will stop,您的账户计费将停止,
|
||||
You can log in later to re-enable your account. Do you want to continue?,您可以稍后登录以重新启用您的账户。您要继续吗?,
|
||||
Enter your 2FA code to confirm,输入您的2FA代码以确认,
|
||||
Your account will be enabled,您的账户将被启用,
|
||||
Your suspended sites will be reactivated,您暂停的站点将重新激活,
|
||||
Your account billing will resume,您的账户计费将恢复,
|
||||
Do you want to continue?,您要继续吗?,
|
||||
Your feedback has been submitted successfully,您的反馈已成功提交,
|
||||
Your account has been disabled successfully,您的账户已成功禁用,
|
||||
Your account has been enabled successfully,您的账户已成功启用,
|
||||
Your profile has been updated successfully,您的个人资料已成功更新,
|
||||
Clear Unpaid Invoice,清除未支付发票,
|
||||
You have an unpaid invoice of {amount}. Please settle it before deactivating your account.,您有一张未支付的发票,金额为{amount}。请在停用账户前结清它。,
|
||||
Settle Now,立即结算,
|
||||
Become a Marketplace Developer?,成为市场应用开发者?,
|
||||
After confirmation, you will be able to publish apps to our marketplace.,确认后,您将能够将应用发布到我们的市场。,
|
||||
You can now publish apps to our marketplace,您现在可以将应用发布到我们的市场,
|
||||
Failed to mark you as a developer,标记您为开发者失败,
|
||||
Setting you as a developer...,正在将您设为开发者...,
|
||||
Role,角色,
|
||||
Members,成员,
|
||||
Edit Permissions,编辑权限,
|
||||
Configure Role,配置角色,
|
||||
Delete Role,删除角色,
|
||||
Are you sure you want to delete role <b>{role}</b>?,确定要删除角色 <b>{role}</b> 吗?,
|
||||
Deleting role...,正在删除角色...,
|
||||
Role {role} deleted,角色 {role} 已删除,
|
||||
New Role,新建角色,
|
||||
Create Role,创建角色,
|
||||
All Roles,所有角色,
|
||||
Admin Role,管理员角色,
|
||||
Allowed Sites,允许的站点,
|
||||
Allowed Release Groups,允许的发布组,
|
||||
Allowed Servers,允许的服务器,
|
||||
Items deleted successfully,项目删除成功,
|
||||
Add Permission,添加权限,
|
||||
Select {type},选择 {type},
|
||||
Adding permission...,正在添加权限...,
|
||||
Permission added successfully,权限添加成功,
|
||||
Select member to add,选择要添加的成员,
|
||||
No members have been added to this role yet.,此角色尚未添加任何成员。,
|
||||
Admin Access,管理员权限,
|
||||
Grant members permissions similar to team owner. Includes access to all pages and settings.,授予成员类似团队所有者的权限。包括访问所有页面和设置。,
|
||||
Page Access Permissions,页面访问权限,
|
||||
Allow Billing Access,允许访问计费,
|
||||
Allow Apps Access,允许访问应用,
|
||||
Allow Partner Access,允许访问合作伙伴,
|
||||
Allow Site Creation,允许创建站点,
|
||||
Allow Release Group Creation,允许创建站点分组,
|
||||
Allow Server Creation,允许创建服务器,
|
||||
Allow Webhook Configuration,允许配置Webhook,
|
||||
Added {user} to {role},已添加 {user} 到 {role},
|
||||
Removed {user} from {role},已从 {role} 中移除 {user},
|
||||
Referral Program,推荐有礼,
|
||||
Your exclusive referral link,您的专属推荐链接,
|
||||
Invite others to join Jingrow,,邀请他人加入 今果 Jingrow,,
|
||||
when they register and spend at least ¥100, you will get ¥20,当他们注册并消费至少 ¥100 时,您将获得 ¥20,
|
||||
Jingrow Partner,Jingrow 合作伙伴,
|
||||
Jingrow partner associated with your account,与您的账户关联的 Jingrow 合作伙伴,
|
||||
Add Partner Code,添加合作伙伴代码,
|
||||
Unlink Partner,取消关联合作伙伴,
|
||||
Have a Jingrow partner referral code? Click,有 Jingrow 合作伙伴推荐代码吗?点击,
|
||||
to associate with your partner team.,以与您的合作伙伴团队关联。,
|
||||
Link Partner Account,关联合作伙伴账户,
|
||||
Enter the partner code provided by your partner,输入您的合作伙伴提供的合作伙伴代码,
|
||||
After linking with a partner, the following information will be shared with your partner team:,与合作伙伴关联后,以下信息将与您的合作伙伴团队共享:,
|
||||
Monthly Billing Amount,月度账单金额,
|
||||
For example: rGjw3hJ81b,例如:rGjw3hJ81b,
|
||||
Referral code {code} belongs to {partner},推荐代码 {code} 属于 {partner},
|
||||
Remove Partner,移除合作伙伴,
|
||||
This will remove the partner associated with your account. Are you sure you want to remove this partner?,这将移除与您的账户关联的合作伙伴。您确定要移除该合作伙伴吗?,
|
||||
Approval Request has already been sent to Partner,批准请求已发送给合作伙伴,
|
||||
Approval Request has been sent to Partner,批准请求已发送给合作伙伴,
|
||||
Failed to add Partner Code,添加合作伙伴代码失败,
|
||||
Partner removed successfully,合作伙伴已成功移除,
|
||||
Failed to remove partner,移除合作伙伴失败,
|
||||
{code} is an invalid referral code,{code} 是无效的推荐码,
|
||||
Settings,设置,
|
||||
Developer,开发者,
|
||||
User,用户,
|
||||
Default,默认,
|
||||
Release Group,发布组,
|
||||
Note,注意,
|
||||
Submit,提交,
|
||||
|
||||
|
Can't render this file because it has a wrong number of fields in line 390.
|
Loading…
x
Reference in New Issue
Block a user