智能押金与33优化
This commit is contained in:
@@ -51,6 +51,7 @@ type PublishOptionsDTO struct {
|
||||
BanEvidenceOptions []string `json:"ban_evidence_options"`
|
||||
FireLevelMin int `json:"fire_level_min"`
|
||||
PriceConfig PublishPriceConfig `json:"price_config"`
|
||||
DepositRecommend PublishDepositRecommend `json:"deposit_recommend_config"`
|
||||
RatioConfig PublishRatioConfig `json:"ratio_config"`
|
||||
}
|
||||
|
||||
@@ -80,6 +81,17 @@ type PublishPriceConfig struct {
|
||||
RatioDescription string `json:"ratio_description"`
|
||||
}
|
||||
|
||||
type PublishDepositRecommend struct {
|
||||
BaseAmount float64 `json:"base_amount"`
|
||||
SkinGroupRules []PublishDepositSkinGroupRule `json:"skin_group_rules"`
|
||||
}
|
||||
|
||||
type PublishDepositSkinGroupRule struct {
|
||||
GroupKey string `json:"group_key"`
|
||||
Label string `json:"label"`
|
||||
AmountPerItem float64 `json:"amount_per_item"`
|
||||
}
|
||||
|
||||
type PublishRatioConfig struct {
|
||||
InsuranceBaseRatios []PublishInsuranceBaseRatio `json:"insurance_base_ratios"`
|
||||
ConfigItems []PublishRatioConfigItem `json:"config_items"`
|
||||
|
||||
@@ -87,6 +87,14 @@ func DefaultPublishOptions() PublishOptionsDTO {
|
||||
PricePlaceholder: "填写币数、保险、体力和负重后自动计算;发布总价=纯币基础价+额外消耗品",
|
||||
RatioDescription: "1:xx 表示 1 元人民币(RMB)等价兑换 xx 万哈夫币",
|
||||
},
|
||||
DepositRecommend: PublishDepositRecommend{
|
||||
BaseAmount: 50,
|
||||
SkinGroupRules: []PublishDepositSkinGroupRule{
|
||||
{GroupKey: "melee", Label: "刀皮", AmountPerItem: 5},
|
||||
{GroupKey: "operatorGold", Label: "干员金皮", AmountPerItem: 10},
|
||||
{GroupKey: "operatorRed", Label: "干员红皮", AmountPerItem: 30},
|
||||
},
|
||||
},
|
||||
RatioConfig: PublishRatioConfig{
|
||||
InsuranceBaseRatios: []PublishInsuranceBaseRatio{
|
||||
{Insurance: "3*3", Ratio: 40},
|
||||
|
||||
@@ -229,6 +229,12 @@ func normalizePublishOptions(options *PublishOptionsDTO) {
|
||||
if options.PriceConfig.RatioDescription == "" {
|
||||
options.PriceConfig.RatioDescription = defaults.PriceConfig.RatioDescription
|
||||
}
|
||||
if options.DepositRecommend.BaseAmount <= 0 {
|
||||
options.DepositRecommend.BaseAmount = defaults.DepositRecommend.BaseAmount
|
||||
}
|
||||
if len(options.DepositRecommend.SkinGroupRules) == 0 {
|
||||
options.DepositRecommend.SkinGroupRules = defaults.DepositRecommend.SkinGroupRules
|
||||
}
|
||||
if len(options.RatioConfig.InsuranceBaseRatios) == 0 {
|
||||
options.RatioConfig.InsuranceBaseRatios = defaults.RatioConfig.InsuranceBaseRatios
|
||||
}
|
||||
|
||||
@@ -34,6 +34,17 @@ export interface PublishPriceConfig {
|
||||
ratio_description: string
|
||||
}
|
||||
|
||||
export interface PublishDepositSkinGroupRule {
|
||||
group_key: string
|
||||
label: string
|
||||
amount_per_item: number
|
||||
}
|
||||
|
||||
export interface PublishDepositRecommendConfig {
|
||||
base_amount: number
|
||||
skin_group_rules: PublishDepositSkinGroupRule[]
|
||||
}
|
||||
|
||||
export interface PublishInsuranceBaseRatio {
|
||||
insurance: string
|
||||
ratio: number
|
||||
@@ -90,6 +101,7 @@ export interface ListingPublishOptions {
|
||||
ban_evidence_options: string[]
|
||||
fire_level_min: number
|
||||
price_config: PublishPriceConfig
|
||||
deposit_recommend_config: PublishDepositRecommendConfig
|
||||
ratio_config: PublishRatioConfig
|
||||
}
|
||||
|
||||
@@ -118,6 +130,14 @@ export const emptyListingPublishOptions: ListingPublishOptions = {
|
||||
price_placeholder: '',
|
||||
ratio_description: '',
|
||||
},
|
||||
deposit_recommend_config: {
|
||||
base_amount: 50,
|
||||
skin_group_rules: [
|
||||
{ group_key: 'melee', label: '刀皮', amount_per_item: 5 },
|
||||
{ group_key: 'operatorGold', label: '干员金皮', amount_per_item: 10 },
|
||||
{ group_key: 'operatorRed', label: '干员红皮', amount_per_item: 30 },
|
||||
],
|
||||
},
|
||||
ratio_config: {
|
||||
insurance_base_ratios: [],
|
||||
config_items: [],
|
||||
@@ -169,6 +189,7 @@ export function mergeListingPublishOptions(options?: Partial<ListingPublishOptio
|
||||
ban_evidence_options: normalizeStringList(options?.ban_evidence_options),
|
||||
fire_level_min: readPositiveInteger(options?.fire_level_min, 38),
|
||||
price_config: normalizePriceConfig(options?.price_config),
|
||||
deposit_recommend_config: normalizeDepositRecommendConfig(options?.deposit_recommend_config),
|
||||
ratio_config: normalizeRatioConfig(options?.ratio_config),
|
||||
}
|
||||
}
|
||||
@@ -236,6 +257,36 @@ function normalizePriceConfig(value?: unknown): PublishPriceConfig {
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeDepositRecommendConfig(value?: unknown): PublishDepositRecommendConfig {
|
||||
const row = isRecord(value) ? value : {}
|
||||
const config = {
|
||||
base_amount: readNumber(row.base_amount),
|
||||
skin_group_rules: normalizeDepositSkinGroupRules(
|
||||
Array.isArray(row.skin_group_rules) ? row.skin_group_rules : [],
|
||||
),
|
||||
}
|
||||
if (config.base_amount <= 0) {
|
||||
config.base_amount = emptyListingPublishOptions.deposit_recommend_config.base_amount
|
||||
}
|
||||
if (config.skin_group_rules.length === 0) {
|
||||
config.skin_group_rules = [...emptyListingPublishOptions.deposit_recommend_config.skin_group_rules]
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
function normalizeDepositSkinGroupRules(values: unknown[]): PublishDepositSkinGroupRule[] {
|
||||
return values
|
||||
.map((item) => {
|
||||
const row = isRecord(item) ? item : {}
|
||||
return {
|
||||
group_key: typeof row.group_key === 'string' ? row.group_key.trim() : '',
|
||||
label: typeof row.label === 'string' ? row.label.trim() : '',
|
||||
amount_per_item: readNumber(row.amount_per_item),
|
||||
}
|
||||
})
|
||||
.filter((item) => item.group_key && item.label && item.amount_per_item >= 0)
|
||||
}
|
||||
|
||||
function normalizeRatioConfig(value?: unknown): PublishRatioConfig {
|
||||
const row = isRecord(value) ? value : {}
|
||||
return {
|
||||
|
||||
@@ -325,6 +325,18 @@ function removeCoinCorrection(index: number) {
|
||||
publishOptionsDraft.value.ratio_config.coin_corrections.splice(index, 1)
|
||||
}
|
||||
|
||||
function addDepositSkinGroupRule() {
|
||||
publishOptionsDraft.value.deposit_recommend_config.skin_group_rules.push({
|
||||
group_key: '',
|
||||
label: '',
|
||||
amount_per_item: 0,
|
||||
})
|
||||
}
|
||||
|
||||
function removeDepositSkinGroupRule(index: number) {
|
||||
publishOptionsDraft.value.deposit_recommend_config.skin_group_rules.splice(index, 1)
|
||||
}
|
||||
|
||||
function addSaleFixedMarkupRule() {
|
||||
salePriceConfigDraft.value.fixed_markup_rules.push({
|
||||
min_m: 0,
|
||||
@@ -668,6 +680,37 @@ function readError(error: unknown, fallback: string) {
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div class="editor-block">
|
||||
<div class="editor-block-title">
|
||||
<strong>智能推荐押金</strong>
|
||||
<el-button size="small" @click="addDepositSkinGroupRule">添加皮肤规则</el-button>
|
||||
</div>
|
||||
<el-form-item label="基础押金">
|
||||
<el-input-number
|
||||
v-model="publishOptionsDraft.deposit_recommend_config.base_amount"
|
||||
:min="0"
|
||||
:step="5"
|
||||
class="full-control"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-table :data="publishOptionsDraft.deposit_recommend_config.skin_group_rules" size="small" border>
|
||||
<el-table-column label="皮肤分组 Key" min-width="150">
|
||||
<template #default="{ row }"><el-input v-model="row.group_key" placeholder="operatorRed" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="名称" min-width="140">
|
||||
<template #default="{ row }"><el-input v-model="row.label" placeholder="干员红皮" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="每个增加押金" min-width="140">
|
||||
<template #default="{ row }"><el-input-number v-model="row.amount_per_item" :min="0" :step="5" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="90">
|
||||
<template #default="{ $index }">
|
||||
<el-button size="small" type="danger" plain @click="removeDepositSkinGroupRule($index)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="editor-block">
|
||||
<div class="editor-block-title">
|
||||
<strong>比例计算配置</strong>
|
||||
|
||||
@@ -257,6 +257,10 @@
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.quantity-row.disabled {
|
||||
opacity: 0.62;
|
||||
}
|
||||
|
||||
.quantity-card {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 72px;
|
||||
@@ -314,6 +318,10 @@
|
||||
border-color: #1477ff;
|
||||
}
|
||||
|
||||
.quantity-input:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.mode-toggle {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
@@ -336,6 +344,10 @@
|
||||
color: #e65f00;
|
||||
}
|
||||
|
||||
.mode-toggle button:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.skin-groups {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
@@ -504,6 +516,18 @@
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.deposit-recommend-btn {
|
||||
min-height: 32px;
|
||||
margin: -2px 0 10px 88px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid #ffba86;
|
||||
border-radius: 16px;
|
||||
background: #fff7f0;
|
||||
color: #e65f00;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.ratio-reference {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -121,6 +121,7 @@ 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 depositRecommendConfig = computed(() => publishOptions.value.deposit_recommend_config);
|
||||
const fireLevelMin = computed(() => publishOptions.value.fire_level_min || 38);
|
||||
const fireLevelPlaceholder = computed(
|
||||
() => `等级低于${fireLevelMin.value}级的号无法发布`
|
||||
@@ -161,6 +162,7 @@ const calculatedSellerPrice = computed(() =>
|
||||
);
|
||||
const calculatedPlatformPricing = computed(() => calculatePlatformPricing());
|
||||
const calculatedFinalPrice = computed(() => calculatedPlatformPricing.value.buyerTotalPrice);
|
||||
const recommendedDepositAmount = computed(() => calculateRecommendedDeposit());
|
||||
const calculatedRatioText = computed(() =>
|
||||
calculatedRatio.value > 0 ? `1:${formatNumber(calculatedRatio.value)}` : ""
|
||||
);
|
||||
@@ -189,6 +191,18 @@ watch(
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
watch(recommendedDepositAmount, () => {
|
||||
syncRecommendedDeposit();
|
||||
}, { immediate: true });
|
||||
|
||||
watch(
|
||||
[() => form.season_insurance, quantityItems],
|
||||
() => {
|
||||
clearForbiddenQuantityItems();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
async function loadPublishOptions() {
|
||||
try {
|
||||
const [nextPublishOptions, nextSalePriceConfig] = await Promise.all([
|
||||
@@ -397,6 +411,55 @@ function handleAcceleratedSaleRatioInput(value: string | number) {
|
||||
form.accelerated_sale_ratio = Number.isFinite(ratio) ? ratio : "";
|
||||
}
|
||||
|
||||
function syncRecommendedDeposit() {
|
||||
const recommended = recommendedDepositAmount.value;
|
||||
if (recommended <= 0) return;
|
||||
const current = Number(form.deposit_amount);
|
||||
if (form.deposit_amount === "" || !Number.isFinite(current) || current < recommended) {
|
||||
form.deposit_amount = recommended;
|
||||
}
|
||||
}
|
||||
|
||||
function calculateRecommendedDeposit() {
|
||||
const baseAmount = Number(depositRecommendConfig.value.base_amount || 0);
|
||||
const skinAmount = depositRecommendConfig.value.skin_group_rules.reduce((sum, rule) => {
|
||||
const group = skinGroups.value.find((item) => item.key === rule.group_key);
|
||||
if (!group) return sum;
|
||||
const selectedCount = group.options.filter((skin) =>
|
||||
selectedSkins.value.includes(skin)
|
||||
).length;
|
||||
return sum + selectedCount * Number(rule.amount_per_item || 0);
|
||||
}, 0);
|
||||
return roundMoney(baseAmount + skinAmount);
|
||||
}
|
||||
|
||||
function useRecommendedDeposit() {
|
||||
if (recommendedDepositAmount.value > 0) {
|
||||
form.deposit_amount = recommendedDepositAmount.value;
|
||||
}
|
||||
}
|
||||
|
||||
function isGridCardQuantityItem(item: { key: string; label: string }) {
|
||||
return item.key === "gridCard9" || item.label.includes("9格体验卡");
|
||||
}
|
||||
|
||||
function isQuantityItemDisabled(item: { key: string; label: string }) {
|
||||
return form.season_insurance === "3*3" && isGridCardQuantityItem(item);
|
||||
}
|
||||
|
||||
function clearForbiddenQuantityItems() {
|
||||
for (const item of quantityItems.value) {
|
||||
if (!isQuantityItemDisabled(item)) continue;
|
||||
quantityValues[item.key] = 0;
|
||||
quantityModes[item.key] = "赠送";
|
||||
}
|
||||
}
|
||||
|
||||
function setQuantityMode(item: { key: string; label: string }, mode: ChargeMode) {
|
||||
if (isQuantityItemDisabled(item)) return;
|
||||
quantityModes[item.key] = mode;
|
||||
}
|
||||
|
||||
function clampAcceleratedSaleRatioInput() {
|
||||
if (!hasAcceleratedSaleRatioInput() || calculatedDefaultSaleRatio.value <= 0) return;
|
||||
const ratio = Number(form.accelerated_sale_ratio);
|
||||
@@ -489,6 +552,9 @@ function validateForm() {
|
||||
if (!Number.isFinite(Number(form.deposit_amount))) {
|
||||
return "押金格式不正确";
|
||||
}
|
||||
if (recommendedDepositAmount.value > 0 && Number(form.deposit_amount) < recommendedDepositAmount.value) {
|
||||
return `押金不能低于智能推荐 ¥${recommendedDepositAmount.value}`;
|
||||
}
|
||||
if (calculatedConsumablePrice.value > 0 && Number(form.deposit_amount) <= calculatedConsumablePrice.value) {
|
||||
return `押金必须大于额外消耗品总价值 ¥${calculatedConsumablePrice.value}`;
|
||||
}
|
||||
@@ -503,6 +569,11 @@ function validateForm() {
|
||||
return `请上传${item.label}`;
|
||||
}
|
||||
}
|
||||
for (const item of quantityItems.value) {
|
||||
if (isQuantityItemDisabled(item) && Number(quantityValues[item.key] || 0) > 0) {
|
||||
return "赛季保险选择 3*3 时不能填写 9格体验卡";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -542,8 +613,8 @@ function buildAssetSummary() {
|
||||
key: item.key,
|
||||
label: item.label,
|
||||
price: item.price,
|
||||
quantity: Number(quantityValues[item.key] || 0),
|
||||
mode: quantityModes[item.key] || "收费",
|
||||
quantity: isQuantityItemDisabled(item) ? 0 : Number(quantityValues[item.key] || 0),
|
||||
mode: isQuantityItemDisabled(item) ? "赠送" : quantityModes[item.key] || "收费",
|
||||
})),
|
||||
skin_groups: skinGroups.value.reduce<Record<string, string[]>>((groups, group) => {
|
||||
groups[group.key] = group.options.filter((skin) =>
|
||||
@@ -569,6 +640,7 @@ function calculateConsumablePrice() {
|
||||
const total = quantityItems.value.reduce((sum, item) => {
|
||||
const quantity = Number(quantityValues[item.key] || 0);
|
||||
const mode = quantityModes[item.key] || "收费";
|
||||
if (isQuantityItemDisabled(item)) return sum;
|
||||
if (quantity <= 0 || mode !== "收费") return sum;
|
||||
return sum + quantity * readUnitPrice(item.price);
|
||||
}, 0);
|
||||
@@ -897,7 +969,12 @@ function readError(error: unknown, fallback: string) {
|
||||
|
||||
<div class="form-section">
|
||||
<h2 class="section-title">额外消耗品</h2>
|
||||
<div v-for="item in quantityItems" :key="item.key" class="quantity-row">
|
||||
<div
|
||||
v-for="item in quantityItems"
|
||||
:key="item.key"
|
||||
class="quantity-row"
|
||||
:class="{ disabled: isQuantityItemDisabled(item) }"
|
||||
>
|
||||
<div class="quantity-card">
|
||||
<div class="quantity-meta">
|
||||
<strong
|
||||
@@ -908,7 +985,7 @@ function readError(error: unknown, fallback: string) {
|
||||
>
|
||||
<span>*</span>{{ item.label }}
|
||||
</strong>
|
||||
<small>{{ item.price }}</small>
|
||||
<small>{{ isQuantityItemDisabled(item) ? "3*3 已包含,不能填写" : item.price }}</small>
|
||||
</div>
|
||||
<input
|
||||
v-model="quantityValues[item.key]"
|
||||
@@ -917,20 +994,23 @@ function readError(error: unknown, fallback: string) {
|
||||
min="0"
|
||||
placeholder="0"
|
||||
class="quantity-input"
|
||||
:disabled="isQuantityItemDisabled(item)"
|
||||
/>
|
||||
</div>
|
||||
<div class="mode-toggle">
|
||||
<button
|
||||
type="button"
|
||||
:class="{ active: quantityModes[item.key] === '赠送' }"
|
||||
@click="quantityModes[item.key] = '赠送'"
|
||||
:disabled="isQuantityItemDisabled(item)"
|
||||
@click="setQuantityMode(item, '赠送')"
|
||||
>
|
||||
赠送
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
:class="{ active: (quantityModes[item.key] || '收费') === '收费' }"
|
||||
@click="quantityModes[item.key] = '收费'"
|
||||
:disabled="isQuantityItemDisabled(item)"
|
||||
@click="setQuantityMode(item, '收费')"
|
||||
>
|
||||
收费
|
||||
</button>
|
||||
@@ -1100,6 +1180,9 @@ function readError(error: unknown, fallback: string) {
|
||||
<span class="hint-label" :data-hint="priceConfig.deposit_placeholder" tabindex="0">押金</span>
|
||||
</template>
|
||||
</van-field>
|
||||
<button class="deposit-recommend-btn" type="button" @click="useRecommendedDeposit">
|
||||
推荐押金 ¥{{ recommendedDepositAmount }}
|
||||
</button>
|
||||
<van-field label="每日损耗" required class="publish-field">
|
||||
<template #input>
|
||||
<div class="radio-group">
|
||||
|
||||
@@ -101,6 +101,7 @@ const skinGroups = computed(() => publishOptions.value.skin_groups)
|
||||
const quantityItems = computed(() => publishOptions.value.quantity_items)
|
||||
const screenshotSlots = computed(() => publishOptions.value.screenshot_slots)
|
||||
const priceConfig = computed(() => publishOptions.value.price_config)
|
||||
const depositRecommendConfig = computed(() => publishOptions.value.deposit_recommend_config)
|
||||
const fireLevelMin = computed(() => publishOptions.value.fire_level_min || 38)
|
||||
const fireLevelPlaceholder = computed(() => `等级低于${fireLevelMin.value}级的号无法发布`)
|
||||
const screenshotUrls = computed(() =>
|
||||
@@ -143,6 +144,7 @@ const acceleratedSaleRatioPlaceholder = computed(() => {
|
||||
const selectedSkinCount = computed(() => selectedSkins.value.length)
|
||||
const uploadedScreenshotCount = computed(() => screenshotUrls.value.length)
|
||||
const requiredScreenshotCount = computed(() => screenshotSlots.value.filter((item) => isScreenshotRequired(item)).length)
|
||||
const recommendedDepositAmount = computed(() => calculateRecommendedDeposit())
|
||||
const publishTitle = computed(() => {
|
||||
const parts = [form.server_region, form.rank_level, coinMAmount.value ? `${coinMAmount.value}M哈夫币` : ''].filter(Boolean)
|
||||
return parts.length ? parts.join(' ') : '待完善账号信息'
|
||||
@@ -165,6 +167,18 @@ watch(
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
watch(recommendedDepositAmount, () => {
|
||||
syncRecommendedDeposit()
|
||||
}, { immediate: true })
|
||||
|
||||
watch(
|
||||
[() => form.season_insurance, quantityItems],
|
||||
() => {
|
||||
clearForbiddenQuantityItems()
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
async function loadPublishOptions() {
|
||||
try {
|
||||
const [nextPublishOptions, nextSalePriceConfig] = await Promise.all([
|
||||
@@ -354,6 +368,53 @@ function handleAcceleratedSaleRatioInput(value: string | number | undefined) {
|
||||
form.accelerated_sale_ratio = Number.isFinite(ratio) ? ratio : ''
|
||||
}
|
||||
|
||||
function syncRecommendedDeposit() {
|
||||
const recommended = recommendedDepositAmount.value
|
||||
if (recommended <= 0) return
|
||||
const current = Number(form.deposit_amount)
|
||||
if (form.deposit_amount === '' || !Number.isFinite(current) || current < recommended) {
|
||||
form.deposit_amount = recommended
|
||||
}
|
||||
}
|
||||
|
||||
function calculateRecommendedDeposit() {
|
||||
const baseAmount = Number(depositRecommendConfig.value.base_amount || 0)
|
||||
const skinAmount = depositRecommendConfig.value.skin_group_rules.reduce((sum, rule) => {
|
||||
const group = skinGroups.value.find((item) => item.key === rule.group_key)
|
||||
if (!group) return sum
|
||||
const selectedCount = group.options.filter((skin) => selectedSkins.value.includes(skin)).length
|
||||
return sum + selectedCount * Number(rule.amount_per_item || 0)
|
||||
}, 0)
|
||||
return roundMoney(baseAmount + skinAmount)
|
||||
}
|
||||
|
||||
function useRecommendedDeposit() {
|
||||
if (recommendedDepositAmount.value > 0) {
|
||||
form.deposit_amount = recommendedDepositAmount.value
|
||||
}
|
||||
}
|
||||
|
||||
function isGridCardQuantityItem(item: { key: string; label: string }) {
|
||||
return item.key === 'gridCard9' || item.label.includes('9格体验卡')
|
||||
}
|
||||
|
||||
function isQuantityItemDisabled(item: { key: string; label: string }) {
|
||||
return form.season_insurance === '3*3' && isGridCardQuantityItem(item)
|
||||
}
|
||||
|
||||
function clearForbiddenQuantityItems() {
|
||||
for (const item of quantityItems.value) {
|
||||
if (!isQuantityItemDisabled(item)) continue
|
||||
quantityValues[item.key] = 0
|
||||
quantityModes[item.key] = '赠送'
|
||||
}
|
||||
}
|
||||
|
||||
function setQuantityMode(item: { key: string; label: string }, mode: ChargeMode) {
|
||||
if (isQuantityItemDisabled(item)) return
|
||||
quantityModes[item.key] = mode
|
||||
}
|
||||
|
||||
function clampAcceleratedSaleRatioInput() {
|
||||
if (!hasAcceleratedSaleRatioInput() || calculatedDefaultSaleRatio.value <= 0) return
|
||||
const ratio = Number(form.accelerated_sale_ratio)
|
||||
@@ -432,6 +493,9 @@ function validateForm() {
|
||||
if (banRecordOptions.value.length && !form.ban_record) return '请选择封禁记录'
|
||||
if (form.deposit_amount === '' || Number(form.deposit_amount) < 0) return '请填写押金'
|
||||
if (!Number.isFinite(Number(form.deposit_amount))) return '押金格式不正确'
|
||||
if (recommendedDepositAmount.value > 0 && Number(form.deposit_amount) < recommendedDepositAmount.value) {
|
||||
return `押金不能低于智能推荐 ¥${recommendedDepositAmount.value}`
|
||||
}
|
||||
if (calculatedConsumablePrice.value > 0 && Number(form.deposit_amount) <= calculatedConsumablePrice.value) {
|
||||
return `押金必须大于额外消耗品总价值 ¥${calculatedConsumablePrice.value}`
|
||||
}
|
||||
@@ -440,6 +504,11 @@ function validateForm() {
|
||||
for (const item of screenshotSlots.value) {
|
||||
if (isScreenshotRequired(item) && !screenshotFiles[item.key]) return `请上传${item.label}`
|
||||
}
|
||||
for (const item of quantityItems.value) {
|
||||
if (isQuantityItemDisabled(item) && Number(quantityValues[item.key] || 0) > 0) {
|
||||
return '赛季保险选择 3*3 时不能填写 9格体验卡'
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
@@ -481,8 +550,8 @@ function buildAssetSummary() {
|
||||
key: item.key,
|
||||
label: item.label,
|
||||
price: item.price,
|
||||
quantity: Number(quantityValues[item.key] || 0),
|
||||
mode: quantityModes[item.key] || '收费',
|
||||
quantity: isQuantityItemDisabled(item) ? 0 : Number(quantityValues[item.key] || 0),
|
||||
mode: isQuantityItemDisabled(item) ? '赠送' : quantityModes[item.key] || '收费',
|
||||
})),
|
||||
skin_groups: skinGroups.value.reduce<Record<string, string[]>>((groups, group) => {
|
||||
groups[group.key] = group.options.filter((skin) => selectedSkins.value.includes(skin))
|
||||
@@ -506,6 +575,7 @@ function calculateConsumablePrice() {
|
||||
const total = quantityItems.value.reduce((sum, item) => {
|
||||
const quantity = Number(quantityValues[item.key] || 0)
|
||||
const mode = quantityModes[item.key] || '收费'
|
||||
if (isQuantityItemDisabled(item)) return sum
|
||||
if (quantity <= 0 || mode !== '收费') return sum
|
||||
return sum + quantity * readUnitPrice(item.price)
|
||||
}, 0)
|
||||
@@ -808,24 +878,36 @@ function readError(error: unknown, fallback: string) {
|
||||
<span>收费项会计入每日价格</span>
|
||||
</div>
|
||||
<div class="quantity-grid">
|
||||
<div v-for="item in quantityItems" :key="item.key" class="quantity-item">
|
||||
<div
|
||||
v-for="item in quantityItems"
|
||||
:key="item.key"
|
||||
class="quantity-item"
|
||||
:class="{ disabled: isQuantityItemDisabled(item) }"
|
||||
>
|
||||
<div class="quantity-meta">
|
||||
<strong :title="item.placeholder || item.label">{{ item.label }}</strong>
|
||||
<small>{{ item.price }}</small>
|
||||
<small>{{ isQuantityItemDisabled(item) ? '3*3 已包含,不能填写' : item.price }}</small>
|
||||
</div>
|
||||
<el-input-number v-model="quantityValues[item.key]" :min="0" :controls="false" />
|
||||
<el-input-number
|
||||
v-model="quantityValues[item.key]"
|
||||
:min="0"
|
||||
:controls="false"
|
||||
:disabled="isQuantityItemDisabled(item)"
|
||||
/>
|
||||
<div class="mode-toggle">
|
||||
<button
|
||||
type="button"
|
||||
:class="{ active: quantityModes[item.key] === '赠送' }"
|
||||
@click="quantityModes[item.key] = '赠送'"
|
||||
:disabled="isQuantityItemDisabled(item)"
|
||||
@click="setQuantityMode(item, '赠送')"
|
||||
>
|
||||
赠送
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
:class="{ active: (quantityModes[item.key] || '收费') === '收费' }"
|
||||
@click="quantityModes[item.key] = '收费'"
|
||||
:disabled="isQuantityItemDisabled(item)"
|
||||
@click="setQuantityMode(item, '收费')"
|
||||
>
|
||||
收费
|
||||
</button>
|
||||
@@ -973,6 +1055,9 @@ function readError(error: unknown, fallback: string) {
|
||||
:controls="false"
|
||||
:placeholder="priceConfig.deposit_placeholder"
|
||||
/>
|
||||
<button class="recommend-button" type="button" @click="useRecommendedDeposit">
|
||||
推荐押金 ¥{{ recommendedDepositAmount }}
|
||||
</button>
|
||||
</label>
|
||||
<label class="input-block">
|
||||
<span>每日损耗<b>*</b></span>
|
||||
@@ -1078,6 +1163,10 @@ function readError(error: unknown, fallback: string) {
|
||||
<span>押金</span>
|
||||
<strong>{{ form.deposit_amount === '' ? '--' : `¥${form.deposit_amount}` }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>推荐押金</span>
|
||||
<strong>¥{{ recommendedDepositAmount }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>截图材料</span>
|
||||
<strong>{{ uploadedScreenshotCount }}/{{ requiredScreenshotCount }}</strong>
|
||||
@@ -1321,6 +1410,10 @@ function readError(error: unknown, fallback: string) {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.quantity-item.disabled {
|
||||
opacity: 0.62;
|
||||
}
|
||||
|
||||
.quantity-item :deep(.el-input-number) {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
@@ -1341,12 +1434,29 @@ function readError(error: unknown, fallback: string) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mode-toggle button:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.mode-toggle button.active {
|
||||
border-color: #ff6a00;
|
||||
background: #fff3ea;
|
||||
color: #e65f00;
|
||||
}
|
||||
|
||||
.recommend-button {
|
||||
justify-self: start;
|
||||
height: 28px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid #ffba86;
|
||||
border-radius: 14px;
|
||||
background: #fff7f0;
|
||||
color: #e65f00;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.skin-groups {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
|
||||
Reference in New Issue
Block a user