完善个人资料页面

This commit is contained in:
jingrow 2025-12-30 00:01:51 +08:00
parent 980f52ee3b
commit 023cbf428c
5 changed files with 253 additions and 124 deletions

View File

@ -1,112 +1,158 @@
<template>
<Card
v-if="!$team.pg?.jerp_partner"
:title="$t('Jingrow Partner')"
:subtitle="$t('Jingrow partner associated with your account')"
class="mx-auto max-w-3xl"
>
<template #actions>
<Button
v-if="!$team.pg?.partner_email"
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>
<n-card v-if="!$team.pg?.jerp_partner" class="partner-card">
<template #header>
<div class="flex items-center justify-between">
<div>
<h3 class="text-lg font-semibold">{{ $t('Jingrow Partner') }}</h3>
<p class="text-sm text-gray-600 mt-1">{{ $t('Jingrow partner associated with your account') }}</p>
</div>
<n-button
v-if="!$team.pg?.partner_email"
@click="showAddPartnerCodeDialog = true"
>
<template #icon>
<n-icon><EditIcon /></n-icon>
</template>
{{ $t('Add Partner Code') }}
</n-button>
<n-button
v-else
type="error"
@click="showRemovePartnerDialog = true"
>
<template #icon>
<n-icon><TrashIcon /></n-icon>
</template>
{{ $t('Unlink Partner') }}
</n-button>
</div>
</template>
<div class="py-4">
<span
class="text-base font-medium text-gray-700"
<div class="py-2">
<p
v-if="!$team.pg?.partner_email"
class="text-sm text-gray-700"
>
{{ $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
:title="partner_billing_name"
:subtitle="$team.pg?.partner_email"
/>
</p>
<div v-else class="flex items-center gap-3">
<div class="flex-1">
<p class="text-sm font-medium text-gray-900">{{ partner_billing_name }}</p>
<p class="text-sm text-gray-600">{{ $team.pg?.partner_email }}</p>
</div>
</div>
</div>
<Dialog
:options="{
title: $t('Link Partner Account'),
actions: [
{
label: $t('Submit'),
variant: 'solid',
onClick: () => $resources.addPartnerCode.submit(),
},
],
}"
v-model="showAddPartnerCodeDialog"
<!-- 添加合作伙伴代码对话框 -->
<n-modal
v-model:show="showAddPartnerCodeDialog"
preset="card"
:title="$t('Link Partner Account')"
:style="modalStyle"
:mask-closable="true"
:close-on-esc="true"
>
<template v-slot:body-content>
<p class="pb-2 text-p-base">
<template #header>
<span class="text-lg font-semibold">{{ $t('Link Partner Account') }}</span>
</template>
<n-space vertical :size="16">
<p class="text-sm text-gray-700">
{{ $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>{{ $t('Note') }}</strong>: {{ $t('After linking with a partner, the following information will be shared with your partner team:') }}
<br />
<n-alert type="info" :title="$t('Note')">
{{ $t('After linking with a partner, the following information will be shared with your partner team:') }}
<ul class="mt-2 ml-4 list-disc">
<li>{{ $t('Billing Name') }}</li>
<li>{{ $t('Monthly Billing Amount') }}</li>
</span>
</div>
<FormControl
:placeholder="$t('For example: rGjw3hJ81b')"
v-model="code"
@input="referralCodeChange"
</ul>
</n-alert>
<n-form-item :label="$t('Partner Code')">
<n-input
v-model:value="code"
:placeholder="$t('For example: rGjw3hJ81b')"
:size="inputSize"
@input="referralCodeChange"
/>
</n-form-item>
<n-alert
v-if="partnerExists"
type="success"
:title="$t('Referral code {code} belongs to {partner}', { code, partner })"
/>
<div class="mt-1">
<div v-if="partnerExists" class="text-sm text-green-600" role="alert">
{{ $t('Referral code {code} belongs to {partner}', { code, partner }) }}
</div>
</div>
<n-alert
v-if="errorMessage"
type="error"
:title="errorMessage"
/>
</n-space>
<template #action>
<n-space :vertical="isMobile" :size="isMobile ? 12 : 16" class="w-full">
<n-button @click="showAddPartnerCodeDialog = false" :block="isMobile" :size="buttonSize">
{{ $t('Cancel') }}
</n-button>
<n-button
type="primary"
@click="handleAddPartnerCode"
:loading="$resources.addPartnerCode.loading"
:disabled="!partnerExists"
:block="isMobile"
:size="buttonSize"
>
{{ $t('Submit') }}
</n-button>
</n-space>
</template>
</Dialog>
</n-modal>
<Dialog
v-model="showRemovePartnerDialog"
:options="{
title: $t('Remove Partner'),
actions: [
{
label: $t('Remove'),
variant: 'solid',
theme: 'red',
onClick: () => {
$resources.removePartner.submit();
},
},
],
}"
<!-- 移除合作伙伴对话框 -->
<n-modal
v-model:show="showRemovePartnerDialog"
preset="dialog"
:title="$t('Remove Partner')"
:positive-text="$t('Remove')"
:positive-button-props="{ type: 'error' }"
:loading="$resources.removePartner.loading"
:mask-closable="true"
:close-on-esc="true"
@positive-click="() => $resources.removePartner.submit()"
>
<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>
</Card>
<p class="text-base">
{{ $t('This will remove the partner associated with your account. Are you sure you want to remove this partner?') }}
</p>
</n-modal>
</n-card>
</template>
<script>
import { Card, FormControl, jingrowRequest, debounce } from 'jingrow-ui';
import {
NCard,
NButton,
NIcon,
NModal,
NSpace,
NFormItem,
NInput,
NAlert,
} from 'naive-ui';
import { jingrowRequest, debounce } from 'jingrow-ui';
import { DashboardError } from '../../../utils/error';
import { toast } from 'vue-sonner';
import EditIcon from '~icons/lucide/edit';
import TrashIcon from '~icons/lucide/trash-2';
export default {
name: 'AccountPartner',
components: {
Card,
FormControl,
NCard,
NButton,
NIcon,
NModal,
NSpace,
NFormItem,
NInput,
NAlert,
EditIcon,
TrashIcon,
},
data() {
return {
@ -160,32 +206,89 @@ export default {
};
},
},
computed: {
isMobile() {
return window.innerWidth <= 768;
},
modalStyle() {
return {
width: this.isMobile ? '95vw' : '800px',
maxWidth: this.isMobile ? '95vw' : '90vw',
};
},
inputSize() {
return this.isMobile ? 'medium' : 'large';
},
buttonSize() {
return this.isMobile ? 'medium' : 'medium';
},
partner_billing_name() {
return this.$resources.partnerName?.data || '';
},
},
methods: {
referralCodeChange: debounce(async function (e) {
let code = e.target.value;
this.partnerExists = false;
this.errorMessage = '';
let result = await jingrowRequest({
url: 'jcloud.api.partner.validate_partner_code',
params: { code: code },
});
if (!code) {
return;
}
let isValidCode = result[0];
let partnerName = result[1];
try {
let result = await jingrowRequest({
url: 'jcloud.api.partner.validate_partner_code',
params: { code: code },
});
if (isValidCode) {
this.partnerExists = true;
this.referralCode = code;
this.partner = partnerName;
} else {
this.errorMessage = this.$t('{code} is an invalid referral code', { code });
let isValidCode = result[0];
let partnerName = result[1];
if (isValidCode) {
this.partnerExists = true;
this.partner = partnerName;
} else {
this.errorMessage = this.$t('{code} is an invalid referral code', { code });
}
} catch (error) {
this.errorMessage = this.$t('Failed to validate partner code');
}
}, 500),
},
computed: {
partner_billing_name() {
return this.$resources.partnerName.data;
handleAddPartnerCode() {
if (this.partnerExists) {
this.$resources.addPartnerCode.submit();
}
},
},
};
</script>
<style scoped>
.partner-card {
border-radius: 12px;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
transition: box-shadow 0.2s ease;
}
.partner-card:hover {
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
@media (max-width: 768px) {
.partner-card {
border-radius: 8px;
}
:deep(.partner-card .n-card-header) {
flex-direction: column;
align-items: flex-start;
gap: 12px;
}
:deep(.partner-card .n-card-header .n-button) {
width: 100%;
margin-top: 8px;
}
}
</style>

View File

@ -153,7 +153,14 @@
</n-list-item>
</n-list>
</n-card>
<!-- 推荐有礼组件 -->
<AccountReferral />
<!-- Jingrow 合作伙伴组件 -->
<AccountPartner />
</n-space>
<!-- 编辑资料对话框 -->
<n-modal
v-model:show="showProfileEditDialog"
@ -331,6 +338,8 @@ import TFADialog from './TFADialog.vue';
import ResetPasswordDialog from './ResetPasswordDialog.vue';
import router from '../../../router';
import AddPrepaidCreditsDialog from '../../billing/AddPrepaidCreditsDialog.vue';
import AccountReferral from './AccountReferral.vue';
import AccountPartner from './AccountPartner.vue';
import EditIcon from '~icons/lucide/edit';
export default {
@ -355,6 +364,8 @@ export default {
ResetPasswordDialog,
FileUploader,
AddPrepaidCreditsDialog,
AccountReferral,
AccountPartner,
EditIcon,
},
data() {

View File

@ -1,27 +1,29 @@
<template>
<Card
v-if="referralLink"
: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">
<n-card v-if="referralLink" class="referral-card">
<template #header>
<div>
<h3 class="text-lg font-semibold">{{ $t('Referral Program') }}</h3>
<p class="text-sm text-gray-600 mt-1">{{ $t('Your exclusive referral link') }}</p>
</div>
</template>
<n-space vertical :size="16">
<ClickToCopyField :textContent="referralLink" />
<span class="text-sm font-medium leading-4 text-gray-700">
<p class="text-sm text-gray-700">
{{ $t('Invite others to join Jingrow,') }}
<strong>
{{ $t('when they register and spend at least ¥100, you will get ¥20') }}
</strong>
</span>
</div>
</Card>
<strong>{{ $t('when they register and spend at least ¥100, you will get ¥20') }}</strong>
</p>
</n-space>
</n-card>
</template>
<script>
import { NCard, NSpace } from 'naive-ui';
import ClickToCopyField from '../../ClickToCopyField.vue';
export default {
name: 'AccountReferral',
components: {
NCard,
NSpace,
ClickToCopyField
},
computed: {
@ -34,3 +36,21 @@ export default {
}
};
</script>
<style scoped>
.referral-card {
border-radius: 12px;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
transition: box-shadow 0.2s ease;
}
.referral-card:hover {
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
@media (max-width: 768px) {
.referral-card {
border-radius: 8px;
}
}
</style>

View File

@ -1,17 +1,11 @@
<template>
<div class="p-5">
<div class="space-y-5">
<AccountProfile />
<AccountReferral />
<AccountPartner />
</div>
<AccountProfile />
</div>
</template>
<script setup>
import AccountProfile from './AccountProfile.vue';
import AccountReferral from './AccountReferral.vue';
import AccountPartner from './AccountPartner.vue';
document.title = '设置 - 个人资料';
</script>

View File

@ -969,6 +969,7 @@ when they register and spend at least ¥100, you will get ¥20,当他们注册
Jingrow Partner,Jingrow 合作伙伴,
Jingrow partner associated with your account,与您的账户关联的 Jingrow 合作伙伴,
Add Partner Code,添加合作伙伴代码,
Partner Code,合作伙伴代码,
Unlink Partner,取消关联合作伙伴,
Have a Jingrow partner referral code? Click,有 Jingrow 合作伙伴推荐代码吗?点击,
to associate with your partner team.,以与您的合作伙伴团队关联。,

Can't render this file because it has a wrong number of fields in line 400.