Files
hfb_sys/frontend/src/features/seller/views/SellerListingCreateView.vue
T

969 lines
35 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import {
ArrowRight,
Delete,
DocumentChecked,
InfoFilled,
Picture,
RefreshRight,
UploadFilled,
} from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { computed, ref } from 'vue'
import { usePublishForm } from '@/features/seller/composables/usePublishForm'
import { formatMoney } from '@/shared/utils/money'
import type { AgreementContent } from '@/features/listings/api/listingOptions'
import OptionChips from './components/OptionChips.vue'
import PublishSection from './components/PublishSection.vue'
import GuideImage from '@/components/GuideImage.vue'
import tencentSecurityGuide from '@/assets/tencent-security-guide.webp'
const {
dailyLossOptions,
formatNumber,
loading,
uploading,
router,
publishAgreements,
isQualityOwner,
isQualityOwnerExclusiveRatioActive,
qualityOwnerSaleRatioSelected,
qualityOwnerStatus,
qualityOwnerRatioDiscount,
qualityOwnerSaleRatio,
minAcceleratedSaleRatio,
insuranceFeePercent,
calculatedInsuranceFee,
calculatedTakeHomePrice,
virtualAssetSaleAgreementChecked,
sellerAgreementChecked,
passwordAndDeviceConfirmed,
canPublishAfterAgreements,
showBanRecordRiskHint,
unlockDemulanEnabled,
nextSeasonPassEnabled,
fileInput,
activeUploadKey,
form,
quantityValues,
quantityModes,
selectedSkins,
uploadedScreenshotCount,
requiredScreenshotCount,
isEditMode,
submitButtonText,
serverOptions,
faceOptions,
rankOptions,
insuranceOptions,
levelOptions,
effectiveLoginMethodOptions,
isScanOnlyServer,
regionOptions,
banRecordOptions,
skinGroups,
quantityItems,
screenshotSlots,
priceConfig,
fireLevelPlaceholder,
coinMAmount,
dailyLossMAmount,
calculatedDefaultSaleRatio,
maxAcceleratedSaleRatio,
calculatedCoinBasePrice,
calculatedConsumablePrice,
calculatedSellerPrice,
calculatedRatioText,
calculatedDefaultSaleRatioText,
saleRatioRangeText,
acceleratedSaleRatioPlaceholder,
recommendedDepositAmount,
depositBreakdownItems,
publishTitle,
isDistribution,
hasAcceleratedSaleRatioInput,
isQuantityItemDisabled,
handleSaveDraft,
handleResetDraft,
toggleSkin,
toggleRegion,
triggerUpload,
handleScreenshotUpload,
removeScreenshot,
getScreenshotURLs,
getScreenshotCount,
getScreenshotPreviewURL,
isMultiScreenshotSlot,
canAddScreenshot,
getScreenshotLimitHint,
isAllDayOnline,
onlineTimeRangeHint,
selectAllDayOnline,
handleOnlineStartChange,
handleOnlineEndChange,
handleFireLevelInput,
handleAcceleratedSaleRatioInput,
useRecommendedDeposit,
handleDepositBlur,
setQuantityMode,
clampAcceleratedSaleRatioInput,
useReferenceSaleRatio,
useMaxAcceleratedSaleRatio,
useQualityOwnerSaleRatio,
handleSubmit,
isScreenshotRequired,
} = usePublishForm({
draftKey: 'hfb.pc.publish.draft',
submitSuccessPath: '/seller/listings',
listingGroupChatPathPrefix: '/messages/',
persistBeforeUnload: true,
async confirmReset() {
await ElMessageBox.confirm('将清空当前填写内容和本地草稿。', '重置发布内容', {
confirmButtonText: '重置',
cancelButtonText: '取消',
type: 'warning',
})
},
notifySuccess: message => ElMessage.success(message),
notifyWarning: message => ElMessage.warning(message),
notifyError: message => ElMessage.error(message),
})
const agreementDialogVisible = ref(false)
const activeAgreement = ref<AgreementContent | null>(null)
const activeAgreementTitle = computed(() => activeAgreement.value?.title || '协议内容')
const activeAgreementContent = computed(() => activeAgreement.value?.content || '')
const canChangeGameNameOptions = ['是', '否']
const allHeroOptions = ['是', '否']
const demulanUnlockOptions = ['是', '否']
const nextSeasonPassOptions = ['购买', '不购买']
const earlyMorningResponseOptions = ['是', '否']
// 截图槽位的指导示例图(仅展示、点击放大,不占用上传数量)
const screenshotGuides: Record<string, string> = {
tencentSecurity: tencentSecurityGuide,
}
function toggleSkinOption(value: string | number) {
toggleSkin(String(value))
}
function openPublishAgreement(agreement: AgreementContent) {
activeAgreement.value = agreement
agreementDialogVisible.value = true
}
function selectDailyLoss(value: string | number) {
form.daily_loss_m = Number(value)
}
</script>
<template>
<main class="publish-page">
<div class="publish-layout">
<section class="form-column">
<PublishSection title="基础资料" :description="publishTitle">
<div class="field-row panel-field-row">
<label>区服<span>*</span></label>
<OptionChips
:options="serverOptions"
:model-value="form.server_region"
@select="form.server_region = String($event)"
/>
</div>
<div v-if="faceOptions.length" class="field-row panel-field-row">
<label>是否本人人脸</label>
<OptionChips
:options="faceOptions"
:model-value="form.face_owner"
@select="form.face_owner = String($event)"
/>
</div>
<div class="compact-grid panel-input-grid">
<label class="input-block">
<span>哈夫币/M<b>*</b></span>
<el-input
v-model="form.haf_coin_amount"
type="number"
inputmode="decimal"
min="0"
placeholder="100M 写 100"
/>
</label>
<label class="input-block">
<span>绝密KD</span>
<el-input
v-model="form.secret_kd"
type="number"
inputmode="decimal"
min="0"
step="0.1"
placeholder="填写绝密KD"
/>
</label>
<label class="input-block">
<span>烽火等级<b>*</b></span>
<el-input
:model-value="form.fire_level"
type="number"
inputmode="numeric"
min="0"
:placeholder="fireLevelPlaceholder"
@update:model-value="handleFireLevelInput"
/>
</label>
</div>
<div class="field-row panel-field-row">
<label>段位<span>*</span></label>
<OptionChips
:options="rankOptions"
:model-value="form.rank_level"
@select="form.rank_level = String($event)"
/>
</div>
<div class="field-row panel-field-row">
<label>赛季保险<span>*</span></label>
<OptionChips
:options="insuranceOptions"
:model-value="form.season_insurance"
@select="form.season_insurance = String($event)"
/>
</div>
<div class="level-panel">
<div class="level-row">
<strong><span>*</span>体力</strong>
<div class="level-options">
<button
v-for="opt in levelOptions"
:key="`stamina-${opt}`"
type="button"
class="level-btn"
:class="{ active: form.stamina_level === opt }"
@click="form.stamina_level = opt"
>
{{ opt }}
</button>
</div>
</div>
<div class="level-row">
<strong><span>*</span>负重</strong>
<div class="level-options">
<button
v-for="opt in levelOptions"
:key="`load-${opt}`"
type="button"
class="level-btn"
:class="{ active: form.load_level === opt }"
@click="form.load_level = opt"
>
{{ opt }}
</button>
</div>
</div>
</div>
</PublishSection>
<PublishSection
v-if="quantityItems.length"
title="额外消耗品"
description="收费项会计入发布价格"
>
<div class="quantity-table">
<div class="quantity-header">
<span>物资</span>
<span>单价</span>
<span>数量<b>*</b></span>
<span>计费</span>
<span>状态</span>
</div>
<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"
><span>*</span>{{ item.label }}</strong
>
<small v-if="item.placeholder">{{ item.placeholder }}</small>
</div>
<span class="quantity-price">{{ item.price }}</span>
<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] === '赠送' }"
:disabled="isQuantityItemDisabled(item)"
@click="setQuantityMode(item, '赠送')"
>
赠送
</button>
<button
type="button"
:class="{ active: (quantityModes[item.key] || '收费') === '收费' }"
:disabled="isQuantityItemDisabled(item)"
@click="setQuantityMode(item, '收费')"
>
收费
</button>
</div>
<span class="quantity-status">
{{ isQuantityItemDisabled(item) ? '3*3 已包含' : '可填写' }}
</span>
</div>
</div>
</PublishSection>
<PublishSection title="皮肤与交接">
<div v-if="skinGroups.length" class="skin-groups">
<div v-for="group in skinGroups" :key="group.key" class="skin-group">
<div class="skin-title">
<strong>{{ group.title }}</strong>
</div>
<OptionChips
:options="group.options"
:active-values="selectedSkins"
@select="toggleSkinOption"
/>
</div>
</div>
<div class="field-row login-method-row">
<label>上号方式</label>
<OptionChips
:options="effectiveLoginMethodOptions"
:model-value="form.login_method"
@select="form.login_method = String($event)"
/>
<p class="field-hint">
{{
isScanOnlyServer
? '微信账号仅支持扫码登录,上号方式固定为扫码。'
: '优先推荐账密登录,出售速度通常更快;请结合账号安全情况自行选择。'
}}
</p>
</div>
<div class="field-row panel-field-row can-change-game-name-row">
<label>是否可以修改游戏名<span>*</span></label>
<OptionChips
:options="canChangeGameNameOptions"
:model-value="form.can_change_game_name"
@select="form.can_change_game_name = String($event)"
/>
</div>
<div class="field-row panel-field-row">
<label>是否有全英雄<span>*</span></label>
<OptionChips
:options="allHeroOptions"
:model-value="form.all_hero"
@select="form.all_hero = String($event)"
/>
</div>
<div v-if="unlockDemulanEnabled" class="field-row panel-field-row">
<label>是否解锁德穆兰<span>*</span></label>
<OptionChips
:options="demulanUnlockOptions"
:model-value="form.unlock_demulan"
@select="form.unlock_demulan = String($event)"
/>
</div>
<div v-if="nextSeasonPassEnabled" class="field-row panel-field-row">
<label>下赛季是否购买通行证<span>*</span></label>
<OptionChips
:options="nextSeasonPassOptions"
:model-value="form.next_season_pass"
@select="form.next_season_pass = String($event)"
/>
</div>
<div class="field-row panel-field-row online-time-row">
<label>在线时间<span>*</span></label>
<div class="online-time-body">
<div class="online-time-controls">
<button
type="button"
class="time-chip"
:class="{ active: isAllDayOnline() }"
@click="selectAllDayOnline"
>
全天
</button>
<el-time-picker
v-model="form.online_start"
value-format="HH:mm"
format="HH:mm"
placeholder="开始"
class="time-input"
:clearable="false"
@change="handleOnlineStartChange"
/>
<span class="online-time-sep">至</span>
<em v-if="onlineTimeRangeHint()" class="online-time-next-day">次日</em>
<el-time-picker
v-model="form.online_end"
value-format="HH:mm"
format="HH:mm"
placeholder="结束"
class="time-input"
:class="{ 'is-cross': !!onlineTimeRangeHint() }"
:clearable="false"
@change="handleOnlineEndChange"
/>
</div>
<p v-if="onlineTimeRangeHint()" class="online-time-summary">
{{ onlineTimeRangeHint() }}
</p>
<p class="field-hint">请填写能稳定联系上您的时间,便于扫码、冻结人脸和订单交接。</p>
</div>
</div>
<div class="field-row panel-field-row early-morning-response-row">
<label>凌晨0-2点可响应上号<span>*</span></label>
<OptionChips
:options="earlyMorningResponseOptions"
:model-value="form.early_morning_response"
@select="form.early_morning_response = String($event)"
/>
</div>
<div v-if="banRecordOptions.length" class="field-row panel-field-row">
<label>封禁记录<span>*</span></label>
<OptionChips
:options="banRecordOptions"
:model-value="form.ban_record"
@select="form.ban_record = String($event)"
/>
<p v-if="showBanRecordRiskHint" class="field-hint">
刚出租结算完的账号需冷号七天才可再次出租,IP频繁变动会导致封号。
</p>
</div>
<div v-if="regionOptions.length" class="region-panel login-region-panel">
<div class="region-title">
<strong>常用登录地区</strong>
<span>已选 {{ form.common_regions.length }}</span>
</div>
<div class="region-grid">
<button
v-for="region in regionOptions"
:key="region"
type="button"
class="region-btn"
:class="{ active: form.common_regions.includes(region) }"
@click="toggleRegion(region)"
>
{{ region }}
</button>
</div>
</div>
</PublishSection>
<PublishSection
title="截图材料"
:description="`${uploadedScreenshotCount}/${screenshotSlots.length} 项已上传`"
>
<div class="upload-grid">
<div v-for="slot in screenshotSlots" :key="slot.key" class="upload-item">
<div class="upload-copy">
<strong>{{ slot.label }}<span v-if="isScreenshotRequired(slot)">*</span></strong>
<small>{{ slot.hint }}</small>
<em v-if="getScreenshotLimitHint(slot.key)">{{
getScreenshotLimitHint(slot.key)
}}</em>
<GuideImage
v-if="screenshotGuides[slot.key]"
:src="screenshotGuides[slot.key] || ''"
label="点击查看处罚截图教程"
/>
</div>
<div v-if="getScreenshotCount(slot.key)" class="upload-preview-list">
<div
v-for="(url, index) in getScreenshotURLs(slot.key)"
:key="`${slot.key}-${url}`"
class="upload-preview"
>
<img
:src="getScreenshotPreviewURL(slot.key, index)"
:alt="`${slot.label}${index + 1}`"
/>
<button type="button" @click="removeScreenshot(slot.key, index)">
<el-icon><Delete /></el-icon>
</button>
</div>
<button
v-if="canAddScreenshot(slot.key)"
type="button"
class="upload-add"
:disabled="uploading"
@click="triggerUpload(slot.key)"
>
<el-icon><Picture /></el-icon>
<span>{{
uploading && activeUploadKey === slot.key ? '上传中' : '继续上传'
}}</span>
</button>
</div>
<button
v-else
type="button"
class="upload-add"
:disabled="uploading"
@click="triggerUpload(slot.key)"
>
<el-icon><Picture /></el-icon>
<span>{{ uploading && activeUploadKey === slot.key ? '上传中' : '上传' }}</span>
</button>
</div>
</div>
<input
ref="fileInput"
type="file"
accept="image/jpeg,image/png,image/webp"
:multiple="isMultiScreenshotSlot(activeUploadKey)"
class="hidden-file"
@change="handleScreenshotUpload"
/>
</PublishSection>
<PublishSection title="押金与价格" description="系统按资料自动计算售价">
<div class="compact-grid two">
<label class="input-block">
<span class="deposit-label">
<span class="deposit-label-title">
押金<b>*</b>
<el-popover
v-if="priceConfig.deposit_tooltip"
placement="top-start"
:width="560"
trigger="hover"
popper-class="deposit-guide-popper"
>
<template #reference>
<button type="button" class="deposit-guide-trigger" aria-label="查看押金说明">
<el-icon><InfoFilled /></el-icon>
</button>
</template>
<div class="deposit-guide-content">{{ priceConfig.deposit_tooltip }}</div>
</el-popover>
</span>
<em v-if="priceConfig.deposit_hint">{{ priceConfig.deposit_hint }}</em>
</span>
<div class="deposit-control">
<el-input-number
v-model="form.depositAmountYuan"
:min="0"
:controls="false"
:placeholder="priceConfig.deposit_placeholder"
@blur="handleDepositBlur"
/>
<button class="recommend-button" type="button" @click="useRecommendedDeposit">
使用推荐 ¥{{ formatMoney(recommendedDepositAmount) }}
</button>
</div>
<div class="deposit-breakdown">
<span v-for="item in depositBreakdownItems" :key="`${item.label}-${item.count}`">
{{ item.label }}<template v-if="item.count > 1"> x{{ item.count }}</template> ¥{{
formatMoney(item.amount)
}}
</span>
</div>
</label>
<label class="input-block">
<span>每日损耗<b>*</b></span>
<OptionChips
:options="dailyLossOptions"
:model-value="dailyLossMAmount"
compact
suffix="M/天"
@select="selectDailyLoss"
/>
</label>
</div>
<div class="ratio-panel">
<div class="ratio-reference">
<span>{{ isDistribution ? '主站收号上限' : '参考比例' }}</span>
<strong>{{ calculatedDefaultSaleRatioText }}</strong>
</div>
<p v-if="isDistribution" class="ratio-inherit-hint">
本站可下调收号比例换取更高利润,但不能高于主站比例。当前按站点配置计算。
</p>
<p>{{ saleRatioRangeText }}{{ isDistribution ? '。' : ',比例越高出租速度越快。' }}</p>
<div v-if="!isDistribution" 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
v-if="isQualityOwner && qualityOwnerRatioDiscount > 0"
type="button"
class="ratio-mode-btn ratio-mode-btn--quality"
:class="{ active: qualityOwnerSaleRatioSelected }"
:disabled="qualityOwnerSaleRatio <= 0"
@click="useQualityOwnerSaleRatio"
>
<em>优质号主专享</em>
<strong>基础比例 - {{ formatNumber(qualityOwnerRatioDiscount) }}</strong>
<span>{{
qualityOwnerSaleRatio > 0
? `默认 1元=${formatNumber(qualityOwnerSaleRatio)}万`
: '自动计算'
}}</span>
</button>
<button
type="button"
class="ratio-mode-btn"
:class="{
active:
Number(form.accelerated_sale_ratio) === maxAcceleratedSaleRatio &&
maxAcceleratedSaleRatio > 0,
}"
:disabled="maxAcceleratedSaleRatio <= 0"
@click="useMaxAcceleratedSaleRatio"
>
<strong>最高比例 (加速)</strong>
<span>{{
maxAcceleratedSaleRatio > 0
? `最高 1元=${formatNumber(maxAcceleratedSaleRatio)}万`
: '自动计算'
}}</span>
</button>
<div
class="ratio-custom-card"
:class="{
active:
hasAcceleratedSaleRatioInput() &&
Number(form.accelerated_sale_ratio) !== maxAcceleratedSaleRatio &&
!qualityOwnerSaleRatioSelected,
}"
>
<strong>自定比例</strong>
<el-input-number
:model-value="form.accelerated_sale_ratio"
:min="minAcceleratedSaleRatio || 0"
:controls="false"
:placeholder="acceleratedSaleRatioPlaceholder"
@update:model-value="handleAcceleratedSaleRatioInput"
@blur="clampAcceleratedSaleRatioInput"
/>
</div>
</div>
</div>
<div class="price-grid">
<div class="price-cell">
<span>卖家比例</span>
<strong>{{ calculatedRatioText }}</strong>
</div>
<div class="price-cell">
<span>纯币基础价</span>
<strong>{{
calculatedCoinBasePrice ? `¥${formatMoney(calculatedCoinBasePrice)}` : '--'
}}</strong>
</div>
<div class="price-cell">
<span>额外消耗品</span>
<strong>¥{{ formatMoney(calculatedConsumablePrice) }}</strong>
</div>
<div class="price-cell accent">
<span>发布价格</span>
<strong>{{
calculatedSellerPrice ? `¥${formatMoney(calculatedSellerPrice)}` : '--'
}}</strong>
</div>
</div>
<div
class="premium-coverage-panel"
:class="{
'is-quality-owner': isQualityOwner,
'is-premium-active': form.is_premium,
}"
>
<div class="premium-zone-row">
<div class="premium-zone-copy">
<div class="premium-zone-title">
<strong>极品专区</strong>
<el-popover
v-if="priceConfig.premium_zone_tooltip"
placement="top-start"
:width="560"
trigger="hover"
popper-class="deposit-guide-popper"
>
<template #reference>
<button
type="button"
class="deposit-guide-trigger"
aria-label="查看极品专区说明"
>
<el-icon><InfoFilled /></el-icon>
</button>
</template>
<div class="deposit-guide-content">
{{ priceConfig.premium_zone_tooltip }}
</div>
</el-popover>
<em v-if="isQualityOwner">优质号主已认证</em>
<em v-else class="is-locked">优质号主专享</em>
</div>
<span>
{{
isQualityOwner
? '发布后进入首页极品专区,获得高端账号专属曝光'
: `缴纳 ¥${formatMoney(
(qualityOwnerStatus?.required_deposit_cent || 20000) / 100
)} 保证金后可开通优质号主`
}}
</span>
</div>
<div class="premium-zone-action" :class="{ 'is-active': form.is_premium }">
<span>{{
isQualityOwnerExclusiveRatioActive
? '专享比例已关联专区'
: form.is_premium
? '已开启极品专区'
: isQualityOwner
? '开启极品专区'
: '开通后可发布'
}}</span>
<el-switch
v-model="form.is_premium"
:disabled="!isQualityOwner || isQualityOwnerExclusiveRatioActive"
/>
</div>
</div>
<button
v-if="!isQualityOwner"
type="button"
class="quality-owner-link"
@click="router.push('/profile')"
>
<span>前往个人中心开通优质号主</span>
<el-icon><ArrowRight /></el-icon>
</button>
<div class="coverage-heading">
<strong>选择赔付方案</strong>
<span>保险服务费按实际纯币结算金额计算,额外物品不计入基数</span>
</div>
<div class="coverage-option-grid">
<button
type="button"
class="coverage-option"
:class="{ active: form.compensation_plan === 'normal' }"
@click="form.compensation_plan = 'normal'"
>
<span class="coverage-option-top">
<strong>普通赔付</strong>
<em class="coverage-rate">0%</em>
</span>
<span>按平台规则赔付租金与押金</span>
</button>
<button
type="button"
class="coverage-option"
:class="{ active: form.compensation_plan === 'insurance' }"
@click="form.compensation_plan = 'insurance'"
>
<span class="coverage-option-top">
<strong>无忧租号保险</strong>
<em class="coverage-rate is-insurance">
{{ insuranceFeePercent.toFixed(1) }}%
</em>
</span>
<span>额外保障账号市场价值,费率按实际纯币结算金额扣除</span>
<em v-if="isQualityOwner && form.is_premium" class="coverage-special-rate">
已享极品专区专享费率
</em>
</button>
</div>
<div
class="take-home-preview"
:class="{ 'has-insurance': form.compensation_plan === 'insurance' }"
>
<div>
<span>卖家发布价格</span>
<strong>¥{{ formatMoney(calculatedSellerPrice) }}</strong>
</div>
<div>
<span v-if="form.compensation_plan === 'insurance'">
无忧租号保险({{ insuranceFeePercent.toFixed(1) }}% · 纯币计费)
</span>
<span v-else>普通赔付</span>
<strong :class="{ 'is-deduction': form.compensation_plan === 'insurance' }">
{{
form.compensation_plan === 'insurance'
? `-¥${formatMoney(calculatedInsuranceFee)}`
: '¥0.0'
}}
</strong>
</div>
<div class="take-home-net">
<span>预计到手价格</span>
<strong>¥{{ formatMoney(calculatedTakeHomePrice) }}</strong>
</div>
</div>
<p class="coverage-note">
{{ priceConfig.compensation_note }}
</p>
</div>
<label class="input-block remark">
<span>备注</span>
<el-input
v-model="form.remark"
type="textarea"
:rows="4"
placeholder="如有不可使用的物资,请在此备注,并在买家下单后主动提醒。"
/>
</label>
</PublishSection>
</section>
<aside class="summary-column">
<div class="summary-panel">
<div class="summary-main">
<span>预计到手价格</span>
<strong>{{
calculatedTakeHomePrice ? `¥${formatMoney(calculatedTakeHomePrice)}` : '--'
}}</strong>
</div>
<div class="summary-breakdown">
<div class="summary-breakdown-title">价格明细</div>
<div>
<span>卖家发布价格</span>
<strong>¥{{ formatMoney(calculatedSellerPrice) }}</strong>
</div>
<div>
<span>纯币价格</span>
<strong>{{
calculatedCoinBasePrice ? `¥${formatMoney(calculatedCoinBasePrice)}` : '--'
}}</strong>
</div>
<div>
<span>额外物品价格</span>
<strong>¥{{ formatMoney(calculatedConsumablePrice) }}</strong>
</div>
<div v-if="form.compensation_plan === 'insurance'">
<span>无忧租号保险({{ insuranceFeePercent.toFixed(1) }}% · 纯币计费)</span>
<strong class="is-deduction">-¥{{ formatMoney(calculatedInsuranceFee) }}</strong>
</div>
<div>
<span>押金价格</span>
<strong>{{
form.depositAmountYuan === ''
? '--'
: `¥${formatMoney(Number(form.depositAmountYuan))}`
}}</strong>
</div>
</div>
<div class="summary-list">
<div>
<span>哈夫币</span>
<strong>{{ coinMAmount || '--' }}M</strong>
</div>
<div>
<span>截图材料</span>
<strong>{{ uploadedScreenshotCount }}/{{ requiredScreenshotCount }}</strong>
</div>
</div>
<div class="publish-agreement-panel">
<el-checkbox v-model="virtualAssetSaleAgreementChecked">
<span class="agreement-check-label">
我已阅读并同意
<button
type="button"
class="agreement-link"
:title="publishAgreements.virtual_asset_sale.title"
@click.prevent.stop="openPublishAgreement(publishAgreements.virtual_asset_sale)"
>
《{{ publishAgreements.virtual_asset_sale.title }}》
</button>
</span>
</el-checkbox>
<el-checkbox v-model="sellerAgreementChecked">
<span class="agreement-check-label">
我已阅读并同意
<button
type="button"
class="agreement-link"
:title="publishAgreements.seller_agreement.title"
@click.prevent.stop="openPublishAgreement(publishAgreements.seller_agreement)"
>
《{{ publishAgreements.seller_agreement.title }}》
</button>
</span>
</el-checkbox>
<el-checkbox v-model="passwordAndDeviceConfirmed" class="agreement-notice-checkbox">
<span class="agreement-check-label agreement-notice-label"
>我已知晓:完成后须改密并清除登录设备;若遇封号,须解封后查号方可结算,未经查号即结算者,后续账号问题商行概不负责。</span
>
</el-checkbox>
</div>
<div class="summary-actions">
<el-button
:icon="UploadFilled"
class="btn-publish"
type="primary"
:loading="loading"
:disabled="!canPublishAfterAgreements"
@click="handleSubmit"
>
{{ submitButtonText }}
</el-button>
<el-button
v-if="!isEditMode"
:icon="DocumentChecked"
:disabled="loading"
@click="handleSaveDraft"
>保存草稿</el-button
>
<el-button :icon="RefreshRight" :disabled="loading" @click="handleResetDraft">{{
isEditMode ? '恢复原信息' : '重置草稿'
}}</el-button>
</div>
</div>
</aside>
</div>
<el-dialog
v-model="agreementDialogVisible"
:title="activeAgreementTitle"
width="720px"
class="publish-agreement-dialog"
>
<div class="agreement-dialog-content">{{ activeAgreementContent }}</div>
</el-dialog>
</main>
</template>
<style scoped src="./SellerListingCreateView.css"></style>