修复定价错误

This commit is contained in:
yml2213
2026-05-23 22:31:27 +08:00
parent 78257097f1
commit e082921fa6
6 changed files with 273 additions and 78 deletions
@@ -493,6 +493,9 @@ func publicListings(items []ListingDTO) []ListingDTO {
func applyPublicListingURLs(item *ListingDTO) { func applyPublicListingURLs(item *ListingDTO) {
item.ScreenshotURLS = publicScreenshotURLs(item.ID, item.ScreenshotURLS, item.Status, item.ReviewStatus) item.ScreenshotURLS = publicScreenshotURLs(item.ID, item.ScreenshotURLS, item.Status, item.ReviewStatus)
if item.AssetSummary != nil {
delete(item.AssetSummary, "price_breakdown")
}
} }
func (row listingRow) toDTO() ListingDTO { func (row listingRow) toDTO() ListingDTO {
+20 -3
View File
@@ -126,8 +126,21 @@ export const emptyListingPublishOptions: ListingPublishOptions = {
} }
export const emptyListingSalePriceConfig: PublishSalePriceConfig = { export const emptyListingSalePriceConfig: PublishSalePriceConfig = {
fixed_markup_rules: [], fixed_markup_rules: [
ratio_adjustment_rules: [], { min_m: 10, max_m: 30, markup_amount: 28 },
{ min_m: 30, max_m: 50, markup_amount: 31 },
{ min_m: 50, max_m: 70, markup_amount: 34 },
{ min_m: 70, max_m: 90, markup_amount: 38 },
],
ratio_adjustment_rules: [
{ min_m: 90, max_m: 150, ratio_subtract: 5 },
{ min_m: 150, max_m: 230, ratio_subtract: 4 },
{ min_m: 230, max_m: 310, ratio_subtract: 3.5 },
{ min_m: 310, max_m: 390, ratio_subtract: 3 },
{ min_m: 390, max_m: 470, ratio_subtract: 0 },
{ min_m: 470, max_m: 550, ratio_subtract: 0 },
{ min_m: 550, max_m: 0, ratio_subtract: 0 },
],
} }
export async function fetchListingPublishOptions() { export async function fetchListingPublishOptions() {
@@ -240,7 +253,7 @@ function normalizeRatioConfig(value?: unknown): PublishRatioConfig {
function normalizeSalePriceConfig(value?: unknown): PublishSalePriceConfig { function normalizeSalePriceConfig(value?: unknown): PublishSalePriceConfig {
const row = isRecord(value) ? value : {} const row = isRecord(value) ? value : {}
return { const config = {
fixed_markup_rules: normalizeSaleFixedMarkupRules( fixed_markup_rules: normalizeSaleFixedMarkupRules(
Array.isArray(row.fixed_markup_rules) ? row.fixed_markup_rules : [], Array.isArray(row.fixed_markup_rules) ? row.fixed_markup_rules : [],
), ),
@@ -248,6 +261,10 @@ function normalizeSalePriceConfig(value?: unknown): PublishSalePriceConfig {
Array.isArray(row.ratio_adjustment_rules) ? row.ratio_adjustment_rules : [], Array.isArray(row.ratio_adjustment_rules) ? row.ratio_adjustment_rules : [],
), ),
} }
if (config.fixed_markup_rules.length === 0 && config.ratio_adjustment_rules.length === 0) {
return JSON.parse(JSON.stringify(emptyListingSalePriceConfig)) as PublishSalePriceConfig
}
return config
} }
function normalizeInsuranceBaseRatios(values: unknown[]): PublishInsuranceBaseRatio[] { function normalizeInsuranceBaseRatios(values: unknown[]): PublishInsuranceBaseRatio[] {
@@ -463,7 +463,7 @@ function readError(error: unknown, fallback: string) {
<div> <div>
<p class="eyebrow">Sale Pricing</p> <p class="eyebrow">Sale Pricing</p>
<h2>内部出售定价规则</h2> <h2>内部出售定价规则</h2>
<span>管理出售加价和比例扣减规则仅用于平台内部定价计算不在发布页展示</span> <span>管理出售专用比例计算规则仅用于平台内部定价计算不在发布页展示</span>
</div> </div>
<el-button type="primary" @click="openEdit(salePriceConfig)">编辑出售规则</el-button> <el-button type="primary" @click="openEdit(salePriceConfig)">编辑出售规则</el-button>
</div> </div>
@@ -474,7 +474,7 @@ function readError(error: unknown, fallback: string) {
</div> </div>
<div class="publish-stat"> <div class="publish-stat">
<strong>{{ salePriceStats.ratioCount }}</strong> <strong>{{ salePriceStats.ratioCount }}</strong>
<span>比例扣减</span> <span>比例修正</span>
</div> </div>
<div class="publish-stat"> <div class="publish-stat">
<strong>内部</strong> <strong>内部</strong>
@@ -843,8 +843,8 @@ function readError(error: unknown, fallback: string) {
</el-table> </el-table>
<div class="editor-block-title subtle-title"> <div class="editor-block-title subtle-title">
<span>90M 以上比例调整出售比例 = 回收比例 - 比例扣减最大 M 0 表示无上限</span> <span>90M 以上比例修正出售比例 = 回收比例 - 比例修正最大 M 0 表示无上限</span>
<el-button size="small" @click="addSaleRatioAdjustmentRule">添加比例调整</el-button> <el-button size="small" @click="addSaleRatioAdjustmentRule">添加比例修正</el-button>
</div> </div>
<el-table :data="salePriceConfigDraft.ratio_adjustment_rules" size="small" border> <el-table :data="salePriceConfigDraft.ratio_adjustment_rules" size="small" border>
<el-table-column label="最小 M" min-width="120"> <el-table-column label="最小 M" min-width="120">
@@ -853,7 +853,7 @@ function readError(error: unknown, fallback: string) {
<el-table-column label="最大 M" min-width="120"> <el-table-column label="最大 M" min-width="120">
<template #default="{ row }"><el-input-number v-model="row.max_m" :min="0" :step="10" /></template> <template #default="{ row }"><el-input-number v-model="row.max_m" :min="0" :step="10" /></template>
</el-table-column> </el-table-column>
<el-table-column label="比例扣减" min-width="130"> <el-table-column label="比例修正" min-width="130">
<template #default="{ row }"><el-input-number v-model="row.ratio_subtract" :min="0" :step="0.5" /></template> <template #default="{ row }"><el-input-number v-model="row.ratio_subtract" :min="0" :step="0.5" /></template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="90"> <el-table-column label="操作" width="90">
@@ -498,6 +498,104 @@
font-weight: 800; font-weight: 800;
} }
.ratio-panel {
display: grid;
gap: 10px;
margin-bottom: 8px;
}
.ratio-reference {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
min-height: 48px;
padding: 10px 12px;
border-radius: 8px;
background: #f8fafc;
}
.ratio-reference span {
color: #30343a;
font-size: 13px;
font-weight: 700;
}
.ratio-reference strong {
color: #ff6a00;
font-size: 18px;
font-weight: 900;
line-height: 1;
}
.ratio-range {
margin: 0;
color: #858c96;
font-size: 11px;
font-weight: 600;
line-height: 1.5;
}
.ratio-mode-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
}
.ratio-mode-btn {
display: grid;
gap: 5px;
min-width: 0;
min-height: 74px;
padding: 12px 8px;
border: 1px solid #d7dce3;
border-radius: 8px;
background: #fff;
color: #30343a;
text-align: center;
}
.ratio-mode-btn strong,
.ratio-mode-btn span {
min-width: 0;
overflow-wrap: anywhere;
}
.ratio-mode-btn strong {
font-size: 14px;
font-weight: 800;
}
.ratio-mode-btn span {
color: #858c96;
font-size: 12px;
font-weight: 700;
line-height: 1.25;
}
.ratio-mode-btn.active {
border-color: #ff6a00;
background: #fff7f0;
color: #ff6a00;
}
.ratio-mode-btn.active span {
color: #ff6a00;
}
.ratio-mode-btn:disabled {
cursor: not-allowed;
opacity: 0.58;
}
.ratio-input-field {
margin-bottom: 0;
}
.ratio-input-hint {
margin: -2px 0 0;
}
.total-price-field :deep(.van-field__control) { .total-price-field :deep(.van-field__control) {
font-size: 16px; font-size: 16px;
} }
@@ -125,7 +125,6 @@ const fireLevelMin = computed(() => publishOptions.value.fire_level_min || 38);
const fireLevelPlaceholder = computed( const fireLevelPlaceholder = computed(
() => `等级低于${fireLevelMin.value}级的号无法发布` () => `等级低于${fireLevelMin.value}级的号无法发布`
); );
const ratioConfig = computed(() => publishOptions.value.ratio_config);
const skinGroups = computed(() => publishOptions.value.skin_groups); const skinGroups = computed(() => publishOptions.value.skin_groups);
const quantityItems = computed(() => publishOptions.value.quantity_items); const quantityItems = computed(() => publishOptions.value.quantity_items);
const screenshotSlots = computed(() => publishOptions.value.screenshot_slots); const screenshotSlots = computed(() => publishOptions.value.screenshot_slots);
@@ -140,16 +139,14 @@ const dailyLossMAmount = computed(() => Number(form.daily_loss_m || 10));
const dailyLossRatioAdjustment = computed(() => const dailyLossRatioAdjustment = computed(() =>
Math.min(Math.max(Math.floor((dailyLossMAmount.value - 10) / 10), 0), 4) Math.min(Math.max(Math.floor((dailyLossMAmount.value - 10) / 10), 0), 4)
); );
const calculatedRecycleRatio = computed(() => calculatePublishRatio()); const calculatedSellerReferenceRatio = computed(() => calculateSellerReferenceRatio());
const calculatedSalePricing = computed(() => calculateSalePricing()); const calculatedDefaultSaleRatio = computed(() => calculatedSellerReferenceRatio.value);
const calculatedDefaultSaleRatio = computed(() => calculatedSalePricing.value.saleRatio);
const maxAcceleratedSaleRatio = computed(() => const maxAcceleratedSaleRatio = computed(() =>
calculatedDefaultSaleRatio.value > 0 ? roundRatio(calculatedDefaultSaleRatio.value + 10) : 0 calculatedDefaultSaleRatio.value > 0 ? roundRatio(calculatedDefaultSaleRatio.value + 10) : 0
); );
const calculatedRatio = computed(() => readFinalSaleRatio()); const calculatedRatio = computed(() => readFinalSaleRatio());
const calculatedCoinBasePrice = computed(() => { const calculatedCoinBasePrice = computed(() => {
if (calculatedRatio.value <= 0) return 0; if (calculatedRatio.value <= 0) return 0;
if (!hasAcceleratedSaleRatioInput()) return calculatedSalePricing.value.coinBasePrice;
return roundMoney(coinWanAmount.value / calculatedRatio.value); return roundMoney(coinWanAmount.value / calculatedRatio.value);
}); });
const acceleratedSaleRatioPlaceholder = computed(() => { const acceleratedSaleRatioPlaceholder = computed(() => {
@@ -157,14 +154,23 @@ const acceleratedSaleRatioPlaceholder = computed(() => {
return `默认 ${formatNumber(calculatedDefaultSaleRatio.value)},最高 ${formatNumber(maxAcceleratedSaleRatio.value)}`; return `默认 ${formatNumber(calculatedDefaultSaleRatio.value)},最高 ${formatNumber(maxAcceleratedSaleRatio.value)}`;
}); });
const calculatedConsumablePrice = computed(() => calculateConsumablePrice()); const calculatedConsumablePrice = computed(() => calculateConsumablePrice());
const calculatedFinalPrice = computed(() => const calculatedSellerPrice = computed(() =>
calculatedRatio.value > 0 calculatedRatio.value > 0
? roundMoney(calculatedCoinBasePrice.value + calculatedConsumablePrice.value) ? roundMoney(calculatedCoinBasePrice.value + calculatedConsumablePrice.value)
: 0 : 0
); );
const calculatedPlatformPricing = computed(() => calculatePlatformPricing());
const calculatedFinalPrice = computed(() => calculatedPlatformPricing.value.buyerTotalPrice);
const calculatedRatioText = computed(() => const calculatedRatioText = computed(() =>
calculatedRatio.value > 0 ? `1:${formatNumber(calculatedRatio.value)}` : "" calculatedRatio.value > 0 ? `1:${formatNumber(calculatedRatio.value)}` : ""
); );
const calculatedDefaultSaleRatioText = computed(() =>
calculatedDefaultSaleRatio.value > 0 ? `1:${formatNumber(calculatedDefaultSaleRatio.value)}` : "--"
);
const saleRatioRangeText = computed(() => {
if (calculatedDefaultSaleRatio.value <= 0) return "完成基础信息后自动计算参考比例";
return `可设置 1:${formatNumber(calculatedDefaultSaleRatio.value)} ~ 1:${formatNumber(maxAcceleratedSaleRatio.value)}`;
});
onMounted(() => { onMounted(() => {
restoreDraft(); restoreDraft();
@@ -403,6 +409,15 @@ function clampAcceleratedSaleRatioInput() {
form.accelerated_sale_ratio = roundRatio(Math.min(Math.max(ratio, minRatio), maxRatio)); form.accelerated_sale_ratio = roundRatio(Math.min(Math.max(ratio, minRatio), maxRatio));
} }
function useReferenceSaleRatio() {
form.accelerated_sale_ratio = "";
}
function useMaxAcceleratedSaleRatio() {
if (maxAcceleratedSaleRatio.value <= 0) return;
form.accelerated_sale_ratio = maxAcceleratedSaleRatio.value;
}
async function handleSubmit() { async function handleSubmit() {
const error = validateForm(); const error = validateForm();
if (error) { if (error) {
@@ -505,19 +520,20 @@ function buildAssetSummary() {
secret_kd: form.secret_kd, secret_kd: form.secret_kd,
fire_level: Number(form.fire_level), fire_level: Number(form.fire_level),
daily_loss_m: dailyLossMAmount.value, daily_loss_m: dailyLossMAmount.value,
publish_ratio: calculatedRatio.value, publish_ratio: calculatedPlatformPricing.value.buyerRatio,
price_breakdown: { price_breakdown: {
recycle_coin_base_price: calculatedSalePricing.value.recycleCoinBasePrice, seller_reference_ratio: calculatedSellerReferenceRatio.value,
default_coin_base_price: calculatedSalePricing.value.coinBasePrice, seller_ratio: calculatedRatio.value,
coin_base_price: calculatedCoinBasePrice.value, seller_coin_base_price: calculatedCoinBasePrice.value,
seller_total_price: calculatedSellerPrice.value,
consumable_price: calculatedConsumablePrice.value, consumable_price: calculatedConsumablePrice.value,
final_price: calculatedFinalPrice.value,
recycle_ratio: calculatedRecycleRatio.value,
daily_loss_ratio_adjustment: dailyLossRatioAdjustment.value, daily_loss_ratio_adjustment: dailyLossRatioAdjustment.value,
default_sale_ratio: calculatedDefaultSaleRatio.value,
sale_ratio: calculatedRatio.value,
accelerated_sale_ratio: hasAcceleratedSaleRatioInput() ? Number(form.accelerated_sale_ratio) : calculatedDefaultSaleRatio.value, accelerated_sale_ratio: hasAcceleratedSaleRatioInput() ? Number(form.accelerated_sale_ratio) : calculatedDefaultSaleRatio.value,
sale_rule_type: calculatedSalePricing.value.ruleType, buyer_coin_base_price: calculatedPlatformPricing.value.buyerCoinBasePrice,
buyer_total_price: calculatedFinalPrice.value,
buyer_ratio: calculatedPlatformPricing.value.buyerRatio,
platform_markup_amount: calculatedPlatformPricing.value.platformMarkupAmount,
platform_rule_type: calculatedPlatformPricing.value.ruleType,
}, },
season_insurance: form.season_insurance, season_insurance: form.season_insurance,
stamina_level: form.stamina_level, stamina_level: form.stamina_level,
@@ -571,7 +587,7 @@ function readUnitPrice(priceText: string) {
return singleMatch ? Number(singleMatch[1]) : 0; return singleMatch ? Number(singleMatch[1]) : 0;
} }
function calculatePublishRatio() { function calculateSellerReferenceRatio() {
if ( if (
coinMAmount.value <= 0 || coinMAmount.value <= 0 ||
!form.season_insurance || !form.season_insurance ||
@@ -580,13 +596,13 @@ function calculatePublishRatio() {
) { ) {
return 0; return 0;
} }
const baseRatio = getInsuranceBaseRatio(form.season_insurance); const baseRatio = getInsuranceBaseRatio(publishOptions.value.ratio_config, form.season_insurance);
if (baseRatio <= 0) return 0; if (baseRatio <= 0) return 0;
return ( return (
baseRatio + baseRatio +
calculateConfigPenalty() + calculateConfigPenalty(publishOptions.value.ratio_config) +
getCoinCorrection(coinMAmount.value) + getCoinCorrection(publishOptions.value.ratio_config, coinMAmount.value) +
dailyLossRatioAdjustment.value dailyLossRatioAdjustment.value
); );
} }
@@ -604,42 +620,44 @@ function hasAcceleratedSaleRatioInput() {
return form.accelerated_sale_ratio !== "" && form.accelerated_sale_ratio !== null; return form.accelerated_sale_ratio !== "" && form.accelerated_sale_ratio !== null;
} }
function calculateSalePricing() { function calculatePlatformPricing() {
const recycleRatio = calculatedRecycleRatio.value; if (calculatedRatio.value <= 0 || calculatedCoinBasePrice.value <= 0) {
if (recycleRatio <= 0 || coinWanAmount.value <= 0) { return emptyPlatformPricing();
return emptySalePricing();
} }
const recycleCoinBasePrice = roundMoney(coinWanAmount.value / recycleRatio);
const fixedRule = findSaleFixedMarkupRule(); const fixedRule = findSaleFixedMarkupRule();
if (fixedRule) { if (fixedRule) {
const coinBasePrice = roundMoney(recycleCoinBasePrice + Number(fixedRule.markup_amount || 0)); return buildPlatformPricing(
return { roundMoney(calculatedCoinBasePrice.value + Number(fixedRule.markup_amount || 0)),
recycleCoinBasePrice, "fixed_markup",
coinBasePrice, );
saleRatio: calculateEffectiveRatio(coinBasePrice),
ruleType: "fixed_markup",
};
} }
const ratioRule = findSaleRatioAdjustmentRule(); const ratioRule = findSaleRatioAdjustmentRule();
const ratioSubtract = ratioRule ? Number(ratioRule.ratio_subtract || 0) : 0; const ratioSubtract = ratioRule ? Number(ratioRule.ratio_subtract || 0) : 0;
const saleRatio = recycleRatio - ratioSubtract; const buyerRatio = calculatedRatio.value - ratioSubtract;
if (saleRatio <= 0) { if (buyerRatio > 0 && ratioRule) {
return emptySalePricing(recycleCoinBasePrice); return buildPlatformPricing(roundMoney(coinWanAmount.value / buyerRatio), "ratio_subtract");
} }
return buildPlatformPricing(calculatedCoinBasePrice.value, "none");
}
function buildPlatformPricing(buyerCoinBasePrice: number, ruleType: string) {
const buyerTotalPrice = roundMoney(buyerCoinBasePrice + calculatedConsumablePrice.value);
return { return {
recycleCoinBasePrice, buyerCoinBasePrice,
coinBasePrice: roundMoney(coinWanAmount.value / saleRatio), buyerTotalPrice,
saleRatio, buyerRatio: calculateEffectiveRatio(buyerCoinBasePrice),
ruleType: ratioRule ? "ratio_subtract" : "none", platformMarkupAmount: roundMoney(buyerTotalPrice - calculatedSellerPrice.value),
ruleType,
}; };
} }
function emptySalePricing(recycleCoinBasePrice = 0) { function emptyPlatformPricing() {
return { return {
recycleCoinBasePrice, buyerCoinBasePrice: 0,
coinBasePrice: 0, buyerTotalPrice: 0,
saleRatio: 0, buyerRatio: 0,
platformMarkupAmount: 0,
ruleType: "none", ruleType: "none",
}; };
} }
@@ -647,18 +665,27 @@ function emptySalePricing(recycleCoinBasePrice = 0) {
function findSaleFixedMarkupRule() { function findSaleFixedMarkupRule() {
return [...salePriceConfig.value.fixed_markup_rules] return [...salePriceConfig.value.fixed_markup_rules]
.sort((a, b) => a.min_m - b.min_m) .sort((a, b) => a.min_m - b.min_m)
.find((item) => isCoinInSaleRange(item)); .find((item, index, rules) => isCoinInSaleRange(item, index, rules, { includeLastMax: true }));
} }
function findSaleRatioAdjustmentRule() { function findSaleRatioAdjustmentRule() {
return [...salePriceConfig.value.ratio_adjustment_rules] return [...salePriceConfig.value.ratio_adjustment_rules]
.sort((a, b) => a.min_m - b.min_m) .sort((a, b) => a.min_m - b.min_m)
.find((item) => isCoinInSaleRange(item)); .find((item, index, rules) => isCoinInSaleRange(item, index, rules, { excludeFirstMin: true }));
} }
function isCoinInSaleRange(item: { min_m: number; max_m: number }) { function isCoinInSaleRange(
item: { min_m: number; max_m: number },
index: number,
rules: Array<{ min_m: number; max_m: number }>,
options: { includeLastMax?: boolean; excludeFirstMin?: boolean } = {},
) {
const maxM = Number(item.max_m || 0); const maxM = Number(item.max_m || 0);
return coinMAmount.value >= Number(item.min_m || 0) && (maxM <= 0 || coinMAmount.value <= maxM); const minM = Number(item.min_m || 0);
const minMatched = options.excludeFirstMin && index === 0 ? coinMAmount.value > minM : coinMAmount.value >= minM;
const isLastRule = index === rules.length - 1;
const maxMatched = maxM <= 0 || coinMAmount.value < maxM || (options.includeLastMax && isLastRule && coinMAmount.value <= maxM);
return minMatched && maxMatched;
} }
function calculateEffectiveRatio(price: number) { function calculateEffectiveRatio(price: number) {
@@ -666,14 +693,14 @@ function calculateEffectiveRatio(price: number) {
return roundRatio(coinWanAmount.value / price); return roundRatio(coinWanAmount.value / price);
} }
function getInsuranceBaseRatio(insurance: string) { function getInsuranceBaseRatio(config: { insurance_base_ratios: Array<{ insurance: string; ratio: number }> }, insurance: string) {
return ratioConfig.value.insurance_base_ratios.find( return config.insurance_base_ratios.find(
(item) => item.insurance === insurance (item) => item.insurance === insurance
)?.ratio || 0; )?.ratio || 0;
} }
function calculateConfigPenalty() { function calculateConfigPenalty(config: { config_items: Array<{ kind: string; group_key?: string; missing_penalty: number }> }) {
return ratioConfig.value.config_items.reduce((sum, item) => { return config.config_items.reduce((sum, item) => {
return isRatioConfigItemMatched(item) ? sum : sum + Number(item.missing_penalty || 0); return isRatioConfigItemMatched(item) ? sum : sum + Number(item.missing_penalty || 0);
}, 0); }, 0);
} }
@@ -703,8 +730,8 @@ function readLevelNumber(value: string) {
return match ? Number(match[0]) : 0; return match ? Number(match[0]) : 0;
} }
function getCoinCorrection(coinM: number) { function getCoinCorrection(config: { coin_corrections: Array<{ threshold_m: number; correction: number }> }, coinM: number) {
return [...ratioConfig.value.coin_corrections] return [...config.coin_corrections]
.sort((a, b) => b.threshold_m - a.threshold_m) .sort((a, b) => b.threshold_m - a.threshold_m)
.find((item) => coinM > item.threshold_m)?.correction || 0; .find((item) => coinM > item.threshold_m)?.correction || 0;
} }
@@ -1092,28 +1119,69 @@ function readError(error: unknown, fallback: string) {
<p class="field-hint"> <p class="field-hint">
比例调整在默认10M/每增加10M出租比例相应+1请根据实际需求合理设置感谢您的配合 比例调整在默认10M/每增加10M出租比例相应+1请根据实际需求合理设置感谢您的配合
</p> </p>
<div class="ratio-panel">
<div class="ratio-reference">
<span>参考比例</span>
<strong>{{ calculatedDefaultSaleRatioText }}</strong>
</div>
<p class="ratio-range">{{ saleRatioRangeText }}比例越高出租速度越快自行选择</p>
<div class="ratio-mode-grid">
<button
type="button"
class="ratio-mode-btn"
:class="{ active: !hasAcceleratedSaleRatioInput() }"
:disabled="calculatedDefaultSaleRatio <= 0"
@click="useReferenceSaleRatio"
>
<strong>参考比例</strong>
<span>
{{
calculatedDefaultSaleRatio > 0
? `1元=${formatNumber(calculatedDefaultSaleRatio)}`
: "自动计算"
}}
</span>
</button>
<button
type="button"
class="ratio-mode-btn"
:class="{ active: hasAcceleratedSaleRatioInput() }"
:disabled="maxAcceleratedSaleRatio <= 0"
@click="useMaxAcceleratedSaleRatio"
>
<strong>加速比例</strong>
<span>
{{
maxAcceleratedSaleRatio > 0
? `1元=${formatNumber(calculatedDefaultSaleRatio)}~${formatNumber(maxAcceleratedSaleRatio)}`
: "自动计算"
}}
</span>
</button>
</div>
<van-field <van-field
:model-value="form.accelerated_sale_ratio" :model-value="form.accelerated_sale_ratio"
label="加速出售比例" label="自定比例"
type="number" type="number"
:placeholder="acceleratedSaleRatioPlaceholder" :placeholder="acceleratedSaleRatioPlaceholder"
class="publish-field" class="publish-field ratio-input-field"
@update:model-value="handleAcceleratedSaleRatioInput" @update:model-value="handleAcceleratedSaleRatioInput"
@blur="clampAcceleratedSaleRatioInput" @blur="clampAcceleratedSaleRatioInput"
/> />
<p class="field-hint"> <p class="field-hint ratio-input-hint">
请根据实际需求合理设置感谢您的配合 留空使用参考比例填写后范围为参考比例到参考比例+10请根据实际需求合理设置
</p> </p>
</div>
<div class="result-grid"> <div class="result-grid">
<van-field <van-field
:model-value="calculatedRatioText" :model-value="calculatedRatioText"
label="出售比例" label="卖家比例"
readonly readonly
:placeholder="priceConfig.ratio_description" :placeholder="priceConfig.ratio_description"
class="publish-field result-field" class="publish-field result-field"
> >
<template #label> <template #label>
<span class="hint-label" :data-hint="priceConfig.ratio_description" tabindex="0">出售比例</span> <span class="hint-label" :data-hint="priceConfig.ratio_description" tabindex="0">卖家比例</span>
</template> </template>
</van-field> </van-field>
<van-field <van-field
@@ -1135,8 +1203,8 @@ function readError(error: unknown, fallback: string) {
class="publish-field result-field" class="publish-field result-field"
/> />
<van-field <van-field
:model-value="calculatedFinalPrice ? `¥${calculatedFinalPrice}` : ''" :model-value="calculatedSellerPrice ? `¥${calculatedSellerPrice}` : ''"
label="发布总价" label="卖家价格"
readonly readonly
required required
:placeholder="priceConfig.price_placeholder" :placeholder="priceConfig.price_placeholder"
@@ -1144,7 +1212,7 @@ function readError(error: unknown, fallback: string) {
/> />
</div> </div>
<p class="price-breakdown-hint"> <p class="price-breakdown-hint">
发布总价 = 纯币基础价 + 额外消耗品加速比例仅影响纯币基础价 卖家价格 = 纯币基础价 + 额外消耗品加速比例仅影响纯币基础价
</p> </p>
<van-field <van-field
@@ -36,7 +36,16 @@ async function offline(id: number) {
} }
function listingPrice(row: Listing) { function listingPrice(row: Listing) {
return `¥${Number(row.price_daily || row.price_hourly || 0).toFixed(2)}` return `¥${readSellerPrice(row).toFixed(2)}`
}
function readSellerPrice(row: Listing) {
const breakdown = row.asset_summary?.price_breakdown
if (typeof breakdown === 'object' && breakdown !== null) {
const price = Number((breakdown as Record<string, unknown>).seller_total_price)
if (Number.isFinite(price) && price > 0) return price
}
return Number(row.price_daily || row.price_hourly || 0)
} }
</script> </script>