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