完善个人资料页面

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> <template>
<Card <n-card v-if="!$team.pg?.jerp_partner" class="partner-card">
v-if="!$team.pg?.jerp_partner" <template #header>
:title="$t('Jingrow Partner')" <div class="flex items-center justify-between">
:subtitle="$t('Jingrow partner associated with your account')" <div>
class="mx-auto max-w-3xl" <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>
<template #actions> </div>
<Button <n-button
v-if="!$team.pg?.partner_email" v-if="!$team.pg?.partner_email"
icon-left="edit" @click="showAddPartnerCodeDialog = true"
@click="showAddPartnerCodeDialog = true" >
> <template #icon>
{{ $t('Add Partner Code') }} <n-icon><EditIcon /></n-icon>
</Button> </template>
<Button {{ $t('Add Partner Code') }}
v-else </n-button>
icon-left="trash-2" <n-button
@click="showRemovePartnerDialog = true" v-else
> type="error"
{{ $t('Unlink Partner') }} @click="showRemovePartnerDialog = true"
</Button> >
<template #icon>
<n-icon><TrashIcon /></n-icon>
</template>
{{ $t('Unlink Partner') }}
</n-button>
</div>
</template> </template>
<div class="py-4"> <div class="py-2">
<span <p
class="text-base font-medium text-gray-700"
v-if="!$team.pg?.partner_email" v-if="!$team.pg?.partner_email"
class="text-sm text-gray-700"
> >
{{ $t('Have a Jingrow partner referral code? Click') }} {{ $t('Have a Jingrow partner referral code? Click') }}
<strong>{{ $t('Add Partner Code') }}</strong> <strong>{{ $t('Add Partner Code') }}</strong>
{{ $t('to associate with your partner team.') }} {{ $t('to associate with your partner team.') }}
</span> </p>
<ListItem <div v-else class="flex items-center gap-3">
v-else <div class="flex-1">
:title="partner_billing_name" <p class="text-sm font-medium text-gray-900">{{ partner_billing_name }}</p>
:subtitle="$team.pg?.partner_email" <p class="text-sm text-gray-600">{{ $team.pg?.partner_email }}</p>
/> </div>
</div>
</div> </div>
<Dialog
:options="{ <!-- 添加合作伙伴代码对话框 -->
title: $t('Link Partner Account'), <n-modal
actions: [ v-model:show="showAddPartnerCodeDialog"
{ preset="card"
label: $t('Submit'), :title="$t('Link Partner Account')"
variant: 'solid', :style="modalStyle"
onClick: () => $resources.addPartnerCode.submit(), :mask-closable="true"
}, :close-on-esc="true"
],
}"
v-model="showAddPartnerCodeDialog"
> >
<template v-slot:body-content> <template #header>
<p class="pb-2 text-p-base"> <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') }} {{ $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"> <n-alert type="info" :title="$t('Note')">
<span class="text-sm leading-[1.5] text-gray-700"> {{ $t('After linking with a partner, the following information will be shared with your partner team:') }}
<strong>{{ $t('Note') }}</strong>: {{ $t('After linking with a partner, the following information will be shared with your partner team:') }} <ul class="mt-2 ml-4 list-disc">
<br />
<li>{{ $t('Billing Name') }}</li> <li>{{ $t('Billing Name') }}</li>
<li>{{ $t('Monthly Billing Amount') }}</li> <li>{{ $t('Monthly Billing Amount') }}</li>
</span> </ul>
</div> </n-alert>
<FormControl <n-form-item :label="$t('Partner Code')">
:placeholder="$t('For example: rGjw3hJ81b')" <n-input
v-model="code" v-model:value="code"
@input="referralCodeChange" :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"> <n-alert
<div v-if="partnerExists" class="text-sm text-green-600" role="alert"> v-if="errorMessage"
{{ $t('Referral code {code} belongs to {partner}', { code, partner }) }} type="error"
</div> :title="errorMessage"
</div> />
</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> </template>
</Dialog> </n-modal>
<Dialog <!-- 移除合作伙伴对话框 -->
v-model="showRemovePartnerDialog" <n-modal
:options="{ v-model:show="showRemovePartnerDialog"
title: $t('Remove Partner'), preset="dialog"
actions: [ :title="$t('Remove Partner')"
{ :positive-text="$t('Remove')"
label: $t('Remove'), :positive-button-props="{ type: 'error' }"
variant: 'solid', :loading="$resources.removePartner.loading"
theme: 'red', :mask-closable="true"
onClick: () => { :close-on-esc="true"
$resources.removePartner.submit(); @positive-click="() => $resources.removePartner.submit()"
},
},
],
}"
> >
<template v-slot:body-content> <p class="text-base">
<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?') }}
{{ $t('This will remove the partner associated with your account. Are you sure you want to remove this partner?') }} </p>
</p> </n-modal>
</template> </n-card>
</Dialog>
</Card>
</template> </template>
<script> <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 { DashboardError } from '../../../utils/error';
import { toast } from 'vue-sonner'; import { toast } from 'vue-sonner';
import EditIcon from '~icons/lucide/edit';
import TrashIcon from '~icons/lucide/trash-2';
export default { export default {
name: 'AccountPartner', name: 'AccountPartner',
components: { components: {
Card, NCard,
FormControl, NButton,
NIcon,
NModal,
NSpace,
NFormItem,
NInput,
NAlert,
EditIcon,
TrashIcon,
}, },
data() { data() {
return { 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: { methods: {
referralCodeChange: debounce(async function (e) { referralCodeChange: debounce(async function (e) {
let code = e.target.value; let code = e.target.value;
this.partnerExists = false; this.partnerExists = false;
this.errorMessage = '';
let result = await jingrowRequest({ if (!code) {
url: 'jcloud.api.partner.validate_partner_code', return;
params: { code: code }, }
});
let isValidCode = result[0]; try {
let partnerName = result[1]; let result = await jingrowRequest({
url: 'jcloud.api.partner.validate_partner_code',
params: { code: code },
});
if (isValidCode) { let isValidCode = result[0];
this.partnerExists = true; let partnerName = result[1];
this.referralCode = code;
this.partner = partnerName; if (isValidCode) {
} else { this.partnerExists = true;
this.errorMessage = this.$t('{code} is an invalid referral code', { code }); 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), }, 500),
}, handleAddPartnerCode() {
computed: { if (this.partnerExists) {
partner_billing_name() { this.$resources.addPartnerCode.submit();
return this.$resources.partnerName.data; }
}, },
}, },
}; };
</script> </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-item>
</n-list> </n-list>
</n-card> </n-card>
<!-- 推荐有礼组件 -->
<AccountReferral />
<!-- Jingrow 合作伙伴组件 -->
<AccountPartner />
</n-space> </n-space>
<!-- 编辑资料对话框 --> <!-- 编辑资料对话框 -->
<n-modal <n-modal
v-model:show="showProfileEditDialog" v-model:show="showProfileEditDialog"
@ -331,6 +338,8 @@ import TFADialog from './TFADialog.vue';
import ResetPasswordDialog from './ResetPasswordDialog.vue'; import ResetPasswordDialog from './ResetPasswordDialog.vue';
import router from '../../../router'; import router from '../../../router';
import AddPrepaidCreditsDialog from '../../billing/AddPrepaidCreditsDialog.vue'; import AddPrepaidCreditsDialog from '../../billing/AddPrepaidCreditsDialog.vue';
import AccountReferral from './AccountReferral.vue';
import AccountPartner from './AccountPartner.vue';
import EditIcon from '~icons/lucide/edit'; import EditIcon from '~icons/lucide/edit';
export default { export default {
@ -355,6 +364,8 @@ export default {
ResetPasswordDialog, ResetPasswordDialog,
FileUploader, FileUploader,
AddPrepaidCreditsDialog, AddPrepaidCreditsDialog,
AccountReferral,
AccountPartner,
EditIcon, EditIcon,
}, },
data() { data() {

View File

@ -1,27 +1,29 @@
<template> <template>
<Card <n-card v-if="referralLink" class="referral-card">
v-if="referralLink" <template #header>
:title="$t('Referral Program')" <div>
:subtitle="$t('Your exclusive referral link')" <h3 class="text-lg font-semibold">{{ $t('Referral Program') }}</h3>
class="mx-auto max-w-3xl" <p class="text-sm text-gray-600 mt-1">{{ $t('Your exclusive referral link') }}</p>
> </div>
<div class="flex flex-col space-y-4 overflow-hidden"> </template>
<n-space vertical :size="16">
<ClickToCopyField :textContent="referralLink" /> <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,') }} {{ $t('Invite others to join Jingrow,') }}
<strong> <strong>{{ $t('when they register and spend at least ¥100, you will get ¥20') }}</strong>
{{ $t('when they register and spend at least ¥100, you will get ¥20') }} </p>
</strong> </n-space>
</span> </n-card>
</div>
</Card>
</template> </template>
<script> <script>
import { NCard, NSpace } from 'naive-ui';
import ClickToCopyField from '../../ClickToCopyField.vue'; import ClickToCopyField from '../../ClickToCopyField.vue';
export default { export default {
name: 'AccountReferral', name: 'AccountReferral',
components: { components: {
NCard,
NSpace,
ClickToCopyField ClickToCopyField
}, },
computed: { computed: {
@ -33,4 +35,22 @@ export default {
} }
} }
}; };
</script> </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> <template>
<div class="p-5"> <div class="p-5">
<div class="space-y-5"> <AccountProfile />
<AccountProfile />
<AccountReferral />
<AccountPartner />
</div>
</div> </div>
</template> </template>
<script setup> <script setup>
import AccountProfile from './AccountProfile.vue'; import AccountProfile from './AccountProfile.vue';
import AccountReferral from './AccountReferral.vue';
import AccountPartner from './AccountPartner.vue';
document.title = '设置 - 个人资料'; document.title = '设置 - 个人资料';
</script> </script>

View File

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

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