内部加价分离, 不展示
This commit is contained in:
@@ -5,10 +5,13 @@ import { showDialog, showToast } from "vant";
|
||||
|
||||
import { fetchFileBlobByURL, uploadFile } from "@/api/files";
|
||||
import {
|
||||
emptyListingSalePriceConfig,
|
||||
emptyListingPublishOptions,
|
||||
fetchListingPublishOptions,
|
||||
fetchListingSalePriceConfig,
|
||||
type ChargeMode,
|
||||
type ListingPublishOptions,
|
||||
type PublishSalePriceConfig,
|
||||
type QuantityKey,
|
||||
type ScreenshotKey,
|
||||
} from "@/api/listingOptions";
|
||||
@@ -21,6 +24,8 @@ type PublishForm = {
|
||||
rank_level: string;
|
||||
secret_kd: string;
|
||||
fire_level: number | "";
|
||||
daily_loss_m: number | "";
|
||||
accelerated_sale_ratio: number | "";
|
||||
season_insurance: string;
|
||||
stamina_level: string;
|
||||
load_level: string;
|
||||
@@ -42,12 +47,14 @@ interface PublishDraft {
|
||||
}
|
||||
|
||||
const draftKey = "hfb.mobile.publish.draft";
|
||||
const dailyLossOptions = [10, 20, 30, 40, 50];
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const loading = ref(false);
|
||||
const uploading = ref(false);
|
||||
const suppressDraftSave = ref(false);
|
||||
const publishOptions = ref<ListingPublishOptions>(emptyListingPublishOptions);
|
||||
const salePriceConfig = ref<PublishSalePriceConfig>(emptyListingSalePriceConfig);
|
||||
|
||||
function isNavActive(path: string) {
|
||||
if (path === "/m") return route.path === "/m";
|
||||
@@ -62,6 +69,8 @@ function defaultForm(): PublishForm {
|
||||
rank_level: "",
|
||||
secret_kd: "",
|
||||
fire_level: "",
|
||||
daily_loss_m: 10,
|
||||
accelerated_sale_ratio: "",
|
||||
season_insurance: "",
|
||||
stamina_level: "",
|
||||
load_level: "",
|
||||
@@ -117,7 +126,6 @@ const fireLevelPlaceholder = computed(
|
||||
() => `等级低于${fireLevelMin.value}级的号无法发布`
|
||||
);
|
||||
const ratioConfig = computed(() => publishOptions.value.ratio_config);
|
||||
const salePriceConfig = computed(() => publishOptions.value.sale_price_config);
|
||||
const skinGroups = computed(() => publishOptions.value.skin_groups);
|
||||
const quantityItems = computed(() => publishOptions.value.quantity_items);
|
||||
const screenshotSlots = computed(() => publishOptions.value.screenshot_slots);
|
||||
@@ -128,11 +136,26 @@ const screenshotUrls = computed(() =>
|
||||
);
|
||||
const coinMAmount = computed(() => Number(form.haf_coin_amount || 0));
|
||||
const coinWanAmount = computed(() => coinMAmount.value * 100);
|
||||
const dailyLossMAmount = computed(() => Number(form.daily_loss_m || 10));
|
||||
const dailyLossRatioAdjustment = computed(() =>
|
||||
Math.min(Math.max(Math.floor((dailyLossMAmount.value - 10) / 10), 0), 4)
|
||||
);
|
||||
const calculatedRecycleRatio = computed(() => calculatePublishRatio());
|
||||
const calculatedSalePricing = computed(() => calculateSalePricing());
|
||||
const calculatedRatio = computed(() => calculatedSalePricing.value.saleRatio);
|
||||
const calculatedCoinBasePrice = computed(() => calculatedSalePricing.value.coinBasePrice);
|
||||
const calculatedSaleRuleText = computed(() => calculatedSalePricing.value.ruleText);
|
||||
const calculatedDefaultSaleRatio = computed(() => calculatedSalePricing.value.saleRatio);
|
||||
const maxAcceleratedSaleRatio = computed(() =>
|
||||
calculatedDefaultSaleRatio.value > 0 ? roundRatio(calculatedDefaultSaleRatio.value + 10) : 0
|
||||
);
|
||||
const calculatedRatio = computed(() => readFinalSaleRatio());
|
||||
const calculatedCoinBasePrice = computed(() => {
|
||||
if (calculatedRatio.value <= 0) return 0;
|
||||
if (!hasAcceleratedSaleRatioInput()) return calculatedSalePricing.value.coinBasePrice;
|
||||
return roundMoney(coinWanAmount.value / calculatedRatio.value);
|
||||
});
|
||||
const acceleratedSaleRatioPlaceholder = computed(() => {
|
||||
if (calculatedDefaultSaleRatio.value <= 0) return "填写资料后自动生成可设置范围";
|
||||
return `默认 ${formatNumber(calculatedDefaultSaleRatio.value)},最高 ${formatNumber(maxAcceleratedSaleRatio.value)}`;
|
||||
});
|
||||
const calculatedConsumablePrice = computed(() => calculateConsumablePrice());
|
||||
const calculatedFinalPrice = computed(() =>
|
||||
calculatedRatio.value > 0
|
||||
@@ -162,9 +185,15 @@ watch(
|
||||
|
||||
async function loadPublishOptions() {
|
||||
try {
|
||||
publishOptions.value = await fetchListingPublishOptions();
|
||||
const [nextPublishOptions, nextSalePriceConfig] = await Promise.all([
|
||||
fetchListingPublishOptions(),
|
||||
fetchListingSalePriceConfig(),
|
||||
]);
|
||||
publishOptions.value = nextPublishOptions;
|
||||
salePriceConfig.value = nextSalePriceConfig;
|
||||
} catch {
|
||||
publishOptions.value = emptyListingPublishOptions;
|
||||
salePriceConfig.value = emptyListingSalePriceConfig;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,6 +382,27 @@ function handleFireLevelInput(value: string | number) {
|
||||
form.fire_level = Math.trunc(level);
|
||||
}
|
||||
|
||||
function handleAcceleratedSaleRatioInput(value: string | number) {
|
||||
if (value === "") {
|
||||
form.accelerated_sale_ratio = "";
|
||||
return;
|
||||
}
|
||||
const ratio = Number(value);
|
||||
form.accelerated_sale_ratio = Number.isFinite(ratio) ? ratio : "";
|
||||
}
|
||||
|
||||
function clampAcceleratedSaleRatioInput() {
|
||||
if (!hasAcceleratedSaleRatioInput() || calculatedDefaultSaleRatio.value <= 0) return;
|
||||
const ratio = Number(form.accelerated_sale_ratio);
|
||||
if (!Number.isFinite(ratio)) {
|
||||
form.accelerated_sale_ratio = "";
|
||||
return;
|
||||
}
|
||||
const minRatio = calculatedDefaultSaleRatio.value;
|
||||
const maxRatio = maxAcceleratedSaleRatio.value;
|
||||
form.accelerated_sale_ratio = roundRatio(Math.min(Math.max(ratio, minRatio), maxRatio));
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
const error = validateForm();
|
||||
if (error) {
|
||||
@@ -409,6 +459,17 @@ function validateForm() {
|
||||
if (!form.season_insurance) return "请选择赛季保险";
|
||||
if (!form.stamina_level) return "请选择体力等级";
|
||||
if (!form.load_level) return "请选择负重等级";
|
||||
if (!dailyLossOptions.includes(dailyLossMAmount.value)) return "请选择每日损耗";
|
||||
if (hasAcceleratedSaleRatioInput()) {
|
||||
const ratio = Number(form.accelerated_sale_ratio);
|
||||
if (!Number.isFinite(ratio) || ratio <= 0) return "加速出售比例格式不正确";
|
||||
if (calculatedDefaultSaleRatio.value > 0 && ratio < calculatedDefaultSaleRatio.value) {
|
||||
return `加速出售比例不能低于默认比例 1:${formatNumber(calculatedDefaultSaleRatio.value)}`;
|
||||
}
|
||||
if (maxAcceleratedSaleRatio.value > 0 && ratio > maxAcceleratedSaleRatio.value) {
|
||||
return `加速出售比例不能超过 1:${formatNumber(maxAcceleratedSaleRatio.value)}`;
|
||||
}
|
||||
}
|
||||
if (banRecordOptions.value.length && !form.ban_record) return "请选择封禁记录";
|
||||
if (form.deposit_amount === "" || Number(form.deposit_amount) < 0) {
|
||||
return "请填写押金";
|
||||
@@ -443,16 +504,20 @@ function buildAssetSummary() {
|
||||
face_owner: form.face_owner,
|
||||
secret_kd: form.secret_kd,
|
||||
fire_level: Number(form.fire_level),
|
||||
daily_loss_m: dailyLossMAmount.value,
|
||||
publish_ratio: calculatedRatio.value,
|
||||
price_breakdown: {
|
||||
recycle_coin_base_price: calculatedSalePricing.value.recycleCoinBasePrice,
|
||||
default_coin_base_price: calculatedSalePricing.value.coinBasePrice,
|
||||
coin_base_price: calculatedCoinBasePrice.value,
|
||||
consumable_price: calculatedConsumablePrice.value,
|
||||
final_price: calculatedFinalPrice.value,
|
||||
recycle_ratio: calculatedRecycleRatio.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,
|
||||
sale_rule_type: calculatedSalePricing.value.ruleType,
|
||||
sale_rule_text: calculatedSalePricing.value.ruleText,
|
||||
},
|
||||
season_insurance: form.season_insurance,
|
||||
stamina_level: form.stamina_level,
|
||||
@@ -518,7 +583,25 @@ function calculatePublishRatio() {
|
||||
const baseRatio = getInsuranceBaseRatio(form.season_insurance);
|
||||
if (baseRatio <= 0) return 0;
|
||||
|
||||
return baseRatio + calculateConfigPenalty() + getCoinCorrection(coinMAmount.value);
|
||||
return (
|
||||
baseRatio +
|
||||
calculateConfigPenalty() +
|
||||
getCoinCorrection(coinMAmount.value) +
|
||||
dailyLossRatioAdjustment.value
|
||||
);
|
||||
}
|
||||
|
||||
function readFinalSaleRatio() {
|
||||
const defaultRatio = calculatedDefaultSaleRatio.value;
|
||||
if (defaultRatio <= 0) return 0;
|
||||
if (!hasAcceleratedSaleRatioInput()) return defaultRatio;
|
||||
const ratio = Number(form.accelerated_sale_ratio);
|
||||
if (!Number.isFinite(ratio) || ratio <= 0) return defaultRatio;
|
||||
return roundRatio(Math.min(Math.max(ratio, defaultRatio), maxAcceleratedSaleRatio.value));
|
||||
}
|
||||
|
||||
function hasAcceleratedSaleRatioInput() {
|
||||
return form.accelerated_sale_ratio !== "" && form.accelerated_sale_ratio !== null;
|
||||
}
|
||||
|
||||
function calculateSalePricing() {
|
||||
@@ -535,7 +618,6 @@ function calculateSalePricing() {
|
||||
coinBasePrice,
|
||||
saleRatio: calculateEffectiveRatio(coinBasePrice),
|
||||
ruleType: "fixed_markup",
|
||||
ruleText: `${formatSaleRange(fixedRule)} 加价 ${formatNumber(fixedRule.markup_amount)}元`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -550,9 +632,6 @@ function calculateSalePricing() {
|
||||
coinBasePrice: roundMoney(coinWanAmount.value / saleRatio),
|
||||
saleRatio,
|
||||
ruleType: ratioRule ? "ratio_subtract" : "none",
|
||||
ruleText: ratioRule
|
||||
? `${formatSaleRange(ratioRule)} 出售比例=回收比例-${formatNumber(ratioSubtract)}`
|
||||
: "未匹配出售加价规则,按回收比例计算",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -562,7 +641,6 @@ function emptySalePricing(recycleCoinBasePrice = 0) {
|
||||
coinBasePrice: 0,
|
||||
saleRatio: 0,
|
||||
ruleType: "none",
|
||||
ruleText: "",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -585,13 +663,7 @@ function isCoinInSaleRange(item: { min_m: number; max_m: number }) {
|
||||
|
||||
function calculateEffectiveRatio(price: number) {
|
||||
if (price <= 0) return 0;
|
||||
return Math.round((coinWanAmount.value / price) * 10) / 10;
|
||||
}
|
||||
|
||||
function formatSaleRange(item: { min_m: number; max_m: number }) {
|
||||
const minM = formatNumber(Number(item.min_m || 0));
|
||||
const maxM = Number(item.max_m || 0);
|
||||
return maxM > 0 ? `${minM}M-${formatNumber(maxM)}M` : `${minM}M以上`;
|
||||
return roundRatio(coinWanAmount.value / price);
|
||||
}
|
||||
|
||||
function getInsuranceBaseRatio(insurance: string) {
|
||||
@@ -641,6 +713,10 @@ function formatNumber(value: number) {
|
||||
return Number.isInteger(value) ? `${value}` : `${Math.round(value * 10) / 10}`;
|
||||
}
|
||||
|
||||
function roundRatio(value: number) {
|
||||
return Math.round(value * 10) / 10;
|
||||
}
|
||||
|
||||
function readError(error: unknown, fallback: string) {
|
||||
if (typeof error === "object" && error && "response" in error) {
|
||||
const response = (error as { response?: { data?: { message?: string } } })
|
||||
@@ -997,6 +1073,37 @@ function readError(error: unknown, fallback: string) {
|
||||
<span class="hint-label" :data-hint="priceConfig.deposit_placeholder" tabindex="0">押金</span>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field label="每日损耗" required class="publish-field">
|
||||
<template #input>
|
||||
<div class="radio-group">
|
||||
<button
|
||||
v-for="loss in dailyLossOptions"
|
||||
:key="loss"
|
||||
type="button"
|
||||
class="radio-btn"
|
||||
:class="{ active: dailyLossMAmount === loss }"
|
||||
@click="form.daily_loss_m = loss"
|
||||
>
|
||||
{{ loss }}M/天
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</van-field>
|
||||
<p class="field-hint">
|
||||
比例调整:在默认10M/天,每增加10M,出租比例相应+1,请根据实际需求合理设置,感谢您的配合。
|
||||
</p>
|
||||
<van-field
|
||||
:model-value="form.accelerated_sale_ratio"
|
||||
label="加速出售比例"
|
||||
type="number"
|
||||
:placeholder="acceleratedSaleRatioPlaceholder"
|
||||
class="publish-field"
|
||||
@update:model-value="handleAcceleratedSaleRatioInput"
|
||||
@blur="clampAcceleratedSaleRatioInput"
|
||||
/>
|
||||
<p class="field-hint">
|
||||
请根据实际需求合理设置,感谢您的配合。
|
||||
</p>
|
||||
<div class="result-grid">
|
||||
<van-field
|
||||
:model-value="calculatedRatioText"
|
||||
@@ -1009,12 +1116,6 @@ function readError(error: unknown, fallback: string) {
|
||||
<span class="hint-label" :data-hint="priceConfig.ratio_description" tabindex="0">出售比例</span>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field
|
||||
:model-value="calculatedSaleRuleText"
|
||||
label="出售规则"
|
||||
readonly
|
||||
class="publish-field result-field muted-result-field"
|
||||
/>
|
||||
<van-field
|
||||
:model-value="calculatedCoinBasePrice ? `¥${calculatedCoinBasePrice}` : ''"
|
||||
label="纯币基础价"
|
||||
@@ -1043,7 +1144,7 @@ function readError(error: unknown, fallback: string) {
|
||||
/>
|
||||
</div>
|
||||
<p class="price-breakdown-hint">
|
||||
纯币基础价先按回收比例计算,再套出售加价规则;发布总价 = 纯币基础价 + 额外消耗品。
|
||||
发布总价 = 纯币基础价 + 额外消耗品;加速比例仅影响纯币基础价。
|
||||
</p>
|
||||
|
||||
<van-field
|
||||
|
||||
Reference in New Issue
Block a user