发布界面优化
This commit is contained in:
@@ -252,7 +252,7 @@ function isNavActive(path: string) {
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-line">
|
||||
<span class="info-label">比例</span>
|
||||
<span class="info-label">M单价</span>
|
||||
<span class="info-text">{{ formatRatio(listing) }}</span>
|
||||
</div>
|
||||
<div class="info-line">
|
||||
|
||||
@@ -109,6 +109,10 @@ const loginMethodOptions = computed(
|
||||
() => publishOptions.value.login_method_options
|
||||
);
|
||||
const regionOptions = computed(() => publishOptions.value.region_options);
|
||||
const banRecordOptions = computed(() => publishOptions.value.ban_record_options);
|
||||
const banEvidenceOptions = computed(() => publishOptions.value.ban_evidence_options);
|
||||
const priceConfig = computed(() => publishOptions.value.price_config);
|
||||
const ratioConfig = computed(() => publishOptions.value.ratio_config);
|
||||
const skinGroups = computed(() => publishOptions.value.skin_groups);
|
||||
const quantityItems = computed(() => publishOptions.value.quantity_items);
|
||||
const screenshotSlots = computed(() => publishOptions.value.screenshot_slots);
|
||||
@@ -117,7 +121,8 @@ const screenshotUrls = computed(() =>
|
||||
.map((item) => screenshotFiles[item.key])
|
||||
.filter((url): url is string => Boolean(url))
|
||||
);
|
||||
const coinWanAmount = computed(() => Number(form.haf_coin_amount || 0));
|
||||
const coinMAmount = computed(() => Number(form.haf_coin_amount || 0));
|
||||
const coinWanAmount = computed(() => coinMAmount.value * 100);
|
||||
const calculatedRatio = computed(() => calculatePublishRatio());
|
||||
const calculatedConsumablePrice = computed(() => calculateConsumablePrice());
|
||||
const calculatedFinalPrice = computed(() =>
|
||||
@@ -125,8 +130,11 @@ const calculatedFinalPrice = computed(() =>
|
||||
? roundMoney(coinWanAmount.value / calculatedRatio.value + calculatedConsumablePrice.value)
|
||||
: 0
|
||||
);
|
||||
const calculatedRatioText = computed(() =>
|
||||
calculatedRatio.value > 0 ? `1:${formatNumber(calculatedRatio.value)}` : ""
|
||||
);
|
||||
const calculatedRentDays = computed(() =>
|
||||
coinWanAmount.value > 0 ? calculateRentDays(coinWanAmount.value) : 0
|
||||
coinMAmount.value > 0 ? calculateRentDays(coinMAmount.value) : 0
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
@@ -347,7 +355,7 @@ async function handleSubmit() {
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const title = `${form.server_region} ${form.rank_level} ${form.haf_coin_amount}万哈夫币`;
|
||||
const title = `${form.server_region} ${form.rank_level} ${form.haf_coin_amount}M哈夫币`;
|
||||
const rentDays = Math.max(calculatedRentDays.value, 1);
|
||||
const dailyPrice = roundMoney(calculatedFinalPrice.value / rentDays);
|
||||
const hourlyPrice = Math.max(roundMoney(dailyPrice / 24), 0.01);
|
||||
@@ -358,7 +366,7 @@ async function handleSubmit() {
|
||||
server_region: form.server_region,
|
||||
login_platform: form.login_method,
|
||||
rank_level: form.rank_level,
|
||||
haf_coin_amount: Number(form.haf_coin_amount) * 10000,
|
||||
haf_coin_amount: coinMAmount.value * 1000000,
|
||||
asset_summary: buildAssetSummary(),
|
||||
screenshot_urls: screenshotUrls.value,
|
||||
price_hourly: hourlyPrice,
|
||||
@@ -383,13 +391,14 @@ async function handleSubmit() {
|
||||
|
||||
function validateForm() {
|
||||
if (!form.server_region) return "请选择区服";
|
||||
if (Number(form.haf_coin_amount) <= 0) return "请填写哈夫币/万";
|
||||
if (coinMAmount.value <= 0) return "请填写哈夫币/M";
|
||||
if (!form.rank_level) return "请选择段位";
|
||||
if (!form.fire_level) return "请填写烽火等级";
|
||||
if (Number(form.fire_level) < 38) return "烽火等级低于38级的号无法发布";
|
||||
if (!form.season_insurance) return "请选择赛季保险";
|
||||
if (!form.stamina_level) return "请选择体力等级";
|
||||
if (!form.load_level) return "请选择负重等级";
|
||||
if (banRecordOptions.value.length && !form.ban_record) return "请选择封禁记录";
|
||||
if (form.deposit_amount === "" || Number(form.deposit_amount) < 0) {
|
||||
return "请填写押金";
|
||||
}
|
||||
@@ -397,13 +406,21 @@ function validateForm() {
|
||||
return "请完善币数、保险、体力和负重后再发布";
|
||||
}
|
||||
for (const item of screenshotSlots.value) {
|
||||
if (item.required && !screenshotFiles[item.key]) {
|
||||
if (isScreenshotRequired(item) && !screenshotFiles[item.key]) {
|
||||
return `请上传${item.label}`;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function isScreenshotRequired(item: { key: string; required: boolean }) {
|
||||
return item.required || (item.key === "tencentSecurity" && shouldRequireBanEvidence());
|
||||
}
|
||||
|
||||
function shouldRequireBanEvidence() {
|
||||
return banEvidenceOptions.value.includes(form.ban_record);
|
||||
}
|
||||
|
||||
function buildAssetSummary() {
|
||||
return {
|
||||
face_owner: form.face_owner,
|
||||
@@ -462,7 +479,7 @@ function readUnitPrice(priceText: string) {
|
||||
|
||||
function calculatePublishRatio() {
|
||||
if (
|
||||
coinWanAmount.value <= 0 ||
|
||||
coinMAmount.value <= 0 ||
|
||||
!form.season_insurance ||
|
||||
!form.stamina_level ||
|
||||
!form.load_level
|
||||
@@ -472,35 +489,26 @@ function calculatePublishRatio() {
|
||||
const baseRatio = getInsuranceBaseRatio(form.season_insurance);
|
||||
if (baseRatio <= 0) return 0;
|
||||
|
||||
return baseRatio + (5 - getConfigHitCount()) + getCoinCorrection(coinWanAmount.value);
|
||||
return baseRatio + calculateConfigPenalty() + getCoinCorrection(coinMAmount.value);
|
||||
}
|
||||
|
||||
function getInsuranceBaseRatio(insurance: string) {
|
||||
const slots = getInsuranceSlots(insurance);
|
||||
const ratioBySlots: Record<number, number> = {
|
||||
9: 40,
|
||||
6: 42,
|
||||
4: 47,
|
||||
2: 48,
|
||||
};
|
||||
return ratioBySlots[slots] || 0;
|
||||
return ratioConfig.value.insurance_base_ratios.find(
|
||||
(item) => item.insurance === insurance
|
||||
)?.ratio || 0;
|
||||
}
|
||||
|
||||
function getInsuranceSlots(insurance: string) {
|
||||
const match = insurance.match(/^(\d+)\*(\d+)$/);
|
||||
if (!match) return 0;
|
||||
return Number(match[1]) * Number(match[2]);
|
||||
function calculateConfigPenalty() {
|
||||
return ratioConfig.value.config_items.reduce((sum, item) => {
|
||||
return isRatioConfigItemMatched(item) ? sum : sum + Number(item.missing_penalty || 0);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function getConfigHitCount() {
|
||||
const checks = [
|
||||
hasSelectedSkinGroup("operatorRed"),
|
||||
hasSelectedSkinGroup("melee"),
|
||||
isMaxLevel(form.stamina_level),
|
||||
isMaxLevel(form.load_level),
|
||||
hasSelectedSkinGroup("weapon"),
|
||||
];
|
||||
return checks.filter(Boolean).length;
|
||||
function isRatioConfigItemMatched(item: { kind: string; group_key?: string }) {
|
||||
if (item.kind === "skin_group") return hasSelectedSkinGroup(item.group_key || "");
|
||||
if (item.kind === "max_stamina") return isMaxLevel(form.stamina_level);
|
||||
if (item.kind === "max_load") return isMaxLevel(form.load_level);
|
||||
return false;
|
||||
}
|
||||
|
||||
function hasSelectedSkinGroup(groupKey: string) {
|
||||
@@ -521,18 +529,20 @@ function readLevelNumber(value: string) {
|
||||
return match ? Number(match[0]) : 0;
|
||||
}
|
||||
|
||||
function getCoinCorrection(coinWan: number) {
|
||||
if (coinWan > 53000) return 5;
|
||||
if (coinWan > 38000) return 4;
|
||||
if (coinWan > 23000) return 2.5;
|
||||
if (coinWan > 13000) return 1;
|
||||
return 0;
|
||||
function getCoinCorrection(coinM: number) {
|
||||
return [...ratioConfig.value.coin_corrections]
|
||||
.sort((a, b) => b.threshold_m - a.threshold_m)
|
||||
.find((item) => coinM > item.threshold_m)?.correction || 0;
|
||||
}
|
||||
|
||||
function calculateRentDays(coinWan: number) {
|
||||
if (coinWan >= 30000) return 7;
|
||||
if (coinWan >= 10000) return 3;
|
||||
return 1;
|
||||
function calculateRentDays(coinM: number) {
|
||||
return [...ratioConfig.value.rent_rules]
|
||||
.sort((a, b) => b.threshold_m - a.threshold_m)
|
||||
.find((item) => coinM >= item.threshold_m)?.days || 1;
|
||||
}
|
||||
|
||||
function formatNumber(value: number) {
|
||||
return Number.isInteger(value) ? `${value}` : `${Math.round(value * 10) / 10}`;
|
||||
}
|
||||
|
||||
function readError(error: unknown, fallback: string) {
|
||||
@@ -595,10 +605,10 @@ function readError(error: unknown, fallback: string) {
|
||||
|
||||
<van-field
|
||||
v-model="form.haf_coin_amount"
|
||||
label="哈夫币/万"
|
||||
label="哈夫币/M"
|
||||
type="digit"
|
||||
required
|
||||
placeholder="只填写仓库右上角纯币数额,总资产不计算在内(建议清理仓库中所有非保留物品),100M写作10000"
|
||||
placeholder="只填写仓库右上角纯币数额,总资产不计算在内;100M 写 100"
|
||||
class="publish-field"
|
||||
/>
|
||||
|
||||
@@ -788,15 +798,22 @@ function readError(error: unknown, fallback: string) {
|
||||
此在线时间指的是百分百能够联系上您的时间,若是在此期间联系不上您导致无法上号会扣除您的部分租金或上架押金,在线时长太短可能无法上架,请预留充足时间用于扫码以及冻结人脸,请谨慎填写
|
||||
</p>
|
||||
|
||||
<van-field
|
||||
v-model="form.ban_record"
|
||||
label="封禁记录"
|
||||
type="textarea"
|
||||
rows="2"
|
||||
autosize
|
||||
placeholder="请自行填写封禁等信息"
|
||||
class="publish-field"
|
||||
/>
|
||||
<van-field label="封禁记录" required class="publish-field">
|
||||
<template #input>
|
||||
<div class="radio-group">
|
||||
<button
|
||||
v-for="opt in banRecordOptions"
|
||||
:key="opt"
|
||||
type="button"
|
||||
class="radio-btn"
|
||||
:class="{ active: form.ban_record === opt }"
|
||||
@click="selectRadio(opt, (value) => (form.ban_record = value))"
|
||||
>
|
||||
{{ opt }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</van-field>
|
||||
|
||||
<div class="region-panel">
|
||||
<div class="region-title">
|
||||
@@ -826,7 +843,7 @@ function readError(error: unknown, fallback: string) {
|
||||
<div class="upload-list">
|
||||
<div v-for="slot in screenshotSlots" :key="slot.key" class="upload-line">
|
||||
<div class="upload-meta">
|
||||
<strong>{{ slot.label }}<span v-if="slot.required">*</span></strong>
|
||||
<strong>{{ slot.label }}<span v-if="isScreenshotRequired(slot)">*</span></strong>
|
||||
<small>{{ slot.hint }}</small>
|
||||
</div>
|
||||
<div v-if="screenshotFiles[slot.key]" class="upload-preview">
|
||||
@@ -862,17 +879,24 @@ function readError(error: unknown, fallback: string) {
|
||||
label="押金"
|
||||
type="number"
|
||||
required
|
||||
placeholder="温馨提示:本押金用于保障账号安全。若买家在租用期间,使用非纯币类道具/资源(如消耗品、材料等)或导致账号被封禁,相关损失将直接从押金中扣除进行赔付。押金过高会延长出租等待时间(请自行衡量)感谢理解。"
|
||||
:placeholder="priceConfig.deposit_placeholder"
|
||||
suffix="元"
|
||||
class="publish-field"
|
||||
/>
|
||||
<div class="result-grid">
|
||||
<van-field
|
||||
:model-value="calculatedRatioText"
|
||||
label="比例"
|
||||
readonly
|
||||
:placeholder="priceConfig.ratio_description"
|
||||
class="publish-field result-field"
|
||||
/>
|
||||
<van-field
|
||||
:model-value="calculatedFinalPrice ? `¥${calculatedFinalPrice}` : ''"
|
||||
label="价格"
|
||||
readonly
|
||||
required
|
||||
placeholder="填写币数、保险、体力和负重后自动计算"
|
||||
:placeholder="priceConfig.price_placeholder"
|
||||
class="publish-field result-field"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -46,14 +46,19 @@ export function getRatioValue(item: Listing) {
|
||||
}
|
||||
|
||||
export function formatRatio(item: Listing) {
|
||||
const ratio = getRatioValue(item);
|
||||
return ratio > 0 ? `1:${Math.round(ratio)}` : "--";
|
||||
const pricePerM = getPricePerM(item);
|
||||
return pricePerM > 0 ? `1M=¥${formatMoney(pricePerM)}` : "--";
|
||||
}
|
||||
|
||||
export function getValuePerYuanText(item: Listing) {
|
||||
const price = getListingDisplayPrice(item);
|
||||
if (price <= 0) return "";
|
||||
return `1元=${Math.round(getCoinWan(item) / price)}w哈夫币`;
|
||||
const pricePerM = getPricePerM(item);
|
||||
return pricePerM > 0 ? `1M=¥${formatMoney(pricePerM)}` : "";
|
||||
}
|
||||
|
||||
export function getPricePerM(item: Listing) {
|
||||
const coinM = getCoinM(item);
|
||||
if (coinM <= 0) return 0;
|
||||
return roundMoney(getListingDisplayPrice(item) / coinM);
|
||||
}
|
||||
|
||||
export function getLoginMethod(item: Listing) {
|
||||
@@ -219,3 +224,8 @@ function formatResourceShort(item: Listing, key: string, label: string) {
|
||||
function roundMoney(value: number) {
|
||||
return Math.round(value * 100) / 100;
|
||||
}
|
||||
|
||||
function formatMoney(value: number) {
|
||||
const rounded = roundMoney(value);
|
||||
return Number.isInteger(rounded) ? `${rounded}` : rounded.toFixed(2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user