优化首页返回定位与发布在线时间表单
移动端首页用列表缓存和内部滚动恢复浏览位置;发布页在线时间改为全天+起止时间单行布局。
This commit is contained in:
@@ -33,12 +33,7 @@ import {
|
||||
} from '@/features/seller/composables/usePublishDraft'
|
||||
import type { PublishForm } from '@/shared/types/publish'
|
||||
import { yuanToCent } from '@/shared/utils/money'
|
||||
import {
|
||||
commonOnlineTimes,
|
||||
dailyLossOptions,
|
||||
formatNumber,
|
||||
roundRatio,
|
||||
} from '@/shared/utils/pricing'
|
||||
import { dailyLossOptions, formatNumber, roundRatio } from '@/shared/utils/pricing'
|
||||
|
||||
const draftSaveDelay = 400
|
||||
const multiScreenshotLimit = 3
|
||||
@@ -104,13 +99,6 @@ export function usePublishForm(options: UsePublishFormOptions) {
|
||||
sellerAgreementChecked.value &&
|
||||
passwordAndDeviceConfirmed.value
|
||||
)
|
||||
const disabledOnlineStartOptions = computed(() =>
|
||||
commonOnlineTimes.filter(time => !canUseOnlineStart(time))
|
||||
)
|
||||
const disabledOnlineEndOptions = computed(() =>
|
||||
commonOnlineTimes.filter(time => !canUseOnlineEnd(time))
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
if (!isEditMode.value) restoreDraft()
|
||||
if (options.persistBeforeUnload) window.addEventListener('beforeunload', handleBeforeUnload)
|
||||
@@ -360,37 +348,11 @@ export function usePublishForm(options: UsePublishFormOptions) {
|
||||
return form.online_start === '00:00' && form.online_end === '23:59'
|
||||
}
|
||||
|
||||
function isOnlineStartPresetActive(value: string) {
|
||||
return !isAllDayOnline() && form.online_start === value
|
||||
}
|
||||
|
||||
function isOnlineEndPresetActive(value: string) {
|
||||
return !isAllDayOnline() && form.online_end === value
|
||||
}
|
||||
|
||||
function selectAllDayOnline() {
|
||||
form.online_start = '00:00'
|
||||
form.online_end = '23:59'
|
||||
}
|
||||
|
||||
function selectOnlineStart(value: string | number) {
|
||||
const time = String(value)
|
||||
if (!canUseOnlineStart(time)) {
|
||||
options.notifyWarning('开始时间必须早于结束时间')
|
||||
return
|
||||
}
|
||||
form.online_start = time
|
||||
}
|
||||
|
||||
function selectOnlineEnd(value: string | number) {
|
||||
const time = String(value)
|
||||
if (!canUseOnlineEnd(time)) {
|
||||
options.notifyWarning('结束时间必须晚于开始时间')
|
||||
return
|
||||
}
|
||||
form.online_end = time
|
||||
}
|
||||
|
||||
function handleOnlineStartChange(value: string | number | null | undefined) {
|
||||
const time = normalizeOnlineTimeValue(value)
|
||||
form.online_start = time
|
||||
@@ -427,22 +389,6 @@ export function usePublishForm(options: UsePublishFormOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
function canUseOnlineStart(value: string) {
|
||||
const start = parseOnlineTime(value)
|
||||
const lastTime = parseOnlineTime('23:59')
|
||||
if (start === null || lastTime === null || start >= lastTime) return false
|
||||
const end = parseOnlineTime(form.online_end)
|
||||
return end === null || start < end
|
||||
}
|
||||
|
||||
function canUseOnlineEnd(value: string) {
|
||||
const end = parseOnlineTime(value)
|
||||
const firstTime = parseOnlineTime('00:00')
|
||||
if (end === null || firstTime === null || end <= firstTime) return false
|
||||
const start = parseOnlineTime(form.online_start)
|
||||
return start === null || end > start
|
||||
}
|
||||
|
||||
function validateOnlineTime() {
|
||||
const start = parseOnlineTime(form.online_start)
|
||||
const end = parseOnlineTime(form.online_end)
|
||||
@@ -944,7 +890,6 @@ export function usePublishForm(options: UsePublishFormOptions) {
|
||||
return {
|
||||
...pricing,
|
||||
dailyLossOptions,
|
||||
commonOnlineTimes,
|
||||
formatNumber,
|
||||
router,
|
||||
loading,
|
||||
@@ -969,19 +914,13 @@ export function usePublishForm(options: UsePublishFormOptions) {
|
||||
pageTitle,
|
||||
submitButtonText,
|
||||
submitLoadingText,
|
||||
disabledOnlineStartOptions,
|
||||
disabledOnlineEndOptions,
|
||||
loadPublishOptions,
|
||||
saveDraft,
|
||||
handleSaveDraft,
|
||||
resetDraftState,
|
||||
handleResetDraft,
|
||||
isAllDayOnline,
|
||||
isOnlineStartPresetActive,
|
||||
isOnlineEndPresetActive,
|
||||
selectAllDayOnline,
|
||||
selectOnlineStart,
|
||||
selectOnlineEnd,
|
||||
handleOnlineStartChange,
|
||||
handleOnlineEndChange,
|
||||
toggleSkin,
|
||||
@@ -1025,5 +964,9 @@ function parseOnlineTime(value: string) {
|
||||
|
||||
function normalizeOnlineTimeValue(value: string | number | null | undefined) {
|
||||
if (value === null || value === undefined) return ''
|
||||
return String(value)
|
||||
const raw = String(value).trim()
|
||||
// 兼容原生 time 可能带秒:08:00:00 -> 08:00
|
||||
const match = /^(\d{1,2}):(\d{2})(?::\d{2})?/.exec(raw)
|
||||
if (!match?.[1] || !match[2]) return raw
|
||||
return `${match[1].padStart(2, '0')}:${match[2]}`
|
||||
}
|
||||
|
||||
@@ -200,24 +200,58 @@
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.time-row,
|
||||
.result-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.time-field-group {
|
||||
display: grid;
|
||||
.online-time-field :deep(.van-field__control) {
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.online-time-controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.online-time-range {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.time-field-group .publish-field {
|
||||
margin-bottom: 0;
|
||||
.online-time-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: 32px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 999px;
|
||||
background: #f8fafc;
|
||||
color: #1e293b;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.time-preset-chips {
|
||||
padding: 0 2px 4px;
|
||||
.online-time-input:focus {
|
||||
outline: none;
|
||||
border-color: #ffb074;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 3px rgba(255, 106, 0, 0.1);
|
||||
}
|
||||
|
||||
.online-time-sep {
|
||||
flex: 0 0 auto;
|
||||
color: #94a3b8;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.level-panel {
|
||||
@@ -832,11 +866,7 @@
|
||||
background: #ff6a00 !important;
|
||||
}
|
||||
|
||||
@media (min-width: 430px) {
|
||||
.time-row {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 370px) {
|
||||
.level-options,
|
||||
|
||||
@@ -10,7 +10,6 @@ import GuideImage from '@/components/GuideImage.vue'
|
||||
import tencentSecurityGuide from '@/assets/tencent-security-guide.webp'
|
||||
|
||||
const {
|
||||
commonOnlineTimes,
|
||||
dailyLossOptions,
|
||||
formatNumber,
|
||||
router,
|
||||
@@ -32,8 +31,6 @@ const {
|
||||
quantityValues,
|
||||
quantityModes,
|
||||
selectedSkins,
|
||||
disabledOnlineStartOptions,
|
||||
disabledOnlineEndOptions,
|
||||
serverOptions,
|
||||
faceOptions,
|
||||
rankOptions,
|
||||
@@ -73,11 +70,7 @@ const {
|
||||
canAddScreenshot,
|
||||
getScreenshotLimitHint,
|
||||
isAllDayOnline,
|
||||
isOnlineStartPresetActive,
|
||||
isOnlineEndPresetActive,
|
||||
selectAllDayOnline,
|
||||
selectOnlineStart,
|
||||
selectOnlineEnd,
|
||||
handleOnlineStartChange,
|
||||
handleOnlineEndChange,
|
||||
handleFireLevelInput,
|
||||
@@ -386,17 +379,9 @@ const screenshotGuides: Record<string, string> = {
|
||||
</template>
|
||||
</van-field>
|
||||
|
||||
<div class="time-row">
|
||||
<div class="time-field-group">
|
||||
<van-field
|
||||
v-model="form.online_start"
|
||||
label="在线开始"
|
||||
required
|
||||
type="time"
|
||||
class="publish-field time-field"
|
||||
@update:model-value="handleOnlineStartChange"
|
||||
/>
|
||||
<div class="radio-group small time-preset-chips">
|
||||
<van-field label="在线时间" required class="publish-field online-time-field">
|
||||
<template #input>
|
||||
<div class="online-time-controls">
|
||||
<button
|
||||
type="button"
|
||||
class="radio-btn"
|
||||
@@ -405,57 +390,26 @@ const screenshotGuides: Record<string, string> = {
|
||||
>
|
||||
全天
|
||||
</button>
|
||||
<button
|
||||
v-for="opt in commonOnlineTimes"
|
||||
:key="`start-${opt}`"
|
||||
type="button"
|
||||
class="radio-btn"
|
||||
:class="{
|
||||
active: isOnlineStartPresetActive(opt),
|
||||
disabled: disabledOnlineStartOptions.includes(opt),
|
||||
}"
|
||||
:disabled="disabledOnlineStartOptions.includes(opt)"
|
||||
@click="selectOnlineStart(opt)"
|
||||
>
|
||||
{{ opt }}
|
||||
</button>
|
||||
<div class="online-time-range">
|
||||
<input
|
||||
v-model="form.online_start"
|
||||
type="time"
|
||||
class="online-time-input"
|
||||
aria-label="在线开始时间"
|
||||
@change="handleOnlineStartChange(form.online_start)"
|
||||
/>
|
||||
<span class="online-time-sep">至</span>
|
||||
<input
|
||||
v-model="form.online_end"
|
||||
type="time"
|
||||
class="online-time-input"
|
||||
aria-label="在线结束时间"
|
||||
@change="handleOnlineEndChange(form.online_end)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="time-field-group">
|
||||
<van-field
|
||||
v-model="form.online_end"
|
||||
label="在线结束"
|
||||
required
|
||||
type="time"
|
||||
class="publish-field time-field"
|
||||
@update:model-value="handleOnlineEndChange"
|
||||
/>
|
||||
<div class="radio-group small time-preset-chips">
|
||||
<button
|
||||
type="button"
|
||||
class="radio-btn"
|
||||
:class="{ active: isAllDayOnline() }"
|
||||
@click="selectAllDayOnline"
|
||||
>
|
||||
全天
|
||||
</button>
|
||||
<button
|
||||
v-for="opt in commonOnlineTimes"
|
||||
:key="`end-${opt}`"
|
||||
type="button"
|
||||
class="radio-btn"
|
||||
:class="{
|
||||
active: isOnlineEndPresetActive(opt),
|
||||
disabled: disabledOnlineEndOptions.includes(opt),
|
||||
}"
|
||||
:disabled="disabledOnlineEndOptions.includes(opt)"
|
||||
@click="selectOnlineEnd(opt)"
|
||||
>
|
||||
{{ opt }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</van-field>
|
||||
<p class="field-hint">
|
||||
此在线时间指的是百分百能够联系上您的时间,若是在此期间联系不上您导致无法上号会扣除您的部分订单金额或上架押金,在线时长太短可能无法上架,请预留充足时间用于扫码以及冻结人脸,请谨慎填写
|
||||
</p>
|
||||
|
||||
@@ -72,7 +72,6 @@
|
||||
.skin-group,
|
||||
.region-panel,
|
||||
.ratio-panel,
|
||||
.time-preset-row,
|
||||
.price-cell {
|
||||
padding: 12px;
|
||||
border-radius: var(--radius-8);
|
||||
@@ -98,7 +97,6 @@
|
||||
|
||||
.field-row label span,
|
||||
.input-block b,
|
||||
.time-preset-row strong span,
|
||||
.quantity-meta span,
|
||||
.upload-copy span {
|
||||
color: var(--color-danger);
|
||||
@@ -178,7 +176,6 @@
|
||||
}
|
||||
|
||||
.level-panel,
|
||||
.time-preset-panel,
|
||||
.skin-groups {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
@@ -371,41 +368,41 @@
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.time-preset-row {
|
||||
display: grid;
|
||||
grid-template-columns: 88px minmax(0, 1fr);
|
||||
gap: 12px;
|
||||
align-items: start;
|
||||
.online-time-row {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.time-preset-row strong {
|
||||
color: var(--color-text-main);
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
line-height: 32px;
|
||||
.online-time-controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.time-preset-content {
|
||||
display: grid;
|
||||
.online-time-range {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.time-preset-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 7px;
|
||||
min-width: 0;
|
||||
.online-time-sep {
|
||||
flex: 0 0 auto;
|
||||
color: #94a3b8;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.time-chip {
|
||||
min-height: 30px;
|
||||
padding: 0 12px;
|
||||
flex: 0 0 auto;
|
||||
min-height: 32px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid #d6dce5;
|
||||
border-radius: 15px;
|
||||
border-radius: 999px;
|
||||
background: #fff;
|
||||
color: #596474;
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
@@ -414,14 +411,23 @@
|
||||
color 0.16s ease;
|
||||
}
|
||||
|
||||
.time-chip:hover {
|
||||
border-color: #c4ccd8;
|
||||
}
|
||||
|
||||
.time-chip.active {
|
||||
border-color: var(--color-orange-primary);
|
||||
background: var(--color-orange-primary);
|
||||
color: #fff;
|
||||
box-shadow: 0 4px 10px rgba(255, 106, 0, 0.22);
|
||||
}
|
||||
|
||||
.time-input {
|
||||
width: 118px;
|
||||
width: 128px;
|
||||
}
|
||||
|
||||
.online-time-range :deep(.el-input__wrapper) {
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.login-region-panel {
|
||||
@@ -927,12 +933,21 @@
|
||||
|
||||
@media (max-width: 860px) {
|
||||
.field-row,
|
||||
.time-preset-row,
|
||||
.quantity-header,
|
||||
.quantity-item {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.online-time-range {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.time-input {
|
||||
flex: 1;
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.field-hint {
|
||||
grid-column: auto;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import GuideImage from '@/components/GuideImage.vue'
|
||||
import tencentSecurityGuide from '@/assets/tencent-security-guide.webp'
|
||||
|
||||
const {
|
||||
commonOnlineTimes,
|
||||
dailyLossOptions,
|
||||
formatNumber,
|
||||
loading,
|
||||
@@ -39,8 +38,6 @@ const {
|
||||
requiredScreenshotCount,
|
||||
isEditMode,
|
||||
submitButtonText,
|
||||
disabledOnlineStartOptions,
|
||||
disabledOnlineEndOptions,
|
||||
serverOptions,
|
||||
faceOptions,
|
||||
rankOptions,
|
||||
@@ -85,8 +82,6 @@ const {
|
||||
getScreenshotLimitHint,
|
||||
isAllDayOnline,
|
||||
selectAllDayOnline,
|
||||
selectOnlineStart,
|
||||
selectOnlineEnd,
|
||||
handleOnlineStartChange,
|
||||
handleOnlineEndChange,
|
||||
handleFireLevelInput,
|
||||
@@ -344,81 +339,51 @@ function selectDailyLoss(value: string | number) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="time-preset-panel">
|
||||
<div class="time-preset-row">
|
||||
<strong>在线开始<span>*</span></strong>
|
||||
<div class="time-preset-content">
|
||||
<div class="field-row panel-field-row online-time-row">
|
||||
<label>在线时间<span>*</span></label>
|
||||
<div class="online-time-controls">
|
||||
<button
|
||||
type="button"
|
||||
class="time-chip"
|
||||
:class="{ active: isAllDayOnline() }"
|
||||
@click="selectAllDayOnline"
|
||||
>
|
||||
全天
|
||||
</button>
|
||||
<div class="online-time-range">
|
||||
<el-time-picker
|
||||
v-model="form.online_start"
|
||||
value-format="HH:mm"
|
||||
format="HH:mm"
|
||||
placeholder="开始时间"
|
||||
placeholder="开始"
|
||||
class="time-input"
|
||||
@change="handleOnlineStartChange"
|
||||
/>
|
||||
<div class="time-preset-actions">
|
||||
<button
|
||||
type="button"
|
||||
class="time-chip"
|
||||
:class="{ active: isAllDayOnline() }"
|
||||
@click="selectAllDayOnline"
|
||||
>
|
||||
全天
|
||||
</button>
|
||||
<OptionChips
|
||||
:options="commonOnlineTimes"
|
||||
:model-value="isAllDayOnline() ? '' : form.online_start"
|
||||
:disabled-values="disabledOnlineStartOptions"
|
||||
key-prefix="start-"
|
||||
@select="selectOnlineStart"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="time-preset-row">
|
||||
<strong>在线结束<span>*</span></strong>
|
||||
<div class="time-preset-content">
|
||||
<span class="online-time-sep">至</span>
|
||||
<el-time-picker
|
||||
v-model="form.online_end"
|
||||
value-format="HH:mm"
|
||||
format="HH:mm"
|
||||
placeholder="结束时间"
|
||||
placeholder="结束"
|
||||
class="time-input"
|
||||
@change="handleOnlineEndChange"
|
||||
/>
|
||||
<div class="time-preset-actions">
|
||||
<button
|
||||
type="button"
|
||||
class="time-chip"
|
||||
:class="{ active: isAllDayOnline() }"
|
||||
@click="selectAllDayOnline"
|
||||
>
|
||||
全天
|
||||
</button>
|
||||
<OptionChips
|
||||
:options="commonOnlineTimes"
|
||||
:model-value="isAllDayOnline() ? '' : form.online_end"
|
||||
:disabled-values="disabledOnlineEndOptions"
|
||||
key-prefix="end-"
|
||||
@select="selectOnlineEnd"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="field-hint">请填写能稳定联系上您的时间,便于扫码、冻结人脸和订单交接。</p>
|
||||
</div>
|
||||
<p class="field-hint">请填写能稳定联系上您的时间,便于扫码、冻结人脸和订单交接。</p>
|
||||
|
||||
<div v-if="banRecordOptions.length" class="field-row">
|
||||
<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>
|
||||
<p v-if="showBanRecordRiskHint && banRecordOptions.length" class="field-hint">
|
||||
刚出租结算完的账号需冷号七天才可再次出租,IP频繁变动会导致封号。
|
||||
</p>
|
||||
|
||||
<div v-if="regionOptions.length" class="region-panel login-region-panel">
|
||||
<div class="region-title">
|
||||
|
||||
Reference in New Issue
Block a user