diff --git a/backend/internal/modules/systemconfig/dto.go b/backend/internal/modules/systemconfig/dto.go index fbfb2b89..7107569c 100644 --- a/backend/internal/modules/systemconfig/dto.go +++ b/backend/internal/modules/systemconfig/dto.go @@ -166,6 +166,7 @@ type PublishScreenshotSlot struct { type PublishPriceConfig struct { DepositPlaceholder string `json:"deposit_placeholder"` DepositHint string `json:"deposit_hint"` + DepositTooltip string `json:"deposit_tooltip"` PricePlaceholder string `json:"price_placeholder"` RatioDescription string `json:"ratio_description"` } diff --git a/backend/internal/modules/systemconfig/normalize.go b/backend/internal/modules/systemconfig/normalize.go index a5c63427..5d88fb83 100644 --- a/backend/internal/modules/systemconfig/normalize.go +++ b/backend/internal/modules/systemconfig/normalize.go @@ -231,6 +231,9 @@ func normalizePublishOptions(options *PublishOptionsDTO) { if options.PriceConfig.DepositHint == "" { options.PriceConfig.DepositHint = defaults.PriceConfig.DepositHint } + if options.PriceConfig.DepositTooltip == "" { + options.PriceConfig.DepositTooltip = defaults.PriceConfig.DepositTooltip + } if options.PriceConfig.PricePlaceholder == "" { options.PriceConfig.PricePlaceholder = defaults.PriceConfig.PricePlaceholder } diff --git a/backend/internal/modules/systemconfig/publish_options.go b/backend/internal/modules/systemconfig/publish_options.go index 33e51e2d..cf4a635c 100644 --- a/backend/internal/modules/systemconfig/publish_options.go +++ b/backend/internal/modules/systemconfig/publish_options.go @@ -93,8 +93,30 @@ func DefaultPublishOptions() PublishOptionsDTO { PriceConfig: PublishPriceConfig{ DepositPlaceholder: "温馨提示:本押金用于保障账号安全。若买家在租用期间,使用非纯币类道具/资源(如消耗品、材料等)或导致账号被封禁,相关损失将直接从押金中扣除进行赔付。押金过高会延长出租等待时间(请自行衡量)感谢理解。", DepositHint: "租客缴纳,封号必赔付", - PricePlaceholder: "填写币数、保险、体力和负重后自动计算;发布总价=纯币基础价+额外消耗品", - RatioDescription: "1:xx 表示 1 元人民币(RMB)等价兑换 xx 万哈夫币", + DepositTooltip: `1. 系统推荐押金(基础覆盖) + +系统将根据您填写的出租皮肤、特勤处等级等信息,自动计算并生成一个推荐押金额度。该额度已覆盖上述主要资产的合理市场价值,可作为押金设置的基础参考。 + +2. 系统推荐押金未覆盖项目(需自行评估) + +请注意,系统推荐押金暂不包含以下资产类型,您需要根据实际情况额外评估其价值: + +- 收藏室内的稀有物品 +- 其他具有个人纪念意义或情怀价值的虚拟资产(如限定称号、绝版道具等) + +3. 未出租价值折算规则(重点) + +请在「收藏室及不出租部分哈弗币价值」中填写收藏室等不出租资产折合的哈弗币。系统按 1元=50万 计算参考值,并自动更新推荐押金:低于参考值时覆盖为参考值,否则追加参考值的 50%。 + +示例:基础推荐押金 200 元、收藏室价值 200M → 参考值 400 元,200<400,推荐押金更新为 400 元。 + +目的:该举能够有效覆盖租客可能出现的洗号、违规操作导致账号封禁等风险带来的损失,为您提供充分的风险缓冲。 + +4. 额外保障选项 + +若您在设置足额押金后仍感不放心,可选择购买平台提供的《全额包赔服务》。一旦发生封号损失,将获得账号市场价值的全额赔付,让您的出租过程后顾无忧。`, + PricePlaceholder: "填写币数、保险、体力和负重后自动计算;发布总价=纯币基础价+额外消耗品", + RatioDescription: "1:xx 表示 1 元人民币(RMB)等价兑换 xx 万哈夫币", }, DepositRecommend: PublishDepositRecommend{ BaseAmount: 50, diff --git a/backend/internal/modules/systemconfig/publish_options_test.go b/backend/internal/modules/systemconfig/publish_options_test.go index 08b91757..61484cd5 100644 --- a/backend/internal/modules/systemconfig/publish_options_test.go +++ b/backend/internal/modules/systemconfig/publish_options_test.go @@ -136,6 +136,20 @@ func TestNormalizePublishOptionsValueDefaultsQualityOwnerRatioDiscount(t *testin } } +func TestNormalizePublishOptionsValueDefaultsDepositTooltip(t *testing.T) { + normalized, err := normalizePublishOptionsValue(`{"schema_version":2}`) + if err != nil { + t.Fatalf("规范化失败: %v", err) + } + var options PublishOptionsDTO + if err := json.Unmarshal([]byte(normalized), &options); err != nil { + t.Fatalf("解析规范化结果失败: %v", err) + } + if !strings.Contains(options.PriceConfig.DepositTooltip, "系统推荐押金(基础覆盖)") { + t.Fatalf("押金说明默认文案缺失: %q", options.PriceConfig.DepositTooltip) + } +} + func TestNormalizePublishOptionsValuePreservesExplicitZeroPremiumReviewRatioMarkup(t *testing.T) { options := DefaultPublishOptions() options.RatioConfig.PremiumReviewRatioMarkup = 0 diff --git a/frontend/src/features/admin/components/PublishOptionsDialog.vue b/frontend/src/features/admin/components/PublishOptionsDialog.vue index a38f30d1..c808dd44 100644 --- a/frontend/src/features/admin/components/PublishOptionsDialog.vue +++ b/frontend/src/features/admin/components/PublishOptionsDialog.vue @@ -336,6 +336,14 @@ async function handleSave() { placeholder="例如:租客缴纳,封号必赔付" /> + + + { + it('读取后台配置的押金说明', () => { + const options = mergeListingPublishOptions({ + price_config: { + deposit_placeholder: '', + deposit_hint: '', + deposit_tooltip: '自定义押金说明', + price_placeholder: '', + ratio_description: '', + }, + }) + + expect(options.price_config.deposit_tooltip).toBe('自定义押金说明') + }) +}) diff --git a/frontend/src/features/listings/api/listingOptions.ts b/frontend/src/features/listings/api/listingOptions.ts index f9af749a..b4eeac65 100644 --- a/frontend/src/features/listings/api/listingOptions.ts +++ b/frontend/src/features/listings/api/listingOptions.ts @@ -32,6 +32,7 @@ export interface PublishScreenshotSlot { export interface PublishPriceConfig { deposit_placeholder: string deposit_hint: string + deposit_tooltip: string price_placeholder: string ratio_description: string } @@ -122,6 +123,29 @@ export interface ListingPublishOptions { ratio_config: PublishRatioConfig } +const defaultDepositTooltip = `1. 系统推荐押金(基础覆盖) + +系统将根据您填写的出租皮肤、特勤处等级等信息,自动计算并生成一个推荐押金额度。该额度已覆盖上述主要资产的合理市场价值,可作为押金设置的基础参考。 + +2. 系统推荐押金未覆盖项目(需自行评估) + +请注意,系统推荐押金暂不包含以下资产类型,您需要根据实际情况额外评估其价值: + +- 收藏室内的稀有物品 +- 其他具有个人纪念意义或情怀价值的虚拟资产(如限定称号、绝版道具等) + +3. 未出租价值折算规则(重点) + +请在「收藏室及不出租部分哈弗币价值」中填写收藏室等不出租资产折合的哈弗币。系统按 1元=50万 计算参考值,并自动更新推荐押金:低于参考值时覆盖为参考值,否则追加参考值的 50%。 + +示例:基础推荐押金 200 元、收藏室价值 200M → 参考值 400 元,200<400,推荐押金更新为 400 元。 + +目的:该举能够有效覆盖租客可能出现的洗号、违规操作导致账号封禁等风险带来的损失,为您提供充分的风险缓冲。 + +4. 额外保障选项 + +若您在设置足额押金后仍感不放心,可选择购买平台提供的《全额包赔服务》。一旦发生封号损失,将获得账号市场价值的全额赔付,让您的出租过程后顾无忧。` + export const emptyListingPublishOptions: ListingPublishOptions = { unlock_demulan_enabled: false, next_season_pass_enabled: false, @@ -141,6 +165,7 @@ export const emptyListingPublishOptions: ListingPublishOptions = { price_config: { deposit_placeholder: '', deposit_hint: '租客缴纳,封号必赔付', + deposit_tooltip: defaultDepositTooltip, price_placeholder: '', ratio_description: '', }, @@ -325,6 +350,10 @@ function normalizePriceConfig(value?: unknown): PublishPriceConfig { typeof row.deposit_hint === 'string' && row.deposit_hint.trim() ? row.deposit_hint.trim() : emptyListingPublishOptions.price_config.deposit_hint, + deposit_tooltip: + typeof row.deposit_tooltip === 'string' && row.deposit_tooltip.trim() + ? row.deposit_tooltip.trim() + : emptyListingPublishOptions.price_config.deposit_tooltip, price_placeholder: typeof row.price_placeholder === 'string' ? row.price_placeholder.trim() : '', ratio_description: diff --git a/frontend/src/features/seller/views/MobileSellerListingCreateView.css b/frontend/src/features/seller/views/MobileSellerListingCreateView.css index 3bdece21..f5aea167 100644 --- a/frontend/src/features/seller/views/MobileSellerListingCreateView.css +++ b/frontend/src/features/seller/views/MobileSellerListingCreateView.css @@ -837,6 +837,22 @@ min-width: 0; } +.deposit-heading .deposit-heading-title { + gap: 4px; +} + +.deposit-guide-trigger { + display: inline-grid; + width: 22px; + height: 22px; + padding: 0; + place-items: center; + border: 0; + background: transparent; + color: #f97316; + font-size: 17px; +} + .deposit-heading b { color: #f04452; font-size: 13px; diff --git a/frontend/src/features/seller/views/MobileSellerListingCreateView.vue b/frontend/src/features/seller/views/MobileSellerListingCreateView.vue index c7ed494b..0b19d18c 100644 --- a/frontend/src/features/seller/views/MobileSellerListingCreateView.vue +++ b/frontend/src/features/seller/views/MobileSellerListingCreateView.vue @@ -129,6 +129,16 @@ function openPublishAgreement(agreement: AgreementContent) { agreementPopupVisible.value = true } +function showDepositGuide() { + if (!priceConfig.value.deposit_tooltip) return + void showDialog({ + title: '押金说明', + message: priceConfig.value.deposit_tooltip, + messageAlign: 'left', + confirmButtonText: '我知道了', + }) +} + function selectRadio(value: T, setter: (value: T) => void) { setter(value) } @@ -689,7 +699,18 @@ const screenshotGuides: Record = { 押金与价格 - 押金* + + 押金* + + + + {{ priceConfig.deposit_hint }} diff --git a/frontend/src/features/seller/views/SellerListingCreateView.css b/frontend/src/features/seller/views/SellerListingCreateView.css index fa6275fe..14c329b3 100644 --- a/frontend/src/features/seller/views/SellerListingCreateView.css +++ b/frontend/src/features/seller/views/SellerListingCreateView.css @@ -164,6 +164,44 @@ align-items: center; } +.deposit-label-title { + display: inline-flex; + align-items: center; + gap: 3px; +} + +.deposit-guide-trigger { + display: inline-grid; + width: 18px; + height: 18px; + padding: 0; + place-items: center; + border: 0; + background: transparent; + color: var(--color-orange-primary); + cursor: help; +} + +.deposit-guide-trigger:hover, +.deposit-guide-trigger:focus-visible { + color: var(--color-orange-dark); + outline: none; +} + +.deposit-guide-content { + max-height: min(62vh, 560px); + overflow-y: auto; + color: #3f4753; + font-size: 13px; + line-height: 1.75; + white-space: pre-wrap; +} + +:global(.deposit-guide-popper.el-popover) { + max-width: min(560px, calc(100vw - 32px)); + padding: 14px 16px; +} + .deposit-label em { display: inline-flex; align-items: center; diff --git a/frontend/src/features/seller/views/SellerListingCreateView.vue b/frontend/src/features/seller/views/SellerListingCreateView.vue index 4b23680b..03cf99c9 100644 --- a/frontend/src/features/seller/views/SellerListingCreateView.vue +++ b/frontend/src/features/seller/views/SellerListingCreateView.vue @@ -3,6 +3,7 @@ import { ArrowRight, Delete, DocumentChecked, + InfoFilled, Picture, RefreshRight, UploadFilled, @@ -544,10 +545,26 @@ function selectDailyLoss(value: string | number) { - 押金*{{ priceConfig.deposit_hint }} + + + 押金* + + + + + + + {{ priceConfig.deposit_tooltip }} + + + {{ priceConfig.deposit_hint }} +