diff --git a/apps/web-antd/src/adapter/form.ts b/apps/web-antd/src/adapter/form.ts index 831c2b32..0354edfd 100644 --- a/apps/web-antd/src/adapter/form.ts +++ b/apps/web-antd/src/adapter/form.ts @@ -40,6 +40,20 @@ async function initSetupVbenForm() { } return true; }, + /** + * 大陆手机号:必填 + 11 位且号段 13-19(与后端 UtilsService::checkPhone 一致) + * 表单字段 rules: 'mobile' 即可,无需再叠 required + */ + mobile: (value, _params, ctx) => { + const phone = typeof value === 'string' ? value.trim() : value; + if (phone === undefined || phone === null || phone === '') { + return $t('ui.formRules.required', [ctx.label]); + } + if (!/^1[3456789]\d{9}$/.test(String(phone))) { + return $t('ui.formRules.mobile', [ctx.label]); + } + return true; + }, }, }); } diff --git a/apps/web-antd/src/components/vip/StoreVipCell.vue b/apps/web-antd/src/components/vip/StoreVipCell.vue index a7ebd325..6aba069b 100644 --- a/apps/web-antd/src/components/vip/StoreVipCell.vue +++ b/apps/web-antd/src/components/vip/StoreVipCell.vue @@ -1,16 +1,22 @@ @@ -56,14 +60,14 @@ const emit = defineEmits<{ .store-vip-cell__name { font-size: 13px; font-weight: 600; - color: var(--foreground, #1f1f1f); + color: hsl(var(--foreground)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .store-vip-cell__sub { font-size: 11px; - color: #8c8c8c; + color: hsl(var(--muted-foreground)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -72,12 +76,4 @@ html.dark .store-vip-cell:hover, .dark .store-vip-cell:hover { background: rgba(106, 205, 187, 0.2); } -html.dark .store-vip-cell__name, -.dark .store-vip-cell__name { - color: #f4f4f5; -} -html.dark .store-vip-cell__sub, -.dark .store-vip-cell__sub { - color: #a1a1aa; -} diff --git a/apps/web-antd/src/components/vip/VipHubModal.vue b/apps/web-antd/src/components/vip/VipHubModal.vue index e19cb069..5c6c9098 100644 --- a/apps/web-antd/src/components/vip/VipHubModal.vue +++ b/apps/web-antd/src/components/vip/VipHubModal.vue @@ -10,6 +10,7 @@ import { useVbenModal } from '@vben/common-ui'; import VipBadgeCombo from '#/components/vip/VipBadgeCombo.vue'; import VipStoreRecordsModal from '#/components/vip/VipStoreRecordsModal.vue'; import VipUpgradeModal from '#/components/vip/VipUpgradeModal.vue'; +import { formatVipExpireAt } from '#/utils/vip'; const gridApi = ref(); const onSuccess = ref void)>(null); @@ -21,6 +22,7 @@ const title = computed( () => `VIP管理 - ${storeName.value || storeId.value || ''}`, ); +/** 当前等级 + 时长类型 */ const currentLevelText = computed(() => { const v = vip.value; if (!v) return '当前:普通会员'; @@ -32,6 +34,20 @@ const currentLevelText = computed(() => { return duration ? `当前:${level} · ${duration}` : `当前:${level}`; }); +/** 到期时间独立一行,打开弹窗时一眼可见 */ +const currentExpireText = computed(() => { + const v = vip.value; + if (!v) return ''; + const code = String(v.level_code || 'V0').trim() || 'V0'; + if (code === 'V0') return ''; + const expire = formatVipExpireAt(v, { + lifetimeText: '终身', + withPrefix: false, + }); + if (!expire) return ''; + return expire === '终身' ? '到期时间:终身' : `到期时间:${expire}`; +}); + const [UpgradeModalComp, upgradeModalApi] = useVbenModal({ connectedComponent: VipUpgradeModal, }); @@ -107,7 +123,12 @@ function openRecords() { :level-name="vip?.level_name" :duration-label="vip?.duration_label" /> -
{{ currentLevelText }}
+
+
{{ currentLevelText }}
+
+ {{ currentExpireText }} +
+