feat(quality-owner): 增加优质号主、极品专区与无忧租号保险

- 支持保证金余额支付、在线支付开通及后台手动设置审计
- 新增极品专区发布权限、赔付方案与保险结算费率快照
- 优化首页、商品卡和个人中心的优质号主身份展示
This commit is contained in:
yml2213
2026-11-04 15:04:35 +08:00
parent 812e95e7d1
commit 98174c0e33
90 changed files with 3478 additions and 64 deletions
@@ -18,6 +18,18 @@ import { uploadFile } from '@/shared/api/files'
import { formatCent } from '@/shared/utils/money'
import { setPassword } from '@/features/auth/api/auth'
import { fetchUserInvitationSummary } from '@/features/invitation/api/invitation'
import {
activateQualityOwner,
fetchQualityOwnerStatus,
queryQualityOwnerPayment,
startQualityOwnerPayment,
type QualityOwnerStatus,
} from '@/features/quality-owner/api/qualityOwner'
import type { PaymentPayWay } from '@/features/orders/api/orders'
import { useMobilePayWaySelect } from '@/features/orders/composables/useMobilePayWaySelect'
import { useMobilePaymentCashier } from '@/features/orders/composables/useMobilePaymentCashier'
import MobilePayWaySelectPopup from '@/features/orders/components/MobilePayWaySelectPopup.vue'
import MobilePaymentCashierPopup from '@/features/orders/components/MobilePaymentCashierPopup.vue'
import { useDistributionSiteStore } from '@/stores/distributionSite'
@@ -33,6 +45,22 @@ const { unreadCount: notificationUnreadCount, unreadLabel: notificationUnreadLab
/** 数据项 */
const availableBalanceCent = ref(0)
const qualityOwnerStatus = ref<QualityOwnerStatus | null>(null)
const qualityOwnerLoading = ref(false)
const qualityOwnerOnlineLoading = ref(false)
const qualityOwnerPayWaySelect = useMobilePayWaySelect()
const qualityOwnerCashier = useMobilePaymentCashier({
queryPayment: () => queryQualityOwnerPayment(),
paidMessage: '保证金支付成功,优质号主已开通',
})
const {
paymentPopupVisible: qualityOwnerPaymentPopupVisible,
activePayment: activeQualityOwnerPayment,
payURL: qualityOwnerPaymentPayURL,
paymentQRCodeURL: qualityOwnerPaymentQRCodeURL,
qrGenerating: qualityOwnerPaymentQRGenerating,
checkingPayment: qualityOwnerPaymentChecking,
} = qualityOwnerCashier
const showSettings = ref(false)
const showProfileEditor = ref(false)
const savingProfile = ref(false)
@@ -121,10 +149,72 @@ onMounted(async () => {
// 保留本地会话展示,避免个人中心因网络短暂异常整页不可用。
}
void loadBalance()
void loadQualityOwnerStatus()
void loadInviteRate()
}
})
async function loadQualityOwnerStatus() {
try {
qualityOwnerStatus.value = await fetchQualityOwnerStatus()
} catch {
qualityOwnerStatus.value = null
}
}
async function handleActivateQualityOwner() {
const status = qualityOwnerStatus.value
if (!status || status.active) return
try {
await showDialog({
title: '开通优质号主',
message: `确认从可用余额中扣除 ¥${formatCent(
status.required_deposit_cent
)} 作为保证金?取消资格时会退回余额。`,
showCancelButton: true,
confirmButtonText: '确认缴纳',
cancelButtonText: '取消',
})
} catch {
return
}
qualityOwnerLoading.value = true
try {
qualityOwnerStatus.value = await activateQualityOwner()
await session.loadMe()
await loadBalance()
showToast({ message: '优质号主已开通', icon: 'passed' })
} catch (error) {
showToast({ message: readError(error, '开通失败,请确认余额充足'), icon: 'cross' })
} finally {
qualityOwnerLoading.value = false
}
}
async function handleOnlineQualityOwnerPayment() {
const payWay = await qualityOwnerPayWaySelect.select()
if (!payWay) return
qualityOwnerOnlineLoading.value = true
try {
const payment = await startQualityOwnerPayment(payWay as PaymentPayWay)
if (payment.paid) {
await handleQualityOwnerPaid()
return
}
await qualityOwnerCashier.openMobilePaymentCashier(payment, handleQualityOwnerPaid)
} catch (error) {
showToast({ message: readError(error, '发起在线支付失败'), icon: 'cross' })
} finally {
qualityOwnerOnlineLoading.value = false
}
}
async function handleQualityOwnerPaid() {
qualityOwnerStatus.value = await fetchQualityOwnerStatus()
await session.loadMe()
await loadBalance()
}
async function loadBalance() {
try {
const wallet = await fetchWalletBalance()
@@ -366,6 +456,9 @@ function resolveAvatarURL(url: string | undefined | null) {
>
未实名
</van-tag>
<van-tag v-if="qualityOwnerStatus?.active" round class="owner-quality-tag">
<van-icon name="award-o" /> 优质号主
</van-tag>
</div>
<p class="user-meta">ID: {{ displayId }} · {{ maskedPhone }}</p>
</div>
@@ -393,6 +486,65 @@ function resolveAvatarURL(url: string | undefined | null) {
</div>
</section>
<section class="quality-owner-card" :class="{ 'is-active': qualityOwnerStatus?.active }">
<div class="quality-owner-heading">
<div>
<span>优质号主</span>
<strong>{{ qualityOwnerStatus?.active ? '已开通' : '解锁极品专区发布权限' }}</strong>
</div>
<van-tag :type="qualityOwnerStatus?.active ? 'success' : 'default'" round>
{{ qualityOwnerStatus?.active ? '优质号主' : '未开通' }}
</van-tag>
</div>
<div class="quality-owner-info">
<div>
<span>保证金</span>
<strong>¥{{ formatCent(qualityOwnerStatus?.required_deposit_cent || 20000) }}</strong>
</div>
<div>
<span>极品专区保险费率</span>
<strong
>{{
((qualityOwnerStatus?.premium_insurance_fee_bps || 400) / 100).toFixed(1)
}}%</strong
>
</div>
</div>
<div v-if="!qualityOwnerStatus?.active" class="quality-owner-actions">
<van-button
block
round
type="primary"
color="#7c3aed"
:loading="qualityOwnerLoading"
:disabled="
(qualityOwnerStatus?.available_balance_cent || 0) <
(qualityOwnerStatus?.required_deposit_cent || 20000)
"
@click="handleActivateQualityOwner"
>
余额支付开通
</van-button>
<van-button
block
round
plain
type="primary"
:loading="qualityOwnerOnlineLoading"
@click="handleOnlineQualityOwnerPayment"
>
微信 / 支付宝支付开通
</van-button>
</div>
<p v-else>
{{
qualityOwnerStatus?.deposit_source === 'manual'
? '已由后台开通优质号主。'
: '保证金已冻结,取消优质号主资格时退回可用余额。'
}}
</p>
</section>
<!-- 余额卡片 - 银行卡质感暗色卡片 -->
<div class="balance-card">
<div class="balance-row">
@@ -496,6 +648,22 @@ function resolveAvatarURL(url: string | undefined | null) {
</div>
</template>
<MobilePayWaySelectPopup
v-model:show="qualityOwnerPayWaySelect.visible.value"
@choose="qualityOwnerPayWaySelect.choose"
@closed="qualityOwnerPayWaySelect.handleClosed"
/>
<MobilePaymentCashierPopup
v-model:show="qualityOwnerPaymentPopupVisible"
:payment="activeQualityOwnerPayment"
:pay-url="qualityOwnerPaymentPayURL"
:qr-code-url="qualityOwnerPaymentQRCodeURL"
:qr-generating="qualityOwnerPaymentQRGenerating"
:checking="qualityOwnerPaymentChecking"
@refresh="qualityOwnerCashier.refreshPaymentStatus(false)"
/>
<!-- 账单明细 Popup -->
<van-popup
v-model:show="showLedgers"